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-2019, 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 "getopt_long.h" |
42 | |
43 | #include "access/attnum.h" |
44 | #include "access/sysattr.h" |
45 | #include "access/transam.h" |
46 | #include "catalog/pg_aggregate_d.h" |
47 | #include "catalog/pg_am_d.h" |
48 | #include "catalog/pg_attribute_d.h" |
49 | #include "catalog/pg_cast_d.h" |
50 | #include "catalog/pg_class_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_trigger_d.h" |
56 | #include "catalog/pg_type_d.h" |
57 | #include "libpq/libpq-fs.h" |
58 | #include "storage/block.h" |
59 | |
60 | #include "dumputils.h" |
61 | #include "parallel.h" |
62 | #include "pg_backup_db.h" |
63 | #include "pg_backup_utils.h" |
64 | #include "pg_dump.h" |
65 | #include "fe_utils/connect.h" |
66 | #include "fe_utils/string_utils.h" |
67 | |
68 | |
69 | typedef struct |
70 | { |
71 | const char *descr; /* comment for an object */ |
72 | Oid classoid; /* object class (catalog OID) */ |
73 | Oid objoid; /* object OID */ |
74 | int objsubid; /* subobject (table column #) */ |
75 | } ; |
76 | |
77 | typedef struct |
78 | { |
79 | const char *provider; /* label provider of this security label */ |
80 | const char *label; /* security label for an object */ |
81 | Oid classoid; /* object class (catalog OID) */ |
82 | Oid objoid; /* object OID */ |
83 | int objsubid; /* subobject (table column #) */ |
84 | } SecLabelItem; |
85 | |
86 | typedef enum OidOptions |
87 | { |
88 | zeroAsOpaque = 1, |
89 | zeroAsAny = 2, |
90 | zeroAsStar = 4, |
91 | zeroAsNone = 8 |
92 | } OidOptions; |
93 | |
94 | /* global decls */ |
95 | static bool dosync = true; /* Issue fsync() to make dump durable on disk. */ |
96 | |
97 | /* subquery used to convert user ID (eg, datdba) to user name */ |
98 | static const char *username_subquery; |
99 | |
100 | /* |
101 | * For 8.0 and earlier servers, pulled from pg_database, for 8.1+ we use |
102 | * FirstNormalObjectId - 1. |
103 | */ |
104 | static Oid g_last_builtin_oid; /* value of the last builtin oid */ |
105 | |
106 | /* The specified names/patterns should to match at least one entity */ |
107 | static int strict_names = 0; |
108 | |
109 | /* |
110 | * Object inclusion/exclusion lists |
111 | * |
112 | * The string lists record the patterns given by command-line switches, |
113 | * which we then convert to lists of OIDs of matching objects. |
114 | */ |
115 | static SimpleStringList schema_include_patterns = {NULL, NULL}; |
116 | static SimpleOidList schema_include_oids = {NULL, NULL}; |
117 | static SimpleStringList schema_exclude_patterns = {NULL, NULL}; |
118 | static SimpleOidList schema_exclude_oids = {NULL, NULL}; |
119 | |
120 | static SimpleStringList table_include_patterns = {NULL, NULL}; |
121 | static SimpleOidList table_include_oids = {NULL, NULL}; |
122 | static SimpleStringList table_exclude_patterns = {NULL, NULL}; |
123 | static SimpleOidList table_exclude_oids = {NULL, NULL}; |
124 | static SimpleStringList tabledata_exclude_patterns = {NULL, NULL}; |
125 | static SimpleOidList tabledata_exclude_oids = {NULL, NULL}; |
126 | |
127 | |
128 | char g_opaque_type[10]; /* name for the opaque type */ |
129 | |
130 | /* placeholders for the delimiters for comments */ |
131 | char [10]; |
132 | char [10]; |
133 | |
134 | static const CatalogId nilCatalogId = {0, 0}; |
135 | |
136 | /* override for standard extra_float_digits setting */ |
137 | static bool = false; |
138 | static int ; |
139 | |
140 | /* |
141 | * The default number of rows per INSERT when |
142 | * --inserts is specified without --rows-per-insert |
143 | */ |
144 | #define DUMP_DEFAULT_ROWS_PER_INSERT 1 |
145 | |
146 | /* |
147 | * Macro for producing quoted, schema-qualified name of a dumpable object. |
148 | */ |
149 | #define fmtQualifiedDumpable(obj) \ |
150 | fmtQualifiedId((obj)->dobj.namespace->dobj.name, \ |
151 | (obj)->dobj.name) |
152 | |
153 | static void help(const char *progname); |
154 | static void setup_connection(Archive *AH, |
155 | const char *dumpencoding, const char *dumpsnapshot, |
156 | char *use_role); |
157 | static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode); |
158 | static void expand_schema_name_patterns(Archive *fout, |
159 | SimpleStringList *patterns, |
160 | SimpleOidList *oids, |
161 | bool strict_names); |
162 | static void expand_table_name_patterns(Archive *fout, |
163 | SimpleStringList *patterns, |
164 | SimpleOidList *oids, |
165 | bool strict_names); |
166 | static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid); |
167 | static void dumpTableData(Archive *fout, TableDataInfo *tdinfo); |
168 | static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo); |
169 | static void guessConstraintInheritance(TableInfo *tblinfo, int numTables); |
170 | static void dumpComment(Archive *fout, const char *type, const char *name, |
171 | const char *namespace, const char *owner, |
172 | CatalogId catalogId, int subid, DumpId dumpId); |
173 | static int findComments(Archive *fout, Oid classoid, Oid objoid, |
174 | CommentItem **items); |
175 | static int collectComments(Archive *fout, CommentItem **items); |
176 | static void dumpSecLabel(Archive *fout, const char *type, const char *name, |
177 | const char *namespace, const char *owner, |
178 | CatalogId catalogId, int subid, DumpId dumpId); |
179 | static int findSecLabels(Archive *fout, Oid classoid, Oid objoid, |
180 | SecLabelItem **items); |
181 | static int collectSecLabels(Archive *fout, SecLabelItem **items); |
182 | static void dumpDumpableObject(Archive *fout, DumpableObject *dobj); |
183 | static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo); |
184 | static void dumpExtension(Archive *fout, ExtensionInfo *extinfo); |
185 | static void dumpType(Archive *fout, TypeInfo *tyinfo); |
186 | static void dumpBaseType(Archive *fout, TypeInfo *tyinfo); |
187 | static void dumpEnumType(Archive *fout, TypeInfo *tyinfo); |
188 | static void dumpRangeType(Archive *fout, TypeInfo *tyinfo); |
189 | static void dumpUndefinedType(Archive *fout, TypeInfo *tyinfo); |
190 | static void dumpDomain(Archive *fout, TypeInfo *tyinfo); |
191 | static void dumpCompositeType(Archive *fout, TypeInfo *tyinfo); |
192 | static void dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo); |
193 | static void dumpShellType(Archive *fout, ShellTypeInfo *stinfo); |
194 | static void dumpProcLang(Archive *fout, ProcLangInfo *plang); |
195 | static void dumpFunc(Archive *fout, FuncInfo *finfo); |
196 | static void dumpCast(Archive *fout, CastInfo *cast); |
197 | static void dumpTransform(Archive *fout, TransformInfo *transform); |
198 | static void dumpOpr(Archive *fout, OprInfo *oprinfo); |
199 | static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo); |
200 | static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo); |
201 | static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo); |
202 | static void dumpCollation(Archive *fout, CollInfo *collinfo); |
203 | static void dumpConversion(Archive *fout, ConvInfo *convinfo); |
204 | static void dumpRule(Archive *fout, RuleInfo *rinfo); |
205 | static void dumpAgg(Archive *fout, AggInfo *agginfo); |
206 | static void dumpTrigger(Archive *fout, TriggerInfo *tginfo); |
207 | static void dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo); |
208 | static void dumpTable(Archive *fout, TableInfo *tbinfo); |
209 | static void dumpTableSchema(Archive *fout, TableInfo *tbinfo); |
210 | static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo); |
211 | static void dumpSequence(Archive *fout, TableInfo *tbinfo); |
212 | static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo); |
213 | static void dumpIndex(Archive *fout, IndxInfo *indxinfo); |
214 | static void dumpIndexAttach(Archive *fout, IndexAttachInfo *attachinfo); |
215 | static void dumpStatisticsExt(Archive *fout, StatsExtInfo *statsextinfo); |
216 | static void dumpConstraint(Archive *fout, ConstraintInfo *coninfo); |
217 | static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo); |
218 | static void dumpTSParser(Archive *fout, TSParserInfo *prsinfo); |
219 | static void dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo); |
220 | static void dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo); |
221 | static void dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo); |
222 | static void dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo); |
223 | static void dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo); |
224 | static void dumpUserMappings(Archive *fout, |
225 | const char *servername, const char *namespace, |
226 | const char *owner, CatalogId catalogId, DumpId dumpId); |
227 | static void dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo); |
228 | |
229 | static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, |
230 | const char *type, const char *name, const char *subname, |
231 | const char *nspname, const char *owner, |
232 | const char *acls, const char *racls, |
233 | const char *initacls, const char *initracls); |
234 | |
235 | static void getDependencies(Archive *fout); |
236 | static void BuildArchiveDependencies(Archive *fout); |
237 | static void findDumpableDependencies(ArchiveHandle *AH, DumpableObject *dobj, |
238 | DumpId **dependencies, int *nDeps, int *allocDeps); |
239 | |
240 | static DumpableObject *createBoundaryObjects(void); |
241 | static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, |
242 | DumpableObject *boundaryObjs); |
243 | |
244 | static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo); |
245 | static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind); |
246 | static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo); |
247 | static void buildMatViewRefreshDependencies(Archive *fout); |
248 | static void getTableDataFKConstraints(void); |
249 | static char *format_function_arguments(FuncInfo *finfo, char *funcargs, |
250 | bool is_agg); |
251 | static char *format_function_arguments_old(Archive *fout, |
252 | FuncInfo *finfo, int nallargs, |
253 | char **allargtypes, |
254 | char **argmodes, |
255 | char **argnames); |
256 | static char *format_function_signature(Archive *fout, |
257 | FuncInfo *finfo, bool honor_quotes); |
258 | static char *convertRegProcReference(Archive *fout, |
259 | const char *proc); |
260 | static char *getFormattedOperatorName(Archive *fout, const char *oproid); |
261 | static char *convertTSFunction(Archive *fout, Oid funcOid); |
262 | static Oid findLastBuiltinOid_V71(Archive *fout); |
263 | static char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts); |
264 | static void getBlobs(Archive *fout); |
265 | static void dumpBlob(Archive *fout, BlobInfo *binfo); |
266 | static int dumpBlobs(Archive *fout, void *arg); |
267 | static void dumpPolicy(Archive *fout, PolicyInfo *polinfo); |
268 | static void dumpPublication(Archive *fout, PublicationInfo *pubinfo); |
269 | static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo); |
270 | static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo); |
271 | static void dumpDatabase(Archive *AH); |
272 | static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf, |
273 | const char *dbname, Oid dboid); |
274 | static void dumpEncoding(Archive *AH); |
275 | static void dumpStdStrings(Archive *AH); |
276 | static void dumpSearchPath(Archive *AH); |
277 | static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout, |
278 | PQExpBuffer upgrade_buffer, |
279 | Oid pg_type_oid, |
280 | bool force_array_type); |
281 | static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout, |
282 | PQExpBuffer upgrade_buffer, Oid pg_rel_oid); |
283 | static void binary_upgrade_set_pg_class_oids(Archive *fout, |
284 | PQExpBuffer upgrade_buffer, |
285 | Oid pg_class_oid, bool is_index); |
286 | static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, |
287 | DumpableObject *dobj, |
288 | const char *objtype, |
289 | const char *objname, |
290 | const char *objnamespace); |
291 | static const char *getAttrName(int attrnum, TableInfo *tblInfo); |
292 | static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer); |
293 | static bool nonemptyReloptions(const char *reloptions); |
294 | static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, |
295 | const char *prefix, Archive *fout); |
296 | static char *get_synchronized_snapshot(Archive *fout); |
297 | static void setupDumpWorker(Archive *AHX); |
298 | static TableInfo *getRootTableInfo(TableInfo *tbinfo); |
299 | |
300 | |
301 | int |
302 | main(int argc, char **argv) |
303 | { |
304 | int c; |
305 | const char *filename = NULL; |
306 | const char *format = "p" ; |
307 | TableInfo *tblinfo; |
308 | int numTables; |
309 | DumpableObject **dobjs; |
310 | int numObjs; |
311 | DumpableObject *boundaryObjs; |
312 | int i; |
313 | int optindex; |
314 | char *endptr; |
315 | RestoreOptions *ropt; |
316 | Archive *fout; /* the script file */ |
317 | bool g_verbose = false; |
318 | const char *dumpencoding = NULL; |
319 | const char *dumpsnapshot = NULL; |
320 | char *use_role = NULL; |
321 | long rowsPerInsert; |
322 | int numWorkers = 1; |
323 | trivalue prompt_password = TRI_DEFAULT; |
324 | int compressLevel = -1; |
325 | int plainText = 0; |
326 | ArchiveFormat archiveFormat = archUnknown; |
327 | ArchiveMode archiveMode; |
328 | |
329 | static DumpOptions dopt; |
330 | |
331 | static struct option long_options[] = { |
332 | {"data-only" , no_argument, NULL, 'a'}, |
333 | {"blobs" , no_argument, NULL, 'b'}, |
334 | {"no-blobs" , no_argument, NULL, 'B'}, |
335 | {"clean" , no_argument, NULL, 'c'}, |
336 | {"create" , no_argument, NULL, 'C'}, |
337 | {"dbname" , required_argument, NULL, 'd'}, |
338 | {"file" , required_argument, NULL, 'f'}, |
339 | {"format" , required_argument, NULL, 'F'}, |
340 | {"host" , required_argument, NULL, 'h'}, |
341 | {"jobs" , 1, NULL, 'j'}, |
342 | {"no-reconnect" , no_argument, NULL, 'R'}, |
343 | {"no-owner" , no_argument, NULL, 'O'}, |
344 | {"port" , required_argument, NULL, 'p'}, |
345 | {"schema" , required_argument, NULL, 'n'}, |
346 | {"exclude-schema" , required_argument, NULL, 'N'}, |
347 | {"schema-only" , no_argument, NULL, 's'}, |
348 | {"superuser" , required_argument, NULL, 'S'}, |
349 | {"table" , required_argument, NULL, 't'}, |
350 | {"exclude-table" , required_argument, NULL, 'T'}, |
351 | {"no-password" , no_argument, NULL, 'w'}, |
352 | {"password" , no_argument, NULL, 'W'}, |
353 | {"username" , required_argument, NULL, 'U'}, |
354 | {"verbose" , no_argument, NULL, 'v'}, |
355 | {"no-privileges" , no_argument, NULL, 'x'}, |
356 | {"no-acl" , no_argument, NULL, 'x'}, |
357 | {"compress" , required_argument, NULL, 'Z'}, |
358 | {"encoding" , required_argument, NULL, 'E'}, |
359 | {"help" , no_argument, NULL, '?'}, |
360 | {"version" , no_argument, NULL, 'V'}, |
361 | |
362 | /* |
363 | * the following options don't have an equivalent short option letter |
364 | */ |
365 | {"attribute-inserts" , no_argument, &dopt.column_inserts, 1}, |
366 | {"binary-upgrade" , no_argument, &dopt.binary_upgrade, 1}, |
367 | {"column-inserts" , no_argument, &dopt.column_inserts, 1}, |
368 | {"disable-dollar-quoting" , no_argument, &dopt.disable_dollar_quoting, 1}, |
369 | {"disable-triggers" , no_argument, &dopt.disable_triggers, 1}, |
370 | {"enable-row-security" , no_argument, &dopt.enable_row_security, 1}, |
371 | {"exclude-table-data" , required_argument, NULL, 4}, |
372 | {"extra-float-digits" , required_argument, NULL, 8}, |
373 | {"if-exists" , no_argument, &dopt.if_exists, 1}, |
374 | {"inserts" , no_argument, NULL, 9}, |
375 | {"lock-wait-timeout" , required_argument, NULL, 2}, |
376 | {"no-tablespaces" , no_argument, &dopt.outputNoTablespaces, 1}, |
377 | {"quote-all-identifiers" , no_argument, "e_all_identifiers, 1}, |
378 | {"load-via-partition-root" , no_argument, &dopt.load_via_partition_root, 1}, |
379 | {"role" , required_argument, NULL, 3}, |
380 | {"section" , required_argument, NULL, 5}, |
381 | {"serializable-deferrable" , no_argument, &dopt.serializable_deferrable, 1}, |
382 | {"snapshot" , required_argument, NULL, 6}, |
383 | {"strict-names" , no_argument, &strict_names, 1}, |
384 | {"use-set-session-authorization" , no_argument, &dopt.use_setsessauth, 1}, |
385 | {"no-comments" , no_argument, &dopt.no_comments, 1}, |
386 | {"no-publications" , no_argument, &dopt.no_publications, 1}, |
387 | {"no-security-labels" , no_argument, &dopt.no_security_labels, 1}, |
388 | {"no-synchronized-snapshots" , no_argument, &dopt.no_synchronized_snapshots, 1}, |
389 | {"no-unlogged-table-data" , no_argument, &dopt.no_unlogged_table_data, 1}, |
390 | {"no-subscriptions" , no_argument, &dopt.no_subscriptions, 1}, |
391 | {"no-sync" , no_argument, NULL, 7}, |
392 | {"on-conflict-do-nothing" , no_argument, &dopt.do_nothing, 1}, |
393 | {"rows-per-insert" , required_argument, NULL, 10}, |
394 | |
395 | {NULL, 0, NULL, 0} |
396 | }; |
397 | |
398 | pg_logging_init(argv[0]); |
399 | pg_logging_set_level(PG_LOG_WARNING); |
400 | set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump" )); |
401 | |
402 | /* |
403 | * Initialize what we need for parallel execution, especially for thread |
404 | * support on Windows. |
405 | */ |
406 | init_parallel_dump_utils(); |
407 | |
408 | strcpy(g_comment_start, "-- " ); |
409 | g_comment_end[0] = '\0'; |
410 | strcpy(g_opaque_type, "opaque" ); |
411 | |
412 | progname = get_progname(argv[0]); |
413 | |
414 | if (argc > 1) |
415 | { |
416 | if (strcmp(argv[1], "--help" ) == 0 || strcmp(argv[1], "-?" ) == 0) |
417 | { |
418 | help(progname); |
419 | exit_nicely(0); |
420 | } |
421 | if (strcmp(argv[1], "--version" ) == 0 || strcmp(argv[1], "-V" ) == 0) |
422 | { |
423 | puts("pg_dump (PostgreSQL) " PG_VERSION); |
424 | exit_nicely(0); |
425 | } |
426 | } |
427 | |
428 | InitDumpOptions(&dopt); |
429 | |
430 | while ((c = getopt_long(argc, argv, "abBcCd:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:" , |
431 | long_options, &optindex)) != -1) |
432 | { |
433 | switch (c) |
434 | { |
435 | case 'a': /* Dump data only */ |
436 | dopt.dataOnly = true; |
437 | break; |
438 | |
439 | case 'b': /* Dump blobs */ |
440 | dopt.outputBlobs = true; |
441 | break; |
442 | |
443 | case 'B': /* Don't dump blobs */ |
444 | dopt.dontOutputBlobs = true; |
445 | break; |
446 | |
447 | case 'c': /* clean (i.e., drop) schema prior to create */ |
448 | dopt.outputClean = 1; |
449 | break; |
450 | |
451 | case 'C': /* Create DB */ |
452 | dopt.outputCreateDB = 1; |
453 | break; |
454 | |
455 | case 'd': /* database name */ |
456 | dopt.dbname = pg_strdup(optarg); |
457 | break; |
458 | |
459 | case 'E': /* Dump encoding */ |
460 | dumpencoding = pg_strdup(optarg); |
461 | break; |
462 | |
463 | case 'f': |
464 | filename = pg_strdup(optarg); |
465 | break; |
466 | |
467 | case 'F': |
468 | format = pg_strdup(optarg); |
469 | break; |
470 | |
471 | case 'h': /* server host */ |
472 | dopt.pghost = pg_strdup(optarg); |
473 | break; |
474 | |
475 | case 'j': /* number of dump jobs */ |
476 | numWorkers = atoi(optarg); |
477 | break; |
478 | |
479 | case 'n': /* include schema(s) */ |
480 | simple_string_list_append(&schema_include_patterns, optarg); |
481 | dopt.include_everything = false; |
482 | break; |
483 | |
484 | case 'N': /* exclude schema(s) */ |
485 | simple_string_list_append(&schema_exclude_patterns, optarg); |
486 | break; |
487 | |
488 | case 'O': /* Don't reconnect to match owner */ |
489 | dopt.outputNoOwner = 1; |
490 | break; |
491 | |
492 | case 'p': /* server port */ |
493 | dopt.pgport = pg_strdup(optarg); |
494 | break; |
495 | |
496 | case 'R': |
497 | /* no-op, still accepted for backwards compatibility */ |
498 | break; |
499 | |
500 | case 's': /* dump schema only */ |
501 | dopt.schemaOnly = true; |
502 | break; |
503 | |
504 | case 'S': /* Username for superuser in plain text output */ |
505 | dopt.outputSuperuser = pg_strdup(optarg); |
506 | break; |
507 | |
508 | case 't': /* include table(s) */ |
509 | simple_string_list_append(&table_include_patterns, optarg); |
510 | dopt.include_everything = false; |
511 | break; |
512 | |
513 | case 'T': /* exclude table(s) */ |
514 | simple_string_list_append(&table_exclude_patterns, optarg); |
515 | break; |
516 | |
517 | case 'U': |
518 | dopt.username = pg_strdup(optarg); |
519 | break; |
520 | |
521 | case 'v': /* verbose */ |
522 | g_verbose = true; |
523 | pg_logging_set_level(PG_LOG_INFO); |
524 | break; |
525 | |
526 | case 'w': |
527 | prompt_password = TRI_NO; |
528 | break; |
529 | |
530 | case 'W': |
531 | prompt_password = TRI_YES; |
532 | break; |
533 | |
534 | case 'x': /* skip ACL dump */ |
535 | dopt.aclsSkip = true; |
536 | break; |
537 | |
538 | case 'Z': /* Compression Level */ |
539 | compressLevel = atoi(optarg); |
540 | if (compressLevel < 0 || compressLevel > 9) |
541 | { |
542 | pg_log_error("compression level must be in range 0..9" ); |
543 | exit_nicely(1); |
544 | } |
545 | break; |
546 | |
547 | case 0: |
548 | /* This covers the long options. */ |
549 | break; |
550 | |
551 | case 2: /* lock-wait-timeout */ |
552 | dopt.lockWaitTimeout = pg_strdup(optarg); |
553 | break; |
554 | |
555 | case 3: /* SET ROLE */ |
556 | use_role = pg_strdup(optarg); |
557 | break; |
558 | |
559 | case 4: /* exclude table(s) data */ |
560 | simple_string_list_append(&tabledata_exclude_patterns, optarg); |
561 | break; |
562 | |
563 | case 5: /* section */ |
564 | set_dump_section(optarg, &dopt.dumpSections); |
565 | break; |
566 | |
567 | case 6: /* snapshot */ |
568 | dumpsnapshot = pg_strdup(optarg); |
569 | break; |
570 | |
571 | case 7: /* no-sync */ |
572 | dosync = false; |
573 | break; |
574 | |
575 | case 8: |
576 | have_extra_float_digits = true; |
577 | extra_float_digits = atoi(optarg); |
578 | if (extra_float_digits < -15 || extra_float_digits > 3) |
579 | { |
580 | pg_log_error("extra_float_digits must be in range -15..3" ); |
581 | exit_nicely(1); |
582 | } |
583 | break; |
584 | |
585 | case 9: /* inserts */ |
586 | |
587 | /* |
588 | * dump_inserts also stores --rows-per-insert, careful not to |
589 | * overwrite that. |
590 | */ |
591 | if (dopt.dump_inserts == 0) |
592 | dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT; |
593 | break; |
594 | |
595 | case 10: /* rows per insert */ |
596 | errno = 0; |
597 | rowsPerInsert = strtol(optarg, &endptr, 10); |
598 | |
599 | if (endptr == optarg || *endptr != '\0' || |
600 | rowsPerInsert <= 0 || rowsPerInsert > INT_MAX || |
601 | errno == ERANGE) |
602 | { |
603 | pg_log_error("rows-per-insert must be in range %d..%d" , |
604 | 1, INT_MAX); |
605 | exit_nicely(1); |
606 | } |
607 | dopt.dump_inserts = (int) rowsPerInsert; |
608 | break; |
609 | |
610 | default: |
611 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), progname); |
612 | exit_nicely(1); |
613 | } |
614 | } |
615 | |
616 | /* |
617 | * Non-option argument specifies database name as long as it wasn't |
618 | * already specified with -d / --dbname |
619 | */ |
620 | if (optind < argc && dopt.dbname == NULL) |
621 | dopt.dbname = argv[optind++]; |
622 | |
623 | /* Complain if any arguments remain */ |
624 | if (optind < argc) |
625 | { |
626 | pg_log_error("too many command-line arguments (first is \"%s\")" , |
627 | argv[optind]); |
628 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), |
629 | progname); |
630 | exit_nicely(1); |
631 | } |
632 | |
633 | /* --column-inserts implies --inserts */ |
634 | if (dopt.column_inserts && dopt.dump_inserts == 0) |
635 | dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT; |
636 | |
637 | /* |
638 | * Binary upgrade mode implies dumping sequence data even in schema-only |
639 | * mode. This is not exposed as a separate option, but kept separate |
640 | * internally for clarity. |
641 | */ |
642 | if (dopt.binary_upgrade) |
643 | dopt.sequence_data = 1; |
644 | |
645 | if (dopt.dataOnly && dopt.schemaOnly) |
646 | { |
647 | pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together" ); |
648 | exit_nicely(1); |
649 | } |
650 | |
651 | if (dopt.dataOnly && dopt.outputClean) |
652 | { |
653 | pg_log_error("options -c/--clean and -a/--data-only cannot be used together" ); |
654 | exit_nicely(1); |
655 | } |
656 | |
657 | if (dopt.if_exists && !dopt.outputClean) |
658 | fatal("option --if-exists requires option -c/--clean" ); |
659 | |
660 | /* |
661 | * --inserts are already implied above if --column-inserts or |
662 | * --rows-per-insert were specified. |
663 | */ |
664 | if (dopt.do_nothing && dopt.dump_inserts == 0) |
665 | fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts" ); |
666 | |
667 | /* Identify archive format to emit */ |
668 | archiveFormat = parseArchiveFormat(format, &archiveMode); |
669 | |
670 | /* archiveFormat specific setup */ |
671 | if (archiveFormat == archNull) |
672 | plainText = 1; |
673 | |
674 | /* Custom and directory formats are compressed by default, others not */ |
675 | if (compressLevel == -1) |
676 | { |
677 | #ifdef HAVE_LIBZ |
678 | if (archiveFormat == archCustom || archiveFormat == archDirectory) |
679 | compressLevel = Z_DEFAULT_COMPRESSION; |
680 | else |
681 | #endif |
682 | compressLevel = 0; |
683 | } |
684 | |
685 | #ifndef HAVE_LIBZ |
686 | if (compressLevel != 0) |
687 | pg_log_warning("requested compression not available in this installation -- archive will be uncompressed" ); |
688 | compressLevel = 0; |
689 | #endif |
690 | |
691 | /* |
692 | * If emitting an archive format, we always want to emit a DATABASE item, |
693 | * in case --create is specified at pg_restore time. |
694 | */ |
695 | if (!plainText) |
696 | dopt.outputCreateDB = 1; |
697 | |
698 | /* |
699 | * On Windows we can only have at most MAXIMUM_WAIT_OBJECTS (= 64 usually) |
700 | * parallel jobs because that's the maximum limit for the |
701 | * WaitForMultipleObjects() call. |
702 | */ |
703 | if (numWorkers <= 0 |
704 | #ifdef WIN32 |
705 | || numWorkers > MAXIMUM_WAIT_OBJECTS |
706 | #endif |
707 | ) |
708 | fatal("invalid number of parallel jobs" ); |
709 | |
710 | /* Parallel backup only in the directory archive format so far */ |
711 | if (archiveFormat != archDirectory && numWorkers > 1) |
712 | fatal("parallel backup only supported by the directory format" ); |
713 | |
714 | /* Open the output file */ |
715 | fout = CreateArchive(filename, archiveFormat, compressLevel, dosync, |
716 | archiveMode, setupDumpWorker); |
717 | |
718 | /* Make dump options accessible right away */ |
719 | SetArchiveOptions(fout, &dopt, NULL); |
720 | |
721 | /* Register the cleanup hook */ |
722 | on_exit_close_archive(fout); |
723 | |
724 | /* Let the archiver know how noisy to be */ |
725 | fout->verbose = g_verbose; |
726 | |
727 | |
728 | /* |
729 | * We allow the server to be back to 8.0, and up to any minor release of |
730 | * our own major version. (See also version check in pg_dumpall.c.) |
731 | */ |
732 | fout->minRemoteVersion = 80000; |
733 | fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99; |
734 | |
735 | fout->numWorkers = numWorkers; |
736 | |
737 | /* |
738 | * Open the database using the Archiver, so it knows about it. Errors mean |
739 | * death. |
740 | */ |
741 | ConnectDatabase(fout, dopt.dbname, dopt.pghost, dopt.pgport, dopt.username, prompt_password); |
742 | setup_connection(fout, dumpencoding, dumpsnapshot, use_role); |
743 | |
744 | /* |
745 | * Disable security label support if server version < v9.1.x (prevents |
746 | * access to nonexistent pg_seclabel catalog) |
747 | */ |
748 | if (fout->remoteVersion < 90100) |
749 | dopt.no_security_labels = 1; |
750 | |
751 | /* |
752 | * On hot standbys, never try to dump unlogged table data, since it will |
753 | * just throw an error. |
754 | */ |
755 | if (fout->isStandby) |
756 | dopt.no_unlogged_table_data = true; |
757 | |
758 | /* Select the appropriate subquery to convert user IDs to names */ |
759 | if (fout->remoteVersion >= 80100) |
760 | username_subquery = "SELECT rolname FROM pg_catalog.pg_roles WHERE oid =" ; |
761 | else |
762 | username_subquery = "SELECT usename FROM pg_catalog.pg_user WHERE usesysid =" ; |
763 | |
764 | /* check the version for the synchronized snapshots feature */ |
765 | if (numWorkers > 1 && fout->remoteVersion < 90200 |
766 | && !dopt.no_synchronized_snapshots) |
767 | fatal("Synchronized snapshots are not supported by this server version.\n" |
768 | "Run with --no-synchronized-snapshots instead if you do not need\n" |
769 | "synchronized snapshots." ); |
770 | |
771 | /* check the version when a snapshot is explicitly specified by user */ |
772 | if (dumpsnapshot && fout->remoteVersion < 90200) |
773 | fatal("Exported snapshots are not supported by this server version." ); |
774 | |
775 | /* |
776 | * Find the last built-in OID, if needed (prior to 8.1) |
777 | * |
778 | * With 8.1 and above, we can just use FirstNormalObjectId - 1. |
779 | */ |
780 | if (fout->remoteVersion < 80100) |
781 | g_last_builtin_oid = findLastBuiltinOid_V71(fout); |
782 | else |
783 | g_last_builtin_oid = FirstNormalObjectId - 1; |
784 | |
785 | pg_log_info("last built-in OID is %u" , g_last_builtin_oid); |
786 | |
787 | /* Expand schema selection patterns into OID lists */ |
788 | if (schema_include_patterns.head != NULL) |
789 | { |
790 | expand_schema_name_patterns(fout, &schema_include_patterns, |
791 | &schema_include_oids, |
792 | strict_names); |
793 | if (schema_include_oids.head == NULL) |
794 | fatal("no matching schemas were found" ); |
795 | } |
796 | expand_schema_name_patterns(fout, &schema_exclude_patterns, |
797 | &schema_exclude_oids, |
798 | false); |
799 | /* non-matching exclusion patterns aren't an error */ |
800 | |
801 | /* Expand table selection patterns into OID lists */ |
802 | if (table_include_patterns.head != NULL) |
803 | { |
804 | expand_table_name_patterns(fout, &table_include_patterns, |
805 | &table_include_oids, |
806 | strict_names); |
807 | if (table_include_oids.head == NULL) |
808 | fatal("no matching tables were found" ); |
809 | } |
810 | expand_table_name_patterns(fout, &table_exclude_patterns, |
811 | &table_exclude_oids, |
812 | false); |
813 | |
814 | expand_table_name_patterns(fout, &tabledata_exclude_patterns, |
815 | &tabledata_exclude_oids, |
816 | false); |
817 | |
818 | /* non-matching exclusion patterns aren't an error */ |
819 | |
820 | /* |
821 | * Dumping blobs is the default for dumps where an inclusion switch is not |
822 | * used (an "include everything" dump). -B can be used to exclude blobs |
823 | * from those dumps. -b can be used to include blobs even when an |
824 | * inclusion switch is used. |
825 | * |
826 | * -s means "schema only" and blobs are data, not schema, so we never |
827 | * include blobs when -s is used. |
828 | */ |
829 | if (dopt.include_everything && !dopt.schemaOnly && !dopt.dontOutputBlobs) |
830 | dopt.outputBlobs = true; |
831 | |
832 | /* |
833 | * Now scan the database and create DumpableObject structs for all the |
834 | * objects we intend to dump. |
835 | */ |
836 | tblinfo = getSchemaData(fout, &numTables); |
837 | |
838 | if (fout->remoteVersion < 80400) |
839 | guessConstraintInheritance(tblinfo, numTables); |
840 | |
841 | if (!dopt.schemaOnly) |
842 | { |
843 | getTableData(&dopt, tblinfo, numTables, 0); |
844 | buildMatViewRefreshDependencies(fout); |
845 | if (dopt.dataOnly) |
846 | getTableDataFKConstraints(); |
847 | } |
848 | |
849 | if (dopt.schemaOnly && dopt.sequence_data) |
850 | getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE); |
851 | |
852 | /* |
853 | * In binary-upgrade mode, we do not have to worry about the actual blob |
854 | * data or the associated metadata that resides in the pg_largeobject and |
855 | * pg_largeobject_metadata tables, respectively. |
856 | * |
857 | * However, we do need to collect blob information as there may be |
858 | * comments or other information on blobs that we do need to dump out. |
859 | */ |
860 | if (dopt.outputBlobs || dopt.binary_upgrade) |
861 | getBlobs(fout); |
862 | |
863 | /* |
864 | * Collect dependency data to assist in ordering the objects. |
865 | */ |
866 | getDependencies(fout); |
867 | |
868 | /* Lastly, create dummy objects to represent the section boundaries */ |
869 | boundaryObjs = createBoundaryObjects(); |
870 | |
871 | /* Get pointers to all the known DumpableObjects */ |
872 | getDumpableObjects(&dobjs, &numObjs); |
873 | |
874 | /* |
875 | * Add dummy dependencies to enforce the dump section ordering. |
876 | */ |
877 | addBoundaryDependencies(dobjs, numObjs, boundaryObjs); |
878 | |
879 | /* |
880 | * Sort the objects into a safe dump order (no forward references). |
881 | * |
882 | * We rely on dependency information to help us determine a safe order, so |
883 | * the initial sort is mostly for cosmetic purposes: we sort by name to |
884 | * ensure that logically identical schemas will dump identically. |
885 | */ |
886 | sortDumpableObjectsByTypeName(dobjs, numObjs); |
887 | |
888 | sortDumpableObjects(dobjs, numObjs, |
889 | boundaryObjs[0].dumpId, boundaryObjs[1].dumpId); |
890 | |
891 | /* |
892 | * Create archive TOC entries for all the objects to be dumped, in a safe |
893 | * order. |
894 | */ |
895 | |
896 | /* First the special ENCODING, STDSTRINGS, and SEARCHPATH entries. */ |
897 | dumpEncoding(fout); |
898 | dumpStdStrings(fout); |
899 | dumpSearchPath(fout); |
900 | |
901 | /* The database items are always next, unless we don't want them at all */ |
902 | if (dopt.outputCreateDB) |
903 | dumpDatabase(fout); |
904 | |
905 | /* Now the rearrangeable objects. */ |
906 | for (i = 0; i < numObjs; i++) |
907 | dumpDumpableObject(fout, dobjs[i]); |
908 | |
909 | /* |
910 | * Set up options info to ensure we dump what we want. |
911 | */ |
912 | ropt = NewRestoreOptions(); |
913 | ropt->filename = filename; |
914 | |
915 | /* if you change this list, see dumpOptionsFromRestoreOptions */ |
916 | ropt->dropSchema = dopt.outputClean; |
917 | ropt->dataOnly = dopt.dataOnly; |
918 | ropt->schemaOnly = dopt.schemaOnly; |
919 | ropt->if_exists = dopt.if_exists; |
920 | ropt->column_inserts = dopt.column_inserts; |
921 | ropt->dumpSections = dopt.dumpSections; |
922 | ropt->aclsSkip = dopt.aclsSkip; |
923 | ropt->superuser = dopt.outputSuperuser; |
924 | ropt->createDB = dopt.outputCreateDB; |
925 | ropt->noOwner = dopt.outputNoOwner; |
926 | ropt->noTablespace = dopt.outputNoTablespaces; |
927 | ropt->disable_triggers = dopt.disable_triggers; |
928 | ropt->use_setsessauth = dopt.use_setsessauth; |
929 | ropt->disable_dollar_quoting = dopt.disable_dollar_quoting; |
930 | ropt->dump_inserts = dopt.dump_inserts; |
931 | ropt->no_comments = dopt.no_comments; |
932 | ropt->no_publications = dopt.no_publications; |
933 | ropt->no_security_labels = dopt.no_security_labels; |
934 | ropt->no_subscriptions = dopt.no_subscriptions; |
935 | ropt->lockWaitTimeout = dopt.lockWaitTimeout; |
936 | ropt->include_everything = dopt.include_everything; |
937 | ropt->enable_row_security = dopt.enable_row_security; |
938 | ropt->sequence_data = dopt.sequence_data; |
939 | ropt->binary_upgrade = dopt.binary_upgrade; |
940 | |
941 | if (compressLevel == -1) |
942 | ropt->compression = 0; |
943 | else |
944 | ropt->compression = compressLevel; |
945 | |
946 | ropt->suppressDumpWarnings = true; /* We've already shown them */ |
947 | |
948 | SetArchiveOptions(fout, &dopt, ropt); |
949 | |
950 | /* Mark which entries should be output */ |
951 | ProcessArchiveRestoreOptions(fout); |
952 | |
953 | /* |
954 | * The archive's TOC entries are now marked as to which ones will actually |
955 | * be output, so we can set up their dependency lists properly. This isn't |
956 | * necessary for plain-text output, though. |
957 | */ |
958 | if (!plainText) |
959 | BuildArchiveDependencies(fout); |
960 | |
961 | /* |
962 | * And finally we can do the actual output. |
963 | * |
964 | * Note: for non-plain-text output formats, the output file is written |
965 | * inside CloseArchive(). This is, um, bizarre; but not worth changing |
966 | * right now. |
967 | */ |
968 | if (plainText) |
969 | RestoreArchive(fout); |
970 | |
971 | CloseArchive(fout); |
972 | |
973 | exit_nicely(0); |
974 | } |
975 | |
976 | |
977 | static void |
978 | help(const char *progname) |
979 | { |
980 | printf(_("%s dumps a database as a text file or to other formats.\n\n" ), progname); |
981 | printf(_("Usage:\n" )); |
982 | printf(_(" %s [OPTION]... [DBNAME]\n" ), progname); |
983 | |
984 | printf(_("\nGeneral options:\n" )); |
985 | printf(_(" -f, --file=FILENAME output file or directory name\n" )); |
986 | printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n" |
987 | " plain text (default))\n" )); |
988 | printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n" )); |
989 | printf(_(" -v, --verbose verbose mode\n" )); |
990 | printf(_(" -V, --version output version information, then exit\n" )); |
991 | printf(_(" -Z, --compress=0-9 compression level for compressed formats\n" )); |
992 | printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n" )); |
993 | printf(_(" --no-sync do not wait for changes to be written safely to disk\n" )); |
994 | printf(_(" -?, --help show this help, then exit\n" )); |
995 | |
996 | printf(_("\nOptions controlling the output content:\n" )); |
997 | printf(_(" -a, --data-only dump only the data, not the schema\n" )); |
998 | printf(_(" -b, --blobs include large objects in dump\n" )); |
999 | printf(_(" -B, --no-blobs exclude large objects in dump\n" )); |
1000 | printf(_(" -c, --clean clean (drop) database objects before recreating\n" )); |
1001 | printf(_(" -C, --create include commands to create database in dump\n" )); |
1002 | printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n" )); |
1003 | printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n" )); |
1004 | printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n" )); |
1005 | printf(_(" -O, --no-owner skip restoration of object ownership in\n" |
1006 | " plain-text format\n" )); |
1007 | printf(_(" -s, --schema-only dump only the schema, no data\n" )); |
1008 | printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n" )); |
1009 | printf(_(" -t, --table=PATTERN dump the specified table(s) only\n" )); |
1010 | printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n" )); |
1011 | printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n" )); |
1012 | printf(_(" --binary-upgrade for use by upgrade utilities only\n" )); |
1013 | printf(_(" --column-inserts dump data as INSERT commands with column names\n" )); |
1014 | printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n" )); |
1015 | printf(_(" --disable-triggers disable triggers during data-only restore\n" )); |
1016 | printf(_(" --enable-row-security enable row security (dump only content user has\n" |
1017 | " access to)\n" )); |
1018 | printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n" )); |
1019 | printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n" )); |
1020 | printf(_(" --if-exists use IF EXISTS when dropping objects\n" )); |
1021 | printf(_(" --inserts dump data as INSERT commands, rather than COPY\n" )); |
1022 | printf(_(" --load-via-partition-root load partitions via the root table\n" )); |
1023 | printf(_(" --no-comments do not dump comments\n" )); |
1024 | printf(_(" --no-publications do not dump publications\n" )); |
1025 | printf(_(" --no-security-labels do not dump security label assignments\n" )); |
1026 | printf(_(" --no-subscriptions do not dump subscriptions\n" )); |
1027 | printf(_(" --no-synchronized-snapshots do not use synchronized snapshots in parallel jobs\n" )); |
1028 | printf(_(" --no-tablespaces do not dump tablespace assignments\n" )); |
1029 | printf(_(" --no-unlogged-table-data do not dump unlogged table data\n" )); |
1030 | printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n" )); |
1031 | printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n" )); |
1032 | printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n" )); |
1033 | printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n" )); |
1034 | printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n" )); |
1035 | printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n" )); |
1036 | printf(_(" --strict-names require table and/or schema include patterns to\n" |
1037 | " match at least one entity each\n" )); |
1038 | printf(_(" --use-set-session-authorization\n" |
1039 | " use SET SESSION AUTHORIZATION commands instead of\n" |
1040 | " ALTER OWNER commands to set ownership\n" )); |
1041 | |
1042 | printf(_("\nConnection options:\n" )); |
1043 | printf(_(" -d, --dbname=DBNAME database to dump\n" )); |
1044 | printf(_(" -h, --host=HOSTNAME database server host or socket directory\n" )); |
1045 | printf(_(" -p, --port=PORT database server port number\n" )); |
1046 | printf(_(" -U, --username=NAME connect as specified database user\n" )); |
1047 | printf(_(" -w, --no-password never prompt for password\n" )); |
1048 | printf(_(" -W, --password force password prompt (should happen automatically)\n" )); |
1049 | printf(_(" --role=ROLENAME do SET ROLE before dump\n" )); |
1050 | |
1051 | printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n" |
1052 | "variable value is used.\n\n" )); |
1053 | printf(_("Report bugs to <pgsql-bugs@lists.postgresql.org>.\n" )); |
1054 | } |
1055 | |
1056 | static void |
1057 | setup_connection(Archive *AH, const char *dumpencoding, |
1058 | const char *dumpsnapshot, char *use_role) |
1059 | { |
1060 | DumpOptions *dopt = AH->dopt; |
1061 | PGconn *conn = GetConnection(AH); |
1062 | const char *std_strings; |
1063 | |
1064 | PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL)); |
1065 | |
1066 | /* |
1067 | * Set the client encoding if requested. |
1068 | */ |
1069 | if (dumpencoding) |
1070 | { |
1071 | if (PQsetClientEncoding(conn, dumpencoding) < 0) |
1072 | fatal("invalid client encoding \"%s\" specified" , |
1073 | dumpencoding); |
1074 | } |
1075 | |
1076 | /* |
1077 | * Get the active encoding and the standard_conforming_strings setting, so |
1078 | * we know how to escape strings. |
1079 | */ |
1080 | AH->encoding = PQclientEncoding(conn); |
1081 | |
1082 | std_strings = PQparameterStatus(conn, "standard_conforming_strings" ); |
1083 | AH->std_strings = (std_strings && strcmp(std_strings, "on" ) == 0); |
1084 | |
1085 | /* |
1086 | * Set the role if requested. In a parallel dump worker, we'll be passed |
1087 | * use_role == NULL, but AH->use_role is already set (if user specified it |
1088 | * originally) and we should use that. |
1089 | */ |
1090 | if (!use_role && AH->use_role) |
1091 | use_role = AH->use_role; |
1092 | |
1093 | /* Set the role if requested */ |
1094 | if (use_role && AH->remoteVersion >= 80100) |
1095 | { |
1096 | PQExpBuffer query = createPQExpBuffer(); |
1097 | |
1098 | appendPQExpBuffer(query, "SET ROLE %s" , fmtId(use_role)); |
1099 | ExecuteSqlStatement(AH, query->data); |
1100 | destroyPQExpBuffer(query); |
1101 | |
1102 | /* save it for possible later use by parallel workers */ |
1103 | if (!AH->use_role) |
1104 | AH->use_role = pg_strdup(use_role); |
1105 | } |
1106 | |
1107 | /* Set the datestyle to ISO to ensure the dump's portability */ |
1108 | ExecuteSqlStatement(AH, "SET DATESTYLE = ISO" ); |
1109 | |
1110 | /* Likewise, avoid using sql_standard intervalstyle */ |
1111 | if (AH->remoteVersion >= 80400) |
1112 | ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES" ); |
1113 | |
1114 | /* |
1115 | * Use an explicitly specified extra_float_digits if it has been provided. |
1116 | * Otherwise, set extra_float_digits so that we can dump float data |
1117 | * exactly (given correctly implemented float I/O code, anyway). |
1118 | */ |
1119 | if (have_extra_float_digits) |
1120 | { |
1121 | PQExpBuffer q = createPQExpBuffer(); |
1122 | |
1123 | appendPQExpBuffer(q, "SET extra_float_digits TO %d" , |
1124 | extra_float_digits); |
1125 | ExecuteSqlStatement(AH, q->data); |
1126 | destroyPQExpBuffer(q); |
1127 | } |
1128 | else if (AH->remoteVersion >= 90000) |
1129 | ExecuteSqlStatement(AH, "SET extra_float_digits TO 3" ); |
1130 | else |
1131 | ExecuteSqlStatement(AH, "SET extra_float_digits TO 2" ); |
1132 | |
1133 | /* |
1134 | * If synchronized scanning is supported, disable it, to prevent |
1135 | * unpredictable changes in row ordering across a dump and reload. |
1136 | */ |
1137 | if (AH->remoteVersion >= 80300) |
1138 | ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off" ); |
1139 | |
1140 | /* |
1141 | * Disable timeouts if supported. |
1142 | */ |
1143 | ExecuteSqlStatement(AH, "SET statement_timeout = 0" ); |
1144 | if (AH->remoteVersion >= 90300) |
1145 | ExecuteSqlStatement(AH, "SET lock_timeout = 0" ); |
1146 | if (AH->remoteVersion >= 90600) |
1147 | ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0" ); |
1148 | |
1149 | /* |
1150 | * Quote all identifiers, if requested. |
1151 | */ |
1152 | if (quote_all_identifiers && AH->remoteVersion >= 90100) |
1153 | ExecuteSqlStatement(AH, "SET quote_all_identifiers = true" ); |
1154 | |
1155 | /* |
1156 | * Adjust row-security mode, if supported. |
1157 | */ |
1158 | if (AH->remoteVersion >= 90500) |
1159 | { |
1160 | if (dopt->enable_row_security) |
1161 | ExecuteSqlStatement(AH, "SET row_security = on" ); |
1162 | else |
1163 | ExecuteSqlStatement(AH, "SET row_security = off" ); |
1164 | } |
1165 | |
1166 | /* |
1167 | * Start transaction-snapshot mode transaction to dump consistent data. |
1168 | */ |
1169 | ExecuteSqlStatement(AH, "BEGIN" ); |
1170 | if (AH->remoteVersion >= 90100) |
1171 | { |
1172 | /* |
1173 | * To support the combination of serializable_deferrable with the jobs |
1174 | * option we use REPEATABLE READ for the worker connections that are |
1175 | * passed a snapshot. As long as the snapshot is acquired in a |
1176 | * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a |
1177 | * REPEATABLE READ transaction provides the appropriate integrity |
1178 | * guarantees. This is a kluge, but safe for back-patching. |
1179 | */ |
1180 | if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL) |
1181 | ExecuteSqlStatement(AH, |
1182 | "SET TRANSACTION ISOLATION LEVEL " |
1183 | "SERIALIZABLE, READ ONLY, DEFERRABLE" ); |
1184 | else |
1185 | ExecuteSqlStatement(AH, |
1186 | "SET TRANSACTION ISOLATION LEVEL " |
1187 | "REPEATABLE READ, READ ONLY" ); |
1188 | } |
1189 | else |
1190 | { |
1191 | ExecuteSqlStatement(AH, |
1192 | "SET TRANSACTION ISOLATION LEVEL " |
1193 | "SERIALIZABLE, READ ONLY" ); |
1194 | } |
1195 | |
1196 | /* |
1197 | * If user specified a snapshot to use, select that. In a parallel dump |
1198 | * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id |
1199 | * is already set (if the server can handle it) and we should use that. |
1200 | */ |
1201 | if (dumpsnapshot) |
1202 | AH->sync_snapshot_id = pg_strdup(dumpsnapshot); |
1203 | |
1204 | if (AH->sync_snapshot_id) |
1205 | { |
1206 | PQExpBuffer query = createPQExpBuffer(); |
1207 | |
1208 | appendPQExpBuffer(query, "SET TRANSACTION SNAPSHOT " ); |
1209 | appendStringLiteralConn(query, AH->sync_snapshot_id, conn); |
1210 | ExecuteSqlStatement(AH, query->data); |
1211 | destroyPQExpBuffer(query); |
1212 | } |
1213 | else if (AH->numWorkers > 1 && |
1214 | AH->remoteVersion >= 90200 && |
1215 | !dopt->no_synchronized_snapshots) |
1216 | { |
1217 | if (AH->isStandby && AH->remoteVersion < 100000) |
1218 | fatal("Synchronized snapshots on standby servers are not supported by this server version.\n" |
1219 | "Run with --no-synchronized-snapshots instead if you do not need\n" |
1220 | "synchronized snapshots." ); |
1221 | |
1222 | |
1223 | AH->sync_snapshot_id = get_synchronized_snapshot(AH); |
1224 | } |
1225 | } |
1226 | |
1227 | /* Set up connection for a parallel worker process */ |
1228 | static void |
1229 | setupDumpWorker(Archive *AH) |
1230 | { |
1231 | /* |
1232 | * We want to re-select all the same values the master connection is |
1233 | * using. We'll have inherited directly-usable values in |
1234 | * AH->sync_snapshot_id and AH->use_role, but we need to translate the |
1235 | * inherited encoding value back to a string to pass to setup_connection. |
1236 | */ |
1237 | setup_connection(AH, |
1238 | pg_encoding_to_char(AH->encoding), |
1239 | NULL, |
1240 | NULL); |
1241 | } |
1242 | |
1243 | static char * |
1244 | get_synchronized_snapshot(Archive *fout) |
1245 | { |
1246 | char *query = "SELECT pg_catalog.pg_export_snapshot()" ; |
1247 | char *result; |
1248 | PGresult *res; |
1249 | |
1250 | res = ExecuteSqlQueryForSingleRow(fout, query); |
1251 | result = pg_strdup(PQgetvalue(res, 0, 0)); |
1252 | PQclear(res); |
1253 | |
1254 | return result; |
1255 | } |
1256 | |
1257 | static ArchiveFormat |
1258 | parseArchiveFormat(const char *format, ArchiveMode *mode) |
1259 | { |
1260 | ArchiveFormat archiveFormat; |
1261 | |
1262 | *mode = archModeWrite; |
1263 | |
1264 | if (pg_strcasecmp(format, "a" ) == 0 || pg_strcasecmp(format, "append" ) == 0) |
1265 | { |
1266 | /* This is used by pg_dumpall, and is not documented */ |
1267 | archiveFormat = archNull; |
1268 | *mode = archModeAppend; |
1269 | } |
1270 | else if (pg_strcasecmp(format, "c" ) == 0) |
1271 | archiveFormat = archCustom; |
1272 | else if (pg_strcasecmp(format, "custom" ) == 0) |
1273 | archiveFormat = archCustom; |
1274 | else if (pg_strcasecmp(format, "d" ) == 0) |
1275 | archiveFormat = archDirectory; |
1276 | else if (pg_strcasecmp(format, "directory" ) == 0) |
1277 | archiveFormat = archDirectory; |
1278 | else if (pg_strcasecmp(format, "p" ) == 0) |
1279 | archiveFormat = archNull; |
1280 | else if (pg_strcasecmp(format, "plain" ) == 0) |
1281 | archiveFormat = archNull; |
1282 | else if (pg_strcasecmp(format, "t" ) == 0) |
1283 | archiveFormat = archTar; |
1284 | else if (pg_strcasecmp(format, "tar" ) == 0) |
1285 | archiveFormat = archTar; |
1286 | else |
1287 | fatal("invalid output format \"%s\" specified" , format); |
1288 | return archiveFormat; |
1289 | } |
1290 | |
1291 | /* |
1292 | * Find the OIDs of all schemas matching the given list of patterns, |
1293 | * and append them to the given OID list. |
1294 | */ |
1295 | static void |
1296 | expand_schema_name_patterns(Archive *fout, |
1297 | SimpleStringList *patterns, |
1298 | SimpleOidList *oids, |
1299 | bool strict_names) |
1300 | { |
1301 | PQExpBuffer query; |
1302 | PGresult *res; |
1303 | SimpleStringListCell *cell; |
1304 | int i; |
1305 | |
1306 | if (patterns->head == NULL) |
1307 | return; /* nothing to do */ |
1308 | |
1309 | query = createPQExpBuffer(); |
1310 | |
1311 | /* |
1312 | * The loop below runs multiple SELECTs might sometimes result in |
1313 | * duplicate entries in the OID list, but we don't care. |
1314 | */ |
1315 | |
1316 | for (cell = patterns->head; cell; cell = cell->next) |
1317 | { |
1318 | appendPQExpBuffer(query, |
1319 | "SELECT oid FROM pg_catalog.pg_namespace n\n" ); |
1320 | processSQLNamePattern(GetConnection(fout), query, cell->val, false, |
1321 | false, NULL, "n.nspname" , NULL, NULL); |
1322 | |
1323 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
1324 | if (strict_names && PQntuples(res) == 0) |
1325 | fatal("no matching schemas were found for pattern \"%s\"" , cell->val); |
1326 | |
1327 | for (i = 0; i < PQntuples(res); i++) |
1328 | { |
1329 | simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); |
1330 | } |
1331 | |
1332 | PQclear(res); |
1333 | resetPQExpBuffer(query); |
1334 | } |
1335 | |
1336 | destroyPQExpBuffer(query); |
1337 | } |
1338 | |
1339 | /* |
1340 | * Find the OIDs of all tables matching the given list of patterns, |
1341 | * and append them to the given OID list. See also expand_dbname_patterns() |
1342 | * in pg_dumpall.c |
1343 | */ |
1344 | static void |
1345 | expand_table_name_patterns(Archive *fout, |
1346 | SimpleStringList *patterns, SimpleOidList *oids, |
1347 | bool strict_names) |
1348 | { |
1349 | PQExpBuffer query; |
1350 | PGresult *res; |
1351 | SimpleStringListCell *cell; |
1352 | int i; |
1353 | |
1354 | if (patterns->head == NULL) |
1355 | return; /* nothing to do */ |
1356 | |
1357 | query = createPQExpBuffer(); |
1358 | |
1359 | /* |
1360 | * this might sometimes result in duplicate entries in the OID list, but |
1361 | * we don't care. |
1362 | */ |
1363 | |
1364 | for (cell = patterns->head; cell; cell = cell->next) |
1365 | { |
1366 | /* |
1367 | * Query must remain ABSOLUTELY devoid of unqualified names. This |
1368 | * would be unnecessary given a pg_table_is_visible() variant taking a |
1369 | * search_path argument. |
1370 | */ |
1371 | appendPQExpBuffer(query, |
1372 | "SELECT c.oid" |
1373 | "\nFROM pg_catalog.pg_class c" |
1374 | "\n LEFT JOIN pg_catalog.pg_namespace n" |
1375 | "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace" |
1376 | "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY" |
1377 | "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n" , |
1378 | RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, |
1379 | RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, |
1380 | RELKIND_PARTITIONED_TABLE); |
1381 | processSQLNamePattern(GetConnection(fout), query, cell->val, true, |
1382 | false, "n.nspname" , "c.relname" , NULL, |
1383 | "pg_catalog.pg_table_is_visible(c.oid)" ); |
1384 | |
1385 | ExecuteSqlStatement(fout, "RESET search_path" ); |
1386 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
1387 | PQclear(ExecuteSqlQueryForSingleRow(fout, |
1388 | ALWAYS_SECURE_SEARCH_PATH_SQL)); |
1389 | if (strict_names && PQntuples(res) == 0) |
1390 | fatal("no matching tables were found for pattern \"%s\"" , cell->val); |
1391 | |
1392 | for (i = 0; i < PQntuples(res); i++) |
1393 | { |
1394 | simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); |
1395 | } |
1396 | |
1397 | PQclear(res); |
1398 | resetPQExpBuffer(query); |
1399 | } |
1400 | |
1401 | destroyPQExpBuffer(query); |
1402 | } |
1403 | |
1404 | /* |
1405 | * checkExtensionMembership |
1406 | * Determine whether object is an extension member, and if so, |
1407 | * record an appropriate dependency and set the object's dump flag. |
1408 | * |
1409 | * It's important to call this for each object that could be an extension |
1410 | * member. Generally, we integrate this with determining the object's |
1411 | * to-be-dumped-ness, since extension membership overrides other rules for that. |
1412 | * |
1413 | * Returns true if object is an extension member, else false. |
1414 | */ |
1415 | static bool |
1416 | checkExtensionMembership(DumpableObject *dobj, Archive *fout) |
1417 | { |
1418 | ExtensionInfo *ext = findOwningExtension(dobj->catId); |
1419 | |
1420 | if (ext == NULL) |
1421 | return false; |
1422 | |
1423 | dobj->ext_member = true; |
1424 | |
1425 | /* Record dependency so that getDependencies needn't deal with that */ |
1426 | addObjectDependency(dobj, ext->dobj.dumpId); |
1427 | |
1428 | /* |
1429 | * In 9.6 and above, mark the member object to have any non-initial ACL, |
1430 | * policies, and security labels dumped. |
1431 | * |
1432 | * Note that any initial ACLs (see pg_init_privs) will be removed when we |
1433 | * extract the information about the object. We don't provide support for |
1434 | * initial policies and security labels and it seems unlikely for those to |
1435 | * ever exist, but we may have to revisit this later. |
1436 | * |
1437 | * Prior to 9.6, we do not include any extension member components. |
1438 | * |
1439 | * In binary upgrades, we still dump all components of the members |
1440 | * individually, since the idea is to exactly reproduce the database |
1441 | * contents rather than replace the extension contents with something |
1442 | * different. |
1443 | */ |
1444 | if (fout->dopt->binary_upgrade) |
1445 | dobj->dump = ext->dobj.dump; |
1446 | else |
1447 | { |
1448 | if (fout->remoteVersion < 90600) |
1449 | dobj->dump = DUMP_COMPONENT_NONE; |
1450 | else |
1451 | dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL | |
1452 | DUMP_COMPONENT_SECLABEL | |
1453 | DUMP_COMPONENT_POLICY); |
1454 | } |
1455 | |
1456 | return true; |
1457 | } |
1458 | |
1459 | /* |
1460 | * selectDumpableNamespace: policy-setting subroutine |
1461 | * Mark a namespace as to be dumped or not |
1462 | */ |
1463 | static void |
1464 | selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout) |
1465 | { |
1466 | /* |
1467 | * If specific tables are being dumped, do not dump any complete |
1468 | * namespaces. If specific namespaces are being dumped, dump just those |
1469 | * namespaces. Otherwise, dump all non-system namespaces. |
1470 | */ |
1471 | if (table_include_oids.head != NULL) |
1472 | nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE; |
1473 | else if (schema_include_oids.head != NULL) |
1474 | nsinfo->dobj.dump_contains = nsinfo->dobj.dump = |
1475 | simple_oid_list_member(&schema_include_oids, |
1476 | nsinfo->dobj.catId.oid) ? |
1477 | DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; |
1478 | else if (fout->remoteVersion >= 90600 && |
1479 | strcmp(nsinfo->dobj.name, "pg_catalog" ) == 0) |
1480 | { |
1481 | /* |
1482 | * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if |
1483 | * they are interesting (and not the original ACLs which were set at |
1484 | * initdb time, see pg_init_privs). |
1485 | */ |
1486 | nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL; |
1487 | } |
1488 | else if (strncmp(nsinfo->dobj.name, "pg_" , 3) == 0 || |
1489 | strcmp(nsinfo->dobj.name, "information_schema" ) == 0) |
1490 | { |
1491 | /* Other system schemas don't get dumped */ |
1492 | nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE; |
1493 | } |
1494 | else if (strcmp(nsinfo->dobj.name, "public" ) == 0) |
1495 | { |
1496 | /* |
1497 | * The public schema is a strange beast that sits in a sort of |
1498 | * no-mans-land between being a system object and a user object. We |
1499 | * don't want to dump creation or comment commands for it, because |
1500 | * that complicates matters for non-superuser use of pg_dump. But we |
1501 | * should dump any ACL changes that have occurred for it, and of |
1502 | * course we should dump contained objects. |
1503 | */ |
1504 | nsinfo->dobj.dump = DUMP_COMPONENT_ACL; |
1505 | nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL; |
1506 | } |
1507 | else |
1508 | nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL; |
1509 | |
1510 | /* |
1511 | * In any case, a namespace can be excluded by an exclusion switch |
1512 | */ |
1513 | if (nsinfo->dobj.dump_contains && |
1514 | simple_oid_list_member(&schema_exclude_oids, |
1515 | nsinfo->dobj.catId.oid)) |
1516 | nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE; |
1517 | |
1518 | /* |
1519 | * If the schema belongs to an extension, allow extension membership to |
1520 | * override the dump decision for the schema itself. However, this does |
1521 | * not change dump_contains, so this won't change what we do with objects |
1522 | * within the schema. (If they belong to the extension, they'll get |
1523 | * suppressed by it, otherwise not.) |
1524 | */ |
1525 | (void) checkExtensionMembership(&nsinfo->dobj, fout); |
1526 | } |
1527 | |
1528 | /* |
1529 | * selectDumpableTable: policy-setting subroutine |
1530 | * Mark a table as to be dumped or not |
1531 | */ |
1532 | static void |
1533 | selectDumpableTable(TableInfo *tbinfo, Archive *fout) |
1534 | { |
1535 | if (checkExtensionMembership(&tbinfo->dobj, fout)) |
1536 | return; /* extension membership overrides all else */ |
1537 | |
1538 | /* |
1539 | * If specific tables are being dumped, dump just those tables; else, dump |
1540 | * according to the parent namespace's dump flag. |
1541 | */ |
1542 | if (table_include_oids.head != NULL) |
1543 | tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids, |
1544 | tbinfo->dobj.catId.oid) ? |
1545 | DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; |
1546 | else |
1547 | tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains; |
1548 | |
1549 | /* |
1550 | * In any case, a table can be excluded by an exclusion switch |
1551 | */ |
1552 | if (tbinfo->dobj.dump && |
1553 | simple_oid_list_member(&table_exclude_oids, |
1554 | tbinfo->dobj.catId.oid)) |
1555 | tbinfo->dobj.dump = DUMP_COMPONENT_NONE; |
1556 | } |
1557 | |
1558 | /* |
1559 | * selectDumpableType: policy-setting subroutine |
1560 | * Mark a type as to be dumped or not |
1561 | * |
1562 | * If it's a table's rowtype or an autogenerated array type, we also apply a |
1563 | * special type code to facilitate sorting into the desired order. (We don't |
1564 | * want to consider those to be ordinary types because that would bring tables |
1565 | * up into the datatype part of the dump order.) We still set the object's |
1566 | * dump flag; that's not going to cause the dummy type to be dumped, but we |
1567 | * need it so that casts involving such types will be dumped correctly -- see |
1568 | * dumpCast. This means the flag should be set the same as for the underlying |
1569 | * object (the table or base type). |
1570 | */ |
1571 | static void |
1572 | selectDumpableType(TypeInfo *tyinfo, Archive *fout) |
1573 | { |
1574 | /* skip complex types, except for standalone composite types */ |
1575 | if (OidIsValid(tyinfo->typrelid) && |
1576 | tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE) |
1577 | { |
1578 | TableInfo *tytable = findTableByOid(tyinfo->typrelid); |
1579 | |
1580 | tyinfo->dobj.objType = DO_DUMMY_TYPE; |
1581 | if (tytable != NULL) |
1582 | tyinfo->dobj.dump = tytable->dobj.dump; |
1583 | else |
1584 | tyinfo->dobj.dump = DUMP_COMPONENT_NONE; |
1585 | return; |
1586 | } |
1587 | |
1588 | /* skip auto-generated array types */ |
1589 | if (tyinfo->isArray) |
1590 | { |
1591 | tyinfo->dobj.objType = DO_DUMMY_TYPE; |
1592 | |
1593 | /* |
1594 | * Fall through to set the dump flag; we assume that the subsequent |
1595 | * rules will do the same thing as they would for the array's base |
1596 | * type. (We cannot reliably look up the base type here, since |
1597 | * getTypes may not have processed it yet.) |
1598 | */ |
1599 | } |
1600 | |
1601 | if (checkExtensionMembership(&tyinfo->dobj, fout)) |
1602 | return; /* extension membership overrides all else */ |
1603 | |
1604 | /* Dump based on if the contents of the namespace are being dumped */ |
1605 | tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains; |
1606 | } |
1607 | |
1608 | /* |
1609 | * selectDumpableDefaultACL: policy-setting subroutine |
1610 | * Mark a default ACL as to be dumped or not |
1611 | * |
1612 | * For per-schema default ACLs, dump if the schema is to be dumped. |
1613 | * Otherwise dump if we are dumping "everything". Note that dataOnly |
1614 | * and aclsSkip are checked separately. |
1615 | */ |
1616 | static void |
1617 | selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt) |
1618 | { |
1619 | /* Default ACLs can't be extension members */ |
1620 | |
1621 | if (dinfo->dobj.namespace) |
1622 | /* default ACLs are considered part of the namespace */ |
1623 | dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains; |
1624 | else |
1625 | dinfo->dobj.dump = dopt->include_everything ? |
1626 | DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; |
1627 | } |
1628 | |
1629 | /* |
1630 | * selectDumpableCast: policy-setting subroutine |
1631 | * Mark a cast as to be dumped or not |
1632 | * |
1633 | * Casts do not belong to any particular namespace (since they haven't got |
1634 | * names), nor do they have identifiable owners. To distinguish user-defined |
1635 | * casts from built-in ones, we must resort to checking whether the cast's |
1636 | * OID is in the range reserved for initdb. |
1637 | */ |
1638 | static void |
1639 | selectDumpableCast(CastInfo *cast, Archive *fout) |
1640 | { |
1641 | if (checkExtensionMembership(&cast->dobj, fout)) |
1642 | return; /* extension membership overrides all else */ |
1643 | |
1644 | /* |
1645 | * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not |
1646 | * support ACLs currently. |
1647 | */ |
1648 | if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid) |
1649 | cast->dobj.dump = DUMP_COMPONENT_NONE; |
1650 | else |
1651 | cast->dobj.dump = fout->dopt->include_everything ? |
1652 | DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; |
1653 | } |
1654 | |
1655 | /* |
1656 | * selectDumpableProcLang: policy-setting subroutine |
1657 | * Mark a procedural language as to be dumped or not |
1658 | * |
1659 | * Procedural languages do not belong to any particular namespace. To |
1660 | * identify built-in languages, we must resort to checking whether the |
1661 | * language's OID is in the range reserved for initdb. |
1662 | */ |
1663 | static void |
1664 | selectDumpableProcLang(ProcLangInfo *plang, Archive *fout) |
1665 | { |
1666 | if (checkExtensionMembership(&plang->dobj, fout)) |
1667 | return; /* extension membership overrides all else */ |
1668 | |
1669 | /* |
1670 | * Only include procedural languages when we are dumping everything. |
1671 | * |
1672 | * For from-initdb procedural languages, only include ACLs, as we do for |
1673 | * the pg_catalog namespace. We need this because procedural languages do |
1674 | * not live in any namespace. |
1675 | */ |
1676 | if (!fout->dopt->include_everything) |
1677 | plang->dobj.dump = DUMP_COMPONENT_NONE; |
1678 | else |
1679 | { |
1680 | if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid) |
1681 | plang->dobj.dump = fout->remoteVersion < 90600 ? |
1682 | DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL; |
1683 | else |
1684 | plang->dobj.dump = DUMP_COMPONENT_ALL; |
1685 | } |
1686 | } |
1687 | |
1688 | /* |
1689 | * selectDumpableAccessMethod: policy-setting subroutine |
1690 | * Mark an access method as to be dumped or not |
1691 | * |
1692 | * Access methods do not belong to any particular namespace. To identify |
1693 | * built-in access methods, we must resort to checking whether the |
1694 | * method's OID is in the range reserved for initdb. |
1695 | */ |
1696 | static void |
1697 | selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout) |
1698 | { |
1699 | if (checkExtensionMembership(&method->dobj, fout)) |
1700 | return; /* extension membership overrides all else */ |
1701 | |
1702 | /* |
1703 | * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but |
1704 | * they do not support ACLs currently. |
1705 | */ |
1706 | if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid) |
1707 | method->dobj.dump = DUMP_COMPONENT_NONE; |
1708 | else |
1709 | method->dobj.dump = fout->dopt->include_everything ? |
1710 | DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; |
1711 | } |
1712 | |
1713 | /* |
1714 | * selectDumpableExtension: policy-setting subroutine |
1715 | * Mark an extension as to be dumped or not |
1716 | * |
1717 | * Built-in extensions should be skipped except for checking ACLs, since we |
1718 | * assume those will already be installed in the target database. We identify |
1719 | * such extensions by their having OIDs in the range reserved for initdb. |
1720 | * We dump all user-added extensions by default, or none of them if |
1721 | * include_everything is false (i.e., a --schema or --table switch was given). |
1722 | */ |
1723 | static void |
1724 | selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt) |
1725 | { |
1726 | /* |
1727 | * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to |
1728 | * change permissions on their member objects, if they wish to, and have |
1729 | * those changes preserved. |
1730 | */ |
1731 | if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid) |
1732 | extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL; |
1733 | else |
1734 | extinfo->dobj.dump = extinfo->dobj.dump_contains = |
1735 | dopt->include_everything ? DUMP_COMPONENT_ALL : |
1736 | DUMP_COMPONENT_NONE; |
1737 | } |
1738 | |
1739 | /* |
1740 | * selectDumpablePublicationTable: policy-setting subroutine |
1741 | * Mark a publication table as to be dumped or not |
1742 | * |
1743 | * Publication tables have schemas, but those are ignored in decision making, |
1744 | * because publications are only dumped when we are dumping everything. |
1745 | */ |
1746 | static void |
1747 | selectDumpablePublicationTable(DumpableObject *dobj, Archive *fout) |
1748 | { |
1749 | if (checkExtensionMembership(dobj, fout)) |
1750 | return; /* extension membership overrides all else */ |
1751 | |
1752 | dobj->dump = fout->dopt->include_everything ? |
1753 | DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; |
1754 | } |
1755 | |
1756 | /* |
1757 | * selectDumpableObject: policy-setting subroutine |
1758 | * Mark a generic dumpable object as to be dumped or not |
1759 | * |
1760 | * Use this only for object types without a special-case routine above. |
1761 | */ |
1762 | static void |
1763 | selectDumpableObject(DumpableObject *dobj, Archive *fout) |
1764 | { |
1765 | if (checkExtensionMembership(dobj, fout)) |
1766 | return; /* extension membership overrides all else */ |
1767 | |
1768 | /* |
1769 | * Default policy is to dump if parent namespace is dumpable, or for |
1770 | * non-namespace-associated items, dump if we're dumping "everything". |
1771 | */ |
1772 | if (dobj->namespace) |
1773 | dobj->dump = dobj->namespace->dobj.dump_contains; |
1774 | else |
1775 | dobj->dump = fout->dopt->include_everything ? |
1776 | DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE; |
1777 | } |
1778 | |
1779 | /* |
1780 | * Dump a table's contents for loading using the COPY command |
1781 | * - this routine is called by the Archiver when it wants the table |
1782 | * to be dumped. |
1783 | */ |
1784 | |
1785 | static int |
1786 | dumpTableData_copy(Archive *fout, void *dcontext) |
1787 | { |
1788 | TableDataInfo *tdinfo = (TableDataInfo *) dcontext; |
1789 | TableInfo *tbinfo = tdinfo->tdtable; |
1790 | const char *classname = tbinfo->dobj.name; |
1791 | PQExpBuffer q = createPQExpBuffer(); |
1792 | |
1793 | /* |
1794 | * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId |
1795 | * which uses it already. |
1796 | */ |
1797 | PQExpBuffer clistBuf = createPQExpBuffer(); |
1798 | PGconn *conn = GetConnection(fout); |
1799 | PGresult *res; |
1800 | int ret; |
1801 | char *copybuf; |
1802 | const char *column_list; |
1803 | |
1804 | pg_log_info("dumping contents of table \"%s.%s\"" , |
1805 | tbinfo->dobj.namespace->dobj.name, classname); |
1806 | |
1807 | /* |
1808 | * Specify the column list explicitly so that we have no possibility of |
1809 | * retrieving data in the wrong column order. (The default column |
1810 | * ordering of COPY will not be what we want in certain corner cases |
1811 | * involving ADD COLUMN and inheritance.) |
1812 | */ |
1813 | column_list = fmtCopyColumnList(tbinfo, clistBuf); |
1814 | |
1815 | if (tdinfo->filtercond) |
1816 | { |
1817 | /* Note: this syntax is only supported in 8.2 and up */ |
1818 | appendPQExpBufferStr(q, "COPY (SELECT " ); |
1819 | /* klugery to get rid of parens in column list */ |
1820 | if (strlen(column_list) > 2) |
1821 | { |
1822 | appendPQExpBufferStr(q, column_list + 1); |
1823 | q->data[q->len - 1] = ' '; |
1824 | } |
1825 | else |
1826 | appendPQExpBufferStr(q, "* " ); |
1827 | appendPQExpBuffer(q, "FROM %s %s) TO stdout;" , |
1828 | fmtQualifiedDumpable(tbinfo), |
1829 | tdinfo->filtercond); |
1830 | } |
1831 | else |
1832 | { |
1833 | appendPQExpBuffer(q, "COPY %s %s TO stdout;" , |
1834 | fmtQualifiedDumpable(tbinfo), |
1835 | column_list); |
1836 | } |
1837 | res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT); |
1838 | PQclear(res); |
1839 | destroyPQExpBuffer(clistBuf); |
1840 | |
1841 | for (;;) |
1842 | { |
1843 | ret = PQgetCopyData(conn, ©buf, 0); |
1844 | |
1845 | if (ret < 0) |
1846 | break; /* done or error */ |
1847 | |
1848 | if (copybuf) |
1849 | { |
1850 | WriteData(fout, copybuf, ret); |
1851 | PQfreemem(copybuf); |
1852 | } |
1853 | |
1854 | /* ---------- |
1855 | * THROTTLE: |
1856 | * |
1857 | * There was considerable discussion in late July, 2000 regarding |
1858 | * slowing down pg_dump when backing up large tables. Users with both |
1859 | * slow & fast (multi-processor) machines experienced performance |
1860 | * degradation when doing a backup. |
1861 | * |
1862 | * Initial attempts based on sleeping for a number of ms for each ms |
1863 | * of work were deemed too complex, then a simple 'sleep in each loop' |
1864 | * implementation was suggested. The latter failed because the loop |
1865 | * was too tight. Finally, the following was implemented: |
1866 | * |
1867 | * If throttle is non-zero, then |
1868 | * See how long since the last sleep. |
1869 | * Work out how long to sleep (based on ratio). |
1870 | * If sleep is more than 100ms, then |
1871 | * sleep |
1872 | * reset timer |
1873 | * EndIf |
1874 | * EndIf |
1875 | * |
1876 | * where the throttle value was the number of ms to sleep per ms of |
1877 | * work. The calculation was done in each loop. |
1878 | * |
1879 | * Most of the hard work is done in the backend, and this solution |
1880 | * still did not work particularly well: on slow machines, the ratio |
1881 | * was 50:1, and on medium paced machines, 1:1, and on fast |
1882 | * multi-processor machines, it had little or no effect, for reasons |
1883 | * that were unclear. |
1884 | * |
1885 | * Further discussion ensued, and the proposal was dropped. |
1886 | * |
1887 | * For those people who want this feature, it can be implemented using |
1888 | * gettimeofday in each loop, calculating the time since last sleep, |
1889 | * multiplying that by the sleep ratio, then if the result is more |
1890 | * than a preset 'minimum sleep time' (say 100ms), call the 'select' |
1891 | * function to sleep for a subsecond period ie. |
1892 | * |
1893 | * select(0, NULL, NULL, NULL, &tvi); |
1894 | * |
1895 | * This will return after the interval specified in the structure tvi. |
1896 | * Finally, call gettimeofday again to save the 'last sleep time'. |
1897 | * ---------- |
1898 | */ |
1899 | } |
1900 | archprintf(fout, "\\.\n\n\n" ); |
1901 | |
1902 | if (ret == -2) |
1903 | { |
1904 | /* copy data transfer failed */ |
1905 | pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed." , classname); |
1906 | pg_log_error("Error message from server: %s" , PQerrorMessage(conn)); |
1907 | pg_log_error("The command was: %s" , q->data); |
1908 | exit_nicely(1); |
1909 | } |
1910 | |
1911 | /* Check command status and return to normal libpq state */ |
1912 | res = PQgetResult(conn); |
1913 | if (PQresultStatus(res) != PGRES_COMMAND_OK) |
1914 | { |
1915 | pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed." , classname); |
1916 | pg_log_error("Error message from server: %s" , PQerrorMessage(conn)); |
1917 | pg_log_error("The command was: %s" , q->data); |
1918 | exit_nicely(1); |
1919 | } |
1920 | PQclear(res); |
1921 | |
1922 | /* Do this to ensure we've pumped libpq back to idle state */ |
1923 | if (PQgetResult(conn) != NULL) |
1924 | pg_log_warning("unexpected extra results during COPY of table \"%s\"" , |
1925 | classname); |
1926 | |
1927 | destroyPQExpBuffer(q); |
1928 | return 1; |
1929 | } |
1930 | |
1931 | /* |
1932 | * Dump table data using INSERT commands. |
1933 | * |
1934 | * Caution: when we restore from an archive file direct to database, the |
1935 | * INSERT commands emitted by this function have to be parsed by |
1936 | * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments, |
1937 | * E'' strings, or dollar-quoted strings. So don't emit anything like that. |
1938 | */ |
1939 | static int |
1940 | dumpTableData_insert(Archive *fout, void *dcontext) |
1941 | { |
1942 | TableDataInfo *tdinfo = (TableDataInfo *) dcontext; |
1943 | TableInfo *tbinfo = tdinfo->tdtable; |
1944 | DumpOptions *dopt = fout->dopt; |
1945 | PQExpBuffer q = createPQExpBuffer(); |
1946 | PQExpBuffer insertStmt = NULL; |
1947 | PGresult *res; |
1948 | int nfields; |
1949 | int rows_per_statement = dopt->dump_inserts; |
1950 | int rows_this_statement = 0; |
1951 | |
1952 | appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " |
1953 | "SELECT * FROM ONLY %s" , |
1954 | fmtQualifiedDumpable(tbinfo)); |
1955 | if (tdinfo->filtercond) |
1956 | appendPQExpBuffer(q, " %s" , tdinfo->filtercond); |
1957 | |
1958 | ExecuteSqlStatement(fout, q->data); |
1959 | |
1960 | while (1) |
1961 | { |
1962 | res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor" , |
1963 | PGRES_TUPLES_OK); |
1964 | nfields = PQnfields(res); |
1965 | |
1966 | /* |
1967 | * First time through, we build as much of the INSERT statement as |
1968 | * possible in "insertStmt", which we can then just print for each |
1969 | * statement. If the table happens to have zero columns then this will |
1970 | * be a complete statement, otherwise it will end in "VALUES" and be |
1971 | * ready to have the row's column values printed. |
1972 | */ |
1973 | if (insertStmt == NULL) |
1974 | { |
1975 | TableInfo *targettab; |
1976 | |
1977 | insertStmt = createPQExpBuffer(); |
1978 | |
1979 | /* |
1980 | * When load-via-partition-root is set, get the root table name |
1981 | * for the partition table, so that we can reload data through the |
1982 | * root table. |
1983 | */ |
1984 | if (dopt->load_via_partition_root && tbinfo->ispartition) |
1985 | targettab = getRootTableInfo(tbinfo); |
1986 | else |
1987 | targettab = tbinfo; |
1988 | |
1989 | appendPQExpBuffer(insertStmt, "INSERT INTO %s " , |
1990 | fmtQualifiedDumpable(targettab)); |
1991 | |
1992 | /* corner case for zero-column table */ |
1993 | if (nfields == 0) |
1994 | { |
1995 | appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n" ); |
1996 | } |
1997 | else |
1998 | { |
1999 | /* append the list of column names if required */ |
2000 | if (dopt->column_inserts) |
2001 | { |
2002 | appendPQExpBufferChar(insertStmt, '('); |
2003 | for (int field = 0; field < nfields; field++) |
2004 | { |
2005 | if (field > 0) |
2006 | appendPQExpBufferStr(insertStmt, ", " ); |
2007 | appendPQExpBufferStr(insertStmt, |
2008 | fmtId(PQfname(res, field))); |
2009 | } |
2010 | appendPQExpBufferStr(insertStmt, ") " ); |
2011 | } |
2012 | |
2013 | if (tbinfo->needs_override) |
2014 | appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE " ); |
2015 | |
2016 | appendPQExpBufferStr(insertStmt, "VALUES" ); |
2017 | } |
2018 | } |
2019 | |
2020 | for (int tuple = 0; tuple < PQntuples(res); tuple++) |
2021 | { |
2022 | /* Write the INSERT if not in the middle of a multi-row INSERT. */ |
2023 | if (rows_this_statement == 0) |
2024 | archputs(insertStmt->data, fout); |
2025 | |
2026 | /* |
2027 | * If it is zero-column table then we've already written the |
2028 | * complete statement, which will mean we've disobeyed |
2029 | * --rows-per-insert when it's set greater than 1. We do support |
2030 | * a way to make this multi-row with: SELECT UNION ALL SELECT |
2031 | * UNION ALL ... but that's non-standard so we should avoid it |
2032 | * given that using INSERTs is mostly only ever needed for |
2033 | * cross-database exports. |
2034 | */ |
2035 | if (nfields == 0) |
2036 | continue; |
2037 | |
2038 | /* Emit a row heading */ |
2039 | if (rows_per_statement == 1) |
2040 | archputs(" (" , fout); |
2041 | else if (rows_this_statement > 0) |
2042 | archputs(",\n\t(" , fout); |
2043 | else |
2044 | archputs("\n\t(" , fout); |
2045 | |
2046 | for (int field = 0; field < nfields; field++) |
2047 | { |
2048 | if (field > 0) |
2049 | archputs(", " , fout); |
2050 | if (tbinfo->attgenerated[field]) |
2051 | { |
2052 | archputs("DEFAULT" , fout); |
2053 | continue; |
2054 | } |
2055 | if (PQgetisnull(res, tuple, field)) |
2056 | { |
2057 | archputs("NULL" , fout); |
2058 | continue; |
2059 | } |
2060 | |
2061 | /* XXX This code is partially duplicated in ruleutils.c */ |
2062 | switch (PQftype(res, field)) |
2063 | { |
2064 | case INT2OID: |
2065 | case INT4OID: |
2066 | case INT8OID: |
2067 | case OIDOID: |
2068 | case FLOAT4OID: |
2069 | case FLOAT8OID: |
2070 | case NUMERICOID: |
2071 | { |
2072 | /* |
2073 | * These types are printed without quotes unless |
2074 | * they contain values that aren't accepted by the |
2075 | * scanner unquoted (e.g., 'NaN'). Note that |
2076 | * strtod() and friends might accept NaN, so we |
2077 | * can't use that to test. |
2078 | * |
2079 | * In reality we only need to defend against |
2080 | * infinity and NaN, so we need not get too crazy |
2081 | * about pattern matching here. |
2082 | */ |
2083 | const char *s = PQgetvalue(res, tuple, field); |
2084 | |
2085 | if (strspn(s, "0123456789 +-eE." ) == strlen(s)) |
2086 | archputs(s, fout); |
2087 | else |
2088 | archprintf(fout, "'%s'" , s); |
2089 | } |
2090 | break; |
2091 | |
2092 | case BITOID: |
2093 | case VARBITOID: |
2094 | archprintf(fout, "B'%s'" , |
2095 | PQgetvalue(res, tuple, field)); |
2096 | break; |
2097 | |
2098 | case BOOLOID: |
2099 | if (strcmp(PQgetvalue(res, tuple, field), "t" ) == 0) |
2100 | archputs("true" , fout); |
2101 | else |
2102 | archputs("false" , fout); |
2103 | break; |
2104 | |
2105 | default: |
2106 | /* All other types are printed as string literals. */ |
2107 | resetPQExpBuffer(q); |
2108 | appendStringLiteralAH(q, |
2109 | PQgetvalue(res, tuple, field), |
2110 | fout); |
2111 | archputs(q->data, fout); |
2112 | break; |
2113 | } |
2114 | } |
2115 | |
2116 | /* Terminate the row ... */ |
2117 | archputs(")" , fout); |
2118 | |
2119 | /* ... and the statement, if the target no. of rows is reached */ |
2120 | if (++rows_this_statement >= rows_per_statement) |
2121 | { |
2122 | if (dopt->do_nothing) |
2123 | archputs(" ON CONFLICT DO NOTHING;\n" , fout); |
2124 | else |
2125 | archputs(";\n" , fout); |
2126 | /* Reset the row counter */ |
2127 | rows_this_statement = 0; |
2128 | } |
2129 | } |
2130 | |
2131 | if (PQntuples(res) <= 0) |
2132 | { |
2133 | PQclear(res); |
2134 | break; |
2135 | } |
2136 | PQclear(res); |
2137 | } |
2138 | |
2139 | /* Terminate any statements that didn't make the row count. */ |
2140 | if (rows_this_statement > 0) |
2141 | { |
2142 | if (dopt->do_nothing) |
2143 | archputs(" ON CONFLICT DO NOTHING;\n" , fout); |
2144 | else |
2145 | archputs(";\n" , fout); |
2146 | } |
2147 | |
2148 | archputs("\n\n" , fout); |
2149 | |
2150 | ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor" ); |
2151 | |
2152 | destroyPQExpBuffer(q); |
2153 | if (insertStmt != NULL) |
2154 | destroyPQExpBuffer(insertStmt); |
2155 | |
2156 | return 1; |
2157 | } |
2158 | |
2159 | /* |
2160 | * getRootTableInfo: |
2161 | * get the root TableInfo for the given partition table. |
2162 | */ |
2163 | static TableInfo * |
2164 | getRootTableInfo(TableInfo *tbinfo) |
2165 | { |
2166 | TableInfo *parentTbinfo; |
2167 | |
2168 | Assert(tbinfo->ispartition); |
2169 | Assert(tbinfo->numParents == 1); |
2170 | |
2171 | parentTbinfo = tbinfo->parents[0]; |
2172 | while (parentTbinfo->ispartition) |
2173 | { |
2174 | Assert(parentTbinfo->numParents == 1); |
2175 | parentTbinfo = parentTbinfo->parents[0]; |
2176 | } |
2177 | |
2178 | return parentTbinfo; |
2179 | } |
2180 | |
2181 | /* |
2182 | * dumpTableData - |
2183 | * dump the contents of a single table |
2184 | * |
2185 | * Actually, this just makes an ArchiveEntry for the table contents. |
2186 | */ |
2187 | static void |
2188 | dumpTableData(Archive *fout, TableDataInfo *tdinfo) |
2189 | { |
2190 | DumpOptions *dopt = fout->dopt; |
2191 | TableInfo *tbinfo = tdinfo->tdtable; |
2192 | PQExpBuffer copyBuf = createPQExpBuffer(); |
2193 | PQExpBuffer clistBuf = createPQExpBuffer(); |
2194 | DataDumperPtr dumpFn; |
2195 | char *copyStmt; |
2196 | const char *copyFrom; |
2197 | |
2198 | if (!dopt->dump_inserts) |
2199 | { |
2200 | /* Dump/restore using COPY */ |
2201 | dumpFn = dumpTableData_copy; |
2202 | |
2203 | /* |
2204 | * When load-via-partition-root is set, get the root table name for |
2205 | * the partition table, so that we can reload data through the root |
2206 | * table. |
2207 | */ |
2208 | if (dopt->load_via_partition_root && tbinfo->ispartition) |
2209 | { |
2210 | TableInfo *parentTbinfo; |
2211 | |
2212 | parentTbinfo = getRootTableInfo(tbinfo); |
2213 | copyFrom = fmtQualifiedDumpable(parentTbinfo); |
2214 | } |
2215 | else |
2216 | copyFrom = fmtQualifiedDumpable(tbinfo); |
2217 | |
2218 | /* must use 2 steps here 'cause fmtId is nonreentrant */ |
2219 | appendPQExpBuffer(copyBuf, "COPY %s " , |
2220 | copyFrom); |
2221 | appendPQExpBuffer(copyBuf, "%s FROM stdin;\n" , |
2222 | fmtCopyColumnList(tbinfo, clistBuf)); |
2223 | copyStmt = copyBuf->data; |
2224 | } |
2225 | else |
2226 | { |
2227 | /* Restore using INSERT */ |
2228 | dumpFn = dumpTableData_insert; |
2229 | copyStmt = NULL; |
2230 | } |
2231 | |
2232 | /* |
2233 | * Note: although the TableDataInfo is a full DumpableObject, we treat its |
2234 | * dependency on its table as "special" and pass it to ArchiveEntry now. |
2235 | * See comments for BuildArchiveDependencies. |
2236 | */ |
2237 | if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA) |
2238 | { |
2239 | TocEntry *te; |
2240 | |
2241 | te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId, |
2242 | ARCHIVE_OPTS(.tag = tbinfo->dobj.name, |
2243 | .namespace = tbinfo->dobj.namespace->dobj.name, |
2244 | .owner = tbinfo->rolname, |
2245 | .description = "TABLE DATA" , |
2246 | .section = SECTION_DATA, |
2247 | .copyStmt = copyStmt, |
2248 | .deps = &(tbinfo->dobj.dumpId), |
2249 | .nDeps = 1, |
2250 | .dumpFn = dumpFn, |
2251 | .dumpArg = tdinfo)); |
2252 | |
2253 | /* |
2254 | * Set the TocEntry's dataLength in case we are doing a parallel dump |
2255 | * and want to order dump jobs by table size. We choose to measure |
2256 | * dataLength in table pages during dump, so no scaling is needed. |
2257 | * However, relpages is declared as "integer" in pg_class, and hence |
2258 | * also in TableInfo, but it's really BlockNumber a/k/a unsigned int. |
2259 | * Cast so that we get the right interpretation of table sizes |
2260 | * exceeding INT_MAX pages. |
2261 | */ |
2262 | te->dataLength = (BlockNumber) tbinfo->relpages; |
2263 | } |
2264 | |
2265 | destroyPQExpBuffer(copyBuf); |
2266 | destroyPQExpBuffer(clistBuf); |
2267 | } |
2268 | |
2269 | /* |
2270 | * refreshMatViewData - |
2271 | * load or refresh the contents of a single materialized view |
2272 | * |
2273 | * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW |
2274 | * statement. |
2275 | */ |
2276 | static void |
2277 | refreshMatViewData(Archive *fout, TableDataInfo *tdinfo) |
2278 | { |
2279 | TableInfo *tbinfo = tdinfo->tdtable; |
2280 | PQExpBuffer q; |
2281 | |
2282 | /* If the materialized view is not flagged as populated, skip this. */ |
2283 | if (!tbinfo->relispopulated) |
2284 | return; |
2285 | |
2286 | q = createPQExpBuffer(); |
2287 | |
2288 | appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n" , |
2289 | fmtQualifiedDumpable(tbinfo)); |
2290 | |
2291 | if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA) |
2292 | ArchiveEntry(fout, |
2293 | tdinfo->dobj.catId, /* catalog ID */ |
2294 | tdinfo->dobj.dumpId, /* dump ID */ |
2295 | ARCHIVE_OPTS(.tag = tbinfo->dobj.name, |
2296 | .namespace = tbinfo->dobj.namespace->dobj.name, |
2297 | .owner = tbinfo->rolname, |
2298 | .description = "MATERIALIZED VIEW DATA" , |
2299 | .section = SECTION_POST_DATA, |
2300 | .createStmt = q->data, |
2301 | .deps = tdinfo->dobj.dependencies, |
2302 | .nDeps = tdinfo->dobj.nDeps)); |
2303 | |
2304 | destroyPQExpBuffer(q); |
2305 | } |
2306 | |
2307 | /* |
2308 | * getTableData - |
2309 | * set up dumpable objects representing the contents of tables |
2310 | */ |
2311 | static void |
2312 | getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind) |
2313 | { |
2314 | int i; |
2315 | |
2316 | for (i = 0; i < numTables; i++) |
2317 | { |
2318 | if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA && |
2319 | (!relkind || tblinfo[i].relkind == relkind)) |
2320 | makeTableDataInfo(dopt, &(tblinfo[i])); |
2321 | } |
2322 | } |
2323 | |
2324 | /* |
2325 | * Make a dumpable object for the data of this specific table |
2326 | * |
2327 | * Note: we make a TableDataInfo if and only if we are going to dump the |
2328 | * table data; the "dump" flag in such objects isn't used. |
2329 | */ |
2330 | static void |
2331 | makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo) |
2332 | { |
2333 | TableDataInfo *tdinfo; |
2334 | |
2335 | /* |
2336 | * Nothing to do if we already decided to dump the table. This will |
2337 | * happen for "config" tables. |
2338 | */ |
2339 | if (tbinfo->dataObj != NULL) |
2340 | return; |
2341 | |
2342 | /* Skip VIEWs (no data to dump) */ |
2343 | if (tbinfo->relkind == RELKIND_VIEW) |
2344 | return; |
2345 | /* Skip FOREIGN TABLEs (no data to dump) */ |
2346 | if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) |
2347 | return; |
2348 | /* Skip partitioned tables (data in partitions) */ |
2349 | if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) |
2350 | return; |
2351 | |
2352 | /* Don't dump data in unlogged tables, if so requested */ |
2353 | if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && |
2354 | dopt->no_unlogged_table_data) |
2355 | return; |
2356 | |
2357 | /* Check that the data is not explicitly excluded */ |
2358 | if (simple_oid_list_member(&tabledata_exclude_oids, |
2359 | tbinfo->dobj.catId.oid)) |
2360 | return; |
2361 | |
2362 | /* OK, let's dump it */ |
2363 | tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo)); |
2364 | |
2365 | if (tbinfo->relkind == RELKIND_MATVIEW) |
2366 | tdinfo->dobj.objType = DO_REFRESH_MATVIEW; |
2367 | else if (tbinfo->relkind == RELKIND_SEQUENCE) |
2368 | tdinfo->dobj.objType = DO_SEQUENCE_SET; |
2369 | else |
2370 | tdinfo->dobj.objType = DO_TABLE_DATA; |
2371 | |
2372 | /* |
2373 | * Note: use tableoid 0 so that this object won't be mistaken for |
2374 | * something that pg_depend entries apply to. |
2375 | */ |
2376 | tdinfo->dobj.catId.tableoid = 0; |
2377 | tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid; |
2378 | AssignDumpId(&tdinfo->dobj); |
2379 | tdinfo->dobj.name = tbinfo->dobj.name; |
2380 | tdinfo->dobj.namespace = tbinfo->dobj.namespace; |
2381 | tdinfo->tdtable = tbinfo; |
2382 | tdinfo->filtercond = NULL; /* might get set later */ |
2383 | addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId); |
2384 | |
2385 | tbinfo->dataObj = tdinfo; |
2386 | } |
2387 | |
2388 | /* |
2389 | * The refresh for a materialized view must be dependent on the refresh for |
2390 | * any materialized view that this one is dependent on. |
2391 | * |
2392 | * This must be called after all the objects are created, but before they are |
2393 | * sorted. |
2394 | */ |
2395 | static void |
2396 | buildMatViewRefreshDependencies(Archive *fout) |
2397 | { |
2398 | PQExpBuffer query; |
2399 | PGresult *res; |
2400 | int ntups, |
2401 | i; |
2402 | int i_classid, |
2403 | i_objid, |
2404 | i_refobjid; |
2405 | |
2406 | /* No Mat Views before 9.3. */ |
2407 | if (fout->remoteVersion < 90300) |
2408 | return; |
2409 | |
2410 | query = createPQExpBuffer(); |
2411 | |
2412 | appendPQExpBufferStr(query, "WITH RECURSIVE w AS " |
2413 | "( " |
2414 | "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind " |
2415 | "FROM pg_depend d1 " |
2416 | "JOIN pg_class c1 ON c1.oid = d1.objid " |
2417 | "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW) |
2418 | " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid " |
2419 | "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass " |
2420 | "AND d2.objid = r1.oid " |
2421 | "AND d2.refobjid <> d1.objid " |
2422 | "JOIN pg_class c2 ON c2.oid = d2.refobjid " |
2423 | "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) "," |
2424 | CppAsString2(RELKIND_VIEW) ") " |
2425 | "WHERE d1.classid = 'pg_class'::regclass " |
2426 | "UNION " |
2427 | "SELECT w.objid, d3.refobjid, c3.relkind " |
2428 | "FROM w " |
2429 | "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid " |
2430 | "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass " |
2431 | "AND d3.objid = r3.oid " |
2432 | "AND d3.refobjid <> w.refobjid " |
2433 | "JOIN pg_class c3 ON c3.oid = d3.refobjid " |
2434 | "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) "," |
2435 | CppAsString2(RELKIND_VIEW) ") " |
2436 | ") " |
2437 | "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid " |
2438 | "FROM w " |
2439 | "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW)); |
2440 | |
2441 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
2442 | |
2443 | ntups = PQntuples(res); |
2444 | |
2445 | i_classid = PQfnumber(res, "classid" ); |
2446 | i_objid = PQfnumber(res, "objid" ); |
2447 | i_refobjid = PQfnumber(res, "refobjid" ); |
2448 | |
2449 | for (i = 0; i < ntups; i++) |
2450 | { |
2451 | CatalogId objId; |
2452 | CatalogId refobjId; |
2453 | DumpableObject *dobj; |
2454 | DumpableObject *refdobj; |
2455 | TableInfo *tbinfo; |
2456 | TableInfo *reftbinfo; |
2457 | |
2458 | objId.tableoid = atooid(PQgetvalue(res, i, i_classid)); |
2459 | objId.oid = atooid(PQgetvalue(res, i, i_objid)); |
2460 | refobjId.tableoid = objId.tableoid; |
2461 | refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid)); |
2462 | |
2463 | dobj = findObjectByCatalogId(objId); |
2464 | if (dobj == NULL) |
2465 | continue; |
2466 | |
2467 | Assert(dobj->objType == DO_TABLE); |
2468 | tbinfo = (TableInfo *) dobj; |
2469 | Assert(tbinfo->relkind == RELKIND_MATVIEW); |
2470 | dobj = (DumpableObject *) tbinfo->dataObj; |
2471 | if (dobj == NULL) |
2472 | continue; |
2473 | Assert(dobj->objType == DO_REFRESH_MATVIEW); |
2474 | |
2475 | refdobj = findObjectByCatalogId(refobjId); |
2476 | if (refdobj == NULL) |
2477 | continue; |
2478 | |
2479 | Assert(refdobj->objType == DO_TABLE); |
2480 | reftbinfo = (TableInfo *) refdobj; |
2481 | Assert(reftbinfo->relkind == RELKIND_MATVIEW); |
2482 | refdobj = (DumpableObject *) reftbinfo->dataObj; |
2483 | if (refdobj == NULL) |
2484 | continue; |
2485 | Assert(refdobj->objType == DO_REFRESH_MATVIEW); |
2486 | |
2487 | addObjectDependency(dobj, refdobj->dumpId); |
2488 | |
2489 | if (!reftbinfo->relispopulated) |
2490 | tbinfo->relispopulated = false; |
2491 | } |
2492 | |
2493 | PQclear(res); |
2494 | |
2495 | destroyPQExpBuffer(query); |
2496 | } |
2497 | |
2498 | /* |
2499 | * getTableDataFKConstraints - |
2500 | * add dump-order dependencies reflecting foreign key constraints |
2501 | * |
2502 | * This code is executed only in a data-only dump --- in schema+data dumps |
2503 | * we handle foreign key issues by not creating the FK constraints until |
2504 | * after the data is loaded. In a data-only dump, however, we want to |
2505 | * order the table data objects in such a way that a table's referenced |
2506 | * tables are restored first. (In the presence of circular references or |
2507 | * self-references this may be impossible; we'll detect and complain about |
2508 | * that during the dependency sorting step.) |
2509 | */ |
2510 | static void |
2511 | getTableDataFKConstraints(void) |
2512 | { |
2513 | DumpableObject **dobjs; |
2514 | int numObjs; |
2515 | int i; |
2516 | |
2517 | /* Search through all the dumpable objects for FK constraints */ |
2518 | getDumpableObjects(&dobjs, &numObjs); |
2519 | for (i = 0; i < numObjs; i++) |
2520 | { |
2521 | if (dobjs[i]->objType == DO_FK_CONSTRAINT) |
2522 | { |
2523 | ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i]; |
2524 | TableInfo *ftable; |
2525 | |
2526 | /* Not interesting unless both tables are to be dumped */ |
2527 | if (cinfo->contable == NULL || |
2528 | cinfo->contable->dataObj == NULL) |
2529 | continue; |
2530 | ftable = findTableByOid(cinfo->confrelid); |
2531 | if (ftable == NULL || |
2532 | ftable->dataObj == NULL) |
2533 | continue; |
2534 | |
2535 | /* |
2536 | * Okay, make referencing table's TABLE_DATA object depend on the |
2537 | * referenced table's TABLE_DATA object. |
2538 | */ |
2539 | addObjectDependency(&cinfo->contable->dataObj->dobj, |
2540 | ftable->dataObj->dobj.dumpId); |
2541 | } |
2542 | } |
2543 | free(dobjs); |
2544 | } |
2545 | |
2546 | |
2547 | /* |
2548 | * guessConstraintInheritance: |
2549 | * In pre-8.4 databases, we can't tell for certain which constraints |
2550 | * are inherited. We assume a CHECK constraint is inherited if its name |
2551 | * matches the name of any constraint in the parent. Originally this code |
2552 | * tried to compare the expression texts, but that can fail for various |
2553 | * reasons --- for example, if the parent and child tables are in different |
2554 | * schemas, reverse-listing of function calls may produce different text |
2555 | * (schema-qualified or not) depending on search path. |
2556 | * |
2557 | * In 8.4 and up we can rely on the conislocal field to decide which |
2558 | * constraints must be dumped; much safer. |
2559 | * |
2560 | * This function assumes all conislocal flags were initialized to true. |
2561 | * It clears the flag on anything that seems to be inherited. |
2562 | */ |
2563 | static void |
2564 | guessConstraintInheritance(TableInfo *tblinfo, int numTables) |
2565 | { |
2566 | int i, |
2567 | j, |
2568 | k; |
2569 | |
2570 | for (i = 0; i < numTables; i++) |
2571 | { |
2572 | TableInfo *tbinfo = &(tblinfo[i]); |
2573 | int numParents; |
2574 | TableInfo **parents; |
2575 | TableInfo *parent; |
2576 | |
2577 | /* Sequences and views never have parents */ |
2578 | if (tbinfo->relkind == RELKIND_SEQUENCE || |
2579 | tbinfo->relkind == RELKIND_VIEW) |
2580 | continue; |
2581 | |
2582 | /* Don't bother computing anything for non-target tables, either */ |
2583 | if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) |
2584 | continue; |
2585 | |
2586 | numParents = tbinfo->numParents; |
2587 | parents = tbinfo->parents; |
2588 | |
2589 | if (numParents == 0) |
2590 | continue; /* nothing to see here, move along */ |
2591 | |
2592 | /* scan for inherited CHECK constraints */ |
2593 | for (j = 0; j < tbinfo->ncheck; j++) |
2594 | { |
2595 | ConstraintInfo *constr; |
2596 | |
2597 | constr = &(tbinfo->checkexprs[j]); |
2598 | |
2599 | for (k = 0; k < numParents; k++) |
2600 | { |
2601 | int l; |
2602 | |
2603 | parent = parents[k]; |
2604 | for (l = 0; l < parent->ncheck; l++) |
2605 | { |
2606 | ConstraintInfo *pconstr = &(parent->checkexprs[l]); |
2607 | |
2608 | if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0) |
2609 | { |
2610 | constr->conislocal = false; |
2611 | break; |
2612 | } |
2613 | } |
2614 | if (!constr->conislocal) |
2615 | break; |
2616 | } |
2617 | } |
2618 | } |
2619 | } |
2620 | |
2621 | |
2622 | /* |
2623 | * dumpDatabase: |
2624 | * dump the database definition |
2625 | */ |
2626 | static void |
2627 | dumpDatabase(Archive *fout) |
2628 | { |
2629 | DumpOptions *dopt = fout->dopt; |
2630 | PQExpBuffer dbQry = createPQExpBuffer(); |
2631 | PQExpBuffer delQry = createPQExpBuffer(); |
2632 | PQExpBuffer creaQry = createPQExpBuffer(); |
2633 | PQExpBuffer labelq = createPQExpBuffer(); |
2634 | PGconn *conn = GetConnection(fout); |
2635 | PGresult *res; |
2636 | int i_tableoid, |
2637 | i_oid, |
2638 | i_datname, |
2639 | i_dba, |
2640 | i_encoding, |
2641 | i_collate, |
2642 | i_ctype, |
2643 | i_frozenxid, |
2644 | i_minmxid, |
2645 | i_datacl, |
2646 | i_rdatacl, |
2647 | i_datistemplate, |
2648 | i_datconnlimit, |
2649 | i_tablespace; |
2650 | CatalogId dbCatId; |
2651 | DumpId dbDumpId; |
2652 | const char *datname, |
2653 | *dba, |
2654 | *encoding, |
2655 | *collate, |
2656 | *ctype, |
2657 | *datacl, |
2658 | *rdatacl, |
2659 | *datistemplate, |
2660 | *datconnlimit, |
2661 | *tablespace; |
2662 | uint32 frozenxid, |
2663 | minmxid; |
2664 | char *qdatname; |
2665 | |
2666 | pg_log_info("saving database definition" ); |
2667 | |
2668 | /* |
2669 | * Fetch the database-level properties for this database. |
2670 | * |
2671 | * The order in which privileges are in the ACL string (the order they |
2672 | * have been GRANT'd in, which the backend maintains) must be preserved to |
2673 | * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on |
2674 | * those are dumped in the correct order. Note that initial privileges |
2675 | * (pg_init_privs) are not supported on databases, so this logic cannot |
2676 | * make use of buildACLQueries(). |
2677 | */ |
2678 | if (fout->remoteVersion >= 90600) |
2679 | { |
2680 | appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " |
2681 | "(%s datdba) AS dba, " |
2682 | "pg_encoding_to_char(encoding) AS encoding, " |
2683 | "datcollate, datctype, datfrozenxid, datminmxid, " |
2684 | "(SELECT array_agg(acl ORDER BY row_n) FROM " |
2685 | " (SELECT acl, row_n FROM " |
2686 | " unnest(coalesce(datacl,acldefault('d',datdba))) " |
2687 | " WITH ORDINALITY AS perm(acl,row_n) " |
2688 | " WHERE NOT EXISTS ( " |
2689 | " SELECT 1 " |
2690 | " FROM unnest(acldefault('d',datdba)) " |
2691 | " AS init(init_acl) " |
2692 | " WHERE acl = init_acl)) AS datacls) " |
2693 | " AS datacl, " |
2694 | "(SELECT array_agg(acl ORDER BY row_n) FROM " |
2695 | " (SELECT acl, row_n FROM " |
2696 | " unnest(acldefault('d',datdba)) " |
2697 | " WITH ORDINALITY AS initp(acl,row_n) " |
2698 | " WHERE NOT EXISTS ( " |
2699 | " SELECT 1 " |
2700 | " FROM unnest(coalesce(datacl,acldefault('d',datdba))) " |
2701 | " AS permp(orig_acl) " |
2702 | " WHERE acl = orig_acl)) AS rdatacls) " |
2703 | " AS rdatacl, " |
2704 | "datistemplate, datconnlimit, " |
2705 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " |
2706 | "shobj_description(oid, 'pg_database') AS description " |
2707 | |
2708 | "FROM pg_database " |
2709 | "WHERE datname = current_database()" , |
2710 | username_subquery); |
2711 | } |
2712 | else if (fout->remoteVersion >= 90300) |
2713 | { |
2714 | appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " |
2715 | "(%s datdba) AS dba, " |
2716 | "pg_encoding_to_char(encoding) AS encoding, " |
2717 | "datcollate, datctype, datfrozenxid, datminmxid, " |
2718 | "datacl, '' as rdatacl, datistemplate, datconnlimit, " |
2719 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " |
2720 | "shobj_description(oid, 'pg_database') AS description " |
2721 | |
2722 | "FROM pg_database " |
2723 | "WHERE datname = current_database()" , |
2724 | username_subquery); |
2725 | } |
2726 | else if (fout->remoteVersion >= 80400) |
2727 | { |
2728 | appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " |
2729 | "(%s datdba) AS dba, " |
2730 | "pg_encoding_to_char(encoding) AS encoding, " |
2731 | "datcollate, datctype, datfrozenxid, 0 AS datminmxid, " |
2732 | "datacl, '' as rdatacl, datistemplate, datconnlimit, " |
2733 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " |
2734 | "shobj_description(oid, 'pg_database') AS description " |
2735 | |
2736 | "FROM pg_database " |
2737 | "WHERE datname = current_database()" , |
2738 | username_subquery); |
2739 | } |
2740 | else if (fout->remoteVersion >= 80200) |
2741 | { |
2742 | appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " |
2743 | "(%s datdba) AS dba, " |
2744 | "pg_encoding_to_char(encoding) AS encoding, " |
2745 | "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, " |
2746 | "datacl, '' as rdatacl, datistemplate, datconnlimit, " |
2747 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " |
2748 | "shobj_description(oid, 'pg_database') AS description " |
2749 | |
2750 | "FROM pg_database " |
2751 | "WHERE datname = current_database()" , |
2752 | username_subquery); |
2753 | } |
2754 | else |
2755 | { |
2756 | appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, " |
2757 | "(%s datdba) AS dba, " |
2758 | "pg_encoding_to_char(encoding) AS encoding, " |
2759 | "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, " |
2760 | "datacl, '' as rdatacl, datistemplate, " |
2761 | "-1 as datconnlimit, " |
2762 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace " |
2763 | "FROM pg_database " |
2764 | "WHERE datname = current_database()" , |
2765 | username_subquery); |
2766 | } |
2767 | |
2768 | res = ExecuteSqlQueryForSingleRow(fout, dbQry->data); |
2769 | |
2770 | i_tableoid = PQfnumber(res, "tableoid" ); |
2771 | i_oid = PQfnumber(res, "oid" ); |
2772 | i_datname = PQfnumber(res, "datname" ); |
2773 | i_dba = PQfnumber(res, "dba" ); |
2774 | i_encoding = PQfnumber(res, "encoding" ); |
2775 | i_collate = PQfnumber(res, "datcollate" ); |
2776 | i_ctype = PQfnumber(res, "datctype" ); |
2777 | i_frozenxid = PQfnumber(res, "datfrozenxid" ); |
2778 | i_minmxid = PQfnumber(res, "datminmxid" ); |
2779 | i_datacl = PQfnumber(res, "datacl" ); |
2780 | i_rdatacl = PQfnumber(res, "rdatacl" ); |
2781 | i_datistemplate = PQfnumber(res, "datistemplate" ); |
2782 | i_datconnlimit = PQfnumber(res, "datconnlimit" ); |
2783 | i_tablespace = PQfnumber(res, "tablespace" ); |
2784 | |
2785 | dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid)); |
2786 | dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid)); |
2787 | datname = PQgetvalue(res, 0, i_datname); |
2788 | dba = PQgetvalue(res, 0, i_dba); |
2789 | encoding = PQgetvalue(res, 0, i_encoding); |
2790 | collate = PQgetvalue(res, 0, i_collate); |
2791 | ctype = PQgetvalue(res, 0, i_ctype); |
2792 | frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid)); |
2793 | minmxid = atooid(PQgetvalue(res, 0, i_minmxid)); |
2794 | datacl = PQgetvalue(res, 0, i_datacl); |
2795 | rdatacl = PQgetvalue(res, 0, i_rdatacl); |
2796 | datistemplate = PQgetvalue(res, 0, i_datistemplate); |
2797 | datconnlimit = PQgetvalue(res, 0, i_datconnlimit); |
2798 | tablespace = PQgetvalue(res, 0, i_tablespace); |
2799 | |
2800 | qdatname = pg_strdup(fmtId(datname)); |
2801 | |
2802 | /* |
2803 | * Prepare the CREATE DATABASE command. We must specify encoding, locale, |
2804 | * and tablespace since those can't be altered later. Other DB properties |
2805 | * are left to the DATABASE PROPERTIES entry, so that they can be applied |
2806 | * after reconnecting to the target DB. |
2807 | */ |
2808 | appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0" , |
2809 | qdatname); |
2810 | if (strlen(encoding) > 0) |
2811 | { |
2812 | appendPQExpBufferStr(creaQry, " ENCODING = " ); |
2813 | appendStringLiteralAH(creaQry, encoding, fout); |
2814 | } |
2815 | if (strlen(collate) > 0) |
2816 | { |
2817 | appendPQExpBufferStr(creaQry, " LC_COLLATE = " ); |
2818 | appendStringLiteralAH(creaQry, collate, fout); |
2819 | } |
2820 | if (strlen(ctype) > 0) |
2821 | { |
2822 | appendPQExpBufferStr(creaQry, " LC_CTYPE = " ); |
2823 | appendStringLiteralAH(creaQry, ctype, fout); |
2824 | } |
2825 | |
2826 | /* |
2827 | * Note: looking at dopt->outputNoTablespaces here is completely the wrong |
2828 | * thing; the decision whether to specify a tablespace should be left till |
2829 | * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd |
2830 | * label the DATABASE entry with the tablespace and let the normal |
2831 | * tablespace selection logic work ... but CREATE DATABASE doesn't pay |
2832 | * attention to default_tablespace, so that won't work. |
2833 | */ |
2834 | if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default" ) != 0 && |
2835 | !dopt->outputNoTablespaces) |
2836 | appendPQExpBuffer(creaQry, " TABLESPACE = %s" , |
2837 | fmtId(tablespace)); |
2838 | appendPQExpBufferStr(creaQry, ";\n" ); |
2839 | |
2840 | appendPQExpBuffer(delQry, "DROP DATABASE %s;\n" , |
2841 | qdatname); |
2842 | |
2843 | dbDumpId = createDumpId(); |
2844 | |
2845 | ArchiveEntry(fout, |
2846 | dbCatId, /* catalog ID */ |
2847 | dbDumpId, /* dump ID */ |
2848 | ARCHIVE_OPTS(.tag = datname, |
2849 | .owner = dba, |
2850 | .description = "DATABASE" , |
2851 | .section = SECTION_PRE_DATA, |
2852 | .createStmt = creaQry->data, |
2853 | .dropStmt = delQry->data)); |
2854 | |
2855 | /* Compute correct tag for archive entry */ |
2856 | appendPQExpBuffer(labelq, "DATABASE %s" , qdatname); |
2857 | |
2858 | /* Dump DB comment if any */ |
2859 | if (fout->remoteVersion >= 80200) |
2860 | { |
2861 | /* |
2862 | * 8.2 and up keep comments on shared objects in a shared table, so we |
2863 | * cannot use the dumpComment() code used for other database objects. |
2864 | * Be careful that the ArchiveEntry parameters match that function. |
2865 | */ |
2866 | char * = PQgetvalue(res, 0, PQfnumber(res, "description" )); |
2867 | |
2868 | if (comment && *comment && !dopt->no_comments) |
2869 | { |
2870 | resetPQExpBuffer(dbQry); |
2871 | |
2872 | /* |
2873 | * Generates warning when loaded into a differently-named |
2874 | * database. |
2875 | */ |
2876 | appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS " , qdatname); |
2877 | appendStringLiteralAH(dbQry, comment, fout); |
2878 | appendPQExpBufferStr(dbQry, ";\n" ); |
2879 | |
2880 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
2881 | ARCHIVE_OPTS(.tag = labelq->data, |
2882 | .owner = dba, |
2883 | .description = "COMMENT" , |
2884 | .section = SECTION_NONE, |
2885 | .createStmt = dbQry->data, |
2886 | .deps = &dbDumpId, |
2887 | .nDeps = 1)); |
2888 | } |
2889 | } |
2890 | else |
2891 | { |
2892 | dumpComment(fout, "DATABASE" , qdatname, NULL, dba, |
2893 | dbCatId, 0, dbDumpId); |
2894 | } |
2895 | |
2896 | /* Dump DB security label, if enabled */ |
2897 | if (!dopt->no_security_labels && fout->remoteVersion >= 90200) |
2898 | { |
2899 | PGresult *shres; |
2900 | PQExpBuffer seclabelQry; |
2901 | |
2902 | seclabelQry = createPQExpBuffer(); |
2903 | |
2904 | buildShSecLabelQuery(conn, "pg_database" , dbCatId.oid, seclabelQry); |
2905 | shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK); |
2906 | resetPQExpBuffer(seclabelQry); |
2907 | emitShSecLabels(conn, shres, seclabelQry, "DATABASE" , datname); |
2908 | if (seclabelQry->len > 0) |
2909 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
2910 | ARCHIVE_OPTS(.tag = labelq->data, |
2911 | .owner = dba, |
2912 | .description = "SECURITY LABEL" , |
2913 | .section = SECTION_NONE, |
2914 | .createStmt = seclabelQry->data, |
2915 | .deps = &dbDumpId, |
2916 | .nDeps = 1)); |
2917 | destroyPQExpBuffer(seclabelQry); |
2918 | PQclear(shres); |
2919 | } |
2920 | |
2921 | /* |
2922 | * Dump ACL if any. Note that we do not support initial privileges |
2923 | * (pg_init_privs) on databases. |
2924 | */ |
2925 | dumpACL(fout, dbCatId, dbDumpId, "DATABASE" , |
2926 | qdatname, NULL, NULL, |
2927 | dba, datacl, rdatacl, "" , "" ); |
2928 | |
2929 | /* |
2930 | * Now construct a DATABASE PROPERTIES archive entry to restore any |
2931 | * non-default database-level properties. (The reason this must be |
2932 | * separate is that we cannot put any additional commands into the TOC |
2933 | * entry that has CREATE DATABASE. pg_restore would execute such a group |
2934 | * in an implicit transaction block, and the backend won't allow CREATE |
2935 | * DATABASE in that context.) |
2936 | */ |
2937 | resetPQExpBuffer(creaQry); |
2938 | resetPQExpBuffer(delQry); |
2939 | |
2940 | if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1" ) != 0) |
2941 | appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n" , |
2942 | qdatname, datconnlimit); |
2943 | |
2944 | if (strcmp(datistemplate, "t" ) == 0) |
2945 | { |
2946 | appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n" , |
2947 | qdatname); |
2948 | |
2949 | /* |
2950 | * The backend won't accept DROP DATABASE on a template database. We |
2951 | * can deal with that by removing the template marking before the DROP |
2952 | * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but |
2953 | * since no such command is currently supported, fake it with a direct |
2954 | * UPDATE on pg_database. |
2955 | */ |
2956 | appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database " |
2957 | "SET datistemplate = false WHERE datname = " ); |
2958 | appendStringLiteralAH(delQry, datname, fout); |
2959 | appendPQExpBufferStr(delQry, ";\n" ); |
2960 | } |
2961 | |
2962 | /* Add database-specific SET options */ |
2963 | dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid); |
2964 | |
2965 | /* |
2966 | * We stick this binary-upgrade query into the DATABASE PROPERTIES archive |
2967 | * entry, too, for lack of a better place. |
2968 | */ |
2969 | if (dopt->binary_upgrade) |
2970 | { |
2971 | appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n" ); |
2972 | appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n" |
2973 | "SET datfrozenxid = '%u', datminmxid = '%u'\n" |
2974 | "WHERE datname = " , |
2975 | frozenxid, minmxid); |
2976 | appendStringLiteralAH(creaQry, datname, fout); |
2977 | appendPQExpBufferStr(creaQry, ";\n" ); |
2978 | } |
2979 | |
2980 | if (creaQry->len > 0) |
2981 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
2982 | ARCHIVE_OPTS(.tag = datname, |
2983 | .owner = dba, |
2984 | .description = "DATABASE PROPERTIES" , |
2985 | .section = SECTION_PRE_DATA, |
2986 | .createStmt = creaQry->data, |
2987 | .dropStmt = delQry->data, |
2988 | .deps = &dbDumpId)); |
2989 | |
2990 | /* |
2991 | * pg_largeobject comes from the old system intact, so set its |
2992 | * relfrozenxids and relminmxids. |
2993 | */ |
2994 | if (dopt->binary_upgrade) |
2995 | { |
2996 | PGresult *lo_res; |
2997 | PQExpBuffer loFrozenQry = createPQExpBuffer(); |
2998 | PQExpBuffer loOutQry = createPQExpBuffer(); |
2999 | int i_relfrozenxid, |
3000 | i_relminmxid; |
3001 | |
3002 | /* |
3003 | * pg_largeobject |
3004 | */ |
3005 | if (fout->remoteVersion >= 90300) |
3006 | appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid\n" |
3007 | "FROM pg_catalog.pg_class\n" |
3008 | "WHERE oid = %u;\n" , |
3009 | LargeObjectRelationId); |
3010 | else |
3011 | appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid\n" |
3012 | "FROM pg_catalog.pg_class\n" |
3013 | "WHERE oid = %u;\n" , |
3014 | LargeObjectRelationId); |
3015 | |
3016 | lo_res = ExecuteSqlQueryForSingleRow(fout, loFrozenQry->data); |
3017 | |
3018 | i_relfrozenxid = PQfnumber(lo_res, "relfrozenxid" ); |
3019 | i_relminmxid = PQfnumber(lo_res, "relminmxid" ); |
3020 | |
3021 | appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n" ); |
3022 | appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n" |
3023 | "SET relfrozenxid = '%u', relminmxid = '%u'\n" |
3024 | "WHERE oid = %u;\n" , |
3025 | atooid(PQgetvalue(lo_res, 0, i_relfrozenxid)), |
3026 | atooid(PQgetvalue(lo_res, 0, i_relminmxid)), |
3027 | LargeObjectRelationId); |
3028 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
3029 | ARCHIVE_OPTS(.tag = "pg_largeobject" , |
3030 | .description = "pg_largeobject" , |
3031 | .section = SECTION_PRE_DATA, |
3032 | .createStmt = loOutQry->data)); |
3033 | |
3034 | PQclear(lo_res); |
3035 | |
3036 | destroyPQExpBuffer(loFrozenQry); |
3037 | destroyPQExpBuffer(loOutQry); |
3038 | } |
3039 | |
3040 | PQclear(res); |
3041 | |
3042 | free(qdatname); |
3043 | destroyPQExpBuffer(dbQry); |
3044 | destroyPQExpBuffer(delQry); |
3045 | destroyPQExpBuffer(creaQry); |
3046 | destroyPQExpBuffer(labelq); |
3047 | } |
3048 | |
3049 | /* |
3050 | * Collect any database-specific or role-and-database-specific SET options |
3051 | * for this database, and append them to outbuf. |
3052 | */ |
3053 | static void |
3054 | dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf, |
3055 | const char *dbname, Oid dboid) |
3056 | { |
3057 | PGconn *conn = GetConnection(AH); |
3058 | PQExpBuffer buf = createPQExpBuffer(); |
3059 | PGresult *res; |
3060 | int count = 1; |
3061 | |
3062 | /* |
3063 | * First collect database-specific options. Pre-8.4 server versions lack |
3064 | * unnest(), so we do this the hard way by querying once per subscript. |
3065 | */ |
3066 | for (;;) |
3067 | { |
3068 | if (AH->remoteVersion >= 90000) |
3069 | printfPQExpBuffer(buf, "SELECT setconfig[%d] FROM pg_db_role_setting " |
3070 | "WHERE setrole = 0 AND setdatabase = '%u'::oid" , |
3071 | count, dboid); |
3072 | else |
3073 | printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE oid = '%u'::oid" , count, dboid); |
3074 | |
3075 | res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK); |
3076 | |
3077 | if (PQntuples(res) == 1 && |
3078 | !PQgetisnull(res, 0, 0)) |
3079 | { |
3080 | makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0), |
3081 | "DATABASE" , dbname, NULL, NULL, |
3082 | outbuf); |
3083 | PQclear(res); |
3084 | count++; |
3085 | } |
3086 | else |
3087 | { |
3088 | PQclear(res); |
3089 | break; |
3090 | } |
3091 | } |
3092 | |
3093 | /* Now look for role-and-database-specific options */ |
3094 | if (AH->remoteVersion >= 90000) |
3095 | { |
3096 | /* Here we can assume we have unnest() */ |
3097 | printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) " |
3098 | "FROM pg_db_role_setting s, pg_roles r " |
3099 | "WHERE setrole = r.oid AND setdatabase = '%u'::oid" , |
3100 | dboid); |
3101 | |
3102 | res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK); |
3103 | |
3104 | if (PQntuples(res) > 0) |
3105 | { |
3106 | int i; |
3107 | |
3108 | for (i = 0; i < PQntuples(res); i++) |
3109 | makeAlterConfigCommand(conn, PQgetvalue(res, i, 1), |
3110 | "ROLE" , PQgetvalue(res, i, 0), |
3111 | "DATABASE" , dbname, |
3112 | outbuf); |
3113 | } |
3114 | |
3115 | PQclear(res); |
3116 | } |
3117 | |
3118 | destroyPQExpBuffer(buf); |
3119 | } |
3120 | |
3121 | /* |
3122 | * dumpEncoding: put the correct encoding into the archive |
3123 | */ |
3124 | static void |
3125 | dumpEncoding(Archive *AH) |
3126 | { |
3127 | const char *encname = pg_encoding_to_char(AH->encoding); |
3128 | PQExpBuffer qry = createPQExpBuffer(); |
3129 | |
3130 | pg_log_info("saving encoding = %s" , encname); |
3131 | |
3132 | appendPQExpBufferStr(qry, "SET client_encoding = " ); |
3133 | appendStringLiteralAH(qry, encname, AH); |
3134 | appendPQExpBufferStr(qry, ";\n" ); |
3135 | |
3136 | ArchiveEntry(AH, nilCatalogId, createDumpId(), |
3137 | ARCHIVE_OPTS(.tag = "ENCODING" , |
3138 | .description = "ENCODING" , |
3139 | .section = SECTION_PRE_DATA, |
3140 | .createStmt = qry->data)); |
3141 | |
3142 | destroyPQExpBuffer(qry); |
3143 | } |
3144 | |
3145 | |
3146 | /* |
3147 | * dumpStdStrings: put the correct escape string behavior into the archive |
3148 | */ |
3149 | static void |
3150 | dumpStdStrings(Archive *AH) |
3151 | { |
3152 | const char *stdstrings = AH->std_strings ? "on" : "off" ; |
3153 | PQExpBuffer qry = createPQExpBuffer(); |
3154 | |
3155 | pg_log_info("saving standard_conforming_strings = %s" , |
3156 | stdstrings); |
3157 | |
3158 | appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n" , |
3159 | stdstrings); |
3160 | |
3161 | ArchiveEntry(AH, nilCatalogId, createDumpId(), |
3162 | ARCHIVE_OPTS(.tag = "STDSTRINGS" , |
3163 | .description = "STDSTRINGS" , |
3164 | .section = SECTION_PRE_DATA, |
3165 | .createStmt = qry->data)); |
3166 | |
3167 | destroyPQExpBuffer(qry); |
3168 | } |
3169 | |
3170 | /* |
3171 | * dumpSearchPath: record the active search_path in the archive |
3172 | */ |
3173 | static void |
3174 | dumpSearchPath(Archive *AH) |
3175 | { |
3176 | PQExpBuffer qry = createPQExpBuffer(); |
3177 | PQExpBuffer path = createPQExpBuffer(); |
3178 | PGresult *res; |
3179 | char **schemanames = NULL; |
3180 | int nschemanames = 0; |
3181 | int i; |
3182 | |
3183 | /* |
3184 | * We use the result of current_schemas(), not the search_path GUC, |
3185 | * because that might contain wildcards such as "$user", which won't |
3186 | * necessarily have the same value during restore. Also, this way avoids |
3187 | * listing schemas that may appear in search_path but not actually exist, |
3188 | * which seems like a prudent exclusion. |
3189 | */ |
3190 | res = ExecuteSqlQueryForSingleRow(AH, |
3191 | "SELECT pg_catalog.current_schemas(false)" ); |
3192 | |
3193 | if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames)) |
3194 | fatal("could not parse result of current_schemas()" ); |
3195 | |
3196 | /* |
3197 | * We use set_config(), not a simple "SET search_path" command, because |
3198 | * the latter has less-clean behavior if the search path is empty. While |
3199 | * that's likely to get fixed at some point, it seems like a good idea to |
3200 | * be as backwards-compatible as possible in what we put into archives. |
3201 | */ |
3202 | for (i = 0; i < nschemanames; i++) |
3203 | { |
3204 | if (i > 0) |
3205 | appendPQExpBufferStr(path, ", " ); |
3206 | appendPQExpBufferStr(path, fmtId(schemanames[i])); |
3207 | } |
3208 | |
3209 | appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', " ); |
3210 | appendStringLiteralAH(qry, path->data, AH); |
3211 | appendPQExpBufferStr(qry, ", false);\n" ); |
3212 | |
3213 | pg_log_info("saving search_path = %s" , path->data); |
3214 | |
3215 | ArchiveEntry(AH, nilCatalogId, createDumpId(), |
3216 | ARCHIVE_OPTS(.tag = "SEARCHPATH" , |
3217 | .description = "SEARCHPATH" , |
3218 | .section = SECTION_PRE_DATA, |
3219 | .createStmt = qry->data)); |
3220 | |
3221 | /* Also save it in AH->searchpath, in case we're doing plain text dump */ |
3222 | AH->searchpath = pg_strdup(qry->data); |
3223 | |
3224 | if (schemanames) |
3225 | free(schemanames); |
3226 | PQclear(res); |
3227 | destroyPQExpBuffer(qry); |
3228 | destroyPQExpBuffer(path); |
3229 | } |
3230 | |
3231 | |
3232 | /* |
3233 | * getBlobs: |
3234 | * Collect schema-level data about large objects |
3235 | */ |
3236 | static void |
3237 | getBlobs(Archive *fout) |
3238 | { |
3239 | DumpOptions *dopt = fout->dopt; |
3240 | PQExpBuffer blobQry = createPQExpBuffer(); |
3241 | BlobInfo *binfo; |
3242 | DumpableObject *bdata; |
3243 | PGresult *res; |
3244 | int ntups; |
3245 | int i; |
3246 | int i_oid; |
3247 | int i_lomowner; |
3248 | int i_lomacl; |
3249 | int i_rlomacl; |
3250 | int i_initlomacl; |
3251 | int i_initrlomacl; |
3252 | |
3253 | pg_log_info("reading large objects" ); |
3254 | |
3255 | /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ |
3256 | if (fout->remoteVersion >= 90600) |
3257 | { |
3258 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
3259 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
3260 | PQExpBuffer init_acl_subquery = createPQExpBuffer(); |
3261 | PQExpBuffer init_racl_subquery = createPQExpBuffer(); |
3262 | |
3263 | buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery, |
3264 | init_racl_subquery, "l.lomacl" , "l.lomowner" , "'L'" , |
3265 | dopt->binary_upgrade); |
3266 | |
3267 | appendPQExpBuffer(blobQry, |
3268 | "SELECT l.oid, (%s l.lomowner) AS rolname, " |
3269 | "%s AS lomacl, " |
3270 | "%s AS rlomacl, " |
3271 | "%s AS initlomacl, " |
3272 | "%s AS initrlomacl " |
3273 | "FROM pg_largeobject_metadata l " |
3274 | "LEFT JOIN pg_init_privs pip ON " |
3275 | "(l.oid = pip.objoid " |
3276 | "AND pip.classoid = 'pg_largeobject'::regclass " |
3277 | "AND pip.objsubid = 0) " , |
3278 | username_subquery, |
3279 | acl_subquery->data, |
3280 | racl_subquery->data, |
3281 | init_acl_subquery->data, |
3282 | init_racl_subquery->data); |
3283 | |
3284 | destroyPQExpBuffer(acl_subquery); |
3285 | destroyPQExpBuffer(racl_subquery); |
3286 | destroyPQExpBuffer(init_acl_subquery); |
3287 | destroyPQExpBuffer(init_racl_subquery); |
3288 | } |
3289 | else if (fout->remoteVersion >= 90000) |
3290 | appendPQExpBuffer(blobQry, |
3291 | "SELECT oid, (%s lomowner) AS rolname, lomacl, " |
3292 | "NULL AS rlomacl, NULL AS initlomacl, " |
3293 | "NULL AS initrlomacl " |
3294 | " FROM pg_largeobject_metadata" , |
3295 | username_subquery); |
3296 | else |
3297 | appendPQExpBufferStr(blobQry, |
3298 | "SELECT DISTINCT loid AS oid, " |
3299 | "NULL::name AS rolname, NULL::oid AS lomacl, " |
3300 | "NULL::oid AS rlomacl, NULL::oid AS initlomacl, " |
3301 | "NULL::oid AS initrlomacl " |
3302 | " FROM pg_largeobject" ); |
3303 | |
3304 | res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); |
3305 | |
3306 | i_oid = PQfnumber(res, "oid" ); |
3307 | i_lomowner = PQfnumber(res, "rolname" ); |
3308 | i_lomacl = PQfnumber(res, "lomacl" ); |
3309 | i_rlomacl = PQfnumber(res, "rlomacl" ); |
3310 | i_initlomacl = PQfnumber(res, "initlomacl" ); |
3311 | i_initrlomacl = PQfnumber(res, "initrlomacl" ); |
3312 | |
3313 | ntups = PQntuples(res); |
3314 | |
3315 | /* |
3316 | * Each large object has its own BLOB archive entry. |
3317 | */ |
3318 | binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); |
3319 | |
3320 | for (i = 0; i < ntups; i++) |
3321 | { |
3322 | binfo[i].dobj.objType = DO_BLOB; |
3323 | binfo[i].dobj.catId.tableoid = LargeObjectRelationId; |
3324 | binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
3325 | AssignDumpId(&binfo[i].dobj); |
3326 | |
3327 | binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); |
3328 | binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); |
3329 | binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); |
3330 | binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); |
3331 | binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); |
3332 | binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); |
3333 | |
3334 | if (PQgetisnull(res, i, i_lomacl) && |
3335 | PQgetisnull(res, i, i_rlomacl) && |
3336 | PQgetisnull(res, i, i_initlomacl) && |
3337 | PQgetisnull(res, i, i_initrlomacl)) |
3338 | binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
3339 | |
3340 | /* |
3341 | * In binary-upgrade mode for blobs, we do *not* dump out the blob |
3342 | * data, as it will be copied by pg_upgrade, which simply copies the |
3343 | * pg_largeobject table. We *do* however dump out anything but the |
3344 | * data, as pg_upgrade copies just pg_largeobject, but not |
3345 | * pg_largeobject_metadata, after the dump is restored. |
3346 | */ |
3347 | if (dopt->binary_upgrade) |
3348 | binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; |
3349 | } |
3350 | |
3351 | /* |
3352 | * If we have any large objects, a "BLOBS" archive entry is needed. This |
3353 | * is just a placeholder for sorting; it carries no data now. |
3354 | */ |
3355 | if (ntups > 0) |
3356 | { |
3357 | bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); |
3358 | bdata->objType = DO_BLOB_DATA; |
3359 | bdata->catId = nilCatalogId; |
3360 | AssignDumpId(bdata); |
3361 | bdata->name = pg_strdup("BLOBS" ); |
3362 | } |
3363 | |
3364 | PQclear(res); |
3365 | destroyPQExpBuffer(blobQry); |
3366 | } |
3367 | |
3368 | /* |
3369 | * dumpBlob |
3370 | * |
3371 | * dump the definition (metadata) of the given large object |
3372 | */ |
3373 | static void |
3374 | dumpBlob(Archive *fout, BlobInfo *binfo) |
3375 | { |
3376 | PQExpBuffer cquery = createPQExpBuffer(); |
3377 | PQExpBuffer dquery = createPQExpBuffer(); |
3378 | |
3379 | appendPQExpBuffer(cquery, |
3380 | "SELECT pg_catalog.lo_create('%s');\n" , |
3381 | binfo->dobj.name); |
3382 | |
3383 | appendPQExpBuffer(dquery, |
3384 | "SELECT pg_catalog.lo_unlink('%s');\n" , |
3385 | binfo->dobj.name); |
3386 | |
3387 | if (binfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
3388 | ArchiveEntry(fout, binfo->dobj.catId, binfo->dobj.dumpId, |
3389 | ARCHIVE_OPTS(.tag = binfo->dobj.name, |
3390 | .owner = binfo->rolname, |
3391 | .description = "BLOB" , |
3392 | .section = SECTION_PRE_DATA, |
3393 | .createStmt = cquery->data, |
3394 | .dropStmt = dquery->data)); |
3395 | |
3396 | /* Dump comment if any */ |
3397 | if (binfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
3398 | dumpComment(fout, "LARGE OBJECT" , binfo->dobj.name, |
3399 | NULL, binfo->rolname, |
3400 | binfo->dobj.catId, 0, binfo->dobj.dumpId); |
3401 | |
3402 | /* Dump security label if any */ |
3403 | if (binfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
3404 | dumpSecLabel(fout, "LARGE OBJECT" , binfo->dobj.name, |
3405 | NULL, binfo->rolname, |
3406 | binfo->dobj.catId, 0, binfo->dobj.dumpId); |
3407 | |
3408 | /* Dump ACL if any */ |
3409 | if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL)) |
3410 | dumpACL(fout, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT" , |
3411 | binfo->dobj.name, NULL, |
3412 | NULL, binfo->rolname, binfo->blobacl, binfo->rblobacl, |
3413 | binfo->initblobacl, binfo->initrblobacl); |
3414 | |
3415 | destroyPQExpBuffer(cquery); |
3416 | destroyPQExpBuffer(dquery); |
3417 | } |
3418 | |
3419 | /* |
3420 | * dumpBlobs: |
3421 | * dump the data contents of all large objects |
3422 | */ |
3423 | static int |
3424 | dumpBlobs(Archive *fout, void *arg) |
3425 | { |
3426 | const char *blobQry; |
3427 | const char *blobFetchQry; |
3428 | PGconn *conn = GetConnection(fout); |
3429 | PGresult *res; |
3430 | char buf[LOBBUFSIZE]; |
3431 | int ntups; |
3432 | int i; |
3433 | int cnt; |
3434 | |
3435 | pg_log_info("saving large objects" ); |
3436 | |
3437 | /* |
3438 | * Currently, we re-fetch all BLOB OIDs using a cursor. Consider scanning |
3439 | * the already-in-memory dumpable objects instead... |
3440 | */ |
3441 | if (fout->remoteVersion >= 90000) |
3442 | blobQry = |
3443 | "DECLARE bloboid CURSOR FOR " |
3444 | "SELECT oid FROM pg_largeobject_metadata ORDER BY 1" ; |
3445 | else |
3446 | blobQry = |
3447 | "DECLARE bloboid CURSOR FOR " |
3448 | "SELECT DISTINCT loid FROM pg_largeobject ORDER BY 1" ; |
3449 | |
3450 | ExecuteSqlStatement(fout, blobQry); |
3451 | |
3452 | /* Command to fetch from cursor */ |
3453 | blobFetchQry = "FETCH 1000 IN bloboid" ; |
3454 | |
3455 | do |
3456 | { |
3457 | /* Do a fetch */ |
3458 | res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); |
3459 | |
3460 | /* Process the tuples, if any */ |
3461 | ntups = PQntuples(res); |
3462 | for (i = 0; i < ntups; i++) |
3463 | { |
3464 | Oid blobOid; |
3465 | int loFd; |
3466 | |
3467 | blobOid = atooid(PQgetvalue(res, i, 0)); |
3468 | /* Open the BLOB */ |
3469 | loFd = lo_open(conn, blobOid, INV_READ); |
3470 | if (loFd == -1) |
3471 | fatal("could not open large object %u: %s" , |
3472 | blobOid, PQerrorMessage(conn)); |
3473 | |
3474 | StartBlob(fout, blobOid); |
3475 | |
3476 | /* Now read it in chunks, sending data to archive */ |
3477 | do |
3478 | { |
3479 | cnt = lo_read(conn, loFd, buf, LOBBUFSIZE); |
3480 | if (cnt < 0) |
3481 | fatal("error reading large object %u: %s" , |
3482 | blobOid, PQerrorMessage(conn)); |
3483 | |
3484 | WriteData(fout, buf, cnt); |
3485 | } while (cnt > 0); |
3486 | |
3487 | lo_close(conn, loFd); |
3488 | |
3489 | EndBlob(fout, blobOid); |
3490 | } |
3491 | |
3492 | PQclear(res); |
3493 | } while (ntups > 0); |
3494 | |
3495 | return 1; |
3496 | } |
3497 | |
3498 | /* |
3499 | * getPolicies |
3500 | * get information about policies on a dumpable table. |
3501 | */ |
3502 | void |
3503 | getPolicies(Archive *fout, TableInfo tblinfo[], int numTables) |
3504 | { |
3505 | PQExpBuffer query; |
3506 | PGresult *res; |
3507 | PolicyInfo *polinfo; |
3508 | int i_oid; |
3509 | int i_tableoid; |
3510 | int i_polname; |
3511 | int i_polcmd; |
3512 | int i_polpermissive; |
3513 | int i_polroles; |
3514 | int i_polqual; |
3515 | int i_polwithcheck; |
3516 | int i, |
3517 | j, |
3518 | ntups; |
3519 | |
3520 | if (fout->remoteVersion < 90500) |
3521 | return; |
3522 | |
3523 | query = createPQExpBuffer(); |
3524 | |
3525 | for (i = 0; i < numTables; i++) |
3526 | { |
3527 | TableInfo *tbinfo = &tblinfo[i]; |
3528 | |
3529 | /* Ignore row security on tables not to be dumped */ |
3530 | if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY)) |
3531 | continue; |
3532 | |
3533 | pg_log_info("reading row security enabled for table \"%s.%s\"" , |
3534 | tbinfo->dobj.namespace->dobj.name, |
3535 | tbinfo->dobj.name); |
3536 | |
3537 | /* |
3538 | * Get row security enabled information for the table. We represent |
3539 | * RLS being enabled on a table by creating a PolicyInfo object with |
3540 | * null polname. |
3541 | */ |
3542 | if (tbinfo->rowsec) |
3543 | { |
3544 | /* |
3545 | * Note: use tableoid 0 so that this object won't be mistaken for |
3546 | * something that pg_depend entries apply to. |
3547 | */ |
3548 | polinfo = pg_malloc(sizeof(PolicyInfo)); |
3549 | polinfo->dobj.objType = DO_POLICY; |
3550 | polinfo->dobj.catId.tableoid = 0; |
3551 | polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid; |
3552 | AssignDumpId(&polinfo->dobj); |
3553 | polinfo->dobj.namespace = tbinfo->dobj.namespace; |
3554 | polinfo->dobj.name = pg_strdup(tbinfo->dobj.name); |
3555 | polinfo->poltable = tbinfo; |
3556 | polinfo->polname = NULL; |
3557 | polinfo->polcmd = '\0'; |
3558 | polinfo->polpermissive = 0; |
3559 | polinfo->polroles = NULL; |
3560 | polinfo->polqual = NULL; |
3561 | polinfo->polwithcheck = NULL; |
3562 | } |
3563 | |
3564 | pg_log_info("reading policies for table \"%s.%s\"" , |
3565 | tbinfo->dobj.namespace->dobj.name, |
3566 | tbinfo->dobj.name); |
3567 | |
3568 | resetPQExpBuffer(query); |
3569 | |
3570 | /* Get the policies for the table. */ |
3571 | if (fout->remoteVersion >= 100000) |
3572 | appendPQExpBuffer(query, |
3573 | "SELECT oid, tableoid, pol.polname, pol.polcmd, pol.polpermissive, " |
3574 | "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE " |
3575 | " 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, " |
3576 | "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, " |
3577 | "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck " |
3578 | "FROM pg_catalog.pg_policy pol " |
3579 | "WHERE polrelid = '%u'" , |
3580 | tbinfo->dobj.catId.oid); |
3581 | else |
3582 | appendPQExpBuffer(query, |
3583 | "SELECT oid, tableoid, pol.polname, pol.polcmd, 't' as polpermissive, " |
3584 | "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE " |
3585 | " 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, " |
3586 | "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, " |
3587 | "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck " |
3588 | "FROM pg_catalog.pg_policy pol " |
3589 | "WHERE polrelid = '%u'" , |
3590 | tbinfo->dobj.catId.oid); |
3591 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
3592 | |
3593 | ntups = PQntuples(res); |
3594 | |
3595 | if (ntups == 0) |
3596 | { |
3597 | /* |
3598 | * No explicit policies to handle (only the default-deny policy, |
3599 | * which is handled as part of the table definition). Clean up |
3600 | * and return. |
3601 | */ |
3602 | PQclear(res); |
3603 | continue; |
3604 | } |
3605 | |
3606 | i_oid = PQfnumber(res, "oid" ); |
3607 | i_tableoid = PQfnumber(res, "tableoid" ); |
3608 | i_polname = PQfnumber(res, "polname" ); |
3609 | i_polcmd = PQfnumber(res, "polcmd" ); |
3610 | i_polpermissive = PQfnumber(res, "polpermissive" ); |
3611 | i_polroles = PQfnumber(res, "polroles" ); |
3612 | i_polqual = PQfnumber(res, "polqual" ); |
3613 | i_polwithcheck = PQfnumber(res, "polwithcheck" ); |
3614 | |
3615 | polinfo = pg_malloc(ntups * sizeof(PolicyInfo)); |
3616 | |
3617 | for (j = 0; j < ntups; j++) |
3618 | { |
3619 | polinfo[j].dobj.objType = DO_POLICY; |
3620 | polinfo[j].dobj.catId.tableoid = |
3621 | atooid(PQgetvalue(res, j, i_tableoid)); |
3622 | polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid)); |
3623 | AssignDumpId(&polinfo[j].dobj); |
3624 | polinfo[j].dobj.namespace = tbinfo->dobj.namespace; |
3625 | polinfo[j].poltable = tbinfo; |
3626 | polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname)); |
3627 | polinfo[j].dobj.name = pg_strdup(polinfo[j].polname); |
3628 | |
3629 | polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd)); |
3630 | polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't'; |
3631 | |
3632 | if (PQgetisnull(res, j, i_polroles)) |
3633 | polinfo[j].polroles = NULL; |
3634 | else |
3635 | polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles)); |
3636 | |
3637 | if (PQgetisnull(res, j, i_polqual)) |
3638 | polinfo[j].polqual = NULL; |
3639 | else |
3640 | polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual)); |
3641 | |
3642 | if (PQgetisnull(res, j, i_polwithcheck)) |
3643 | polinfo[j].polwithcheck = NULL; |
3644 | else |
3645 | polinfo[j].polwithcheck |
3646 | = pg_strdup(PQgetvalue(res, j, i_polwithcheck)); |
3647 | } |
3648 | PQclear(res); |
3649 | } |
3650 | destroyPQExpBuffer(query); |
3651 | } |
3652 | |
3653 | /* |
3654 | * dumpPolicy |
3655 | * dump the definition of the given policy |
3656 | */ |
3657 | static void |
3658 | dumpPolicy(Archive *fout, PolicyInfo *polinfo) |
3659 | { |
3660 | DumpOptions *dopt = fout->dopt; |
3661 | TableInfo *tbinfo = polinfo->poltable; |
3662 | PQExpBuffer query; |
3663 | PQExpBuffer delqry; |
3664 | const char *cmd; |
3665 | char *tag; |
3666 | |
3667 | if (dopt->dataOnly) |
3668 | return; |
3669 | |
3670 | /* |
3671 | * If polname is NULL, then this record is just indicating that ROW LEVEL |
3672 | * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE |
3673 | * ROW LEVEL SECURITY. |
3674 | */ |
3675 | if (polinfo->polname == NULL) |
3676 | { |
3677 | query = createPQExpBuffer(); |
3678 | |
3679 | appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;" , |
3680 | fmtQualifiedDumpable(tbinfo)); |
3681 | |
3682 | /* |
3683 | * We must emit the ROW SECURITY object's dependency on its table |
3684 | * explicitly, because it will not match anything in pg_depend (unlike |
3685 | * the case for other PolicyInfo objects). |
3686 | */ |
3687 | if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY) |
3688 | ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId, |
3689 | ARCHIVE_OPTS(.tag = polinfo->dobj.name, |
3690 | .namespace = polinfo->dobj.namespace->dobj.name, |
3691 | .owner = tbinfo->rolname, |
3692 | .description = "ROW SECURITY" , |
3693 | .section = SECTION_POST_DATA, |
3694 | .createStmt = query->data, |
3695 | .deps = &(tbinfo->dobj.dumpId), |
3696 | .nDeps = 1)); |
3697 | |
3698 | destroyPQExpBuffer(query); |
3699 | return; |
3700 | } |
3701 | |
3702 | if (polinfo->polcmd == '*') |
3703 | cmd = "" ; |
3704 | else if (polinfo->polcmd == 'r') |
3705 | cmd = " FOR SELECT" ; |
3706 | else if (polinfo->polcmd == 'a') |
3707 | cmd = " FOR INSERT" ; |
3708 | else if (polinfo->polcmd == 'w') |
3709 | cmd = " FOR UPDATE" ; |
3710 | else if (polinfo->polcmd == 'd') |
3711 | cmd = " FOR DELETE" ; |
3712 | else |
3713 | { |
3714 | pg_log_error("unexpected policy command type: %c" , |
3715 | polinfo->polcmd); |
3716 | exit_nicely(1); |
3717 | } |
3718 | |
3719 | query = createPQExpBuffer(); |
3720 | delqry = createPQExpBuffer(); |
3721 | |
3722 | appendPQExpBuffer(query, "CREATE POLICY %s" , fmtId(polinfo->polname)); |
3723 | |
3724 | appendPQExpBuffer(query, " ON %s%s%s" , fmtQualifiedDumpable(tbinfo), |
3725 | !polinfo->polpermissive ? " AS RESTRICTIVE" : "" , cmd); |
3726 | |
3727 | if (polinfo->polroles != NULL) |
3728 | appendPQExpBuffer(query, " TO %s" , polinfo->polroles); |
3729 | |
3730 | if (polinfo->polqual != NULL) |
3731 | appendPQExpBuffer(query, " USING (%s)" , polinfo->polqual); |
3732 | |
3733 | if (polinfo->polwithcheck != NULL) |
3734 | appendPQExpBuffer(query, " WITH CHECK (%s)" , polinfo->polwithcheck); |
3735 | |
3736 | appendPQExpBuffer(query, ";\n" ); |
3737 | |
3738 | appendPQExpBuffer(delqry, "DROP POLICY %s" , fmtId(polinfo->polname)); |
3739 | appendPQExpBuffer(delqry, " ON %s;\n" , fmtQualifiedDumpable(tbinfo)); |
3740 | |
3741 | tag = psprintf("%s %s" , tbinfo->dobj.name, polinfo->dobj.name); |
3742 | |
3743 | if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY) |
3744 | ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId, |
3745 | ARCHIVE_OPTS(.tag = tag, |
3746 | .namespace = polinfo->dobj.namespace->dobj.name, |
3747 | .owner = tbinfo->rolname, |
3748 | .description = "POLICY" , |
3749 | .section = SECTION_POST_DATA, |
3750 | .createStmt = query->data, |
3751 | .dropStmt = delqry->data)); |
3752 | |
3753 | free(tag); |
3754 | destroyPQExpBuffer(query); |
3755 | destroyPQExpBuffer(delqry); |
3756 | } |
3757 | |
3758 | /* |
3759 | * getPublications |
3760 | * get information about publications |
3761 | */ |
3762 | void |
3763 | getPublications(Archive *fout) |
3764 | { |
3765 | DumpOptions *dopt = fout->dopt; |
3766 | PQExpBuffer query; |
3767 | PGresult *res; |
3768 | PublicationInfo *pubinfo; |
3769 | int i_tableoid; |
3770 | int i_oid; |
3771 | int i_pubname; |
3772 | int i_rolname; |
3773 | int i_puballtables; |
3774 | int i_pubinsert; |
3775 | int i_pubupdate; |
3776 | int i_pubdelete; |
3777 | int i_pubtruncate; |
3778 | int i, |
3779 | ntups; |
3780 | |
3781 | if (dopt->no_publications || fout->remoteVersion < 100000) |
3782 | return; |
3783 | |
3784 | query = createPQExpBuffer(); |
3785 | |
3786 | resetPQExpBuffer(query); |
3787 | |
3788 | /* Get the publications. */ |
3789 | if (fout->remoteVersion >= 110000) |
3790 | appendPQExpBuffer(query, |
3791 | "SELECT p.tableoid, p.oid, p.pubname, " |
3792 | "(%s p.pubowner) AS rolname, " |
3793 | "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate " |
3794 | "FROM pg_publication p" , |
3795 | username_subquery); |
3796 | else |
3797 | appendPQExpBuffer(query, |
3798 | "SELECT p.tableoid, p.oid, p.pubname, " |
3799 | "(%s p.pubowner) AS rolname, " |
3800 | "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate " |
3801 | "FROM pg_publication p" , |
3802 | username_subquery); |
3803 | |
3804 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
3805 | |
3806 | ntups = PQntuples(res); |
3807 | |
3808 | i_tableoid = PQfnumber(res, "tableoid" ); |
3809 | i_oid = PQfnumber(res, "oid" ); |
3810 | i_pubname = PQfnumber(res, "pubname" ); |
3811 | i_rolname = PQfnumber(res, "rolname" ); |
3812 | i_puballtables = PQfnumber(res, "puballtables" ); |
3813 | i_pubinsert = PQfnumber(res, "pubinsert" ); |
3814 | i_pubupdate = PQfnumber(res, "pubupdate" ); |
3815 | i_pubdelete = PQfnumber(res, "pubdelete" ); |
3816 | i_pubtruncate = PQfnumber(res, "pubtruncate" ); |
3817 | |
3818 | pubinfo = pg_malloc(ntups * sizeof(PublicationInfo)); |
3819 | |
3820 | for (i = 0; i < ntups; i++) |
3821 | { |
3822 | pubinfo[i].dobj.objType = DO_PUBLICATION; |
3823 | pubinfo[i].dobj.catId.tableoid = |
3824 | atooid(PQgetvalue(res, i, i_tableoid)); |
3825 | pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
3826 | AssignDumpId(&pubinfo[i].dobj); |
3827 | pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname)); |
3828 | pubinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
3829 | pubinfo[i].puballtables = |
3830 | (strcmp(PQgetvalue(res, i, i_puballtables), "t" ) == 0); |
3831 | pubinfo[i].pubinsert = |
3832 | (strcmp(PQgetvalue(res, i, i_pubinsert), "t" ) == 0); |
3833 | pubinfo[i].pubupdate = |
3834 | (strcmp(PQgetvalue(res, i, i_pubupdate), "t" ) == 0); |
3835 | pubinfo[i].pubdelete = |
3836 | (strcmp(PQgetvalue(res, i, i_pubdelete), "t" ) == 0); |
3837 | pubinfo[i].pubtruncate = |
3838 | (strcmp(PQgetvalue(res, i, i_pubtruncate), "t" ) == 0); |
3839 | |
3840 | if (strlen(pubinfo[i].rolname) == 0) |
3841 | pg_log_warning("owner of publication \"%s\" appears to be invalid" , |
3842 | pubinfo[i].dobj.name); |
3843 | |
3844 | /* Decide whether we want to dump it */ |
3845 | selectDumpableObject(&(pubinfo[i].dobj), fout); |
3846 | } |
3847 | PQclear(res); |
3848 | |
3849 | destroyPQExpBuffer(query); |
3850 | } |
3851 | |
3852 | /* |
3853 | * dumpPublication |
3854 | * dump the definition of the given publication |
3855 | */ |
3856 | static void |
3857 | dumpPublication(Archive *fout, PublicationInfo *pubinfo) |
3858 | { |
3859 | PQExpBuffer delq; |
3860 | PQExpBuffer query; |
3861 | char *qpubname; |
3862 | bool first = true; |
3863 | |
3864 | if (!(pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) |
3865 | return; |
3866 | |
3867 | delq = createPQExpBuffer(); |
3868 | query = createPQExpBuffer(); |
3869 | |
3870 | qpubname = pg_strdup(fmtId(pubinfo->dobj.name)); |
3871 | |
3872 | appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n" , |
3873 | qpubname); |
3874 | |
3875 | appendPQExpBuffer(query, "CREATE PUBLICATION %s" , |
3876 | qpubname); |
3877 | |
3878 | if (pubinfo->puballtables) |
3879 | appendPQExpBufferStr(query, " FOR ALL TABLES" ); |
3880 | |
3881 | appendPQExpBufferStr(query, " WITH (publish = '" ); |
3882 | if (pubinfo->pubinsert) |
3883 | { |
3884 | appendPQExpBufferStr(query, "insert" ); |
3885 | first = false; |
3886 | } |
3887 | |
3888 | if (pubinfo->pubupdate) |
3889 | { |
3890 | if (!first) |
3891 | appendPQExpBufferStr(query, ", " ); |
3892 | |
3893 | appendPQExpBufferStr(query, "update" ); |
3894 | first = false; |
3895 | } |
3896 | |
3897 | if (pubinfo->pubdelete) |
3898 | { |
3899 | if (!first) |
3900 | appendPQExpBufferStr(query, ", " ); |
3901 | |
3902 | appendPQExpBufferStr(query, "delete" ); |
3903 | first = false; |
3904 | } |
3905 | |
3906 | if (pubinfo->pubtruncate) |
3907 | { |
3908 | if (!first) |
3909 | appendPQExpBufferStr(query, ", " ); |
3910 | |
3911 | appendPQExpBufferStr(query, "truncate" ); |
3912 | first = false; |
3913 | } |
3914 | |
3915 | appendPQExpBufferStr(query, "');\n" ); |
3916 | |
3917 | ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId, |
3918 | ARCHIVE_OPTS(.tag = pubinfo->dobj.name, |
3919 | .owner = pubinfo->rolname, |
3920 | .description = "PUBLICATION" , |
3921 | .section = SECTION_POST_DATA, |
3922 | .createStmt = query->data, |
3923 | .dropStmt = delq->data)); |
3924 | |
3925 | if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
3926 | dumpComment(fout, "PUBLICATION" , qpubname, |
3927 | NULL, pubinfo->rolname, |
3928 | pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId); |
3929 | |
3930 | if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
3931 | dumpSecLabel(fout, "PUBLICATION" , qpubname, |
3932 | NULL, pubinfo->rolname, |
3933 | pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId); |
3934 | |
3935 | destroyPQExpBuffer(delq); |
3936 | destroyPQExpBuffer(query); |
3937 | free(qpubname); |
3938 | } |
3939 | |
3940 | /* |
3941 | * getPublicationTables |
3942 | * get information about publication membership for dumpable tables. |
3943 | */ |
3944 | void |
3945 | getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables) |
3946 | { |
3947 | PQExpBuffer query; |
3948 | PGresult *res; |
3949 | PublicationRelInfo *pubrinfo; |
3950 | DumpOptions *dopt = fout->dopt; |
3951 | int i_tableoid; |
3952 | int i_oid; |
3953 | int i_pubname; |
3954 | int i, |
3955 | j, |
3956 | ntups; |
3957 | |
3958 | if (dopt->no_publications || fout->remoteVersion < 100000) |
3959 | return; |
3960 | |
3961 | query = createPQExpBuffer(); |
3962 | |
3963 | for (i = 0; i < numTables; i++) |
3964 | { |
3965 | TableInfo *tbinfo = &tblinfo[i]; |
3966 | |
3967 | /* Only plain tables can be aded to publications. */ |
3968 | if (tbinfo->relkind != RELKIND_RELATION) |
3969 | continue; |
3970 | |
3971 | /* |
3972 | * Ignore publication membership of tables whose definitions are not |
3973 | * to be dumped. |
3974 | */ |
3975 | if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) |
3976 | continue; |
3977 | |
3978 | pg_log_info("reading publication membership for table \"%s.%s\"" , |
3979 | tbinfo->dobj.namespace->dobj.name, |
3980 | tbinfo->dobj.name); |
3981 | |
3982 | resetPQExpBuffer(query); |
3983 | |
3984 | /* Get the publication membership for the table. */ |
3985 | appendPQExpBuffer(query, |
3986 | "SELECT pr.tableoid, pr.oid, p.pubname " |
3987 | "FROM pg_publication_rel pr, pg_publication p " |
3988 | "WHERE pr.prrelid = '%u'" |
3989 | " AND p.oid = pr.prpubid" , |
3990 | tbinfo->dobj.catId.oid); |
3991 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
3992 | |
3993 | ntups = PQntuples(res); |
3994 | |
3995 | if (ntups == 0) |
3996 | { |
3997 | /* |
3998 | * Table is not member of any publications. Clean up and return. |
3999 | */ |
4000 | PQclear(res); |
4001 | continue; |
4002 | } |
4003 | |
4004 | i_tableoid = PQfnumber(res, "tableoid" ); |
4005 | i_oid = PQfnumber(res, "oid" ); |
4006 | i_pubname = PQfnumber(res, "pubname" ); |
4007 | |
4008 | pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo)); |
4009 | |
4010 | for (j = 0; j < ntups; j++) |
4011 | { |
4012 | pubrinfo[j].dobj.objType = DO_PUBLICATION_REL; |
4013 | pubrinfo[j].dobj.catId.tableoid = |
4014 | atooid(PQgetvalue(res, j, i_tableoid)); |
4015 | pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid)); |
4016 | AssignDumpId(&pubrinfo[j].dobj); |
4017 | pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace; |
4018 | pubrinfo[j].dobj.name = tbinfo->dobj.name; |
4019 | pubrinfo[j].pubname = pg_strdup(PQgetvalue(res, j, i_pubname)); |
4020 | pubrinfo[j].pubtable = tbinfo; |
4021 | |
4022 | /* Decide whether we want to dump it */ |
4023 | selectDumpablePublicationTable(&(pubrinfo[j].dobj), fout); |
4024 | } |
4025 | PQclear(res); |
4026 | } |
4027 | destroyPQExpBuffer(query); |
4028 | } |
4029 | |
4030 | /* |
4031 | * dumpPublicationTable |
4032 | * dump the definition of the given publication table mapping |
4033 | */ |
4034 | static void |
4035 | dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo) |
4036 | { |
4037 | TableInfo *tbinfo = pubrinfo->pubtable; |
4038 | PQExpBuffer query; |
4039 | char *tag; |
4040 | |
4041 | if (!(pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) |
4042 | return; |
4043 | |
4044 | tag = psprintf("%s %s" , pubrinfo->pubname, tbinfo->dobj.name); |
4045 | |
4046 | query = createPQExpBuffer(); |
4047 | |
4048 | appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY" , |
4049 | fmtId(pubrinfo->pubname)); |
4050 | appendPQExpBuffer(query, " %s;\n" , |
4051 | fmtQualifiedDumpable(tbinfo)); |
4052 | |
4053 | /* |
4054 | * There is no point in creating drop query as the drop is done by table |
4055 | * drop. |
4056 | */ |
4057 | ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId, |
4058 | ARCHIVE_OPTS(.tag = tag, |
4059 | .namespace = tbinfo->dobj.namespace->dobj.name, |
4060 | .description = "PUBLICATION TABLE" , |
4061 | .section = SECTION_POST_DATA, |
4062 | .createStmt = query->data)); |
4063 | |
4064 | free(tag); |
4065 | destroyPQExpBuffer(query); |
4066 | } |
4067 | |
4068 | /* |
4069 | * Is the currently connected user a superuser? |
4070 | */ |
4071 | static bool |
4072 | is_superuser(Archive *fout) |
4073 | { |
4074 | ArchiveHandle *AH = (ArchiveHandle *) fout; |
4075 | const char *val; |
4076 | |
4077 | val = PQparameterStatus(AH->connection, "is_superuser" ); |
4078 | |
4079 | if (val && strcmp(val, "on" ) == 0) |
4080 | return true; |
4081 | |
4082 | return false; |
4083 | } |
4084 | |
4085 | /* |
4086 | * getSubscriptions |
4087 | * get information about subscriptions |
4088 | */ |
4089 | void |
4090 | getSubscriptions(Archive *fout) |
4091 | { |
4092 | DumpOptions *dopt = fout->dopt; |
4093 | PQExpBuffer query; |
4094 | PGresult *res; |
4095 | SubscriptionInfo *subinfo; |
4096 | int i_tableoid; |
4097 | int i_oid; |
4098 | int i_subname; |
4099 | int i_rolname; |
4100 | int i_subconninfo; |
4101 | int i_subslotname; |
4102 | int i_subsynccommit; |
4103 | int i_subpublications; |
4104 | int i, |
4105 | ntups; |
4106 | |
4107 | if (dopt->no_subscriptions || fout->remoteVersion < 100000) |
4108 | return; |
4109 | |
4110 | if (!is_superuser(fout)) |
4111 | { |
4112 | int n; |
4113 | |
4114 | res = ExecuteSqlQuery(fout, |
4115 | "SELECT count(*) FROM pg_subscription " |
4116 | "WHERE subdbid = (SELECT oid FROM pg_database" |
4117 | " WHERE datname = current_database())" , |
4118 | PGRES_TUPLES_OK); |
4119 | n = atoi(PQgetvalue(res, 0, 0)); |
4120 | if (n > 0) |
4121 | pg_log_warning("subscriptions not dumped because current user is not a superuser" ); |
4122 | PQclear(res); |
4123 | return; |
4124 | } |
4125 | |
4126 | query = createPQExpBuffer(); |
4127 | |
4128 | resetPQExpBuffer(query); |
4129 | |
4130 | /* Get the subscriptions in current database. */ |
4131 | appendPQExpBuffer(query, |
4132 | "SELECT s.tableoid, s.oid, s.subname," |
4133 | "(%s s.subowner) AS rolname, " |
4134 | " s.subconninfo, s.subslotname, s.subsynccommit, " |
4135 | " s.subpublications " |
4136 | "FROM pg_subscription s " |
4137 | "WHERE s.subdbid = (SELECT oid FROM pg_database" |
4138 | " WHERE datname = current_database())" , |
4139 | username_subquery); |
4140 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
4141 | |
4142 | ntups = PQntuples(res); |
4143 | |
4144 | i_tableoid = PQfnumber(res, "tableoid" ); |
4145 | i_oid = PQfnumber(res, "oid" ); |
4146 | i_subname = PQfnumber(res, "subname" ); |
4147 | i_rolname = PQfnumber(res, "rolname" ); |
4148 | i_subconninfo = PQfnumber(res, "subconninfo" ); |
4149 | i_subslotname = PQfnumber(res, "subslotname" ); |
4150 | i_subsynccommit = PQfnumber(res, "subsynccommit" ); |
4151 | i_subpublications = PQfnumber(res, "subpublications" ); |
4152 | |
4153 | subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo)); |
4154 | |
4155 | for (i = 0; i < ntups; i++) |
4156 | { |
4157 | subinfo[i].dobj.objType = DO_SUBSCRIPTION; |
4158 | subinfo[i].dobj.catId.tableoid = |
4159 | atooid(PQgetvalue(res, i, i_tableoid)); |
4160 | subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
4161 | AssignDumpId(&subinfo[i].dobj); |
4162 | subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname)); |
4163 | subinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
4164 | subinfo[i].subconninfo = pg_strdup(PQgetvalue(res, i, i_subconninfo)); |
4165 | if (PQgetisnull(res, i, i_subslotname)) |
4166 | subinfo[i].subslotname = NULL; |
4167 | else |
4168 | subinfo[i].subslotname = pg_strdup(PQgetvalue(res, i, i_subslotname)); |
4169 | subinfo[i].subsynccommit = |
4170 | pg_strdup(PQgetvalue(res, i, i_subsynccommit)); |
4171 | subinfo[i].subpublications = |
4172 | pg_strdup(PQgetvalue(res, i, i_subpublications)); |
4173 | |
4174 | if (strlen(subinfo[i].rolname) == 0) |
4175 | pg_log_warning("owner of subscription \"%s\" appears to be invalid" , |
4176 | subinfo[i].dobj.name); |
4177 | |
4178 | /* Decide whether we want to dump it */ |
4179 | selectDumpableObject(&(subinfo[i].dobj), fout); |
4180 | } |
4181 | PQclear(res); |
4182 | |
4183 | destroyPQExpBuffer(query); |
4184 | } |
4185 | |
4186 | /* |
4187 | * dumpSubscription |
4188 | * dump the definition of the given subscription |
4189 | */ |
4190 | static void |
4191 | dumpSubscription(Archive *fout, SubscriptionInfo *subinfo) |
4192 | { |
4193 | PQExpBuffer delq; |
4194 | PQExpBuffer query; |
4195 | PQExpBuffer publications; |
4196 | char *qsubname; |
4197 | char **pubnames = NULL; |
4198 | int npubnames = 0; |
4199 | int i; |
4200 | |
4201 | if (!(subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) |
4202 | return; |
4203 | |
4204 | delq = createPQExpBuffer(); |
4205 | query = createPQExpBuffer(); |
4206 | |
4207 | qsubname = pg_strdup(fmtId(subinfo->dobj.name)); |
4208 | |
4209 | appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n" , |
4210 | qsubname); |
4211 | |
4212 | appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION " , |
4213 | qsubname); |
4214 | appendStringLiteralAH(query, subinfo->subconninfo, fout); |
4215 | |
4216 | /* Build list of quoted publications and append them to query. */ |
4217 | if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames)) |
4218 | { |
4219 | pg_log_warning("could not parse subpublications array" ); |
4220 | if (pubnames) |
4221 | free(pubnames); |
4222 | pubnames = NULL; |
4223 | npubnames = 0; |
4224 | } |
4225 | |
4226 | publications = createPQExpBuffer(); |
4227 | for (i = 0; i < npubnames; i++) |
4228 | { |
4229 | if (i > 0) |
4230 | appendPQExpBufferStr(publications, ", " ); |
4231 | |
4232 | appendPQExpBufferStr(publications, fmtId(pubnames[i])); |
4233 | } |
4234 | |
4235 | appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = " , publications->data); |
4236 | if (subinfo->subslotname) |
4237 | appendStringLiteralAH(query, subinfo->subslotname, fout); |
4238 | else |
4239 | appendPQExpBufferStr(query, "NONE" ); |
4240 | |
4241 | if (strcmp(subinfo->subsynccommit, "off" ) != 0) |
4242 | appendPQExpBuffer(query, ", synchronous_commit = %s" , fmtId(subinfo->subsynccommit)); |
4243 | |
4244 | appendPQExpBufferStr(query, ");\n" ); |
4245 | |
4246 | ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId, |
4247 | ARCHIVE_OPTS(.tag = subinfo->dobj.name, |
4248 | .owner = subinfo->rolname, |
4249 | .description = "SUBSCRIPTION" , |
4250 | .section = SECTION_POST_DATA, |
4251 | .createStmt = query->data, |
4252 | .dropStmt = delq->data)); |
4253 | |
4254 | if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
4255 | dumpComment(fout, "SUBSCRIPTION" , qsubname, |
4256 | NULL, subinfo->rolname, |
4257 | subinfo->dobj.catId, 0, subinfo->dobj.dumpId); |
4258 | |
4259 | if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
4260 | dumpSecLabel(fout, "SUBSCRIPTION" , qsubname, |
4261 | NULL, subinfo->rolname, |
4262 | subinfo->dobj.catId, 0, subinfo->dobj.dumpId); |
4263 | |
4264 | destroyPQExpBuffer(publications); |
4265 | if (pubnames) |
4266 | free(pubnames); |
4267 | |
4268 | destroyPQExpBuffer(delq); |
4269 | destroyPQExpBuffer(query); |
4270 | free(qsubname); |
4271 | } |
4272 | |
4273 | static void |
4274 | binary_upgrade_set_type_oids_by_type_oid(Archive *fout, |
4275 | PQExpBuffer upgrade_buffer, |
4276 | Oid pg_type_oid, |
4277 | bool force_array_type) |
4278 | { |
4279 | PQExpBuffer upgrade_query = createPQExpBuffer(); |
4280 | PGresult *res; |
4281 | Oid pg_type_array_oid; |
4282 | |
4283 | appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n" ); |
4284 | appendPQExpBuffer(upgrade_buffer, |
4285 | "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n" , |
4286 | pg_type_oid); |
4287 | |
4288 | /* we only support old >= 8.3 for binary upgrades */ |
4289 | appendPQExpBuffer(upgrade_query, |
4290 | "SELECT typarray " |
4291 | "FROM pg_catalog.pg_type " |
4292 | "WHERE oid = '%u'::pg_catalog.oid;" , |
4293 | pg_type_oid); |
4294 | |
4295 | res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); |
4296 | |
4297 | pg_type_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray" ))); |
4298 | |
4299 | PQclear(res); |
4300 | |
4301 | if (!OidIsValid(pg_type_array_oid) && force_array_type) |
4302 | { |
4303 | /* |
4304 | * If the old version didn't assign an array type, but the new version |
4305 | * does, we must select an unused type OID to assign. This currently |
4306 | * only happens for domains, when upgrading pre-v11 to v11 and up. |
4307 | * |
4308 | * Note: local state here is kind of ugly, but we must have some, |
4309 | * since we mustn't choose the same unused OID more than once. |
4310 | */ |
4311 | static Oid next_possible_free_oid = FirstNormalObjectId; |
4312 | bool is_dup; |
4313 | |
4314 | do |
4315 | { |
4316 | ++next_possible_free_oid; |
4317 | printfPQExpBuffer(upgrade_query, |
4318 | "SELECT EXISTS(SELECT 1 " |
4319 | "FROM pg_catalog.pg_type " |
4320 | "WHERE oid = '%u'::pg_catalog.oid);" , |
4321 | next_possible_free_oid); |
4322 | res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); |
4323 | is_dup = (PQgetvalue(res, 0, 0)[0] == 't'); |
4324 | PQclear(res); |
4325 | } while (is_dup); |
4326 | |
4327 | pg_type_array_oid = next_possible_free_oid; |
4328 | } |
4329 | |
4330 | if (OidIsValid(pg_type_array_oid)) |
4331 | { |
4332 | appendPQExpBufferStr(upgrade_buffer, |
4333 | "\n-- For binary upgrade, must preserve pg_type array oid\n" ); |
4334 | appendPQExpBuffer(upgrade_buffer, |
4335 | "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n" , |
4336 | pg_type_array_oid); |
4337 | } |
4338 | |
4339 | destroyPQExpBuffer(upgrade_query); |
4340 | } |
4341 | |
4342 | static bool |
4343 | binary_upgrade_set_type_oids_by_rel_oid(Archive *fout, |
4344 | PQExpBuffer upgrade_buffer, |
4345 | Oid pg_rel_oid) |
4346 | { |
4347 | PQExpBuffer upgrade_query = createPQExpBuffer(); |
4348 | PGresult *upgrade_res; |
4349 | Oid pg_type_oid; |
4350 | bool toast_set = false; |
4351 | |
4352 | /* |
4353 | * We only support old >= 8.3 for binary upgrades. |
4354 | * |
4355 | * We purposefully ignore toast OIDs for partitioned tables; the reason is |
4356 | * that versions 10 and 11 have them, but 12 does not, so emitting them |
4357 | * causes the upgrade to fail. |
4358 | */ |
4359 | appendPQExpBuffer(upgrade_query, |
4360 | "SELECT c.reltype AS crel, t.reltype AS trel " |
4361 | "FROM pg_catalog.pg_class c " |
4362 | "LEFT JOIN pg_catalog.pg_class t ON " |
4363 | " (c.reltoastrelid = t.oid AND c.relkind <> '%c') " |
4364 | "WHERE c.oid = '%u'::pg_catalog.oid;" , |
4365 | RELKIND_PARTITIONED_TABLE, pg_rel_oid); |
4366 | |
4367 | upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); |
4368 | |
4369 | pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel" ))); |
4370 | |
4371 | binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer, |
4372 | pg_type_oid, false); |
4373 | |
4374 | if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel" ))) |
4375 | { |
4376 | /* Toast tables do not have pg_type array rows */ |
4377 | Oid pg_type_toast_oid = atooid(PQgetvalue(upgrade_res, 0, |
4378 | PQfnumber(upgrade_res, "trel" ))); |
4379 | |
4380 | appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type toast oid\n" ); |
4381 | appendPQExpBuffer(upgrade_buffer, |
4382 | "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_type_oid('%u'::pg_catalog.oid);\n\n" , |
4383 | pg_type_toast_oid); |
4384 | |
4385 | toast_set = true; |
4386 | } |
4387 | |
4388 | PQclear(upgrade_res); |
4389 | destroyPQExpBuffer(upgrade_query); |
4390 | |
4391 | return toast_set; |
4392 | } |
4393 | |
4394 | static void |
4395 | binary_upgrade_set_pg_class_oids(Archive *fout, |
4396 | PQExpBuffer upgrade_buffer, Oid pg_class_oid, |
4397 | bool is_index) |
4398 | { |
4399 | PQExpBuffer upgrade_query = createPQExpBuffer(); |
4400 | PGresult *upgrade_res; |
4401 | Oid pg_class_reltoastrelid; |
4402 | Oid pg_index_indexrelid; |
4403 | |
4404 | appendPQExpBuffer(upgrade_query, |
4405 | "SELECT c.reltoastrelid, i.indexrelid " |
4406 | "FROM pg_catalog.pg_class c LEFT JOIN " |
4407 | "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) " |
4408 | "WHERE c.oid = '%u'::pg_catalog.oid;" , |
4409 | pg_class_oid); |
4410 | |
4411 | upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); |
4412 | |
4413 | pg_class_reltoastrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastrelid" ))); |
4414 | pg_index_indexrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "indexrelid" ))); |
4415 | |
4416 | appendPQExpBufferStr(upgrade_buffer, |
4417 | "\n-- For binary upgrade, must preserve pg_class oids\n" ); |
4418 | |
4419 | if (!is_index) |
4420 | { |
4421 | appendPQExpBuffer(upgrade_buffer, |
4422 | "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n" , |
4423 | pg_class_oid); |
4424 | /* only tables have toast tables, not indexes */ |
4425 | if (OidIsValid(pg_class_reltoastrelid)) |
4426 | { |
4427 | /* |
4428 | * One complexity is that the table definition might not require |
4429 | * the creation of a TOAST table, and the TOAST table might have |
4430 | * been created long after table creation, when the table was |
4431 | * loaded with wide data. By setting the TOAST oid we force |
4432 | * creation of the TOAST heap and TOAST index by the backend so we |
4433 | * can cleanly copy the files during binary upgrade. |
4434 | */ |
4435 | |
4436 | appendPQExpBuffer(upgrade_buffer, |
4437 | "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n" , |
4438 | pg_class_reltoastrelid); |
4439 | |
4440 | /* every toast table has an index */ |
4441 | appendPQExpBuffer(upgrade_buffer, |
4442 | "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n" , |
4443 | pg_index_indexrelid); |
4444 | } |
4445 | } |
4446 | else |
4447 | appendPQExpBuffer(upgrade_buffer, |
4448 | "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n" , |
4449 | pg_class_oid); |
4450 | |
4451 | appendPQExpBufferChar(upgrade_buffer, '\n'); |
4452 | |
4453 | PQclear(upgrade_res); |
4454 | destroyPQExpBuffer(upgrade_query); |
4455 | } |
4456 | |
4457 | /* |
4458 | * If the DumpableObject is a member of an extension, add a suitable |
4459 | * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer. |
4460 | * |
4461 | * For somewhat historical reasons, objname should already be quoted, |
4462 | * but not objnamespace (if any). |
4463 | */ |
4464 | static void |
4465 | binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, |
4466 | DumpableObject *dobj, |
4467 | const char *objtype, |
4468 | const char *objname, |
4469 | const char *objnamespace) |
4470 | { |
4471 | DumpableObject *extobj = NULL; |
4472 | int i; |
4473 | |
4474 | if (!dobj->ext_member) |
4475 | return; |
4476 | |
4477 | /* |
4478 | * Find the parent extension. We could avoid this search if we wanted to |
4479 | * add a link field to DumpableObject, but the space costs of that would |
4480 | * be considerable. We assume that member objects could only have a |
4481 | * direct dependency on their own extension, not any others. |
4482 | */ |
4483 | for (i = 0; i < dobj->nDeps; i++) |
4484 | { |
4485 | extobj = findObjectByDumpId(dobj->dependencies[i]); |
4486 | if (extobj && extobj->objType == DO_EXTENSION) |
4487 | break; |
4488 | extobj = NULL; |
4489 | } |
4490 | if (extobj == NULL) |
4491 | fatal("could not find parent extension for %s %s" , |
4492 | objtype, objname); |
4493 | |
4494 | appendPQExpBufferStr(upgrade_buffer, |
4495 | "\n-- For binary upgrade, handle extension membership the hard way\n" ); |
4496 | appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s " , |
4497 | fmtId(extobj->name), |
4498 | objtype); |
4499 | if (objnamespace && *objnamespace) |
4500 | appendPQExpBuffer(upgrade_buffer, "%s." , fmtId(objnamespace)); |
4501 | appendPQExpBuffer(upgrade_buffer, "%s;\n" , objname); |
4502 | } |
4503 | |
4504 | /* |
4505 | * getNamespaces: |
4506 | * read all namespaces in the system catalogs and return them in the |
4507 | * NamespaceInfo* structure |
4508 | * |
4509 | * numNamespaces is set to the number of namespaces read in |
4510 | */ |
4511 | NamespaceInfo * |
4512 | getNamespaces(Archive *fout, int *numNamespaces) |
4513 | { |
4514 | DumpOptions *dopt = fout->dopt; |
4515 | PGresult *res; |
4516 | int ntups; |
4517 | int i; |
4518 | PQExpBuffer query; |
4519 | NamespaceInfo *nsinfo; |
4520 | int i_tableoid; |
4521 | int i_oid; |
4522 | int i_nspname; |
4523 | int i_rolname; |
4524 | int i_nspacl; |
4525 | int i_rnspacl; |
4526 | int i_initnspacl; |
4527 | int i_initrnspacl; |
4528 | |
4529 | query = createPQExpBuffer(); |
4530 | |
4531 | /* |
4532 | * we fetch all namespaces including system ones, so that every object we |
4533 | * read in can be linked to a containing namespace. |
4534 | */ |
4535 | if (fout->remoteVersion >= 90600) |
4536 | { |
4537 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
4538 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
4539 | PQExpBuffer init_acl_subquery = createPQExpBuffer(); |
4540 | PQExpBuffer init_racl_subquery = createPQExpBuffer(); |
4541 | |
4542 | buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery, |
4543 | init_racl_subquery, "n.nspacl" , "n.nspowner" , "'n'" , |
4544 | dopt->binary_upgrade); |
4545 | |
4546 | appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, " |
4547 | "(%s nspowner) AS rolname, " |
4548 | "%s as nspacl, " |
4549 | "%s as rnspacl, " |
4550 | "%s as initnspacl, " |
4551 | "%s as initrnspacl " |
4552 | "FROM pg_namespace n " |
4553 | "LEFT JOIN pg_init_privs pip " |
4554 | "ON (n.oid = pip.objoid " |
4555 | "AND pip.classoid = 'pg_namespace'::regclass " |
4556 | "AND pip.objsubid = 0" , |
4557 | username_subquery, |
4558 | acl_subquery->data, |
4559 | racl_subquery->data, |
4560 | init_acl_subquery->data, |
4561 | init_racl_subquery->data); |
4562 | |
4563 | appendPQExpBuffer(query, ") " ); |
4564 | |
4565 | destroyPQExpBuffer(acl_subquery); |
4566 | destroyPQExpBuffer(racl_subquery); |
4567 | destroyPQExpBuffer(init_acl_subquery); |
4568 | destroyPQExpBuffer(init_racl_subquery); |
4569 | } |
4570 | else |
4571 | appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " |
4572 | "(%s nspowner) AS rolname, " |
4573 | "nspacl, NULL as rnspacl, " |
4574 | "NULL AS initnspacl, NULL as initrnspacl " |
4575 | "FROM pg_namespace" , |
4576 | username_subquery); |
4577 | |
4578 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
4579 | |
4580 | ntups = PQntuples(res); |
4581 | |
4582 | nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo)); |
4583 | |
4584 | i_tableoid = PQfnumber(res, "tableoid" ); |
4585 | i_oid = PQfnumber(res, "oid" ); |
4586 | i_nspname = PQfnumber(res, "nspname" ); |
4587 | i_rolname = PQfnumber(res, "rolname" ); |
4588 | i_nspacl = PQfnumber(res, "nspacl" ); |
4589 | i_rnspacl = PQfnumber(res, "rnspacl" ); |
4590 | i_initnspacl = PQfnumber(res, "initnspacl" ); |
4591 | i_initrnspacl = PQfnumber(res, "initrnspacl" ); |
4592 | |
4593 | for (i = 0; i < ntups; i++) |
4594 | { |
4595 | nsinfo[i].dobj.objType = DO_NAMESPACE; |
4596 | nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
4597 | nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
4598 | AssignDumpId(&nsinfo[i].dobj); |
4599 | nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname)); |
4600 | nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
4601 | nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl)); |
4602 | nsinfo[i].rnspacl = pg_strdup(PQgetvalue(res, i, i_rnspacl)); |
4603 | nsinfo[i].initnspacl = pg_strdup(PQgetvalue(res, i, i_initnspacl)); |
4604 | nsinfo[i].initrnspacl = pg_strdup(PQgetvalue(res, i, i_initrnspacl)); |
4605 | |
4606 | /* Decide whether to dump this namespace */ |
4607 | selectDumpableNamespace(&nsinfo[i], fout); |
4608 | |
4609 | /* |
4610 | * Do not try to dump ACL if the ACL is empty or the default. |
4611 | * |
4612 | * This is useful because, for some schemas/objects, the only |
4613 | * component we are going to try and dump is the ACL and if we can |
4614 | * remove that then 'dump' goes to zero/false and we don't consider |
4615 | * this object for dumping at all later on. |
4616 | */ |
4617 | if (PQgetisnull(res, i, i_nspacl) && PQgetisnull(res, i, i_rnspacl) && |
4618 | PQgetisnull(res, i, i_initnspacl) && |
4619 | PQgetisnull(res, i, i_initrnspacl)) |
4620 | nsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
4621 | |
4622 | if (strlen(nsinfo[i].rolname) == 0) |
4623 | pg_log_warning("owner of schema \"%s\" appears to be invalid" , |
4624 | nsinfo[i].dobj.name); |
4625 | } |
4626 | |
4627 | PQclear(res); |
4628 | destroyPQExpBuffer(query); |
4629 | |
4630 | *numNamespaces = ntups; |
4631 | |
4632 | return nsinfo; |
4633 | } |
4634 | |
4635 | /* |
4636 | * findNamespace: |
4637 | * given a namespace OID, look up the info read by getNamespaces |
4638 | */ |
4639 | static NamespaceInfo * |
4640 | findNamespace(Archive *fout, Oid nsoid) |
4641 | { |
4642 | NamespaceInfo *nsinfo; |
4643 | |
4644 | nsinfo = findNamespaceByOid(nsoid); |
4645 | if (nsinfo == NULL) |
4646 | fatal("schema with OID %u does not exist" , nsoid); |
4647 | return nsinfo; |
4648 | } |
4649 | |
4650 | /* |
4651 | * getExtensions: |
4652 | * read all extensions in the system catalogs and return them in the |
4653 | * ExtensionInfo* structure |
4654 | * |
4655 | * numExtensions is set to the number of extensions read in |
4656 | */ |
4657 | ExtensionInfo * |
4658 | getExtensions(Archive *fout, int *numExtensions) |
4659 | { |
4660 | DumpOptions *dopt = fout->dopt; |
4661 | PGresult *res; |
4662 | int ntups; |
4663 | int i; |
4664 | PQExpBuffer query; |
4665 | ExtensionInfo *extinfo; |
4666 | int i_tableoid; |
4667 | int i_oid; |
4668 | int i_extname; |
4669 | int i_nspname; |
4670 | int i_extrelocatable; |
4671 | int i_extversion; |
4672 | int i_extconfig; |
4673 | int i_extcondition; |
4674 | |
4675 | /* |
4676 | * Before 9.1, there are no extensions. |
4677 | */ |
4678 | if (fout->remoteVersion < 90100) |
4679 | { |
4680 | *numExtensions = 0; |
4681 | return NULL; |
4682 | } |
4683 | |
4684 | query = createPQExpBuffer(); |
4685 | |
4686 | appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, " |
4687 | "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition " |
4688 | "FROM pg_extension x " |
4689 | "JOIN pg_namespace n ON n.oid = x.extnamespace" ); |
4690 | |
4691 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
4692 | |
4693 | ntups = PQntuples(res); |
4694 | |
4695 | extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo)); |
4696 | |
4697 | i_tableoid = PQfnumber(res, "tableoid" ); |
4698 | i_oid = PQfnumber(res, "oid" ); |
4699 | i_extname = PQfnumber(res, "extname" ); |
4700 | i_nspname = PQfnumber(res, "nspname" ); |
4701 | i_extrelocatable = PQfnumber(res, "extrelocatable" ); |
4702 | i_extversion = PQfnumber(res, "extversion" ); |
4703 | i_extconfig = PQfnumber(res, "extconfig" ); |
4704 | i_extcondition = PQfnumber(res, "extcondition" ); |
4705 | |
4706 | for (i = 0; i < ntups; i++) |
4707 | { |
4708 | extinfo[i].dobj.objType = DO_EXTENSION; |
4709 | extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
4710 | extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
4711 | AssignDumpId(&extinfo[i].dobj); |
4712 | extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname)); |
4713 | extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname)); |
4714 | extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't'; |
4715 | extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion)); |
4716 | extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig)); |
4717 | extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition)); |
4718 | |
4719 | /* Decide whether we want to dump it */ |
4720 | selectDumpableExtension(&(extinfo[i]), dopt); |
4721 | } |
4722 | |
4723 | PQclear(res); |
4724 | destroyPQExpBuffer(query); |
4725 | |
4726 | *numExtensions = ntups; |
4727 | |
4728 | return extinfo; |
4729 | } |
4730 | |
4731 | /* |
4732 | * getTypes: |
4733 | * read all types in the system catalogs and return them in the |
4734 | * TypeInfo* structure |
4735 | * |
4736 | * numTypes is set to the number of types read in |
4737 | * |
4738 | * NB: this must run after getFuncs() because we assume we can do |
4739 | * findFuncByOid(). |
4740 | */ |
4741 | TypeInfo * |
4742 | getTypes(Archive *fout, int *numTypes) |
4743 | { |
4744 | DumpOptions *dopt = fout->dopt; |
4745 | PGresult *res; |
4746 | int ntups; |
4747 | int i; |
4748 | PQExpBuffer query = createPQExpBuffer(); |
4749 | TypeInfo *tyinfo; |
4750 | ShellTypeInfo *stinfo; |
4751 | int i_tableoid; |
4752 | int i_oid; |
4753 | int i_typname; |
4754 | int i_typnamespace; |
4755 | int i_typacl; |
4756 | int i_rtypacl; |
4757 | int i_inittypacl; |
4758 | int i_initrtypacl; |
4759 | int i_rolname; |
4760 | int i_typelem; |
4761 | int i_typrelid; |
4762 | int i_typrelkind; |
4763 | int i_typtype; |
4764 | int i_typisdefined; |
4765 | int i_isarray; |
4766 | |
4767 | /* |
4768 | * we include even the built-in types because those may be used as array |
4769 | * elements by user-defined types |
4770 | * |
4771 | * we filter out the built-in types when we dump out the types |
4772 | * |
4773 | * same approach for undefined (shell) types and array types |
4774 | * |
4775 | * Note: as of 8.3 we can reliably detect whether a type is an |
4776 | * auto-generated array type by checking the element type's typarray. |
4777 | * (Before that the test is capable of generating false positives.) We |
4778 | * still check for name beginning with '_', though, so as to avoid the |
4779 | * cost of the subselect probe for all standard types. This would have to |
4780 | * be revisited if the backend ever allows renaming of array types. |
4781 | */ |
4782 | |
4783 | if (fout->remoteVersion >= 90600) |
4784 | { |
4785 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
4786 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
4787 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
4788 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
4789 | |
4790 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
4791 | initracl_subquery, "t.typacl" , "t.typowner" , "'T'" , |
4792 | dopt->binary_upgrade); |
4793 | |
4794 | appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, " |
4795 | "t.typnamespace, " |
4796 | "%s AS typacl, " |
4797 | "%s AS rtypacl, " |
4798 | "%s AS inittypacl, " |
4799 | "%s AS initrtypacl, " |
4800 | "(%s t.typowner) AS rolname, " |
4801 | "t.typelem, t.typrelid, " |
4802 | "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" " |
4803 | "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, " |
4804 | "t.typtype, t.typisdefined, " |
4805 | "t.typname[0] = '_' AND t.typelem != 0 AND " |
4806 | "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray " |
4807 | "FROM pg_type t " |
4808 | "LEFT JOIN pg_init_privs pip ON " |
4809 | "(t.oid = pip.objoid " |
4810 | "AND pip.classoid = 'pg_type'::regclass " |
4811 | "AND pip.objsubid = 0) " , |
4812 | acl_subquery->data, |
4813 | racl_subquery->data, |
4814 | initacl_subquery->data, |
4815 | initracl_subquery->data, |
4816 | username_subquery); |
4817 | |
4818 | destroyPQExpBuffer(acl_subquery); |
4819 | destroyPQExpBuffer(racl_subquery); |
4820 | destroyPQExpBuffer(initacl_subquery); |
4821 | destroyPQExpBuffer(initracl_subquery); |
4822 | } |
4823 | else if (fout->remoteVersion >= 90200) |
4824 | { |
4825 | appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " |
4826 | "typnamespace, typacl, NULL as rtypacl, " |
4827 | "NULL AS inittypacl, NULL AS initrtypacl, " |
4828 | "(%s typowner) AS rolname, " |
4829 | "typelem, typrelid, " |
4830 | "CASE WHEN typrelid = 0 THEN ' '::\"char\" " |
4831 | "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " |
4832 | "typtype, typisdefined, " |
4833 | "typname[0] = '_' AND typelem != 0 AND " |
4834 | "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " |
4835 | "FROM pg_type" , |
4836 | username_subquery); |
4837 | } |
4838 | else if (fout->remoteVersion >= 80300) |
4839 | { |
4840 | appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " |
4841 | "typnamespace, NULL AS typacl, NULL as rtypacl, " |
4842 | "NULL AS inittypacl, NULL AS initrtypacl, " |
4843 | "(%s typowner) AS rolname, " |
4844 | "typelem, typrelid, " |
4845 | "CASE WHEN typrelid = 0 THEN ' '::\"char\" " |
4846 | "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " |
4847 | "typtype, typisdefined, " |
4848 | "typname[0] = '_' AND typelem != 0 AND " |
4849 | "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " |
4850 | "FROM pg_type" , |
4851 | username_subquery); |
4852 | } |
4853 | else |
4854 | { |
4855 | appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " |
4856 | "typnamespace, NULL AS typacl, NULL as rtypacl, " |
4857 | "NULL AS inittypacl, NULL AS initrtypacl, " |
4858 | "(%s typowner) AS rolname, " |
4859 | "typelem, typrelid, " |
4860 | "CASE WHEN typrelid = 0 THEN ' '::\"char\" " |
4861 | "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " |
4862 | "typtype, typisdefined, " |
4863 | "typname[0] = '_' AND typelem != 0 AS isarray " |
4864 | "FROM pg_type" , |
4865 | username_subquery); |
4866 | } |
4867 | |
4868 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
4869 | |
4870 | ntups = PQntuples(res); |
4871 | |
4872 | tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo)); |
4873 | |
4874 | i_tableoid = PQfnumber(res, "tableoid" ); |
4875 | i_oid = PQfnumber(res, "oid" ); |
4876 | i_typname = PQfnumber(res, "typname" ); |
4877 | i_typnamespace = PQfnumber(res, "typnamespace" ); |
4878 | i_typacl = PQfnumber(res, "typacl" ); |
4879 | i_rtypacl = PQfnumber(res, "rtypacl" ); |
4880 | i_inittypacl = PQfnumber(res, "inittypacl" ); |
4881 | i_initrtypacl = PQfnumber(res, "initrtypacl" ); |
4882 | i_rolname = PQfnumber(res, "rolname" ); |
4883 | i_typelem = PQfnumber(res, "typelem" ); |
4884 | i_typrelid = PQfnumber(res, "typrelid" ); |
4885 | i_typrelkind = PQfnumber(res, "typrelkind" ); |
4886 | i_typtype = PQfnumber(res, "typtype" ); |
4887 | i_typisdefined = PQfnumber(res, "typisdefined" ); |
4888 | i_isarray = PQfnumber(res, "isarray" ); |
4889 | |
4890 | for (i = 0; i < ntups; i++) |
4891 | { |
4892 | tyinfo[i].dobj.objType = DO_TYPE; |
4893 | tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
4894 | tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
4895 | AssignDumpId(&tyinfo[i].dobj); |
4896 | tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname)); |
4897 | tyinfo[i].dobj.namespace = |
4898 | findNamespace(fout, |
4899 | atooid(PQgetvalue(res, i, i_typnamespace))); |
4900 | tyinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
4901 | tyinfo[i].typacl = pg_strdup(PQgetvalue(res, i, i_typacl)); |
4902 | tyinfo[i].rtypacl = pg_strdup(PQgetvalue(res, i, i_rtypacl)); |
4903 | tyinfo[i].inittypacl = pg_strdup(PQgetvalue(res, i, i_inittypacl)); |
4904 | tyinfo[i].initrtypacl = pg_strdup(PQgetvalue(res, i, i_initrtypacl)); |
4905 | tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem)); |
4906 | tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid)); |
4907 | tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind); |
4908 | tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype); |
4909 | tyinfo[i].shellType = NULL; |
4910 | |
4911 | if (strcmp(PQgetvalue(res, i, i_typisdefined), "t" ) == 0) |
4912 | tyinfo[i].isDefined = true; |
4913 | else |
4914 | tyinfo[i].isDefined = false; |
4915 | |
4916 | if (strcmp(PQgetvalue(res, i, i_isarray), "t" ) == 0) |
4917 | tyinfo[i].isArray = true; |
4918 | else |
4919 | tyinfo[i].isArray = false; |
4920 | |
4921 | /* Decide whether we want to dump it */ |
4922 | selectDumpableType(&tyinfo[i], fout); |
4923 | |
4924 | /* Do not try to dump ACL if no ACL exists. */ |
4925 | if (PQgetisnull(res, i, i_typacl) && PQgetisnull(res, i, i_rtypacl) && |
4926 | PQgetisnull(res, i, i_inittypacl) && |
4927 | PQgetisnull(res, i, i_initrtypacl)) |
4928 | tyinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
4929 | |
4930 | /* |
4931 | * If it's a domain, fetch info about its constraints, if any |
4932 | */ |
4933 | tyinfo[i].nDomChecks = 0; |
4934 | tyinfo[i].domChecks = NULL; |
4935 | if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) && |
4936 | tyinfo[i].typtype == TYPTYPE_DOMAIN) |
4937 | getDomainConstraints(fout, &(tyinfo[i])); |
4938 | |
4939 | /* |
4940 | * If it's a base type, make a DumpableObject representing a shell |
4941 | * definition of the type. We will need to dump that ahead of the I/O |
4942 | * functions for the type. Similarly, range types need a shell |
4943 | * definition in case they have a canonicalize function. |
4944 | * |
4945 | * Note: the shell type doesn't have a catId. You might think it |
4946 | * should copy the base type's catId, but then it might capture the |
4947 | * pg_depend entries for the type, which we don't want. |
4948 | */ |
4949 | if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) && |
4950 | (tyinfo[i].typtype == TYPTYPE_BASE || |
4951 | tyinfo[i].typtype == TYPTYPE_RANGE)) |
4952 | { |
4953 | stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo)); |
4954 | stinfo->dobj.objType = DO_SHELL_TYPE; |
4955 | stinfo->dobj.catId = nilCatalogId; |
4956 | AssignDumpId(&stinfo->dobj); |
4957 | stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name); |
4958 | stinfo->dobj.namespace = tyinfo[i].dobj.namespace; |
4959 | stinfo->baseType = &(tyinfo[i]); |
4960 | tyinfo[i].shellType = stinfo; |
4961 | |
4962 | /* |
4963 | * Initially mark the shell type as not to be dumped. We'll only |
4964 | * dump it if the I/O or canonicalize functions need to be dumped; |
4965 | * this is taken care of while sorting dependencies. |
4966 | */ |
4967 | stinfo->dobj.dump = DUMP_COMPONENT_NONE; |
4968 | } |
4969 | |
4970 | if (strlen(tyinfo[i].rolname) == 0) |
4971 | pg_log_warning("owner of data type \"%s\" appears to be invalid" , |
4972 | tyinfo[i].dobj.name); |
4973 | } |
4974 | |
4975 | *numTypes = ntups; |
4976 | |
4977 | PQclear(res); |
4978 | |
4979 | destroyPQExpBuffer(query); |
4980 | |
4981 | return tyinfo; |
4982 | } |
4983 | |
4984 | /* |
4985 | * getOperators: |
4986 | * read all operators in the system catalogs and return them in the |
4987 | * OprInfo* structure |
4988 | * |
4989 | * numOprs is set to the number of operators read in |
4990 | */ |
4991 | OprInfo * |
4992 | getOperators(Archive *fout, int *numOprs) |
4993 | { |
4994 | PGresult *res; |
4995 | int ntups; |
4996 | int i; |
4997 | PQExpBuffer query = createPQExpBuffer(); |
4998 | OprInfo *oprinfo; |
4999 | int i_tableoid; |
5000 | int i_oid; |
5001 | int i_oprname; |
5002 | int i_oprnamespace; |
5003 | int i_rolname; |
5004 | int i_oprkind; |
5005 | int i_oprcode; |
5006 | |
5007 | /* |
5008 | * find all operators, including builtin operators; we filter out |
5009 | * system-defined operators at dump-out time. |
5010 | */ |
5011 | |
5012 | appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, " |
5013 | "oprnamespace, " |
5014 | "(%s oprowner) AS rolname, " |
5015 | "oprkind, " |
5016 | "oprcode::oid AS oprcode " |
5017 | "FROM pg_operator" , |
5018 | username_subquery); |
5019 | |
5020 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
5021 | |
5022 | ntups = PQntuples(res); |
5023 | *numOprs = ntups; |
5024 | |
5025 | oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo)); |
5026 | |
5027 | i_tableoid = PQfnumber(res, "tableoid" ); |
5028 | i_oid = PQfnumber(res, "oid" ); |
5029 | i_oprname = PQfnumber(res, "oprname" ); |
5030 | i_oprnamespace = PQfnumber(res, "oprnamespace" ); |
5031 | i_rolname = PQfnumber(res, "rolname" ); |
5032 | i_oprkind = PQfnumber(res, "oprkind" ); |
5033 | i_oprcode = PQfnumber(res, "oprcode" ); |
5034 | |
5035 | for (i = 0; i < ntups; i++) |
5036 | { |
5037 | oprinfo[i].dobj.objType = DO_OPERATOR; |
5038 | oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
5039 | oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
5040 | AssignDumpId(&oprinfo[i].dobj); |
5041 | oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname)); |
5042 | oprinfo[i].dobj.namespace = |
5043 | findNamespace(fout, |
5044 | atooid(PQgetvalue(res, i, i_oprnamespace))); |
5045 | oprinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
5046 | oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0]; |
5047 | oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode)); |
5048 | |
5049 | /* Decide whether we want to dump it */ |
5050 | selectDumpableObject(&(oprinfo[i].dobj), fout); |
5051 | |
5052 | /* Operators do not currently have ACLs. */ |
5053 | oprinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
5054 | |
5055 | if (strlen(oprinfo[i].rolname) == 0) |
5056 | pg_log_warning("owner of operator \"%s\" appears to be invalid" , |
5057 | oprinfo[i].dobj.name); |
5058 | } |
5059 | |
5060 | PQclear(res); |
5061 | |
5062 | destroyPQExpBuffer(query); |
5063 | |
5064 | return oprinfo; |
5065 | } |
5066 | |
5067 | /* |
5068 | * getCollations: |
5069 | * read all collations in the system catalogs and return them in the |
5070 | * CollInfo* structure |
5071 | * |
5072 | * numCollations is set to the number of collations read in |
5073 | */ |
5074 | CollInfo * |
5075 | getCollations(Archive *fout, int *numCollations) |
5076 | { |
5077 | PGresult *res; |
5078 | int ntups; |
5079 | int i; |
5080 | PQExpBuffer query; |
5081 | CollInfo *collinfo; |
5082 | int i_tableoid; |
5083 | int i_oid; |
5084 | int i_collname; |
5085 | int i_collnamespace; |
5086 | int i_rolname; |
5087 | |
5088 | /* Collations didn't exist pre-9.1 */ |
5089 | if (fout->remoteVersion < 90100) |
5090 | { |
5091 | *numCollations = 0; |
5092 | return NULL; |
5093 | } |
5094 | |
5095 | query = createPQExpBuffer(); |
5096 | |
5097 | /* |
5098 | * find all collations, including builtin collations; we filter out |
5099 | * system-defined collations at dump-out time. |
5100 | */ |
5101 | |
5102 | appendPQExpBuffer(query, "SELECT tableoid, oid, collname, " |
5103 | "collnamespace, " |
5104 | "(%s collowner) AS rolname " |
5105 | "FROM pg_collation" , |
5106 | username_subquery); |
5107 | |
5108 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
5109 | |
5110 | ntups = PQntuples(res); |
5111 | *numCollations = ntups; |
5112 | |
5113 | collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo)); |
5114 | |
5115 | i_tableoid = PQfnumber(res, "tableoid" ); |
5116 | i_oid = PQfnumber(res, "oid" ); |
5117 | i_collname = PQfnumber(res, "collname" ); |
5118 | i_collnamespace = PQfnumber(res, "collnamespace" ); |
5119 | i_rolname = PQfnumber(res, "rolname" ); |
5120 | |
5121 | for (i = 0; i < ntups; i++) |
5122 | { |
5123 | collinfo[i].dobj.objType = DO_COLLATION; |
5124 | collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
5125 | collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
5126 | AssignDumpId(&collinfo[i].dobj); |
5127 | collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname)); |
5128 | collinfo[i].dobj.namespace = |
5129 | findNamespace(fout, |
5130 | atooid(PQgetvalue(res, i, i_collnamespace))); |
5131 | collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
5132 | |
5133 | /* Decide whether we want to dump it */ |
5134 | selectDumpableObject(&(collinfo[i].dobj), fout); |
5135 | |
5136 | /* Collations do not currently have ACLs. */ |
5137 | collinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
5138 | } |
5139 | |
5140 | PQclear(res); |
5141 | |
5142 | destroyPQExpBuffer(query); |
5143 | |
5144 | return collinfo; |
5145 | } |
5146 | |
5147 | /* |
5148 | * getConversions: |
5149 | * read all conversions in the system catalogs and return them in the |
5150 | * ConvInfo* structure |
5151 | * |
5152 | * numConversions is set to the number of conversions read in |
5153 | */ |
5154 | ConvInfo * |
5155 | getConversions(Archive *fout, int *numConversions) |
5156 | { |
5157 | PGresult *res; |
5158 | int ntups; |
5159 | int i; |
5160 | PQExpBuffer query; |
5161 | ConvInfo *convinfo; |
5162 | int i_tableoid; |
5163 | int i_oid; |
5164 | int i_conname; |
5165 | int i_connamespace; |
5166 | int i_rolname; |
5167 | |
5168 | query = createPQExpBuffer(); |
5169 | |
5170 | /* |
5171 | * find all conversions, including builtin conversions; we filter out |
5172 | * system-defined conversions at dump-out time. |
5173 | */ |
5174 | |
5175 | appendPQExpBuffer(query, "SELECT tableoid, oid, conname, " |
5176 | "connamespace, " |
5177 | "(%s conowner) AS rolname " |
5178 | "FROM pg_conversion" , |
5179 | username_subquery); |
5180 | |
5181 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
5182 | |
5183 | ntups = PQntuples(res); |
5184 | *numConversions = ntups; |
5185 | |
5186 | convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo)); |
5187 | |
5188 | i_tableoid = PQfnumber(res, "tableoid" ); |
5189 | i_oid = PQfnumber(res, "oid" ); |
5190 | i_conname = PQfnumber(res, "conname" ); |
5191 | i_connamespace = PQfnumber(res, "connamespace" ); |
5192 | i_rolname = PQfnumber(res, "rolname" ); |
5193 | |
5194 | for (i = 0; i < ntups; i++) |
5195 | { |
5196 | convinfo[i].dobj.objType = DO_CONVERSION; |
5197 | convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
5198 | convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
5199 | AssignDumpId(&convinfo[i].dobj); |
5200 | convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname)); |
5201 | convinfo[i].dobj.namespace = |
5202 | findNamespace(fout, |
5203 | atooid(PQgetvalue(res, i, i_connamespace))); |
5204 | convinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
5205 | |
5206 | /* Decide whether we want to dump it */ |
5207 | selectDumpableObject(&(convinfo[i].dobj), fout); |
5208 | |
5209 | /* Conversions do not currently have ACLs. */ |
5210 | convinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
5211 | } |
5212 | |
5213 | PQclear(res); |
5214 | |
5215 | destroyPQExpBuffer(query); |
5216 | |
5217 | return convinfo; |
5218 | } |
5219 | |
5220 | /* |
5221 | * getAccessMethods: |
5222 | * read all user-defined access methods in the system catalogs and return |
5223 | * them in the AccessMethodInfo* structure |
5224 | * |
5225 | * numAccessMethods is set to the number of access methods read in |
5226 | */ |
5227 | AccessMethodInfo * |
5228 | getAccessMethods(Archive *fout, int *numAccessMethods) |
5229 | { |
5230 | PGresult *res; |
5231 | int ntups; |
5232 | int i; |
5233 | PQExpBuffer query; |
5234 | AccessMethodInfo *aminfo; |
5235 | int i_tableoid; |
5236 | int i_oid; |
5237 | int i_amname; |
5238 | int i_amhandler; |
5239 | int i_amtype; |
5240 | |
5241 | /* Before 9.6, there are no user-defined access methods */ |
5242 | if (fout->remoteVersion < 90600) |
5243 | { |
5244 | *numAccessMethods = 0; |
5245 | return NULL; |
5246 | } |
5247 | |
5248 | query = createPQExpBuffer(); |
5249 | |
5250 | /* Select all access methods from pg_am table */ |
5251 | appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, " |
5252 | "amhandler::pg_catalog.regproc AS amhandler " |
5253 | "FROM pg_am" ); |
5254 | |
5255 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
5256 | |
5257 | ntups = PQntuples(res); |
5258 | *numAccessMethods = ntups; |
5259 | |
5260 | aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo)); |
5261 | |
5262 | i_tableoid = PQfnumber(res, "tableoid" ); |
5263 | i_oid = PQfnumber(res, "oid" ); |
5264 | i_amname = PQfnumber(res, "amname" ); |
5265 | i_amhandler = PQfnumber(res, "amhandler" ); |
5266 | i_amtype = PQfnumber(res, "amtype" ); |
5267 | |
5268 | for (i = 0; i < ntups; i++) |
5269 | { |
5270 | aminfo[i].dobj.objType = DO_ACCESS_METHOD; |
5271 | aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
5272 | aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
5273 | AssignDumpId(&aminfo[i].dobj); |
5274 | aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname)); |
5275 | aminfo[i].dobj.namespace = NULL; |
5276 | aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler)); |
5277 | aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype)); |
5278 | |
5279 | /* Decide whether we want to dump it */ |
5280 | selectDumpableAccessMethod(&(aminfo[i]), fout); |
5281 | |
5282 | /* Access methods do not currently have ACLs. */ |
5283 | aminfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
5284 | } |
5285 | |
5286 | PQclear(res); |
5287 | |
5288 | destroyPQExpBuffer(query); |
5289 | |
5290 | return aminfo; |
5291 | } |
5292 | |
5293 | |
5294 | /* |
5295 | * getOpclasses: |
5296 | * read all opclasses in the system catalogs and return them in the |
5297 | * OpclassInfo* structure |
5298 | * |
5299 | * numOpclasses is set to the number of opclasses read in |
5300 | */ |
5301 | OpclassInfo * |
5302 | getOpclasses(Archive *fout, int *numOpclasses) |
5303 | { |
5304 | PGresult *res; |
5305 | int ntups; |
5306 | int i; |
5307 | PQExpBuffer query = createPQExpBuffer(); |
5308 | OpclassInfo *opcinfo; |
5309 | int i_tableoid; |
5310 | int i_oid; |
5311 | int i_opcname; |
5312 | int i_opcnamespace; |
5313 | int i_rolname; |
5314 | |
5315 | /* |
5316 | * find all opclasses, including builtin opclasses; we filter out |
5317 | * system-defined opclasses at dump-out time. |
5318 | */ |
5319 | |
5320 | appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, " |
5321 | "opcnamespace, " |
5322 | "(%s opcowner) AS rolname " |
5323 | "FROM pg_opclass" , |
5324 | username_subquery); |
5325 | |
5326 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
5327 | |
5328 | ntups = PQntuples(res); |
5329 | *numOpclasses = ntups; |
5330 | |
5331 | opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo)); |
5332 | |
5333 | i_tableoid = PQfnumber(res, "tableoid" ); |
5334 | i_oid = PQfnumber(res, "oid" ); |
5335 | i_opcname = PQfnumber(res, "opcname" ); |
5336 | i_opcnamespace = PQfnumber(res, "opcnamespace" ); |
5337 | i_rolname = PQfnumber(res, "rolname" ); |
5338 | |
5339 | for (i = 0; i < ntups; i++) |
5340 | { |
5341 | opcinfo[i].dobj.objType = DO_OPCLASS; |
5342 | opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
5343 | opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
5344 | AssignDumpId(&opcinfo[i].dobj); |
5345 | opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname)); |
5346 | opcinfo[i].dobj.namespace = |
5347 | findNamespace(fout, |
5348 | atooid(PQgetvalue(res, i, i_opcnamespace))); |
5349 | opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
5350 | |
5351 | /* Decide whether we want to dump it */ |
5352 | selectDumpableObject(&(opcinfo[i].dobj), fout); |
5353 | |
5354 | /* Op Classes do not currently have ACLs. */ |
5355 | opcinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
5356 | |
5357 | if (strlen(opcinfo[i].rolname) == 0) |
5358 | pg_log_warning("owner of operator class \"%s\" appears to be invalid" , |
5359 | opcinfo[i].dobj.name); |
5360 | } |
5361 | |
5362 | PQclear(res); |
5363 | |
5364 | destroyPQExpBuffer(query); |
5365 | |
5366 | return opcinfo; |
5367 | } |
5368 | |
5369 | /* |
5370 | * getOpfamilies: |
5371 | * read all opfamilies in the system catalogs and return them in the |
5372 | * OpfamilyInfo* structure |
5373 | * |
5374 | * numOpfamilies is set to the number of opfamilies read in |
5375 | */ |
5376 | OpfamilyInfo * |
5377 | getOpfamilies(Archive *fout, int *numOpfamilies) |
5378 | { |
5379 | PGresult *res; |
5380 | int ntups; |
5381 | int i; |
5382 | PQExpBuffer query; |
5383 | OpfamilyInfo *opfinfo; |
5384 | int i_tableoid; |
5385 | int i_oid; |
5386 | int i_opfname; |
5387 | int i_opfnamespace; |
5388 | int i_rolname; |
5389 | |
5390 | /* Before 8.3, there is no separate concept of opfamilies */ |
5391 | if (fout->remoteVersion < 80300) |
5392 | { |
5393 | *numOpfamilies = 0; |
5394 | return NULL; |
5395 | } |
5396 | |
5397 | query = createPQExpBuffer(); |
5398 | |
5399 | /* |
5400 | * find all opfamilies, including builtin opfamilies; we filter out |
5401 | * system-defined opfamilies at dump-out time. |
5402 | */ |
5403 | |
5404 | appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, " |
5405 | "opfnamespace, " |
5406 | "(%s opfowner) AS rolname " |
5407 | "FROM pg_opfamily" , |
5408 | username_subquery); |
5409 | |
5410 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
5411 | |
5412 | ntups = PQntuples(res); |
5413 | *numOpfamilies = ntups; |
5414 | |
5415 | opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo)); |
5416 | |
5417 | i_tableoid = PQfnumber(res, "tableoid" ); |
5418 | i_oid = PQfnumber(res, "oid" ); |
5419 | i_opfname = PQfnumber(res, "opfname" ); |
5420 | i_opfnamespace = PQfnumber(res, "opfnamespace" ); |
5421 | i_rolname = PQfnumber(res, "rolname" ); |
5422 | |
5423 | for (i = 0; i < ntups; i++) |
5424 | { |
5425 | opfinfo[i].dobj.objType = DO_OPFAMILY; |
5426 | opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
5427 | opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
5428 | AssignDumpId(&opfinfo[i].dobj); |
5429 | opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname)); |
5430 | opfinfo[i].dobj.namespace = |
5431 | findNamespace(fout, |
5432 | atooid(PQgetvalue(res, i, i_opfnamespace))); |
5433 | opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
5434 | |
5435 | /* Decide whether we want to dump it */ |
5436 | selectDumpableObject(&(opfinfo[i].dobj), fout); |
5437 | |
5438 | /* Extensions do not currently have ACLs. */ |
5439 | opfinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
5440 | |
5441 | if (strlen(opfinfo[i].rolname) == 0) |
5442 | pg_log_warning("owner of operator family \"%s\" appears to be invalid" , |
5443 | opfinfo[i].dobj.name); |
5444 | } |
5445 | |
5446 | PQclear(res); |
5447 | |
5448 | destroyPQExpBuffer(query); |
5449 | |
5450 | return opfinfo; |
5451 | } |
5452 | |
5453 | /* |
5454 | * getAggregates: |
5455 | * read all the user-defined aggregates in the system catalogs and |
5456 | * return them in the AggInfo* structure |
5457 | * |
5458 | * numAggs is set to the number of aggregates read in |
5459 | */ |
5460 | AggInfo * |
5461 | getAggregates(Archive *fout, int *numAggs) |
5462 | { |
5463 | DumpOptions *dopt = fout->dopt; |
5464 | PGresult *res; |
5465 | int ntups; |
5466 | int i; |
5467 | PQExpBuffer query = createPQExpBuffer(); |
5468 | AggInfo *agginfo; |
5469 | int i_tableoid; |
5470 | int i_oid; |
5471 | int i_aggname; |
5472 | int i_aggnamespace; |
5473 | int i_pronargs; |
5474 | int i_proargtypes; |
5475 | int i_rolname; |
5476 | int i_aggacl; |
5477 | int i_raggacl; |
5478 | int i_initaggacl; |
5479 | int i_initraggacl; |
5480 | |
5481 | /* |
5482 | * Find all interesting aggregates. See comment in getFuncs() for the |
5483 | * rationale behind the filtering logic. |
5484 | */ |
5485 | if (fout->remoteVersion >= 90600) |
5486 | { |
5487 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
5488 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
5489 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
5490 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
5491 | const char *agg_check; |
5492 | |
5493 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
5494 | initracl_subquery, "p.proacl" , "p.proowner" , "'f'" , |
5495 | dopt->binary_upgrade); |
5496 | |
5497 | agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'" |
5498 | : "p.proisagg" ); |
5499 | |
5500 | appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, " |
5501 | "p.proname AS aggname, " |
5502 | "p.pronamespace AS aggnamespace, " |
5503 | "p.pronargs, p.proargtypes, " |
5504 | "(%s p.proowner) AS rolname, " |
5505 | "%s AS aggacl, " |
5506 | "%s AS raggacl, " |
5507 | "%s AS initaggacl, " |
5508 | "%s AS initraggacl " |
5509 | "FROM pg_proc p " |
5510 | "LEFT JOIN pg_init_privs pip ON " |
5511 | "(p.oid = pip.objoid " |
5512 | "AND pip.classoid = 'pg_proc'::regclass " |
5513 | "AND pip.objsubid = 0) " |
5514 | "WHERE %s AND (" |
5515 | "p.pronamespace != " |
5516 | "(SELECT oid FROM pg_namespace " |
5517 | "WHERE nspname = 'pg_catalog') OR " |
5518 | "p.proacl IS DISTINCT FROM pip.initprivs" , |
5519 | username_subquery, |
5520 | acl_subquery->data, |
5521 | racl_subquery->data, |
5522 | initacl_subquery->data, |
5523 | initracl_subquery->data, |
5524 | agg_check); |
5525 | if (dopt->binary_upgrade) |
5526 | appendPQExpBufferStr(query, |
5527 | " OR EXISTS(SELECT 1 FROM pg_depend WHERE " |
5528 | "classid = 'pg_proc'::regclass AND " |
5529 | "objid = p.oid AND " |
5530 | "refclassid = 'pg_extension'::regclass AND " |
5531 | "deptype = 'e')" ); |
5532 | appendPQExpBufferChar(query, ')'); |
5533 | |
5534 | destroyPQExpBuffer(acl_subquery); |
5535 | destroyPQExpBuffer(racl_subquery); |
5536 | destroyPQExpBuffer(initacl_subquery); |
5537 | destroyPQExpBuffer(initracl_subquery); |
5538 | } |
5539 | else if (fout->remoteVersion >= 80200) |
5540 | { |
5541 | appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " |
5542 | "pronamespace AS aggnamespace, " |
5543 | "pronargs, proargtypes, " |
5544 | "(%s proowner) AS rolname, " |
5545 | "proacl AS aggacl, " |
5546 | "NULL AS raggacl, " |
5547 | "NULL AS initaggacl, NULL AS initraggacl " |
5548 | "FROM pg_proc p " |
5549 | "WHERE proisagg AND (" |
5550 | "pronamespace != " |
5551 | "(SELECT oid FROM pg_namespace " |
5552 | "WHERE nspname = 'pg_catalog')" , |
5553 | username_subquery); |
5554 | if (dopt->binary_upgrade && fout->remoteVersion >= 90100) |
5555 | appendPQExpBufferStr(query, |
5556 | " OR EXISTS(SELECT 1 FROM pg_depend WHERE " |
5557 | "classid = 'pg_proc'::regclass AND " |
5558 | "objid = p.oid AND " |
5559 | "refclassid = 'pg_extension'::regclass AND " |
5560 | "deptype = 'e')" ); |
5561 | appendPQExpBufferChar(query, ')'); |
5562 | } |
5563 | else |
5564 | { |
5565 | appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " |
5566 | "pronamespace AS aggnamespace, " |
5567 | "CASE WHEN proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype THEN 0 ELSE 1 END AS pronargs, " |
5568 | "proargtypes, " |
5569 | "(%s proowner) AS rolname, " |
5570 | "proacl AS aggacl, " |
5571 | "NULL AS raggacl, " |
5572 | "NULL AS initaggacl, NULL AS initraggacl " |
5573 | "FROM pg_proc " |
5574 | "WHERE proisagg " |
5575 | "AND pronamespace != " |
5576 | "(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog')" , |
5577 | username_subquery); |
5578 | } |
5579 | |
5580 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
5581 | |
5582 | ntups = PQntuples(res); |
5583 | *numAggs = ntups; |
5584 | |
5585 | agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo)); |
5586 | |
5587 | i_tableoid = PQfnumber(res, "tableoid" ); |
5588 | i_oid = PQfnumber(res, "oid" ); |
5589 | i_aggname = PQfnumber(res, "aggname" ); |
5590 | i_aggnamespace = PQfnumber(res, "aggnamespace" ); |
5591 | i_pronargs = PQfnumber(res, "pronargs" ); |
5592 | i_proargtypes = PQfnumber(res, "proargtypes" ); |
5593 | i_rolname = PQfnumber(res, "rolname" ); |
5594 | i_aggacl = PQfnumber(res, "aggacl" ); |
5595 | i_raggacl = PQfnumber(res, "raggacl" ); |
5596 | i_initaggacl = PQfnumber(res, "initaggacl" ); |
5597 | i_initraggacl = PQfnumber(res, "initraggacl" ); |
5598 | |
5599 | for (i = 0; i < ntups; i++) |
5600 | { |
5601 | agginfo[i].aggfn.dobj.objType = DO_AGG; |
5602 | agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
5603 | agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
5604 | AssignDumpId(&agginfo[i].aggfn.dobj); |
5605 | agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname)); |
5606 | agginfo[i].aggfn.dobj.namespace = |
5607 | findNamespace(fout, |
5608 | atooid(PQgetvalue(res, i, i_aggnamespace))); |
5609 | agginfo[i].aggfn.rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
5610 | if (strlen(agginfo[i].aggfn.rolname) == 0) |
5611 | pg_log_warning("owner of aggregate function \"%s\" appears to be invalid" , |
5612 | agginfo[i].aggfn.dobj.name); |
5613 | agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */ |
5614 | agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */ |
5615 | agginfo[i].aggfn.proacl = pg_strdup(PQgetvalue(res, i, i_aggacl)); |
5616 | agginfo[i].aggfn.rproacl = pg_strdup(PQgetvalue(res, i, i_raggacl)); |
5617 | agginfo[i].aggfn.initproacl = pg_strdup(PQgetvalue(res, i, i_initaggacl)); |
5618 | agginfo[i].aggfn.initrproacl = pg_strdup(PQgetvalue(res, i, i_initraggacl)); |
5619 | agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs)); |
5620 | if (agginfo[i].aggfn.nargs == 0) |
5621 | agginfo[i].aggfn.argtypes = NULL; |
5622 | else |
5623 | { |
5624 | agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid)); |
5625 | parseOidArray(PQgetvalue(res, i, i_proargtypes), |
5626 | agginfo[i].aggfn.argtypes, |
5627 | agginfo[i].aggfn.nargs); |
5628 | } |
5629 | |
5630 | /* Decide whether we want to dump it */ |
5631 | selectDumpableObject(&(agginfo[i].aggfn.dobj), fout); |
5632 | |
5633 | /* Do not try to dump ACL if no ACL exists. */ |
5634 | if (PQgetisnull(res, i, i_aggacl) && PQgetisnull(res, i, i_raggacl) && |
5635 | PQgetisnull(res, i, i_initaggacl) && |
5636 | PQgetisnull(res, i, i_initraggacl)) |
5637 | agginfo[i].aggfn.dobj.dump &= ~DUMP_COMPONENT_ACL; |
5638 | } |
5639 | |
5640 | PQclear(res); |
5641 | |
5642 | destroyPQExpBuffer(query); |
5643 | |
5644 | return agginfo; |
5645 | } |
5646 | |
5647 | /* |
5648 | * getFuncs: |
5649 | * read all the user-defined functions in the system catalogs and |
5650 | * return them in the FuncInfo* structure |
5651 | * |
5652 | * numFuncs is set to the number of functions read in |
5653 | */ |
5654 | FuncInfo * |
5655 | getFuncs(Archive *fout, int *numFuncs) |
5656 | { |
5657 | DumpOptions *dopt = fout->dopt; |
5658 | PGresult *res; |
5659 | int ntups; |
5660 | int i; |
5661 | PQExpBuffer query = createPQExpBuffer(); |
5662 | FuncInfo *finfo; |
5663 | int i_tableoid; |
5664 | int i_oid; |
5665 | int i_proname; |
5666 | int i_pronamespace; |
5667 | int i_rolname; |
5668 | int i_prolang; |
5669 | int i_pronargs; |
5670 | int i_proargtypes; |
5671 | int i_prorettype; |
5672 | int i_proacl; |
5673 | int i_rproacl; |
5674 | int i_initproacl; |
5675 | int i_initrproacl; |
5676 | |
5677 | /* |
5678 | * Find all interesting functions. This is a bit complicated: |
5679 | * |
5680 | * 1. Always exclude aggregates; those are handled elsewhere. |
5681 | * |
5682 | * 2. Always exclude functions that are internally dependent on something |
5683 | * else, since presumably those will be created as a result of creating |
5684 | * the something else. This currently acts only to suppress constructor |
5685 | * functions for range types (so we only need it in 9.2 and up). Note |
5686 | * this is OK only because the constructors don't have any dependencies |
5687 | * the range type doesn't have; otherwise we might not get creation |
5688 | * ordering correct. |
5689 | * |
5690 | * 3. Otherwise, we normally exclude functions in pg_catalog. However, if |
5691 | * they're members of extensions and we are in binary-upgrade mode then |
5692 | * include them, since we want to dump extension members individually in |
5693 | * that mode. Also, if they are used by casts or transforms then we need |
5694 | * to gather the information about them, though they won't be dumped if |
5695 | * they are built-in. Also, in 9.6 and up, include functions in |
5696 | * pg_catalog if they have an ACL different from what's shown in |
5697 | * pg_init_privs. |
5698 | */ |
5699 | if (fout->remoteVersion >= 90600) |
5700 | { |
5701 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
5702 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
5703 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
5704 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
5705 | const char *not_agg_check; |
5706 | |
5707 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
5708 | initracl_subquery, "p.proacl" , "p.proowner" , "'f'" , |
5709 | dopt->binary_upgrade); |
5710 | |
5711 | not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'" |
5712 | : "NOT p.proisagg" ); |
5713 | |
5714 | appendPQExpBuffer(query, |
5715 | "SELECT p.tableoid, p.oid, p.proname, p.prolang, " |
5716 | "p.pronargs, p.proargtypes, p.prorettype, " |
5717 | "%s AS proacl, " |
5718 | "%s AS rproacl, " |
5719 | "%s AS initproacl, " |
5720 | "%s AS initrproacl, " |
5721 | "p.pronamespace, " |
5722 | "(%s p.proowner) AS rolname " |
5723 | "FROM pg_proc p " |
5724 | "LEFT JOIN pg_init_privs pip ON " |
5725 | "(p.oid = pip.objoid " |
5726 | "AND pip.classoid = 'pg_proc'::regclass " |
5727 | "AND pip.objsubid = 0) " |
5728 | "WHERE %s" |
5729 | "\n AND NOT EXISTS (SELECT 1 FROM pg_depend " |
5730 | "WHERE classid = 'pg_proc'::regclass AND " |
5731 | "objid = p.oid AND deptype = 'i')" |
5732 | "\n AND (" |
5733 | "\n pronamespace != " |
5734 | "(SELECT oid FROM pg_namespace " |
5735 | "WHERE nspname = 'pg_catalog')" |
5736 | "\n OR EXISTS (SELECT 1 FROM pg_cast" |
5737 | "\n WHERE pg_cast.oid > %u " |
5738 | "\n AND p.oid = pg_cast.castfunc)" |
5739 | "\n OR EXISTS (SELECT 1 FROM pg_transform" |
5740 | "\n WHERE pg_transform.oid > %u AND " |
5741 | "\n (p.oid = pg_transform.trffromsql" |
5742 | "\n OR p.oid = pg_transform.trftosql))" , |
5743 | acl_subquery->data, |
5744 | racl_subquery->data, |
5745 | initacl_subquery->data, |
5746 | initracl_subquery->data, |
5747 | username_subquery, |
5748 | not_agg_check, |
5749 | g_last_builtin_oid, |
5750 | g_last_builtin_oid); |
5751 | if (dopt->binary_upgrade) |
5752 | appendPQExpBufferStr(query, |
5753 | "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE " |
5754 | "classid = 'pg_proc'::regclass AND " |
5755 | "objid = p.oid AND " |
5756 | "refclassid = 'pg_extension'::regclass AND " |
5757 | "deptype = 'e')" ); |
5758 | appendPQExpBufferStr(query, |
5759 | "\n OR p.proacl IS DISTINCT FROM pip.initprivs" ); |
5760 | appendPQExpBufferChar(query, ')'); |
5761 | |
5762 | destroyPQExpBuffer(acl_subquery); |
5763 | destroyPQExpBuffer(racl_subquery); |
5764 | destroyPQExpBuffer(initacl_subquery); |
5765 | destroyPQExpBuffer(initracl_subquery); |
5766 | } |
5767 | else |
5768 | { |
5769 | appendPQExpBuffer(query, |
5770 | "SELECT tableoid, oid, proname, prolang, " |
5771 | "pronargs, proargtypes, prorettype, proacl, " |
5772 | "NULL as rproacl, " |
5773 | "NULL as initproacl, NULL AS initrproacl, " |
5774 | "pronamespace, " |
5775 | "(%s proowner) AS rolname " |
5776 | "FROM pg_proc p " |
5777 | "WHERE NOT proisagg" , |
5778 | username_subquery); |
5779 | if (fout->remoteVersion >= 90200) |
5780 | appendPQExpBufferStr(query, |
5781 | "\n AND NOT EXISTS (SELECT 1 FROM pg_depend " |
5782 | "WHERE classid = 'pg_proc'::regclass AND " |
5783 | "objid = p.oid AND deptype = 'i')" ); |
5784 | appendPQExpBuffer(query, |
5785 | "\n AND (" |
5786 | "\n pronamespace != " |
5787 | "(SELECT oid FROM pg_namespace " |
5788 | "WHERE nspname = 'pg_catalog')" |
5789 | "\n OR EXISTS (SELECT 1 FROM pg_cast" |
5790 | "\n WHERE pg_cast.oid > '%u'::oid" |
5791 | "\n AND p.oid = pg_cast.castfunc)" , |
5792 | g_last_builtin_oid); |
5793 | |
5794 | if (fout->remoteVersion >= 90500) |
5795 | appendPQExpBuffer(query, |
5796 | "\n OR EXISTS (SELECT 1 FROM pg_transform" |
5797 | "\n WHERE pg_transform.oid > '%u'::oid" |
5798 | "\n AND (p.oid = pg_transform.trffromsql" |
5799 | "\n OR p.oid = pg_transform.trftosql))" , |
5800 | g_last_builtin_oid); |
5801 | |
5802 | if (dopt->binary_upgrade && fout->remoteVersion >= 90100) |
5803 | appendPQExpBufferStr(query, |
5804 | "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE " |
5805 | "classid = 'pg_proc'::regclass AND " |
5806 | "objid = p.oid AND " |
5807 | "refclassid = 'pg_extension'::regclass AND " |
5808 | "deptype = 'e')" ); |
5809 | appendPQExpBufferChar(query, ')'); |
5810 | } |
5811 | |
5812 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
5813 | |
5814 | ntups = PQntuples(res); |
5815 | |
5816 | *numFuncs = ntups; |
5817 | |
5818 | finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo)); |
5819 | |
5820 | i_tableoid = PQfnumber(res, "tableoid" ); |
5821 | i_oid = PQfnumber(res, "oid" ); |
5822 | i_proname = PQfnumber(res, "proname" ); |
5823 | i_pronamespace = PQfnumber(res, "pronamespace" ); |
5824 | i_rolname = PQfnumber(res, "rolname" ); |
5825 | i_prolang = PQfnumber(res, "prolang" ); |
5826 | i_pronargs = PQfnumber(res, "pronargs" ); |
5827 | i_proargtypes = PQfnumber(res, "proargtypes" ); |
5828 | i_prorettype = PQfnumber(res, "prorettype" ); |
5829 | i_proacl = PQfnumber(res, "proacl" ); |
5830 | i_rproacl = PQfnumber(res, "rproacl" ); |
5831 | i_initproacl = PQfnumber(res, "initproacl" ); |
5832 | i_initrproacl = PQfnumber(res, "initrproacl" ); |
5833 | |
5834 | for (i = 0; i < ntups; i++) |
5835 | { |
5836 | finfo[i].dobj.objType = DO_FUNC; |
5837 | finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
5838 | finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
5839 | AssignDumpId(&finfo[i].dobj); |
5840 | finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname)); |
5841 | finfo[i].dobj.namespace = |
5842 | findNamespace(fout, |
5843 | atooid(PQgetvalue(res, i, i_pronamespace))); |
5844 | finfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
5845 | finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang)); |
5846 | finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype)); |
5847 | finfo[i].proacl = pg_strdup(PQgetvalue(res, i, i_proacl)); |
5848 | finfo[i].rproacl = pg_strdup(PQgetvalue(res, i, i_rproacl)); |
5849 | finfo[i].initproacl = pg_strdup(PQgetvalue(res, i, i_initproacl)); |
5850 | finfo[i].initrproacl = pg_strdup(PQgetvalue(res, i, i_initrproacl)); |
5851 | finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs)); |
5852 | if (finfo[i].nargs == 0) |
5853 | finfo[i].argtypes = NULL; |
5854 | else |
5855 | { |
5856 | finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid)); |
5857 | parseOidArray(PQgetvalue(res, i, i_proargtypes), |
5858 | finfo[i].argtypes, finfo[i].nargs); |
5859 | } |
5860 | |
5861 | /* Decide whether we want to dump it */ |
5862 | selectDumpableObject(&(finfo[i].dobj), fout); |
5863 | |
5864 | /* Do not try to dump ACL if no ACL exists. */ |
5865 | if (PQgetisnull(res, i, i_proacl) && PQgetisnull(res, i, i_rproacl) && |
5866 | PQgetisnull(res, i, i_initproacl) && |
5867 | PQgetisnull(res, i, i_initrproacl)) |
5868 | finfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
5869 | |
5870 | if (strlen(finfo[i].rolname) == 0) |
5871 | pg_log_warning("owner of function \"%s\" appears to be invalid" , |
5872 | finfo[i].dobj.name); |
5873 | } |
5874 | |
5875 | PQclear(res); |
5876 | |
5877 | destroyPQExpBuffer(query); |
5878 | |
5879 | return finfo; |
5880 | } |
5881 | |
5882 | /* |
5883 | * getTables |
5884 | * read all the tables (no indexes) |
5885 | * in the system catalogs return them in the TableInfo* structure |
5886 | * |
5887 | * numTables is set to the number of tables read in |
5888 | */ |
5889 | TableInfo * |
5890 | getTables(Archive *fout, int *numTables) |
5891 | { |
5892 | DumpOptions *dopt = fout->dopt; |
5893 | PGresult *res; |
5894 | int ntups; |
5895 | int i; |
5896 | PQExpBuffer query = createPQExpBuffer(); |
5897 | TableInfo *tblinfo; |
5898 | int i_reltableoid; |
5899 | int i_reloid; |
5900 | int i_relname; |
5901 | int i_relnamespace; |
5902 | int i_relkind; |
5903 | int i_relacl; |
5904 | int i_rrelacl; |
5905 | int i_initrelacl; |
5906 | int i_initrrelacl; |
5907 | int i_rolname; |
5908 | int i_relchecks; |
5909 | int i_relhastriggers; |
5910 | int i_relhasindex; |
5911 | int i_relhasrules; |
5912 | int i_relrowsec; |
5913 | int i_relforcerowsec; |
5914 | int i_relhasoids; |
5915 | int i_relfrozenxid; |
5916 | int i_relminmxid; |
5917 | int i_toastoid; |
5918 | int i_toastfrozenxid; |
5919 | int i_toastminmxid; |
5920 | int i_relpersistence; |
5921 | int i_relispopulated; |
5922 | int i_relreplident; |
5923 | int i_owning_tab; |
5924 | int i_owning_col; |
5925 | int i_reltablespace; |
5926 | int i_reloptions; |
5927 | int i_checkoption; |
5928 | int i_toastreloptions; |
5929 | int i_reloftype; |
5930 | int i_relpages; |
5931 | int i_is_identity_sequence; |
5932 | int i_changed_acl; |
5933 | int i_partkeydef; |
5934 | int i_ispartition; |
5935 | int i_partbound; |
5936 | int i_amname; |
5937 | |
5938 | /* |
5939 | * Find all the tables and table-like objects. |
5940 | * |
5941 | * We include system catalogs, so that we can work if a user table is |
5942 | * defined to inherit from a system catalog (pretty weird, but...) |
5943 | * |
5944 | * We ignore relations that are not ordinary tables, sequences, views, |
5945 | * materialized views, composite types, or foreign tables. |
5946 | * |
5947 | * Composite-type table entries won't be dumped as such, but we have to |
5948 | * make a DumpableObject for them so that we can track dependencies of the |
5949 | * composite type (pg_depend entries for columns of the composite type |
5950 | * link to the pg_class entry not the pg_type entry). |
5951 | * |
5952 | * Note: in this phase we should collect only a minimal amount of |
5953 | * information about each table, basically just enough to decide if it is |
5954 | * interesting. We must fetch all tables in this phase because otherwise |
5955 | * we cannot correctly identify inherited columns, owned sequences, etc. |
5956 | * |
5957 | * We purposefully ignore toast OIDs for partitioned tables; the reason is |
5958 | * that versions 10 and 11 have them, but 12 does not, so emitting them |
5959 | * causes the upgrade to fail. |
5960 | */ |
5961 | |
5962 | if (fout->remoteVersion >= 90600) |
5963 | { |
5964 | char *partkeydef = "NULL" ; |
5965 | char *ispartition = "false" ; |
5966 | char *partbound = "NULL" ; |
5967 | char *relhasoids = "c.relhasoids" ; |
5968 | |
5969 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
5970 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
5971 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
5972 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
5973 | |
5974 | PQExpBuffer attacl_subquery = createPQExpBuffer(); |
5975 | PQExpBuffer attracl_subquery = createPQExpBuffer(); |
5976 | PQExpBuffer attinitacl_subquery = createPQExpBuffer(); |
5977 | PQExpBuffer attinitracl_subquery = createPQExpBuffer(); |
5978 | |
5979 | /* |
5980 | * Collect the information about any partitioned tables, which were |
5981 | * added in PG10. |
5982 | */ |
5983 | |
5984 | if (fout->remoteVersion >= 100000) |
5985 | { |
5986 | partkeydef = "pg_get_partkeydef(c.oid)" ; |
5987 | ispartition = "c.relispartition" ; |
5988 | partbound = "pg_get_expr(c.relpartbound, c.oid)" ; |
5989 | } |
5990 | |
5991 | /* In PG12 upwards WITH OIDS does not exist anymore. */ |
5992 | if (fout->remoteVersion >= 120000) |
5993 | relhasoids = "'f'::bool" ; |
5994 | |
5995 | /* |
5996 | * Left join to pick up dependency info linking sequences to their |
5997 | * owning column, if any (note this dependency is AUTO as of 8.2) |
5998 | * |
5999 | * Left join to detect if any privileges are still as-set-at-init, in |
6000 | * which case we won't dump out ACL commands for those. |
6001 | */ |
6002 | |
6003 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
6004 | initracl_subquery, "c.relacl" , "c.relowner" , |
6005 | "CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE) |
6006 | " THEN 's' ELSE 'r' END::\"char\"" , |
6007 | dopt->binary_upgrade); |
6008 | |
6009 | buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery, |
6010 | attinitracl_subquery, "at.attacl" , "c.relowner" , "'c'" , |
6011 | dopt->binary_upgrade); |
6012 | |
6013 | appendPQExpBuffer(query, |
6014 | "SELECT c.tableoid, c.oid, c.relname, " |
6015 | "%s AS relacl, %s as rrelacl, " |
6016 | "%s AS initrelacl, %s as initrrelacl, " |
6017 | "c.relkind, c.relnamespace, " |
6018 | "(%s c.relowner) AS rolname, " |
6019 | "c.relchecks, c.relhastriggers, " |
6020 | "c.relhasindex, c.relhasrules, %s AS relhasoids, " |
6021 | "c.relrowsecurity, c.relforcerowsecurity, " |
6022 | "c.relfrozenxid, c.relminmxid, tc.oid AS toid, " |
6023 | "tc.relfrozenxid AS tfrozenxid, " |
6024 | "tc.relminmxid AS tminmxid, " |
6025 | "c.relpersistence, c.relispopulated, " |
6026 | "c.relreplident, c.relpages, am.amname, " |
6027 | "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " |
6028 | "d.refobjid AS owning_tab, " |
6029 | "d.refobjsubid AS owning_col, " |
6030 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6031 | "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, " |
6032 | "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " |
6033 | "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " |
6034 | "tc.reloptions AS toast_reloptions, " |
6035 | "c.relkind = '%c' AND EXISTS (SELECT 1 FROM pg_depend WHERE classid = 'pg_class'::regclass AND objid = c.oid AND objsubid = 0 AND refclassid = 'pg_class'::regclass AND deptype = 'i') AS is_identity_sequence, " |
6036 | "EXISTS (SELECT 1 FROM pg_attribute at LEFT JOIN pg_init_privs pip ON " |
6037 | "(c.oid = pip.objoid " |
6038 | "AND pip.classoid = 'pg_class'::regclass " |
6039 | "AND pip.objsubid = at.attnum)" |
6040 | "WHERE at.attrelid = c.oid AND (" |
6041 | "%s IS NOT NULL " |
6042 | "OR %s IS NOT NULL " |
6043 | "OR %s IS NOT NULL " |
6044 | "OR %s IS NOT NULL" |
6045 | "))" |
6046 | "AS changed_acl, " |
6047 | "%s AS partkeydef, " |
6048 | "%s AS ispartition, " |
6049 | "%s AS partbound " |
6050 | "FROM pg_class c " |
6051 | "LEFT JOIN pg_depend d ON " |
6052 | "(c.relkind = '%c' AND " |
6053 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6054 | "d.objsubid = 0 AND " |
6055 | "d.refclassid = c.tableoid AND d.deptype IN ('a', 'i')) " |
6056 | "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid AND c.relkind <> '%c') " |
6057 | "LEFT JOIN pg_am am ON (c.relam = am.oid) " |
6058 | "LEFT JOIN pg_init_privs pip ON " |
6059 | "(c.oid = pip.objoid " |
6060 | "AND pip.classoid = 'pg_class'::regclass " |
6061 | "AND pip.objsubid = 0) " |
6062 | "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " |
6063 | "ORDER BY c.oid" , |
6064 | acl_subquery->data, |
6065 | racl_subquery->data, |
6066 | initacl_subquery->data, |
6067 | initracl_subquery->data, |
6068 | username_subquery, |
6069 | relhasoids, |
6070 | RELKIND_SEQUENCE, |
6071 | attacl_subquery->data, |
6072 | attracl_subquery->data, |
6073 | attinitacl_subquery->data, |
6074 | attinitracl_subquery->data, |
6075 | partkeydef, |
6076 | ispartition, |
6077 | partbound, |
6078 | RELKIND_SEQUENCE, |
6079 | RELKIND_PARTITIONED_TABLE, |
6080 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6081 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, |
6082 | RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, |
6083 | RELKIND_PARTITIONED_TABLE); |
6084 | |
6085 | destroyPQExpBuffer(acl_subquery); |
6086 | destroyPQExpBuffer(racl_subquery); |
6087 | destroyPQExpBuffer(initacl_subquery); |
6088 | destroyPQExpBuffer(initracl_subquery); |
6089 | |
6090 | destroyPQExpBuffer(attacl_subquery); |
6091 | destroyPQExpBuffer(attracl_subquery); |
6092 | destroyPQExpBuffer(attinitacl_subquery); |
6093 | destroyPQExpBuffer(attinitracl_subquery); |
6094 | } |
6095 | else if (fout->remoteVersion >= 90500) |
6096 | { |
6097 | /* |
6098 | * Left join to pick up dependency info linking sequences to their |
6099 | * owning column, if any (note this dependency is AUTO as of 8.2) |
6100 | */ |
6101 | appendPQExpBuffer(query, |
6102 | "SELECT c.tableoid, c.oid, c.relname, " |
6103 | "c.relacl, NULL as rrelacl, " |
6104 | "NULL AS initrelacl, NULL AS initrrelacl, " |
6105 | "c.relkind, " |
6106 | "c.relnamespace, " |
6107 | "(%s c.relowner) AS rolname, " |
6108 | "c.relchecks, c.relhastriggers, " |
6109 | "c.relhasindex, c.relhasrules, c.relhasoids, " |
6110 | "c.relrowsecurity, c.relforcerowsecurity, " |
6111 | "c.relfrozenxid, c.relminmxid, tc.oid AS toid, " |
6112 | "tc.relfrozenxid AS tfrozenxid, " |
6113 | "tc.relminmxid AS tminmxid, " |
6114 | "c.relpersistence, c.relispopulated, " |
6115 | "c.relreplident, c.relpages, " |
6116 | "NULL AS amname, " |
6117 | "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " |
6118 | "d.refobjid AS owning_tab, " |
6119 | "d.refobjsubid AS owning_col, " |
6120 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6121 | "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, " |
6122 | "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " |
6123 | "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " |
6124 | "tc.reloptions AS toast_reloptions, " |
6125 | "NULL AS changed_acl, " |
6126 | "NULL AS partkeydef, " |
6127 | "false AS ispartition, " |
6128 | "NULL AS partbound " |
6129 | "FROM pg_class c " |
6130 | "LEFT JOIN pg_depend d ON " |
6131 | "(c.relkind = '%c' AND " |
6132 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6133 | "d.objsubid = 0 AND " |
6134 | "d.refclassid = c.tableoid AND d.deptype = 'a') " |
6135 | "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " |
6136 | "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " |
6137 | "ORDER BY c.oid" , |
6138 | username_subquery, |
6139 | RELKIND_SEQUENCE, |
6140 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6141 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, |
6142 | RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); |
6143 | } |
6144 | else if (fout->remoteVersion >= 90400) |
6145 | { |
6146 | /* |
6147 | * Left join to pick up dependency info linking sequences to their |
6148 | * owning column, if any (note this dependency is AUTO as of 8.2) |
6149 | */ |
6150 | appendPQExpBuffer(query, |
6151 | "SELECT c.tableoid, c.oid, c.relname, " |
6152 | "c.relacl, NULL as rrelacl, " |
6153 | "NULL AS initrelacl, NULL AS initrrelacl, " |
6154 | "c.relkind, " |
6155 | "c.relnamespace, " |
6156 | "(%s c.relowner) AS rolname, " |
6157 | "c.relchecks, c.relhastriggers, " |
6158 | "c.relhasindex, c.relhasrules, c.relhasoids, " |
6159 | "'f'::bool AS relrowsecurity, " |
6160 | "'f'::bool AS relforcerowsecurity, " |
6161 | "c.relfrozenxid, c.relminmxid, tc.oid AS toid, " |
6162 | "tc.relfrozenxid AS tfrozenxid, " |
6163 | "tc.relminmxid AS tminmxid, " |
6164 | "c.relpersistence, c.relispopulated, " |
6165 | "c.relreplident, c.relpages, " |
6166 | "NULL AS amname, " |
6167 | "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " |
6168 | "d.refobjid AS owning_tab, " |
6169 | "d.refobjsubid AS owning_col, " |
6170 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6171 | "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, " |
6172 | "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " |
6173 | "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " |
6174 | "tc.reloptions AS toast_reloptions, " |
6175 | "NULL AS changed_acl, " |
6176 | "NULL AS partkeydef, " |
6177 | "false AS ispartition, " |
6178 | "NULL AS partbound " |
6179 | "FROM pg_class c " |
6180 | "LEFT JOIN pg_depend d ON " |
6181 | "(c.relkind = '%c' AND " |
6182 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6183 | "d.objsubid = 0 AND " |
6184 | "d.refclassid = c.tableoid AND d.deptype = 'a') " |
6185 | "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " |
6186 | "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " |
6187 | "ORDER BY c.oid" , |
6188 | username_subquery, |
6189 | RELKIND_SEQUENCE, |
6190 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6191 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, |
6192 | RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); |
6193 | } |
6194 | else if (fout->remoteVersion >= 90300) |
6195 | { |
6196 | /* |
6197 | * Left join to pick up dependency info linking sequences to their |
6198 | * owning column, if any (note this dependency is AUTO as of 8.2) |
6199 | */ |
6200 | appendPQExpBuffer(query, |
6201 | "SELECT c.tableoid, c.oid, c.relname, " |
6202 | "c.relacl, NULL as rrelacl, " |
6203 | "NULL AS initrelacl, NULL AS initrrelacl, " |
6204 | "c.relkind, " |
6205 | "c.relnamespace, " |
6206 | "(%s c.relowner) AS rolname, " |
6207 | "c.relchecks, c.relhastriggers, " |
6208 | "c.relhasindex, c.relhasrules, c.relhasoids, " |
6209 | "'f'::bool AS relrowsecurity, " |
6210 | "'f'::bool AS relforcerowsecurity, " |
6211 | "c.relfrozenxid, c.relminmxid, tc.oid AS toid, " |
6212 | "tc.relfrozenxid AS tfrozenxid, " |
6213 | "tc.relminmxid AS tminmxid, " |
6214 | "c.relpersistence, c.relispopulated, " |
6215 | "'d' AS relreplident, c.relpages, " |
6216 | "NULL AS amname, " |
6217 | "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " |
6218 | "d.refobjid AS owning_tab, " |
6219 | "d.refobjsubid AS owning_col, " |
6220 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6221 | "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, " |
6222 | "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " |
6223 | "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " |
6224 | "tc.reloptions AS toast_reloptions, " |
6225 | "NULL AS changed_acl, " |
6226 | "NULL AS partkeydef, " |
6227 | "false AS ispartition, " |
6228 | "NULL AS partbound " |
6229 | "FROM pg_class c " |
6230 | "LEFT JOIN pg_depend d ON " |
6231 | "(c.relkind = '%c' AND " |
6232 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6233 | "d.objsubid = 0 AND " |
6234 | "d.refclassid = c.tableoid AND d.deptype = 'a') " |
6235 | "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " |
6236 | "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " |
6237 | "ORDER BY c.oid" , |
6238 | username_subquery, |
6239 | RELKIND_SEQUENCE, |
6240 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6241 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, |
6242 | RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); |
6243 | } |
6244 | else if (fout->remoteVersion >= 90100) |
6245 | { |
6246 | /* |
6247 | * Left join to pick up dependency info linking sequences to their |
6248 | * owning column, if any (note this dependency is AUTO as of 8.2) |
6249 | */ |
6250 | appendPQExpBuffer(query, |
6251 | "SELECT c.tableoid, c.oid, c.relname, " |
6252 | "c.relacl, NULL as rrelacl, " |
6253 | "NULL AS initrelacl, NULL AS initrrelacl, " |
6254 | "c.relkind, " |
6255 | "c.relnamespace, " |
6256 | "(%s c.relowner) AS rolname, " |
6257 | "c.relchecks, c.relhastriggers, " |
6258 | "c.relhasindex, c.relhasrules, c.relhasoids, " |
6259 | "'f'::bool AS relrowsecurity, " |
6260 | "'f'::bool AS relforcerowsecurity, " |
6261 | "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, " |
6262 | "tc.relfrozenxid AS tfrozenxid, " |
6263 | "0 AS tminmxid, " |
6264 | "c.relpersistence, 't' as relispopulated, " |
6265 | "'d' AS relreplident, c.relpages, " |
6266 | "NULL AS amname, " |
6267 | "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " |
6268 | "d.refobjid AS owning_tab, " |
6269 | "d.refobjsubid AS owning_col, " |
6270 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6271 | "c.reloptions AS reloptions, " |
6272 | "tc.reloptions AS toast_reloptions, " |
6273 | "NULL AS changed_acl, " |
6274 | "NULL AS partkeydef, " |
6275 | "false AS ispartition, " |
6276 | "NULL AS partbound " |
6277 | "FROM pg_class c " |
6278 | "LEFT JOIN pg_depend d ON " |
6279 | "(c.relkind = '%c' AND " |
6280 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6281 | "d.objsubid = 0 AND " |
6282 | "d.refclassid = c.tableoid AND d.deptype = 'a') " |
6283 | "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " |
6284 | "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " |
6285 | "ORDER BY c.oid" , |
6286 | username_subquery, |
6287 | RELKIND_SEQUENCE, |
6288 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6289 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, |
6290 | RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); |
6291 | } |
6292 | else if (fout->remoteVersion >= 90000) |
6293 | { |
6294 | /* |
6295 | * Left join to pick up dependency info linking sequences to their |
6296 | * owning column, if any (note this dependency is AUTO as of 8.2) |
6297 | */ |
6298 | appendPQExpBuffer(query, |
6299 | "SELECT c.tableoid, c.oid, c.relname, " |
6300 | "c.relacl, NULL as rrelacl, " |
6301 | "NULL AS initrelacl, NULL AS initrrelacl, " |
6302 | "c.relkind, " |
6303 | "c.relnamespace, " |
6304 | "(%s c.relowner) AS rolname, " |
6305 | "c.relchecks, c.relhastriggers, " |
6306 | "c.relhasindex, c.relhasrules, c.relhasoids, " |
6307 | "'f'::bool AS relrowsecurity, " |
6308 | "'f'::bool AS relforcerowsecurity, " |
6309 | "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, " |
6310 | "tc.relfrozenxid AS tfrozenxid, " |
6311 | "0 AS tminmxid, " |
6312 | "'p' AS relpersistence, 't' as relispopulated, " |
6313 | "'d' AS relreplident, c.relpages, " |
6314 | "NULL AS amname, " |
6315 | "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " |
6316 | "d.refobjid AS owning_tab, " |
6317 | "d.refobjsubid AS owning_col, " |
6318 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6319 | "c.reloptions AS reloptions, " |
6320 | "tc.reloptions AS toast_reloptions, " |
6321 | "NULL AS changed_acl, " |
6322 | "NULL AS partkeydef, " |
6323 | "false AS ispartition, " |
6324 | "NULL AS partbound " |
6325 | "FROM pg_class c " |
6326 | "LEFT JOIN pg_depend d ON " |
6327 | "(c.relkind = '%c' AND " |
6328 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6329 | "d.objsubid = 0 AND " |
6330 | "d.refclassid = c.tableoid AND d.deptype = 'a') " |
6331 | "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " |
6332 | "WHERE c.relkind in ('%c', '%c', '%c', '%c') " |
6333 | "ORDER BY c.oid" , |
6334 | username_subquery, |
6335 | RELKIND_SEQUENCE, |
6336 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6337 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); |
6338 | } |
6339 | else if (fout->remoteVersion >= 80400) |
6340 | { |
6341 | /* |
6342 | * Left join to pick up dependency info linking sequences to their |
6343 | * owning column, if any (note this dependency is AUTO as of 8.2) |
6344 | */ |
6345 | appendPQExpBuffer(query, |
6346 | "SELECT c.tableoid, c.oid, c.relname, " |
6347 | "c.relacl, NULL as rrelacl, " |
6348 | "NULL AS initrelacl, NULL AS initrrelacl, " |
6349 | "c.relkind, " |
6350 | "c.relnamespace, " |
6351 | "(%s c.relowner) AS rolname, " |
6352 | "c.relchecks, c.relhastriggers, " |
6353 | "c.relhasindex, c.relhasrules, c.relhasoids, " |
6354 | "'f'::bool AS relrowsecurity, " |
6355 | "'f'::bool AS relforcerowsecurity, " |
6356 | "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, " |
6357 | "tc.relfrozenxid AS tfrozenxid, " |
6358 | "0 AS tminmxid, " |
6359 | "'p' AS relpersistence, 't' as relispopulated, " |
6360 | "'d' AS relreplident, c.relpages, " |
6361 | "NULL AS amname, " |
6362 | "NULL AS reloftype, " |
6363 | "d.refobjid AS owning_tab, " |
6364 | "d.refobjsubid AS owning_col, " |
6365 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6366 | "c.reloptions AS reloptions, " |
6367 | "tc.reloptions AS toast_reloptions, " |
6368 | "NULL AS changed_acl, " |
6369 | "NULL AS partkeydef, " |
6370 | "false AS ispartition, " |
6371 | "NULL AS partbound " |
6372 | "FROM pg_class c " |
6373 | "LEFT JOIN pg_depend d ON " |
6374 | "(c.relkind = '%c' AND " |
6375 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6376 | "d.objsubid = 0 AND " |
6377 | "d.refclassid = c.tableoid AND d.deptype = 'a') " |
6378 | "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " |
6379 | "WHERE c.relkind in ('%c', '%c', '%c', '%c') " |
6380 | "ORDER BY c.oid" , |
6381 | username_subquery, |
6382 | RELKIND_SEQUENCE, |
6383 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6384 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); |
6385 | } |
6386 | else if (fout->remoteVersion >= 80200) |
6387 | { |
6388 | /* |
6389 | * Left join to pick up dependency info linking sequences to their |
6390 | * owning column, if any (note this dependency is AUTO as of 8.2) |
6391 | */ |
6392 | appendPQExpBuffer(query, |
6393 | "SELECT c.tableoid, c.oid, c.relname, " |
6394 | "c.relacl, NULL as rrelacl, " |
6395 | "NULL AS initrelacl, NULL AS initrrelacl, " |
6396 | "c.relkind, " |
6397 | "c.relnamespace, " |
6398 | "(%s c.relowner) AS rolname, " |
6399 | "c.relchecks, (c.reltriggers <> 0) AS relhastriggers, " |
6400 | "c.relhasindex, c.relhasrules, c.relhasoids, " |
6401 | "'f'::bool AS relrowsecurity, " |
6402 | "'f'::bool AS relforcerowsecurity, " |
6403 | "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, " |
6404 | "tc.relfrozenxid AS tfrozenxid, " |
6405 | "0 AS tminmxid, " |
6406 | "'p' AS relpersistence, 't' as relispopulated, " |
6407 | "'d' AS relreplident, c.relpages, " |
6408 | "NULL AS amname, " |
6409 | "NULL AS reloftype, " |
6410 | "d.refobjid AS owning_tab, " |
6411 | "d.refobjsubid AS owning_col, " |
6412 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6413 | "c.reloptions AS reloptions, " |
6414 | "NULL AS toast_reloptions, " |
6415 | "NULL AS changed_acl, " |
6416 | "NULL AS partkeydef, " |
6417 | "false AS ispartition, " |
6418 | "NULL AS partbound " |
6419 | "FROM pg_class c " |
6420 | "LEFT JOIN pg_depend d ON " |
6421 | "(c.relkind = '%c' AND " |
6422 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6423 | "d.objsubid = 0 AND " |
6424 | "d.refclassid = c.tableoid AND d.deptype = 'a') " |
6425 | "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " |
6426 | "WHERE c.relkind in ('%c', '%c', '%c', '%c') " |
6427 | "ORDER BY c.oid" , |
6428 | username_subquery, |
6429 | RELKIND_SEQUENCE, |
6430 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6431 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); |
6432 | } |
6433 | else |
6434 | { |
6435 | /* |
6436 | * Left join to pick up dependency info linking sequences to their |
6437 | * owning column, if any |
6438 | */ |
6439 | appendPQExpBuffer(query, |
6440 | "SELECT c.tableoid, c.oid, relname, " |
6441 | "relacl, NULL as rrelacl, " |
6442 | "NULL AS initrelacl, NULL AS initrrelacl, " |
6443 | "relkind, relnamespace, " |
6444 | "(%s relowner) AS rolname, " |
6445 | "relchecks, (reltriggers <> 0) AS relhastriggers, " |
6446 | "relhasindex, relhasrules, relhasoids, " |
6447 | "'f'::bool AS relrowsecurity, " |
6448 | "'f'::bool AS relforcerowsecurity, " |
6449 | "0 AS relfrozenxid, 0 AS relminmxid," |
6450 | "0 AS toid, " |
6451 | "0 AS tfrozenxid, 0 AS tminmxid," |
6452 | "'p' AS relpersistence, 't' as relispopulated, " |
6453 | "'d' AS relreplident, relpages, " |
6454 | "NULL AS amname, " |
6455 | "NULL AS reloftype, " |
6456 | "d.refobjid AS owning_tab, " |
6457 | "d.refobjsubid AS owning_col, " |
6458 | "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " |
6459 | "NULL AS reloptions, " |
6460 | "NULL AS toast_reloptions, " |
6461 | "NULL AS changed_acl, " |
6462 | "NULL AS partkeydef, " |
6463 | "false AS ispartition, " |
6464 | "NULL AS partbound " |
6465 | "FROM pg_class c " |
6466 | "LEFT JOIN pg_depend d ON " |
6467 | "(c.relkind = '%c' AND " |
6468 | "d.classid = c.tableoid AND d.objid = c.oid AND " |
6469 | "d.objsubid = 0 AND " |
6470 | "d.refclassid = c.tableoid AND d.deptype = 'i') " |
6471 | "WHERE relkind in ('%c', '%c', '%c', '%c') " |
6472 | "ORDER BY c.oid" , |
6473 | username_subquery, |
6474 | RELKIND_SEQUENCE, |
6475 | RELKIND_RELATION, RELKIND_SEQUENCE, |
6476 | RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); |
6477 | } |
6478 | |
6479 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
6480 | |
6481 | ntups = PQntuples(res); |
6482 | |
6483 | *numTables = ntups; |
6484 | |
6485 | /* |
6486 | * Extract data from result and lock dumpable tables. We do the locking |
6487 | * before anything else, to minimize the window wherein a table could |
6488 | * disappear under us. |
6489 | * |
6490 | * Note that we have to save info about all tables here, even when dumping |
6491 | * only one, because we don't yet know which tables might be inheritance |
6492 | * ancestors of the target table. |
6493 | */ |
6494 | tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo)); |
6495 | |
6496 | i_reltableoid = PQfnumber(res, "tableoid" ); |
6497 | i_reloid = PQfnumber(res, "oid" ); |
6498 | i_relname = PQfnumber(res, "relname" ); |
6499 | i_relnamespace = PQfnumber(res, "relnamespace" ); |
6500 | i_relacl = PQfnumber(res, "relacl" ); |
6501 | i_rrelacl = PQfnumber(res, "rrelacl" ); |
6502 | i_initrelacl = PQfnumber(res, "initrelacl" ); |
6503 | i_initrrelacl = PQfnumber(res, "initrrelacl" ); |
6504 | i_relkind = PQfnumber(res, "relkind" ); |
6505 | i_rolname = PQfnumber(res, "rolname" ); |
6506 | i_relchecks = PQfnumber(res, "relchecks" ); |
6507 | i_relhastriggers = PQfnumber(res, "relhastriggers" ); |
6508 | i_relhasindex = PQfnumber(res, "relhasindex" ); |
6509 | i_relhasrules = PQfnumber(res, "relhasrules" ); |
6510 | i_relrowsec = PQfnumber(res, "relrowsecurity" ); |
6511 | i_relforcerowsec = PQfnumber(res, "relforcerowsecurity" ); |
6512 | i_relhasoids = PQfnumber(res, "relhasoids" ); |
6513 | i_relfrozenxid = PQfnumber(res, "relfrozenxid" ); |
6514 | i_relminmxid = PQfnumber(res, "relminmxid" ); |
6515 | i_toastoid = PQfnumber(res, "toid" ); |
6516 | i_toastfrozenxid = PQfnumber(res, "tfrozenxid" ); |
6517 | i_toastminmxid = PQfnumber(res, "tminmxid" ); |
6518 | i_relpersistence = PQfnumber(res, "relpersistence" ); |
6519 | i_relispopulated = PQfnumber(res, "relispopulated" ); |
6520 | i_relreplident = PQfnumber(res, "relreplident" ); |
6521 | i_relpages = PQfnumber(res, "relpages" ); |
6522 | i_owning_tab = PQfnumber(res, "owning_tab" ); |
6523 | i_owning_col = PQfnumber(res, "owning_col" ); |
6524 | i_reltablespace = PQfnumber(res, "reltablespace" ); |
6525 | i_reloptions = PQfnumber(res, "reloptions" ); |
6526 | i_checkoption = PQfnumber(res, "checkoption" ); |
6527 | i_toastreloptions = PQfnumber(res, "toast_reloptions" ); |
6528 | i_reloftype = PQfnumber(res, "reloftype" ); |
6529 | i_is_identity_sequence = PQfnumber(res, "is_identity_sequence" ); |
6530 | i_changed_acl = PQfnumber(res, "changed_acl" ); |
6531 | i_partkeydef = PQfnumber(res, "partkeydef" ); |
6532 | i_ispartition = PQfnumber(res, "ispartition" ); |
6533 | i_partbound = PQfnumber(res, "partbound" ); |
6534 | i_amname = PQfnumber(res, "amname" ); |
6535 | |
6536 | if (dopt->lockWaitTimeout) |
6537 | { |
6538 | /* |
6539 | * Arrange to fail instead of waiting forever for a table lock. |
6540 | * |
6541 | * NB: this coding assumes that the only queries issued within the |
6542 | * following loop are LOCK TABLEs; else the timeout may be undesirably |
6543 | * applied to other things too. |
6544 | */ |
6545 | resetPQExpBuffer(query); |
6546 | appendPQExpBufferStr(query, "SET statement_timeout = " ); |
6547 | appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout)); |
6548 | ExecuteSqlStatement(fout, query->data); |
6549 | } |
6550 | |
6551 | for (i = 0; i < ntups; i++) |
6552 | { |
6553 | tblinfo[i].dobj.objType = DO_TABLE; |
6554 | tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid)); |
6555 | tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid)); |
6556 | AssignDumpId(&tblinfo[i].dobj); |
6557 | tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname)); |
6558 | tblinfo[i].dobj.namespace = |
6559 | findNamespace(fout, |
6560 | atooid(PQgetvalue(res, i, i_relnamespace))); |
6561 | tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
6562 | tblinfo[i].relacl = pg_strdup(PQgetvalue(res, i, i_relacl)); |
6563 | tblinfo[i].rrelacl = pg_strdup(PQgetvalue(res, i, i_rrelacl)); |
6564 | tblinfo[i].initrelacl = pg_strdup(PQgetvalue(res, i, i_initrelacl)); |
6565 | tblinfo[i].initrrelacl = pg_strdup(PQgetvalue(res, i, i_initrrelacl)); |
6566 | tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind)); |
6567 | tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence)); |
6568 | tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t" ) == 0); |
6569 | tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t" ) == 0); |
6570 | tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t" ) == 0); |
6571 | tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t" ) == 0); |
6572 | tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t" ) == 0); |
6573 | tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t" ) == 0); |
6574 | tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t" ) == 0); |
6575 | tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident)); |
6576 | tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages)); |
6577 | tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); |
6578 | tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid)); |
6579 | tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid)); |
6580 | tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid)); |
6581 | tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid)); |
6582 | if (PQgetisnull(res, i, i_reloftype)) |
6583 | tblinfo[i].reloftype = NULL; |
6584 | else |
6585 | tblinfo[i].reloftype = pg_strdup(PQgetvalue(res, i, i_reloftype)); |
6586 | tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks)); |
6587 | if (PQgetisnull(res, i, i_owning_tab)) |
6588 | { |
6589 | tblinfo[i].owning_tab = InvalidOid; |
6590 | tblinfo[i].owning_col = 0; |
6591 | } |
6592 | else |
6593 | { |
6594 | tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab)); |
6595 | tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col)); |
6596 | } |
6597 | tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace)); |
6598 | tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions)); |
6599 | if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption)) |
6600 | tblinfo[i].checkoption = NULL; |
6601 | else |
6602 | tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); |
6603 | tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); |
6604 | if (PQgetisnull(res, i, i_amname)) |
6605 | tblinfo[i].amname = NULL; |
6606 | else |
6607 | tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname)); |
6608 | |
6609 | /* other fields were zeroed above */ |
6610 | |
6611 | /* |
6612 | * Decide whether we want to dump this table. |
6613 | */ |
6614 | if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE) |
6615 | tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE; |
6616 | else |
6617 | selectDumpableTable(&tblinfo[i], fout); |
6618 | |
6619 | /* |
6620 | * If the table-level and all column-level ACLs for this table are |
6621 | * unchanged, then we don't need to worry about including the ACLs for |
6622 | * this table. If any column-level ACLs have been changed, the |
6623 | * 'changed_acl' column from the query will indicate that. |
6624 | * |
6625 | * This can result in a significant performance improvement in cases |
6626 | * where we are only looking to dump out the ACL (eg: pg_catalog). |
6627 | */ |
6628 | if (PQgetisnull(res, i, i_relacl) && PQgetisnull(res, i, i_rrelacl) && |
6629 | PQgetisnull(res, i, i_initrelacl) && |
6630 | PQgetisnull(res, i, i_initrrelacl) && |
6631 | strcmp(PQgetvalue(res, i, i_changed_acl), "f" ) == 0) |
6632 | tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
6633 | |
6634 | tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false; |
6635 | tblinfo[i].dummy_view = false; /* might get set during sort */ |
6636 | tblinfo[i].postponed_def = false; /* might get set during sort */ |
6637 | |
6638 | tblinfo[i].is_identity_sequence = (i_is_identity_sequence >= 0 && |
6639 | strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t" ) == 0); |
6640 | |
6641 | /* Partition key string or NULL */ |
6642 | tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); |
6643 | tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t" ) == 0); |
6644 | tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound)); |
6645 | |
6646 | /* |
6647 | * Read-lock target tables to make sure they aren't DROPPED or altered |
6648 | * in schema before we get around to dumping them. |
6649 | * |
6650 | * Note that we don't explicitly lock parents of the target tables; we |
6651 | * assume our lock on the child is enough to prevent schema |
6652 | * alterations to parent tables. |
6653 | * |
6654 | * NOTE: it'd be kinda nice to lock other relations too, not only |
6655 | * plain or partitioned tables, but the backend doesn't presently |
6656 | * allow that. |
6657 | * |
6658 | * We only need to lock the table for certain components; see |
6659 | * pg_dump.h |
6660 | */ |
6661 | if (tblinfo[i].dobj.dump && |
6662 | (tblinfo[i].relkind == RELKIND_RELATION || |
6663 | tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && |
6664 | (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) |
6665 | { |
6666 | resetPQExpBuffer(query); |
6667 | appendPQExpBuffer(query, |
6668 | "LOCK TABLE %s IN ACCESS SHARE MODE" , |
6669 | fmtQualifiedDumpable(&tblinfo[i])); |
6670 | ExecuteSqlStatement(fout, query->data); |
6671 | } |
6672 | |
6673 | /* Emit notice if join for owner failed */ |
6674 | if (strlen(tblinfo[i].rolname) == 0) |
6675 | pg_log_warning("owner of table \"%s\" appears to be invalid" , |
6676 | tblinfo[i].dobj.name); |
6677 | } |
6678 | |
6679 | if (dopt->lockWaitTimeout) |
6680 | { |
6681 | ExecuteSqlStatement(fout, "SET statement_timeout = 0" ); |
6682 | } |
6683 | |
6684 | PQclear(res); |
6685 | |
6686 | destroyPQExpBuffer(query); |
6687 | |
6688 | return tblinfo; |
6689 | } |
6690 | |
6691 | /* |
6692 | * getOwnedSeqs |
6693 | * identify owned sequences and mark them as dumpable if owning table is |
6694 | * |
6695 | * We used to do this in getTables(), but it's better to do it after the |
6696 | * index used by findTableByOid() has been set up. |
6697 | */ |
6698 | void |
6699 | getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables) |
6700 | { |
6701 | int i; |
6702 | |
6703 | /* |
6704 | * Force sequences that are "owned" by table columns to be dumped whenever |
6705 | * their owning table is being dumped. |
6706 | */ |
6707 | for (i = 0; i < numTables; i++) |
6708 | { |
6709 | TableInfo *seqinfo = &tblinfo[i]; |
6710 | TableInfo *owning_tab; |
6711 | |
6712 | if (!OidIsValid(seqinfo->owning_tab)) |
6713 | continue; /* not an owned sequence */ |
6714 | |
6715 | owning_tab = findTableByOid(seqinfo->owning_tab); |
6716 | if (owning_tab == NULL) |
6717 | fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found" , |
6718 | seqinfo->owning_tab, seqinfo->dobj.catId.oid); |
6719 | |
6720 | /* |
6721 | * Only dump identity sequences if we're going to dump the table that |
6722 | * it belongs to. |
6723 | */ |
6724 | if (owning_tab->dobj.dump == DUMP_COMPONENT_NONE && |
6725 | seqinfo->is_identity_sequence) |
6726 | { |
6727 | seqinfo->dobj.dump = DUMP_COMPONENT_NONE; |
6728 | continue; |
6729 | } |
6730 | |
6731 | /* |
6732 | * Otherwise we need to dump the components that are being dumped for |
6733 | * the table and any components which the sequence is explicitly |
6734 | * marked with. |
6735 | * |
6736 | * We can't simply use the set of components which are being dumped |
6737 | * for the table as the table might be in an extension (and only the |
6738 | * non-extension components, eg: ACLs if changed, security labels, and |
6739 | * policies, are being dumped) while the sequence is not (and |
6740 | * therefore the definition and other components should also be |
6741 | * dumped). |
6742 | * |
6743 | * If the sequence is part of the extension then it should be properly |
6744 | * marked by checkExtensionMembership() and this will be a no-op as |
6745 | * the table will be equivalently marked. |
6746 | */ |
6747 | seqinfo->dobj.dump = seqinfo->dobj.dump | owning_tab->dobj.dump; |
6748 | |
6749 | if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE) |
6750 | seqinfo->interesting = true; |
6751 | } |
6752 | } |
6753 | |
6754 | /* |
6755 | * getInherits |
6756 | * read all the inheritance information |
6757 | * from the system catalogs return them in the InhInfo* structure |
6758 | * |
6759 | * numInherits is set to the number of pairs read in |
6760 | */ |
6761 | InhInfo * |
6762 | getInherits(Archive *fout, int *numInherits) |
6763 | { |
6764 | PGresult *res; |
6765 | int ntups; |
6766 | int i; |
6767 | PQExpBuffer query = createPQExpBuffer(); |
6768 | InhInfo *inhinfo; |
6769 | |
6770 | int i_inhrelid; |
6771 | int i_inhparent; |
6772 | |
6773 | /* |
6774 | * Find all the inheritance information, excluding implicit inheritance |
6775 | * via partitioning. We handle that case using getPartitions(), because |
6776 | * we want more information about partitions than just the parent-child |
6777 | * relationship. |
6778 | */ |
6779 | appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits" ); |
6780 | |
6781 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
6782 | |
6783 | ntups = PQntuples(res); |
6784 | |
6785 | *numInherits = ntups; |
6786 | |
6787 | inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo)); |
6788 | |
6789 | i_inhrelid = PQfnumber(res, "inhrelid" ); |
6790 | i_inhparent = PQfnumber(res, "inhparent" ); |
6791 | |
6792 | for (i = 0; i < ntups; i++) |
6793 | { |
6794 | inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid)); |
6795 | inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent)); |
6796 | } |
6797 | |
6798 | PQclear(res); |
6799 | |
6800 | destroyPQExpBuffer(query); |
6801 | |
6802 | return inhinfo; |
6803 | } |
6804 | |
6805 | /* |
6806 | * getIndexes |
6807 | * get information about every index on a dumpable table |
6808 | * |
6809 | * Note: index data is not returned directly to the caller, but it |
6810 | * does get entered into the DumpableObject tables. |
6811 | */ |
6812 | void |
6813 | getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) |
6814 | { |
6815 | int i, |
6816 | j; |
6817 | PQExpBuffer query = createPQExpBuffer(); |
6818 | PGresult *res; |
6819 | IndxInfo *indxinfo; |
6820 | ConstraintInfo *constrinfo; |
6821 | int i_tableoid, |
6822 | i_oid, |
6823 | i_indexname, |
6824 | i_parentidx, |
6825 | i_indexdef, |
6826 | i_indnkeyatts, |
6827 | i_indnatts, |
6828 | i_indkey, |
6829 | i_indisclustered, |
6830 | i_indisreplident, |
6831 | i_contype, |
6832 | i_conname, |
6833 | i_condeferrable, |
6834 | i_condeferred, |
6835 | i_contableoid, |
6836 | i_conoid, |
6837 | i_condef, |
6838 | i_tablespace, |
6839 | i_indreloptions, |
6840 | i_indstatcols, |
6841 | i_indstatvals; |
6842 | int ntups; |
6843 | |
6844 | for (i = 0; i < numTables; i++) |
6845 | { |
6846 | TableInfo *tbinfo = &tblinfo[i]; |
6847 | |
6848 | if (!tbinfo->hasindex) |
6849 | continue; |
6850 | |
6851 | /* |
6852 | * Ignore indexes of tables whose definitions are not to be dumped. |
6853 | * |
6854 | * We also need indexes on partitioned tables which have partitions to |
6855 | * be dumped, in order to dump the indexes on the partitions. |
6856 | */ |
6857 | if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) && |
6858 | !tbinfo->interesting) |
6859 | continue; |
6860 | |
6861 | pg_log_info("reading indexes for table \"%s.%s\"" , |
6862 | tbinfo->dobj.namespace->dobj.name, |
6863 | tbinfo->dobj.name); |
6864 | |
6865 | /* |
6866 | * The point of the messy-looking outer join is to find a constraint |
6867 | * that is related by an internal dependency link to the index. If we |
6868 | * find one, create a CONSTRAINT entry linked to the INDEX entry. We |
6869 | * assume an index won't have more than one internal dependency. |
6870 | * |
6871 | * As of 9.0 we don't need to look at pg_depend but can check for a |
6872 | * match to pg_constraint.conindid. The check on conrelid is |
6873 | * redundant but useful because that column is indexed while conindid |
6874 | * is not. |
6875 | */ |
6876 | resetPQExpBuffer(query); |
6877 | if (fout->remoteVersion >= 110000) |
6878 | { |
6879 | appendPQExpBuffer(query, |
6880 | "SELECT t.tableoid, t.oid, " |
6881 | "t.relname AS indexname, " |
6882 | "inh.inhparent AS parentidx, " |
6883 | "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " |
6884 | "i.indnkeyatts AS indnkeyatts, " |
6885 | "i.indnatts AS indnatts, " |
6886 | "i.indkey, i.indisclustered, " |
6887 | "i.indisreplident, " |
6888 | "c.contype, c.conname, " |
6889 | "c.condeferrable, c.condeferred, " |
6890 | "c.tableoid AS contableoid, " |
6891 | "c.oid AS conoid, " |
6892 | "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " |
6893 | "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " |
6894 | "t.reloptions AS indreloptions, " |
6895 | "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) " |
6896 | " FROM pg_catalog.pg_attribute " |
6897 | " WHERE attrelid = i.indexrelid AND " |
6898 | " attstattarget >= 0) AS indstatcols," |
6899 | "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) " |
6900 | " FROM pg_catalog.pg_attribute " |
6901 | " WHERE attrelid = i.indexrelid AND " |
6902 | " attstattarget >= 0) AS indstatvals " |
6903 | "FROM pg_catalog.pg_index i " |
6904 | "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " |
6905 | "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) " |
6906 | "LEFT JOIN pg_catalog.pg_constraint c " |
6907 | "ON (i.indrelid = c.conrelid AND " |
6908 | "i.indexrelid = c.conindid AND " |
6909 | "c.contype IN ('p','u','x')) " |
6910 | "LEFT JOIN pg_catalog.pg_inherits inh " |
6911 | "ON (inh.inhrelid = indexrelid) " |
6912 | "WHERE i.indrelid = '%u'::pg_catalog.oid " |
6913 | "AND (i.indisvalid OR t2.relkind = 'p') " |
6914 | "AND i.indisready " |
6915 | "ORDER BY indexname" , |
6916 | tbinfo->dobj.catId.oid); |
6917 | } |
6918 | else if (fout->remoteVersion >= 90400) |
6919 | { |
6920 | /* |
6921 | * the test on indisready is necessary in 9.2, and harmless in |
6922 | * earlier/later versions |
6923 | */ |
6924 | appendPQExpBuffer(query, |
6925 | "SELECT t.tableoid, t.oid, " |
6926 | "t.relname AS indexname, " |
6927 | "0 AS parentidx, " |
6928 | "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " |
6929 | "i.indnatts AS indnkeyatts, " |
6930 | "i.indnatts AS indnatts, " |
6931 | "i.indkey, i.indisclustered, " |
6932 | "i.indisreplident, " |
6933 | "c.contype, c.conname, " |
6934 | "c.condeferrable, c.condeferred, " |
6935 | "c.tableoid AS contableoid, " |
6936 | "c.oid AS conoid, " |
6937 | "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " |
6938 | "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " |
6939 | "t.reloptions AS indreloptions, " |
6940 | "'' AS indstatcols, " |
6941 | "'' AS indstatvals " |
6942 | "FROM pg_catalog.pg_index i " |
6943 | "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " |
6944 | "LEFT JOIN pg_catalog.pg_constraint c " |
6945 | "ON (i.indrelid = c.conrelid AND " |
6946 | "i.indexrelid = c.conindid AND " |
6947 | "c.contype IN ('p','u','x')) " |
6948 | "WHERE i.indrelid = '%u'::pg_catalog.oid " |
6949 | "AND i.indisvalid AND i.indisready " |
6950 | "ORDER BY indexname" , |
6951 | tbinfo->dobj.catId.oid); |
6952 | } |
6953 | else if (fout->remoteVersion >= 90000) |
6954 | { |
6955 | /* |
6956 | * the test on indisready is necessary in 9.2, and harmless in |
6957 | * earlier/later versions |
6958 | */ |
6959 | appendPQExpBuffer(query, |
6960 | "SELECT t.tableoid, t.oid, " |
6961 | "t.relname AS indexname, " |
6962 | "0 AS parentidx, " |
6963 | "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " |
6964 | "i.indnatts AS indnkeyatts, " |
6965 | "i.indnatts AS indnatts, " |
6966 | "i.indkey, i.indisclustered, " |
6967 | "false AS indisreplident, " |
6968 | "c.contype, c.conname, " |
6969 | "c.condeferrable, c.condeferred, " |
6970 | "c.tableoid AS contableoid, " |
6971 | "c.oid AS conoid, " |
6972 | "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " |
6973 | "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " |
6974 | "t.reloptions AS indreloptions, " |
6975 | "'' AS indstatcols, " |
6976 | "'' AS indstatvals " |
6977 | "FROM pg_catalog.pg_index i " |
6978 | "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " |
6979 | "LEFT JOIN pg_catalog.pg_constraint c " |
6980 | "ON (i.indrelid = c.conrelid AND " |
6981 | "i.indexrelid = c.conindid AND " |
6982 | "c.contype IN ('p','u','x')) " |
6983 | "WHERE i.indrelid = '%u'::pg_catalog.oid " |
6984 | "AND i.indisvalid AND i.indisready " |
6985 | "ORDER BY indexname" , |
6986 | tbinfo->dobj.catId.oid); |
6987 | } |
6988 | else if (fout->remoteVersion >= 80200) |
6989 | { |
6990 | appendPQExpBuffer(query, |
6991 | "SELECT t.tableoid, t.oid, " |
6992 | "t.relname AS indexname, " |
6993 | "0 AS parentidx, " |
6994 | "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " |
6995 | "i.indnatts AS indnkeyatts, " |
6996 | "i.indnatts AS indnatts, " |
6997 | "i.indkey, i.indisclustered, " |
6998 | "false AS indisreplident, " |
6999 | "c.contype, c.conname, " |
7000 | "c.condeferrable, c.condeferred, " |
7001 | "c.tableoid AS contableoid, " |
7002 | "c.oid AS conoid, " |
7003 | "null AS condef, " |
7004 | "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " |
7005 | "t.reloptions AS indreloptions, " |
7006 | "'' AS indstatcols, " |
7007 | "'' AS indstatvals " |
7008 | "FROM pg_catalog.pg_index i " |
7009 | "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " |
7010 | "LEFT JOIN pg_catalog.pg_depend d " |
7011 | "ON (d.classid = t.tableoid " |
7012 | "AND d.objid = t.oid " |
7013 | "AND d.deptype = 'i') " |
7014 | "LEFT JOIN pg_catalog.pg_constraint c " |
7015 | "ON (d.refclassid = c.tableoid " |
7016 | "AND d.refobjid = c.oid) " |
7017 | "WHERE i.indrelid = '%u'::pg_catalog.oid " |
7018 | "AND i.indisvalid " |
7019 | "ORDER BY indexname" , |
7020 | tbinfo->dobj.catId.oid); |
7021 | } |
7022 | else |
7023 | { |
7024 | appendPQExpBuffer(query, |
7025 | "SELECT t.tableoid, t.oid, " |
7026 | "t.relname AS indexname, " |
7027 | "0 AS parentidx, " |
7028 | "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " |
7029 | "t.relnatts AS indnkeyatts, " |
7030 | "t.relnatts AS indnatts, " |
7031 | "i.indkey, i.indisclustered, " |
7032 | "false AS indisreplident, " |
7033 | "c.contype, c.conname, " |
7034 | "c.condeferrable, c.condeferred, " |
7035 | "c.tableoid AS contableoid, " |
7036 | "c.oid AS conoid, " |
7037 | "null AS condef, " |
7038 | "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " |
7039 | "null AS indreloptions, " |
7040 | "'' AS indstatcols, " |
7041 | "'' AS indstatvals " |
7042 | "FROM pg_catalog.pg_index i " |
7043 | "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " |
7044 | "LEFT JOIN pg_catalog.pg_depend d " |
7045 | "ON (d.classid = t.tableoid " |
7046 | "AND d.objid = t.oid " |
7047 | "AND d.deptype = 'i') " |
7048 | "LEFT JOIN pg_catalog.pg_constraint c " |
7049 | "ON (d.refclassid = c.tableoid " |
7050 | "AND d.refobjid = c.oid) " |
7051 | "WHERE i.indrelid = '%u'::pg_catalog.oid " |
7052 | "ORDER BY indexname" , |
7053 | tbinfo->dobj.catId.oid); |
7054 | } |
7055 | |
7056 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
7057 | |
7058 | ntups = PQntuples(res); |
7059 | |
7060 | i_tableoid = PQfnumber(res, "tableoid" ); |
7061 | i_oid = PQfnumber(res, "oid" ); |
7062 | i_indexname = PQfnumber(res, "indexname" ); |
7063 | i_parentidx = PQfnumber(res, "parentidx" ); |
7064 | i_indexdef = PQfnumber(res, "indexdef" ); |
7065 | i_indnkeyatts = PQfnumber(res, "indnkeyatts" ); |
7066 | i_indnatts = PQfnumber(res, "indnatts" ); |
7067 | i_indkey = PQfnumber(res, "indkey" ); |
7068 | i_indisclustered = PQfnumber(res, "indisclustered" ); |
7069 | i_indisreplident = PQfnumber(res, "indisreplident" ); |
7070 | i_contype = PQfnumber(res, "contype" ); |
7071 | i_conname = PQfnumber(res, "conname" ); |
7072 | i_condeferrable = PQfnumber(res, "condeferrable" ); |
7073 | i_condeferred = PQfnumber(res, "condeferred" ); |
7074 | i_contableoid = PQfnumber(res, "contableoid" ); |
7075 | i_conoid = PQfnumber(res, "conoid" ); |
7076 | i_condef = PQfnumber(res, "condef" ); |
7077 | i_tablespace = PQfnumber(res, "tablespace" ); |
7078 | i_indreloptions = PQfnumber(res, "indreloptions" ); |
7079 | i_indstatcols = PQfnumber(res, "indstatcols" ); |
7080 | i_indstatvals = PQfnumber(res, "indstatvals" ); |
7081 | |
7082 | tbinfo->indexes = indxinfo = |
7083 | (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo)); |
7084 | constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo)); |
7085 | tbinfo->numIndexes = ntups; |
7086 | |
7087 | for (j = 0; j < ntups; j++) |
7088 | { |
7089 | char contype; |
7090 | |
7091 | indxinfo[j].dobj.objType = DO_INDEX; |
7092 | indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid)); |
7093 | indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid)); |
7094 | AssignDumpId(&indxinfo[j].dobj); |
7095 | indxinfo[j].dobj.dump = tbinfo->dobj.dump; |
7096 | indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname)); |
7097 | indxinfo[j].dobj.namespace = tbinfo->dobj.namespace; |
7098 | indxinfo[j].indextable = tbinfo; |
7099 | indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef)); |
7100 | indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts)); |
7101 | indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts)); |
7102 | indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace)); |
7103 | indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions)); |
7104 | indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols)); |
7105 | indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals)); |
7106 | indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid)); |
7107 | parseOidArray(PQgetvalue(res, j, i_indkey), |
7108 | indxinfo[j].indkeys, indxinfo[j].indnattrs); |
7109 | indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't'); |
7110 | indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't'); |
7111 | indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx)); |
7112 | contype = *(PQgetvalue(res, j, i_contype)); |
7113 | |
7114 | if (contype == 'p' || contype == 'u' || contype == 'x') |
7115 | { |
7116 | /* |
7117 | * If we found a constraint matching the index, create an |
7118 | * entry for it. |
7119 | */ |
7120 | constrinfo[j].dobj.objType = DO_CONSTRAINT; |
7121 | constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid)); |
7122 | constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid)); |
7123 | AssignDumpId(&constrinfo[j].dobj); |
7124 | constrinfo[j].dobj.dump = tbinfo->dobj.dump; |
7125 | constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname)); |
7126 | constrinfo[j].dobj.namespace = tbinfo->dobj.namespace; |
7127 | constrinfo[j].contable = tbinfo; |
7128 | constrinfo[j].condomain = NULL; |
7129 | constrinfo[j].contype = contype; |
7130 | if (contype == 'x') |
7131 | constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef)); |
7132 | else |
7133 | constrinfo[j].condef = NULL; |
7134 | constrinfo[j].confrelid = InvalidOid; |
7135 | constrinfo[j].conindex = indxinfo[j].dobj.dumpId; |
7136 | constrinfo[j].condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't'; |
7137 | constrinfo[j].condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't'; |
7138 | constrinfo[j].conislocal = true; |
7139 | constrinfo[j].separate = true; |
7140 | |
7141 | indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId; |
7142 | } |
7143 | else |
7144 | { |
7145 | /* Plain secondary index */ |
7146 | indxinfo[j].indexconstraint = 0; |
7147 | } |
7148 | } |
7149 | |
7150 | PQclear(res); |
7151 | } |
7152 | |
7153 | destroyPQExpBuffer(query); |
7154 | } |
7155 | |
7156 | /* |
7157 | * getExtendedStatistics |
7158 | * get information about extended-statistics objects. |
7159 | * |
7160 | * Note: extended statistics data is not returned directly to the caller, but |
7161 | * it does get entered into the DumpableObject tables. |
7162 | */ |
7163 | void |
7164 | getExtendedStatistics(Archive *fout) |
7165 | { |
7166 | PQExpBuffer query; |
7167 | PGresult *res; |
7168 | StatsExtInfo *statsextinfo; |
7169 | int ntups; |
7170 | int i_tableoid; |
7171 | int i_oid; |
7172 | int i_stxname; |
7173 | int i_stxnamespace; |
7174 | int i_rolname; |
7175 | int i; |
7176 | |
7177 | /* Extended statistics were new in v10 */ |
7178 | if (fout->remoteVersion < 100000) |
7179 | return; |
7180 | |
7181 | query = createPQExpBuffer(); |
7182 | |
7183 | appendPQExpBuffer(query, "SELECT tableoid, oid, stxname, " |
7184 | "stxnamespace, (%s stxowner) AS rolname " |
7185 | "FROM pg_catalog.pg_statistic_ext" , |
7186 | username_subquery); |
7187 | |
7188 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
7189 | |
7190 | ntups = PQntuples(res); |
7191 | |
7192 | i_tableoid = PQfnumber(res, "tableoid" ); |
7193 | i_oid = PQfnumber(res, "oid" ); |
7194 | i_stxname = PQfnumber(res, "stxname" ); |
7195 | i_stxnamespace = PQfnumber(res, "stxnamespace" ); |
7196 | i_rolname = PQfnumber(res, "rolname" ); |
7197 | |
7198 | statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo)); |
7199 | |
7200 | for (i = 0; i < ntups; i++) |
7201 | { |
7202 | statsextinfo[i].dobj.objType = DO_STATSEXT; |
7203 | statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
7204 | statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
7205 | AssignDumpId(&statsextinfo[i].dobj); |
7206 | statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname)); |
7207 | statsextinfo[i].dobj.namespace = |
7208 | findNamespace(fout, |
7209 | atooid(PQgetvalue(res, i, i_stxnamespace))); |
7210 | statsextinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
7211 | |
7212 | /* Decide whether we want to dump it */ |
7213 | selectDumpableObject(&(statsextinfo[i].dobj), fout); |
7214 | |
7215 | /* Stats objects do not currently have ACLs. */ |
7216 | statsextinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
7217 | } |
7218 | |
7219 | PQclear(res); |
7220 | destroyPQExpBuffer(query); |
7221 | } |
7222 | |
7223 | /* |
7224 | * getConstraints |
7225 | * |
7226 | * Get info about constraints on dumpable tables. |
7227 | * |
7228 | * Currently handles foreign keys only. |
7229 | * Unique and primary key constraints are handled with indexes, |
7230 | * while check constraints are processed in getTableAttrs(). |
7231 | */ |
7232 | void |
7233 | getConstraints(Archive *fout, TableInfo tblinfo[], int numTables) |
7234 | { |
7235 | int i, |
7236 | j; |
7237 | ConstraintInfo *constrinfo; |
7238 | PQExpBuffer query; |
7239 | PGresult *res; |
7240 | int i_contableoid, |
7241 | i_conoid, |
7242 | i_conname, |
7243 | i_confrelid, |
7244 | i_condef; |
7245 | int ntups; |
7246 | |
7247 | query = createPQExpBuffer(); |
7248 | |
7249 | for (i = 0; i < numTables; i++) |
7250 | { |
7251 | TableInfo *tbinfo = &tblinfo[i]; |
7252 | |
7253 | /* |
7254 | * For partitioned tables, foreign keys have no triggers so they must |
7255 | * be included anyway in case some foreign keys are defined. |
7256 | */ |
7257 | if ((!tbinfo->hastriggers && |
7258 | tbinfo->relkind != RELKIND_PARTITIONED_TABLE) || |
7259 | !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) |
7260 | continue; |
7261 | |
7262 | pg_log_info("reading foreign key constraints for table \"%s.%s\"" , |
7263 | tbinfo->dobj.namespace->dobj.name, |
7264 | tbinfo->dobj.name); |
7265 | |
7266 | resetPQExpBuffer(query); |
7267 | if (fout->remoteVersion >= 110000) |
7268 | appendPQExpBuffer(query, |
7269 | "SELECT tableoid, oid, conname, confrelid, " |
7270 | "pg_catalog.pg_get_constraintdef(oid) AS condef " |
7271 | "FROM pg_catalog.pg_constraint " |
7272 | "WHERE conrelid = '%u'::pg_catalog.oid " |
7273 | "AND conparentid = 0 " |
7274 | "AND contype = 'f'" , |
7275 | tbinfo->dobj.catId.oid); |
7276 | else |
7277 | appendPQExpBuffer(query, |
7278 | "SELECT tableoid, oid, conname, confrelid, " |
7279 | "pg_catalog.pg_get_constraintdef(oid) AS condef " |
7280 | "FROM pg_catalog.pg_constraint " |
7281 | "WHERE conrelid = '%u'::pg_catalog.oid " |
7282 | "AND contype = 'f'" , |
7283 | tbinfo->dobj.catId.oid); |
7284 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
7285 | |
7286 | ntups = PQntuples(res); |
7287 | |
7288 | i_contableoid = PQfnumber(res, "tableoid" ); |
7289 | i_conoid = PQfnumber(res, "oid" ); |
7290 | i_conname = PQfnumber(res, "conname" ); |
7291 | i_confrelid = PQfnumber(res, "confrelid" ); |
7292 | i_condef = PQfnumber(res, "condef" ); |
7293 | |
7294 | constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo)); |
7295 | |
7296 | for (j = 0; j < ntups; j++) |
7297 | { |
7298 | constrinfo[j].dobj.objType = DO_FK_CONSTRAINT; |
7299 | constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid)); |
7300 | constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid)); |
7301 | AssignDumpId(&constrinfo[j].dobj); |
7302 | constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname)); |
7303 | constrinfo[j].dobj.namespace = tbinfo->dobj.namespace; |
7304 | constrinfo[j].contable = tbinfo; |
7305 | constrinfo[j].condomain = NULL; |
7306 | constrinfo[j].contype = 'f'; |
7307 | constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef)); |
7308 | constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid)); |
7309 | constrinfo[j].conindex = 0; |
7310 | constrinfo[j].condeferrable = false; |
7311 | constrinfo[j].condeferred = false; |
7312 | constrinfo[j].conislocal = true; |
7313 | constrinfo[j].separate = true; |
7314 | } |
7315 | |
7316 | PQclear(res); |
7317 | } |
7318 | |
7319 | destroyPQExpBuffer(query); |
7320 | } |
7321 | |
7322 | /* |
7323 | * getDomainConstraints |
7324 | * |
7325 | * Get info about constraints on a domain. |
7326 | */ |
7327 | static void |
7328 | getDomainConstraints(Archive *fout, TypeInfo *tyinfo) |
7329 | { |
7330 | int i; |
7331 | ConstraintInfo *constrinfo; |
7332 | PQExpBuffer query; |
7333 | PGresult *res; |
7334 | int i_tableoid, |
7335 | i_oid, |
7336 | i_conname, |
7337 | i_consrc; |
7338 | int ntups; |
7339 | |
7340 | query = createPQExpBuffer(); |
7341 | |
7342 | if (fout->remoteVersion >= 90100) |
7343 | appendPQExpBuffer(query, "SELECT tableoid, oid, conname, " |
7344 | "pg_catalog.pg_get_constraintdef(oid) AS consrc, " |
7345 | "convalidated " |
7346 | "FROM pg_catalog.pg_constraint " |
7347 | "WHERE contypid = '%u'::pg_catalog.oid " |
7348 | "ORDER BY conname" , |
7349 | tyinfo->dobj.catId.oid); |
7350 | |
7351 | else |
7352 | appendPQExpBuffer(query, "SELECT tableoid, oid, conname, " |
7353 | "pg_catalog.pg_get_constraintdef(oid) AS consrc, " |
7354 | "true as convalidated " |
7355 | "FROM pg_catalog.pg_constraint " |
7356 | "WHERE contypid = '%u'::pg_catalog.oid " |
7357 | "ORDER BY conname" , |
7358 | tyinfo->dobj.catId.oid); |
7359 | |
7360 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
7361 | |
7362 | ntups = PQntuples(res); |
7363 | |
7364 | i_tableoid = PQfnumber(res, "tableoid" ); |
7365 | i_oid = PQfnumber(res, "oid" ); |
7366 | i_conname = PQfnumber(res, "conname" ); |
7367 | i_consrc = PQfnumber(res, "consrc" ); |
7368 | |
7369 | constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo)); |
7370 | |
7371 | tyinfo->nDomChecks = ntups; |
7372 | tyinfo->domChecks = constrinfo; |
7373 | |
7374 | for (i = 0; i < ntups; i++) |
7375 | { |
7376 | bool validated = PQgetvalue(res, i, 4)[0] == 't'; |
7377 | |
7378 | constrinfo[i].dobj.objType = DO_CONSTRAINT; |
7379 | constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
7380 | constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
7381 | AssignDumpId(&constrinfo[i].dobj); |
7382 | constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname)); |
7383 | constrinfo[i].dobj.namespace = tyinfo->dobj.namespace; |
7384 | constrinfo[i].contable = NULL; |
7385 | constrinfo[i].condomain = tyinfo; |
7386 | constrinfo[i].contype = 'c'; |
7387 | constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc)); |
7388 | constrinfo[i].confrelid = InvalidOid; |
7389 | constrinfo[i].conindex = 0; |
7390 | constrinfo[i].condeferrable = false; |
7391 | constrinfo[i].condeferred = false; |
7392 | constrinfo[i].conislocal = true; |
7393 | |
7394 | constrinfo[i].separate = !validated; |
7395 | |
7396 | /* |
7397 | * Make the domain depend on the constraint, ensuring it won't be |
7398 | * output till any constraint dependencies are OK. If the constraint |
7399 | * has not been validated, it's going to be dumped after the domain |
7400 | * anyway, so this doesn't matter. |
7401 | */ |
7402 | if (validated) |
7403 | addObjectDependency(&tyinfo->dobj, |
7404 | constrinfo[i].dobj.dumpId); |
7405 | } |
7406 | |
7407 | PQclear(res); |
7408 | |
7409 | destroyPQExpBuffer(query); |
7410 | } |
7411 | |
7412 | /* |
7413 | * getRules |
7414 | * get basic information about every rule in the system |
7415 | * |
7416 | * numRules is set to the number of rules read in |
7417 | */ |
7418 | RuleInfo * |
7419 | getRules(Archive *fout, int *numRules) |
7420 | { |
7421 | PGresult *res; |
7422 | int ntups; |
7423 | int i; |
7424 | PQExpBuffer query = createPQExpBuffer(); |
7425 | RuleInfo *ruleinfo; |
7426 | int i_tableoid; |
7427 | int i_oid; |
7428 | int i_rulename; |
7429 | int i_ruletable; |
7430 | int i_ev_type; |
7431 | int i_is_instead; |
7432 | int i_ev_enabled; |
7433 | |
7434 | if (fout->remoteVersion >= 80300) |
7435 | { |
7436 | appendPQExpBufferStr(query, "SELECT " |
7437 | "tableoid, oid, rulename, " |
7438 | "ev_class AS ruletable, ev_type, is_instead, " |
7439 | "ev_enabled " |
7440 | "FROM pg_rewrite " |
7441 | "ORDER BY oid" ); |
7442 | } |
7443 | else |
7444 | { |
7445 | appendPQExpBufferStr(query, "SELECT " |
7446 | "tableoid, oid, rulename, " |
7447 | "ev_class AS ruletable, ev_type, is_instead, " |
7448 | "'O'::char AS ev_enabled " |
7449 | "FROM pg_rewrite " |
7450 | "ORDER BY oid" ); |
7451 | } |
7452 | |
7453 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
7454 | |
7455 | ntups = PQntuples(res); |
7456 | |
7457 | *numRules = ntups; |
7458 | |
7459 | ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo)); |
7460 | |
7461 | i_tableoid = PQfnumber(res, "tableoid" ); |
7462 | i_oid = PQfnumber(res, "oid" ); |
7463 | i_rulename = PQfnumber(res, "rulename" ); |
7464 | i_ruletable = PQfnumber(res, "ruletable" ); |
7465 | i_ev_type = PQfnumber(res, "ev_type" ); |
7466 | i_is_instead = PQfnumber(res, "is_instead" ); |
7467 | i_ev_enabled = PQfnumber(res, "ev_enabled" ); |
7468 | |
7469 | for (i = 0; i < ntups; i++) |
7470 | { |
7471 | Oid ruletableoid; |
7472 | |
7473 | ruleinfo[i].dobj.objType = DO_RULE; |
7474 | ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
7475 | ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
7476 | AssignDumpId(&ruleinfo[i].dobj); |
7477 | ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename)); |
7478 | ruletableoid = atooid(PQgetvalue(res, i, i_ruletable)); |
7479 | ruleinfo[i].ruletable = findTableByOid(ruletableoid); |
7480 | if (ruleinfo[i].ruletable == NULL) |
7481 | fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found" , |
7482 | ruletableoid, ruleinfo[i].dobj.catId.oid); |
7483 | ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace; |
7484 | ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump; |
7485 | ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type)); |
7486 | ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't'; |
7487 | ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled)); |
7488 | if (ruleinfo[i].ruletable) |
7489 | { |
7490 | /* |
7491 | * If the table is a view or materialized view, force its ON |
7492 | * SELECT rule to be sorted before the view itself --- this |
7493 | * ensures that any dependencies for the rule affect the table's |
7494 | * positioning. Other rules are forced to appear after their |
7495 | * table. |
7496 | */ |
7497 | if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW || |
7498 | ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) && |
7499 | ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead) |
7500 | { |
7501 | addObjectDependency(&ruleinfo[i].ruletable->dobj, |
7502 | ruleinfo[i].dobj.dumpId); |
7503 | /* We'll merge the rule into CREATE VIEW, if possible */ |
7504 | ruleinfo[i].separate = false; |
7505 | } |
7506 | else |
7507 | { |
7508 | addObjectDependency(&ruleinfo[i].dobj, |
7509 | ruleinfo[i].ruletable->dobj.dumpId); |
7510 | ruleinfo[i].separate = true; |
7511 | } |
7512 | } |
7513 | else |
7514 | ruleinfo[i].separate = true; |
7515 | } |
7516 | |
7517 | PQclear(res); |
7518 | |
7519 | destroyPQExpBuffer(query); |
7520 | |
7521 | return ruleinfo; |
7522 | } |
7523 | |
7524 | /* |
7525 | * getTriggers |
7526 | * get information about every trigger on a dumpable table |
7527 | * |
7528 | * Note: trigger data is not returned directly to the caller, but it |
7529 | * does get entered into the DumpableObject tables. |
7530 | */ |
7531 | void |
7532 | getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) |
7533 | { |
7534 | int i, |
7535 | j; |
7536 | PQExpBuffer query = createPQExpBuffer(); |
7537 | PGresult *res; |
7538 | TriggerInfo *tginfo; |
7539 | int i_tableoid, |
7540 | i_oid, |
7541 | i_tgname, |
7542 | i_tgfname, |
7543 | i_tgtype, |
7544 | i_tgnargs, |
7545 | i_tgargs, |
7546 | i_tgisconstraint, |
7547 | i_tgconstrname, |
7548 | i_tgconstrrelid, |
7549 | i_tgconstrrelname, |
7550 | i_tgenabled, |
7551 | i_tgdeferrable, |
7552 | i_tginitdeferred, |
7553 | i_tgdef; |
7554 | int ntups; |
7555 | |
7556 | for (i = 0; i < numTables; i++) |
7557 | { |
7558 | TableInfo *tbinfo = &tblinfo[i]; |
7559 | |
7560 | if (!tbinfo->hastriggers || |
7561 | !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)) |
7562 | continue; |
7563 | |
7564 | pg_log_info("reading triggers for table \"%s.%s\"" , |
7565 | tbinfo->dobj.namespace->dobj.name, |
7566 | tbinfo->dobj.name); |
7567 | |
7568 | resetPQExpBuffer(query); |
7569 | if (fout->remoteVersion >= 90000) |
7570 | { |
7571 | /* |
7572 | * NB: think not to use pretty=true in pg_get_triggerdef. It |
7573 | * could result in non-forward-compatible dumps of WHEN clauses |
7574 | * due to under-parenthesization. |
7575 | */ |
7576 | appendPQExpBuffer(query, |
7577 | "SELECT tgname, " |
7578 | "tgfoid::pg_catalog.regproc AS tgfname, " |
7579 | "pg_catalog.pg_get_triggerdef(oid, false) AS tgdef, " |
7580 | "tgenabled, tableoid, oid " |
7581 | "FROM pg_catalog.pg_trigger t " |
7582 | "WHERE tgrelid = '%u'::pg_catalog.oid " |
7583 | "AND NOT tgisinternal" , |
7584 | tbinfo->dobj.catId.oid); |
7585 | } |
7586 | else if (fout->remoteVersion >= 80300) |
7587 | { |
7588 | /* |
7589 | * We ignore triggers that are tied to a foreign-key constraint |
7590 | */ |
7591 | appendPQExpBuffer(query, |
7592 | "SELECT tgname, " |
7593 | "tgfoid::pg_catalog.regproc AS tgfname, " |
7594 | "tgtype, tgnargs, tgargs, tgenabled, " |
7595 | "tgisconstraint, tgconstrname, tgdeferrable, " |
7596 | "tgconstrrelid, tginitdeferred, tableoid, oid, " |
7597 | "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname " |
7598 | "FROM pg_catalog.pg_trigger t " |
7599 | "WHERE tgrelid = '%u'::pg_catalog.oid " |
7600 | "AND tgconstraint = 0" , |
7601 | tbinfo->dobj.catId.oid); |
7602 | } |
7603 | else |
7604 | { |
7605 | /* |
7606 | * We ignore triggers that are tied to a foreign-key constraint, |
7607 | * but in these versions we have to grovel through pg_constraint |
7608 | * to find out |
7609 | */ |
7610 | appendPQExpBuffer(query, |
7611 | "SELECT tgname, " |
7612 | "tgfoid::pg_catalog.regproc AS tgfname, " |
7613 | "tgtype, tgnargs, tgargs, tgenabled, " |
7614 | "tgisconstraint, tgconstrname, tgdeferrable, " |
7615 | "tgconstrrelid, tginitdeferred, tableoid, oid, " |
7616 | "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname " |
7617 | "FROM pg_catalog.pg_trigger t " |
7618 | "WHERE tgrelid = '%u'::pg_catalog.oid " |
7619 | "AND (NOT tgisconstraint " |
7620 | " OR NOT EXISTS" |
7621 | " (SELECT 1 FROM pg_catalog.pg_depend d " |
7622 | " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) " |
7623 | " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))" , |
7624 | tbinfo->dobj.catId.oid); |
7625 | } |
7626 | |
7627 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
7628 | |
7629 | ntups = PQntuples(res); |
7630 | |
7631 | i_tableoid = PQfnumber(res, "tableoid" ); |
7632 | i_oid = PQfnumber(res, "oid" ); |
7633 | i_tgname = PQfnumber(res, "tgname" ); |
7634 | i_tgfname = PQfnumber(res, "tgfname" ); |
7635 | i_tgtype = PQfnumber(res, "tgtype" ); |
7636 | i_tgnargs = PQfnumber(res, "tgnargs" ); |
7637 | i_tgargs = PQfnumber(res, "tgargs" ); |
7638 | i_tgisconstraint = PQfnumber(res, "tgisconstraint" ); |
7639 | i_tgconstrname = PQfnumber(res, "tgconstrname" ); |
7640 | i_tgconstrrelid = PQfnumber(res, "tgconstrrelid" ); |
7641 | i_tgconstrrelname = PQfnumber(res, "tgconstrrelname" ); |
7642 | i_tgenabled = PQfnumber(res, "tgenabled" ); |
7643 | i_tgdeferrable = PQfnumber(res, "tgdeferrable" ); |
7644 | i_tginitdeferred = PQfnumber(res, "tginitdeferred" ); |
7645 | i_tgdef = PQfnumber(res, "tgdef" ); |
7646 | |
7647 | tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo)); |
7648 | |
7649 | tbinfo->numTriggers = ntups; |
7650 | tbinfo->triggers = tginfo; |
7651 | |
7652 | for (j = 0; j < ntups; j++) |
7653 | { |
7654 | tginfo[j].dobj.objType = DO_TRIGGER; |
7655 | tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid)); |
7656 | tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid)); |
7657 | AssignDumpId(&tginfo[j].dobj); |
7658 | tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname)); |
7659 | tginfo[j].dobj.namespace = tbinfo->dobj.namespace; |
7660 | tginfo[j].tgtable = tbinfo; |
7661 | tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled)); |
7662 | if (i_tgdef >= 0) |
7663 | { |
7664 | tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef)); |
7665 | |
7666 | /* remaining fields are not valid if we have tgdef */ |
7667 | tginfo[j].tgfname = NULL; |
7668 | tginfo[j].tgtype = 0; |
7669 | tginfo[j].tgnargs = 0; |
7670 | tginfo[j].tgargs = NULL; |
7671 | tginfo[j].tgisconstraint = false; |
7672 | tginfo[j].tgdeferrable = false; |
7673 | tginfo[j].tginitdeferred = false; |
7674 | tginfo[j].tgconstrname = NULL; |
7675 | tginfo[j].tgconstrrelid = InvalidOid; |
7676 | tginfo[j].tgconstrrelname = NULL; |
7677 | } |
7678 | else |
7679 | { |
7680 | tginfo[j].tgdef = NULL; |
7681 | |
7682 | tginfo[j].tgfname = pg_strdup(PQgetvalue(res, j, i_tgfname)); |
7683 | tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype)); |
7684 | tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs)); |
7685 | tginfo[j].tgargs = pg_strdup(PQgetvalue(res, j, i_tgargs)); |
7686 | tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't'; |
7687 | tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't'; |
7688 | tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't'; |
7689 | |
7690 | if (tginfo[j].tgisconstraint) |
7691 | { |
7692 | tginfo[j].tgconstrname = pg_strdup(PQgetvalue(res, j, i_tgconstrname)); |
7693 | tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid)); |
7694 | if (OidIsValid(tginfo[j].tgconstrrelid)) |
7695 | { |
7696 | if (PQgetisnull(res, j, i_tgconstrrelname)) |
7697 | fatal("query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)" , |
7698 | tginfo[j].dobj.name, |
7699 | tbinfo->dobj.name, |
7700 | tginfo[j].tgconstrrelid); |
7701 | tginfo[j].tgconstrrelname = pg_strdup(PQgetvalue(res, j, i_tgconstrrelname)); |
7702 | } |
7703 | else |
7704 | tginfo[j].tgconstrrelname = NULL; |
7705 | } |
7706 | else |
7707 | { |
7708 | tginfo[j].tgconstrname = NULL; |
7709 | tginfo[j].tgconstrrelid = InvalidOid; |
7710 | tginfo[j].tgconstrrelname = NULL; |
7711 | } |
7712 | } |
7713 | } |
7714 | |
7715 | PQclear(res); |
7716 | } |
7717 | |
7718 | destroyPQExpBuffer(query); |
7719 | } |
7720 | |
7721 | /* |
7722 | * getEventTriggers |
7723 | * get information about event triggers |
7724 | */ |
7725 | EventTriggerInfo * |
7726 | getEventTriggers(Archive *fout, int *numEventTriggers) |
7727 | { |
7728 | int i; |
7729 | PQExpBuffer query; |
7730 | PGresult *res; |
7731 | EventTriggerInfo *evtinfo; |
7732 | int i_tableoid, |
7733 | i_oid, |
7734 | i_evtname, |
7735 | i_evtevent, |
7736 | i_evtowner, |
7737 | i_evttags, |
7738 | i_evtfname, |
7739 | i_evtenabled; |
7740 | int ntups; |
7741 | |
7742 | /* Before 9.3, there are no event triggers */ |
7743 | if (fout->remoteVersion < 90300) |
7744 | { |
7745 | *numEventTriggers = 0; |
7746 | return NULL; |
7747 | } |
7748 | |
7749 | query = createPQExpBuffer(); |
7750 | |
7751 | appendPQExpBuffer(query, |
7752 | "SELECT e.tableoid, e.oid, evtname, evtenabled, " |
7753 | "evtevent, (%s evtowner) AS evtowner, " |
7754 | "array_to_string(array(" |
7755 | "select quote_literal(x) " |
7756 | " from unnest(evttags) as t(x)), ', ') as evttags, " |
7757 | "e.evtfoid::regproc as evtfname " |
7758 | "FROM pg_event_trigger e " |
7759 | "ORDER BY e.oid" , |
7760 | username_subquery); |
7761 | |
7762 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
7763 | |
7764 | ntups = PQntuples(res); |
7765 | |
7766 | *numEventTriggers = ntups; |
7767 | |
7768 | evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo)); |
7769 | |
7770 | i_tableoid = PQfnumber(res, "tableoid" ); |
7771 | i_oid = PQfnumber(res, "oid" ); |
7772 | i_evtname = PQfnumber(res, "evtname" ); |
7773 | i_evtevent = PQfnumber(res, "evtevent" ); |
7774 | i_evtowner = PQfnumber(res, "evtowner" ); |
7775 | i_evttags = PQfnumber(res, "evttags" ); |
7776 | i_evtfname = PQfnumber(res, "evtfname" ); |
7777 | i_evtenabled = PQfnumber(res, "evtenabled" ); |
7778 | |
7779 | for (i = 0; i < ntups; i++) |
7780 | { |
7781 | evtinfo[i].dobj.objType = DO_EVENT_TRIGGER; |
7782 | evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
7783 | evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
7784 | AssignDumpId(&evtinfo[i].dobj); |
7785 | evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname)); |
7786 | evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname)); |
7787 | evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent)); |
7788 | evtinfo[i].evtowner = pg_strdup(PQgetvalue(res, i, i_evtowner)); |
7789 | evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags)); |
7790 | evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname)); |
7791 | evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled)); |
7792 | |
7793 | /* Decide whether we want to dump it */ |
7794 | selectDumpableObject(&(evtinfo[i].dobj), fout); |
7795 | |
7796 | /* Event Triggers do not currently have ACLs. */ |
7797 | evtinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
7798 | } |
7799 | |
7800 | PQclear(res); |
7801 | |
7802 | destroyPQExpBuffer(query); |
7803 | |
7804 | return evtinfo; |
7805 | } |
7806 | |
7807 | /* |
7808 | * getProcLangs |
7809 | * get basic information about every procedural language in the system |
7810 | * |
7811 | * numProcLangs is set to the number of langs read in |
7812 | * |
7813 | * NB: this must run after getFuncs() because we assume we can do |
7814 | * findFuncByOid(). |
7815 | */ |
7816 | ProcLangInfo * |
7817 | getProcLangs(Archive *fout, int *numProcLangs) |
7818 | { |
7819 | DumpOptions *dopt = fout->dopt; |
7820 | PGresult *res; |
7821 | int ntups; |
7822 | int i; |
7823 | PQExpBuffer query = createPQExpBuffer(); |
7824 | ProcLangInfo *planginfo; |
7825 | int i_tableoid; |
7826 | int i_oid; |
7827 | int i_lanname; |
7828 | int i_lanpltrusted; |
7829 | int i_lanplcallfoid; |
7830 | int i_laninline; |
7831 | int i_lanvalidator; |
7832 | int i_lanacl; |
7833 | int i_rlanacl; |
7834 | int i_initlanacl; |
7835 | int i_initrlanacl; |
7836 | int i_lanowner; |
7837 | |
7838 | if (fout->remoteVersion >= 90600) |
7839 | { |
7840 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
7841 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
7842 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
7843 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
7844 | |
7845 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
7846 | initracl_subquery, "l.lanacl" , "l.lanowner" , "'l'" , |
7847 | dopt->binary_upgrade); |
7848 | |
7849 | /* pg_language has a laninline column */ |
7850 | appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, " |
7851 | "l.lanname, l.lanpltrusted, l.lanplcallfoid, " |
7852 | "l.laninline, l.lanvalidator, " |
7853 | "%s AS lanacl, " |
7854 | "%s AS rlanacl, " |
7855 | "%s AS initlanacl, " |
7856 | "%s AS initrlanacl, " |
7857 | "(%s l.lanowner) AS lanowner " |
7858 | "FROM pg_language l " |
7859 | "LEFT JOIN pg_init_privs pip ON " |
7860 | "(l.oid = pip.objoid " |
7861 | "AND pip.classoid = 'pg_language'::regclass " |
7862 | "AND pip.objsubid = 0) " |
7863 | "WHERE l.lanispl " |
7864 | "ORDER BY l.oid" , |
7865 | acl_subquery->data, |
7866 | racl_subquery->data, |
7867 | initacl_subquery->data, |
7868 | initracl_subquery->data, |
7869 | username_subquery); |
7870 | |
7871 | destroyPQExpBuffer(acl_subquery); |
7872 | destroyPQExpBuffer(racl_subquery); |
7873 | destroyPQExpBuffer(initacl_subquery); |
7874 | destroyPQExpBuffer(initracl_subquery); |
7875 | } |
7876 | else if (fout->remoteVersion >= 90000) |
7877 | { |
7878 | /* pg_language has a laninline column */ |
7879 | appendPQExpBuffer(query, "SELECT tableoid, oid, " |
7880 | "lanname, lanpltrusted, lanplcallfoid, " |
7881 | "laninline, lanvalidator, lanacl, NULL AS rlanacl, " |
7882 | "NULL AS initlanacl, NULL AS initrlanacl, " |
7883 | "(%s lanowner) AS lanowner " |
7884 | "FROM pg_language " |
7885 | "WHERE lanispl " |
7886 | "ORDER BY oid" , |
7887 | username_subquery); |
7888 | } |
7889 | else if (fout->remoteVersion >= 80300) |
7890 | { |
7891 | /* pg_language has a lanowner column */ |
7892 | appendPQExpBuffer(query, "SELECT tableoid, oid, " |
7893 | "lanname, lanpltrusted, lanplcallfoid, " |
7894 | "0 AS laninline, lanvalidator, lanacl, " |
7895 | "NULL AS rlanacl, " |
7896 | "NULL AS initlanacl, NULL AS initrlanacl, " |
7897 | "(%s lanowner) AS lanowner " |
7898 | "FROM pg_language " |
7899 | "WHERE lanispl " |
7900 | "ORDER BY oid" , |
7901 | username_subquery); |
7902 | } |
7903 | else if (fout->remoteVersion >= 80100) |
7904 | { |
7905 | /* Languages are owned by the bootstrap superuser, OID 10 */ |
7906 | appendPQExpBuffer(query, "SELECT tableoid, oid, " |
7907 | "lanname, lanpltrusted, lanplcallfoid, " |
7908 | "0 AS laninline, lanvalidator, lanacl, " |
7909 | "NULL AS rlanacl, " |
7910 | "NULL AS initlanacl, NULL AS initrlanacl, " |
7911 | "(%s '10') AS lanowner " |
7912 | "FROM pg_language " |
7913 | "WHERE lanispl " |
7914 | "ORDER BY oid" , |
7915 | username_subquery); |
7916 | } |
7917 | else |
7918 | { |
7919 | /* Languages are owned by the bootstrap superuser, sysid 1 */ |
7920 | appendPQExpBuffer(query, "SELECT tableoid, oid, " |
7921 | "lanname, lanpltrusted, lanplcallfoid, " |
7922 | "0 AS laninline, lanvalidator, lanacl, " |
7923 | "NULL AS rlanacl, " |
7924 | "NULL AS initlanacl, NULL AS initrlanacl, " |
7925 | "(%s '1') AS lanowner " |
7926 | "FROM pg_language " |
7927 | "WHERE lanispl " |
7928 | "ORDER BY oid" , |
7929 | username_subquery); |
7930 | } |
7931 | |
7932 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
7933 | |
7934 | ntups = PQntuples(res); |
7935 | |
7936 | *numProcLangs = ntups; |
7937 | |
7938 | planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo)); |
7939 | |
7940 | i_tableoid = PQfnumber(res, "tableoid" ); |
7941 | i_oid = PQfnumber(res, "oid" ); |
7942 | i_lanname = PQfnumber(res, "lanname" ); |
7943 | i_lanpltrusted = PQfnumber(res, "lanpltrusted" ); |
7944 | i_lanplcallfoid = PQfnumber(res, "lanplcallfoid" ); |
7945 | i_laninline = PQfnumber(res, "laninline" ); |
7946 | i_lanvalidator = PQfnumber(res, "lanvalidator" ); |
7947 | i_lanacl = PQfnumber(res, "lanacl" ); |
7948 | i_rlanacl = PQfnumber(res, "rlanacl" ); |
7949 | i_initlanacl = PQfnumber(res, "initlanacl" ); |
7950 | i_initrlanacl = PQfnumber(res, "initrlanacl" ); |
7951 | i_lanowner = PQfnumber(res, "lanowner" ); |
7952 | |
7953 | for (i = 0; i < ntups; i++) |
7954 | { |
7955 | planginfo[i].dobj.objType = DO_PROCLANG; |
7956 | planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
7957 | planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
7958 | AssignDumpId(&planginfo[i].dobj); |
7959 | |
7960 | planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname)); |
7961 | planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't'; |
7962 | planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid)); |
7963 | planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline)); |
7964 | planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator)); |
7965 | planginfo[i].lanacl = pg_strdup(PQgetvalue(res, i, i_lanacl)); |
7966 | planginfo[i].rlanacl = pg_strdup(PQgetvalue(res, i, i_rlanacl)); |
7967 | planginfo[i].initlanacl = pg_strdup(PQgetvalue(res, i, i_initlanacl)); |
7968 | planginfo[i].initrlanacl = pg_strdup(PQgetvalue(res, i, i_initrlanacl)); |
7969 | planginfo[i].lanowner = pg_strdup(PQgetvalue(res, i, i_lanowner)); |
7970 | |
7971 | /* Decide whether we want to dump it */ |
7972 | selectDumpableProcLang(&(planginfo[i]), fout); |
7973 | |
7974 | /* Do not try to dump ACL if no ACL exists. */ |
7975 | if (PQgetisnull(res, i, i_lanacl) && PQgetisnull(res, i, i_rlanacl) && |
7976 | PQgetisnull(res, i, i_initlanacl) && |
7977 | PQgetisnull(res, i, i_initrlanacl)) |
7978 | planginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
7979 | } |
7980 | |
7981 | PQclear(res); |
7982 | |
7983 | destroyPQExpBuffer(query); |
7984 | |
7985 | return planginfo; |
7986 | } |
7987 | |
7988 | /* |
7989 | * getCasts |
7990 | * get basic information about every cast in the system |
7991 | * |
7992 | * numCasts is set to the number of casts read in |
7993 | */ |
7994 | CastInfo * |
7995 | getCasts(Archive *fout, int *numCasts) |
7996 | { |
7997 | PGresult *res; |
7998 | int ntups; |
7999 | int i; |
8000 | PQExpBuffer query = createPQExpBuffer(); |
8001 | CastInfo *castinfo; |
8002 | int i_tableoid; |
8003 | int i_oid; |
8004 | int i_castsource; |
8005 | int i_casttarget; |
8006 | int i_castfunc; |
8007 | int i_castcontext; |
8008 | int i_castmethod; |
8009 | |
8010 | if (fout->remoteVersion >= 80400) |
8011 | { |
8012 | appendPQExpBufferStr(query, "SELECT tableoid, oid, " |
8013 | "castsource, casttarget, castfunc, castcontext, " |
8014 | "castmethod " |
8015 | "FROM pg_cast ORDER BY 3,4" ); |
8016 | } |
8017 | else |
8018 | { |
8019 | appendPQExpBufferStr(query, "SELECT tableoid, oid, " |
8020 | "castsource, casttarget, castfunc, castcontext, " |
8021 | "CASE WHEN castfunc = 0 THEN 'b' ELSE 'f' END AS castmethod " |
8022 | "FROM pg_cast ORDER BY 3,4" ); |
8023 | } |
8024 | |
8025 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
8026 | |
8027 | ntups = PQntuples(res); |
8028 | |
8029 | *numCasts = ntups; |
8030 | |
8031 | castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo)); |
8032 | |
8033 | i_tableoid = PQfnumber(res, "tableoid" ); |
8034 | i_oid = PQfnumber(res, "oid" ); |
8035 | i_castsource = PQfnumber(res, "castsource" ); |
8036 | i_casttarget = PQfnumber(res, "casttarget" ); |
8037 | i_castfunc = PQfnumber(res, "castfunc" ); |
8038 | i_castcontext = PQfnumber(res, "castcontext" ); |
8039 | i_castmethod = PQfnumber(res, "castmethod" ); |
8040 | |
8041 | for (i = 0; i < ntups; i++) |
8042 | { |
8043 | PQExpBufferData namebuf; |
8044 | TypeInfo *sTypeInfo; |
8045 | TypeInfo *tTypeInfo; |
8046 | |
8047 | castinfo[i].dobj.objType = DO_CAST; |
8048 | castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
8049 | castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
8050 | AssignDumpId(&castinfo[i].dobj); |
8051 | castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource)); |
8052 | castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget)); |
8053 | castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc)); |
8054 | castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext)); |
8055 | castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod)); |
8056 | |
8057 | /* |
8058 | * Try to name cast as concatenation of typnames. This is only used |
8059 | * for purposes of sorting. If we fail to find either type, the name |
8060 | * will be an empty string. |
8061 | */ |
8062 | initPQExpBuffer(&namebuf); |
8063 | sTypeInfo = findTypeByOid(castinfo[i].castsource); |
8064 | tTypeInfo = findTypeByOid(castinfo[i].casttarget); |
8065 | if (sTypeInfo && tTypeInfo) |
8066 | appendPQExpBuffer(&namebuf, "%s %s" , |
8067 | sTypeInfo->dobj.name, tTypeInfo->dobj.name); |
8068 | castinfo[i].dobj.name = namebuf.data; |
8069 | |
8070 | /* Decide whether we want to dump it */ |
8071 | selectDumpableCast(&(castinfo[i]), fout); |
8072 | |
8073 | /* Casts do not currently have ACLs. */ |
8074 | castinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
8075 | } |
8076 | |
8077 | PQclear(res); |
8078 | |
8079 | destroyPQExpBuffer(query); |
8080 | |
8081 | return castinfo; |
8082 | } |
8083 | |
8084 | static char * |
8085 | get_language_name(Archive *fout, Oid langid) |
8086 | { |
8087 | PQExpBuffer query; |
8088 | PGresult *res; |
8089 | char *lanname; |
8090 | |
8091 | query = createPQExpBuffer(); |
8092 | appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u" , langid); |
8093 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
8094 | lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0))); |
8095 | destroyPQExpBuffer(query); |
8096 | PQclear(res); |
8097 | |
8098 | return lanname; |
8099 | } |
8100 | |
8101 | /* |
8102 | * getTransforms |
8103 | * get basic information about every transform in the system |
8104 | * |
8105 | * numTransforms is set to the number of transforms read in |
8106 | */ |
8107 | TransformInfo * |
8108 | getTransforms(Archive *fout, int *numTransforms) |
8109 | { |
8110 | PGresult *res; |
8111 | int ntups; |
8112 | int i; |
8113 | PQExpBuffer query; |
8114 | TransformInfo *transforminfo; |
8115 | int i_tableoid; |
8116 | int i_oid; |
8117 | int i_trftype; |
8118 | int i_trflang; |
8119 | int i_trffromsql; |
8120 | int i_trftosql; |
8121 | |
8122 | /* Transforms didn't exist pre-9.5 */ |
8123 | if (fout->remoteVersion < 90500) |
8124 | { |
8125 | *numTransforms = 0; |
8126 | return NULL; |
8127 | } |
8128 | |
8129 | query = createPQExpBuffer(); |
8130 | |
8131 | appendPQExpBuffer(query, "SELECT tableoid, oid, " |
8132 | "trftype, trflang, trffromsql::oid, trftosql::oid " |
8133 | "FROM pg_transform " |
8134 | "ORDER BY 3,4" ); |
8135 | |
8136 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
8137 | |
8138 | ntups = PQntuples(res); |
8139 | |
8140 | *numTransforms = ntups; |
8141 | |
8142 | transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo)); |
8143 | |
8144 | i_tableoid = PQfnumber(res, "tableoid" ); |
8145 | i_oid = PQfnumber(res, "oid" ); |
8146 | i_trftype = PQfnumber(res, "trftype" ); |
8147 | i_trflang = PQfnumber(res, "trflang" ); |
8148 | i_trffromsql = PQfnumber(res, "trffromsql" ); |
8149 | i_trftosql = PQfnumber(res, "trftosql" ); |
8150 | |
8151 | for (i = 0; i < ntups; i++) |
8152 | { |
8153 | PQExpBufferData namebuf; |
8154 | TypeInfo *typeInfo; |
8155 | char *lanname; |
8156 | |
8157 | transforminfo[i].dobj.objType = DO_TRANSFORM; |
8158 | transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
8159 | transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
8160 | AssignDumpId(&transforminfo[i].dobj); |
8161 | transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype)); |
8162 | transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang)); |
8163 | transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql)); |
8164 | transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql)); |
8165 | |
8166 | /* |
8167 | * Try to name transform as concatenation of type and language name. |
8168 | * This is only used for purposes of sorting. If we fail to find |
8169 | * either, the name will be an empty string. |
8170 | */ |
8171 | initPQExpBuffer(&namebuf); |
8172 | typeInfo = findTypeByOid(transforminfo[i].trftype); |
8173 | lanname = get_language_name(fout, transforminfo[i].trflang); |
8174 | if (typeInfo && lanname) |
8175 | appendPQExpBuffer(&namebuf, "%s %s" , |
8176 | typeInfo->dobj.name, lanname); |
8177 | transforminfo[i].dobj.name = namebuf.data; |
8178 | free(lanname); |
8179 | |
8180 | /* Decide whether we want to dump it */ |
8181 | selectDumpableObject(&(transforminfo[i].dobj), fout); |
8182 | } |
8183 | |
8184 | PQclear(res); |
8185 | |
8186 | destroyPQExpBuffer(query); |
8187 | |
8188 | return transforminfo; |
8189 | } |
8190 | |
8191 | /* |
8192 | * getTableAttrs - |
8193 | * for each interesting table, read info about its attributes |
8194 | * (names, types, default values, CHECK constraints, etc) |
8195 | * |
8196 | * This is implemented in a very inefficient way right now, looping |
8197 | * through the tblinfo and doing a join per table to find the attrs and their |
8198 | * types. However, because we want type names and so forth to be named |
8199 | * relative to the schema of each table, we couldn't do it in just one |
8200 | * query. (Maybe one query per schema?) |
8201 | * |
8202 | * modifies tblinfo |
8203 | */ |
8204 | void |
8205 | getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) |
8206 | { |
8207 | DumpOptions *dopt = fout->dopt; |
8208 | int i, |
8209 | j; |
8210 | PQExpBuffer q = createPQExpBuffer(); |
8211 | int i_attnum; |
8212 | int i_attname; |
8213 | int i_atttypname; |
8214 | int i_atttypmod; |
8215 | int i_attstattarget; |
8216 | int i_attstorage; |
8217 | int i_typstorage; |
8218 | int i_attnotnull; |
8219 | int i_atthasdef; |
8220 | int i_attidentity; |
8221 | int i_attgenerated; |
8222 | int i_attisdropped; |
8223 | int i_attlen; |
8224 | int i_attalign; |
8225 | int i_attislocal; |
8226 | int i_attoptions; |
8227 | int i_attcollation; |
8228 | int i_attfdwoptions; |
8229 | int i_attmissingval; |
8230 | PGresult *res; |
8231 | int ntups; |
8232 | bool hasdefaults; |
8233 | |
8234 | for (i = 0; i < numTables; i++) |
8235 | { |
8236 | TableInfo *tbinfo = &tblinfo[i]; |
8237 | |
8238 | /* Don't bother to collect info for sequences */ |
8239 | if (tbinfo->relkind == RELKIND_SEQUENCE) |
8240 | continue; |
8241 | |
8242 | /* Don't bother with uninteresting tables, either */ |
8243 | if (!tbinfo->interesting) |
8244 | continue; |
8245 | |
8246 | /* find all the user attributes and their types */ |
8247 | |
8248 | /* |
8249 | * we must read the attribute names in attribute number order! because |
8250 | * we will use the attnum to index into the attnames array later. |
8251 | */ |
8252 | pg_log_info("finding the columns and types of table \"%s.%s\"" , |
8253 | tbinfo->dobj.namespace->dobj.name, |
8254 | tbinfo->dobj.name); |
8255 | |
8256 | resetPQExpBuffer(q); |
8257 | |
8258 | appendPQExpBuffer(q, |
8259 | "SELECT\n" |
8260 | "a.attnum,\n" |
8261 | "a.attname,\n" |
8262 | "a.atttypmod,\n" |
8263 | "a.attstattarget,\n" |
8264 | "a.attstorage,\n" |
8265 | "t.typstorage,\n" |
8266 | "a.attnotnull,\n" |
8267 | "a.atthasdef,\n" |
8268 | "a.attisdropped,\n" |
8269 | "a.attlen,\n" |
8270 | "a.attalign,\n" |
8271 | "a.attislocal,\n" |
8272 | "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n" ); |
8273 | |
8274 | if (fout->remoteVersion >= 120000) |
8275 | appendPQExpBuffer(q, |
8276 | "a.attgenerated,\n" ); |
8277 | else |
8278 | appendPQExpBuffer(q, |
8279 | "'' AS attgenerated,\n" ); |
8280 | |
8281 | if (fout->remoteVersion >= 110000) |
8282 | appendPQExpBuffer(q, |
8283 | "CASE WHEN a.atthasmissing AND NOT a.attisdropped " |
8284 | "THEN a.attmissingval ELSE null END AS attmissingval,\n" ); |
8285 | else |
8286 | appendPQExpBuffer(q, |
8287 | "NULL AS attmissingval,\n" ); |
8288 | |
8289 | if (fout->remoteVersion >= 100000) |
8290 | appendPQExpBuffer(q, |
8291 | "a.attidentity,\n" ); |
8292 | else |
8293 | appendPQExpBuffer(q, |
8294 | "'' AS attidentity,\n" ); |
8295 | |
8296 | if (fout->remoteVersion >= 90200) |
8297 | appendPQExpBuffer(q, |
8298 | "pg_catalog.array_to_string(ARRAY(" |
8299 | "SELECT pg_catalog.quote_ident(option_name) || " |
8300 | "' ' || pg_catalog.quote_literal(option_value) " |
8301 | "FROM pg_catalog.pg_options_to_table(attfdwoptions) " |
8302 | "ORDER BY option_name" |
8303 | "), E',\n ') AS attfdwoptions,\n" ); |
8304 | else |
8305 | appendPQExpBuffer(q, |
8306 | "'' AS attfdwoptions,\n" ); |
8307 | |
8308 | if (fout->remoteVersion >= 90100) |
8309 | { |
8310 | /* |
8311 | * Since we only want to dump COLLATE clauses for attributes whose |
8312 | * collation is different from their type's default, we use a CASE |
8313 | * here to suppress uninteresting attcollations cheaply. |
8314 | */ |
8315 | appendPQExpBuffer(q, |
8316 | "CASE WHEN a.attcollation <> t.typcollation " |
8317 | "THEN a.attcollation ELSE 0 END AS attcollation,\n" ); |
8318 | } |
8319 | else |
8320 | appendPQExpBuffer(q, |
8321 | "0 AS attcollation,\n" ); |
8322 | |
8323 | if (fout->remoteVersion >= 90000) |
8324 | appendPQExpBuffer(q, |
8325 | "array_to_string(a.attoptions, ', ') AS attoptions\n" ); |
8326 | else |
8327 | appendPQExpBuffer(q, |
8328 | "'' AS attoptions\n" ); |
8329 | |
8330 | /* need left join here to not fail on dropped columns ... */ |
8331 | appendPQExpBuffer(q, |
8332 | "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " |
8333 | "ON a.atttypid = t.oid\n" |
8334 | "WHERE a.attrelid = '%u'::pg_catalog.oid " |
8335 | "AND a.attnum > 0::pg_catalog.int2\n" |
8336 | "ORDER BY a.attnum" , |
8337 | tbinfo->dobj.catId.oid); |
8338 | |
8339 | res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); |
8340 | |
8341 | ntups = PQntuples(res); |
8342 | |
8343 | i_attnum = PQfnumber(res, "attnum" ); |
8344 | i_attname = PQfnumber(res, "attname" ); |
8345 | i_atttypname = PQfnumber(res, "atttypname" ); |
8346 | i_atttypmod = PQfnumber(res, "atttypmod" ); |
8347 | i_attstattarget = PQfnumber(res, "attstattarget" ); |
8348 | i_attstorage = PQfnumber(res, "attstorage" ); |
8349 | i_typstorage = PQfnumber(res, "typstorage" ); |
8350 | i_attnotnull = PQfnumber(res, "attnotnull" ); |
8351 | i_atthasdef = PQfnumber(res, "atthasdef" ); |
8352 | i_attidentity = PQfnumber(res, "attidentity" ); |
8353 | i_attgenerated = PQfnumber(res, "attgenerated" ); |
8354 | i_attisdropped = PQfnumber(res, "attisdropped" ); |
8355 | i_attlen = PQfnumber(res, "attlen" ); |
8356 | i_attalign = PQfnumber(res, "attalign" ); |
8357 | i_attislocal = PQfnumber(res, "attislocal" ); |
8358 | i_attoptions = PQfnumber(res, "attoptions" ); |
8359 | i_attcollation = PQfnumber(res, "attcollation" ); |
8360 | i_attfdwoptions = PQfnumber(res, "attfdwoptions" ); |
8361 | i_attmissingval = PQfnumber(res, "attmissingval" ); |
8362 | |
8363 | tbinfo->numatts = ntups; |
8364 | tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *)); |
8365 | tbinfo->atttypnames = (char **) pg_malloc(ntups * sizeof(char *)); |
8366 | tbinfo->atttypmod = (int *) pg_malloc(ntups * sizeof(int)); |
8367 | tbinfo->attstattarget = (int *) pg_malloc(ntups * sizeof(int)); |
8368 | tbinfo->attstorage = (char *) pg_malloc(ntups * sizeof(char)); |
8369 | tbinfo->typstorage = (char *) pg_malloc(ntups * sizeof(char)); |
8370 | tbinfo->attidentity = (char *) pg_malloc(ntups * sizeof(char)); |
8371 | tbinfo->attgenerated = (char *) pg_malloc(ntups * sizeof(char)); |
8372 | tbinfo->attisdropped = (bool *) pg_malloc(ntups * sizeof(bool)); |
8373 | tbinfo->attlen = (int *) pg_malloc(ntups * sizeof(int)); |
8374 | tbinfo->attalign = (char *) pg_malloc(ntups * sizeof(char)); |
8375 | tbinfo->attislocal = (bool *) pg_malloc(ntups * sizeof(bool)); |
8376 | tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *)); |
8377 | tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid)); |
8378 | tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *)); |
8379 | tbinfo->attmissingval = (char **) pg_malloc(ntups * sizeof(char *)); |
8380 | tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool)); |
8381 | tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool)); |
8382 | tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *)); |
8383 | hasdefaults = false; |
8384 | |
8385 | for (j = 0; j < ntups; j++) |
8386 | { |
8387 | if (j + 1 != atoi(PQgetvalue(res, j, i_attnum))) |
8388 | fatal("invalid column numbering in table \"%s\"" , |
8389 | tbinfo->dobj.name); |
8390 | tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, j, i_attname)); |
8391 | tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, j, i_atttypname)); |
8392 | tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod)); |
8393 | tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget)); |
8394 | tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage)); |
8395 | tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage)); |
8396 | tbinfo->attidentity[j] = *(PQgetvalue(res, j, i_attidentity)); |
8397 | tbinfo->attgenerated[j] = *(PQgetvalue(res, j, i_attgenerated)); |
8398 | tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS); |
8399 | tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't'); |
8400 | tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); |
8401 | tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); |
8402 | tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); |
8403 | tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); |
8404 | tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions)); |
8405 | tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation)); |
8406 | tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions)); |
8407 | tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, j, i_attmissingval)); |
8408 | tbinfo->attrdefs[j] = NULL; /* fix below */ |
8409 | if (PQgetvalue(res, j, i_atthasdef)[0] == 't') |
8410 | hasdefaults = true; |
8411 | /* these flags will be set in flagInhAttrs() */ |
8412 | tbinfo->inhNotNull[j] = false; |
8413 | } |
8414 | |
8415 | PQclear(res); |
8416 | |
8417 | /* |
8418 | * Get info about column defaults |
8419 | */ |
8420 | if (hasdefaults) |
8421 | { |
8422 | AttrDefInfo *attrdefs; |
8423 | int numDefaults; |
8424 | |
8425 | pg_log_info("finding default expressions of table \"%s.%s\"" , |
8426 | tbinfo->dobj.namespace->dobj.name, |
8427 | tbinfo->dobj.name); |
8428 | |
8429 | printfPQExpBuffer(q, "SELECT tableoid, oid, adnum, " |
8430 | "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc " |
8431 | "FROM pg_catalog.pg_attrdef " |
8432 | "WHERE adrelid = '%u'::pg_catalog.oid" , |
8433 | tbinfo->dobj.catId.oid); |
8434 | |
8435 | res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); |
8436 | |
8437 | numDefaults = PQntuples(res); |
8438 | attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo)); |
8439 | |
8440 | for (j = 0; j < numDefaults; j++) |
8441 | { |
8442 | int adnum; |
8443 | |
8444 | adnum = atoi(PQgetvalue(res, j, 2)); |
8445 | |
8446 | if (adnum <= 0 || adnum > ntups) |
8447 | fatal("invalid adnum value %d for table \"%s\"" , |
8448 | adnum, tbinfo->dobj.name); |
8449 | |
8450 | /* |
8451 | * dropped columns shouldn't have defaults, but just in case, |
8452 | * ignore 'em |
8453 | */ |
8454 | if (tbinfo->attisdropped[adnum - 1]) |
8455 | continue; |
8456 | |
8457 | attrdefs[j].dobj.objType = DO_ATTRDEF; |
8458 | attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0)); |
8459 | attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1)); |
8460 | AssignDumpId(&attrdefs[j].dobj); |
8461 | attrdefs[j].adtable = tbinfo; |
8462 | attrdefs[j].adnum = adnum; |
8463 | attrdefs[j].adef_expr = pg_strdup(PQgetvalue(res, j, 3)); |
8464 | |
8465 | attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name); |
8466 | attrdefs[j].dobj.namespace = tbinfo->dobj.namespace; |
8467 | |
8468 | attrdefs[j].dobj.dump = tbinfo->dobj.dump; |
8469 | |
8470 | /* |
8471 | * Defaults on a VIEW must always be dumped as separate ALTER |
8472 | * TABLE commands. Defaults on regular tables are dumped as |
8473 | * part of the CREATE TABLE if possible, which it won't be if |
8474 | * the column is not going to be emitted explicitly. |
8475 | */ |
8476 | if (tbinfo->relkind == RELKIND_VIEW) |
8477 | { |
8478 | attrdefs[j].separate = true; |
8479 | } |
8480 | else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1)) |
8481 | { |
8482 | /* column will be suppressed, print default separately */ |
8483 | attrdefs[j].separate = true; |
8484 | } |
8485 | else |
8486 | { |
8487 | attrdefs[j].separate = false; |
8488 | |
8489 | /* |
8490 | * Mark the default as needing to appear before the table, |
8491 | * so that any dependencies it has must be emitted before |
8492 | * the CREATE TABLE. If this is not possible, we'll |
8493 | * change to "separate" mode while sorting dependencies. |
8494 | */ |
8495 | addObjectDependency(&tbinfo->dobj, |
8496 | attrdefs[j].dobj.dumpId); |
8497 | } |
8498 | |
8499 | tbinfo->attrdefs[adnum - 1] = &attrdefs[j]; |
8500 | } |
8501 | PQclear(res); |
8502 | } |
8503 | |
8504 | /* |
8505 | * Get info about table CHECK constraints |
8506 | */ |
8507 | if (tbinfo->ncheck > 0) |
8508 | { |
8509 | ConstraintInfo *constrs; |
8510 | int numConstrs; |
8511 | |
8512 | pg_log_info("finding check constraints for table \"%s.%s\"" , |
8513 | tbinfo->dobj.namespace->dobj.name, |
8514 | tbinfo->dobj.name); |
8515 | |
8516 | resetPQExpBuffer(q); |
8517 | if (fout->remoteVersion >= 90200) |
8518 | { |
8519 | /* |
8520 | * convalidated is new in 9.2 (actually, it is there in 9.1, |
8521 | * but it wasn't ever false for check constraints until 9.2). |
8522 | */ |
8523 | appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " |
8524 | "pg_catalog.pg_get_constraintdef(oid) AS consrc, " |
8525 | "conislocal, convalidated " |
8526 | "FROM pg_catalog.pg_constraint " |
8527 | "WHERE conrelid = '%u'::pg_catalog.oid " |
8528 | " AND contype = 'c' " |
8529 | "ORDER BY conname" , |
8530 | tbinfo->dobj.catId.oid); |
8531 | } |
8532 | else if (fout->remoteVersion >= 80400) |
8533 | { |
8534 | /* conislocal is new in 8.4 */ |
8535 | appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " |
8536 | "pg_catalog.pg_get_constraintdef(oid) AS consrc, " |
8537 | "conislocal, true AS convalidated " |
8538 | "FROM pg_catalog.pg_constraint " |
8539 | "WHERE conrelid = '%u'::pg_catalog.oid " |
8540 | " AND contype = 'c' " |
8541 | "ORDER BY conname" , |
8542 | tbinfo->dobj.catId.oid); |
8543 | } |
8544 | else |
8545 | { |
8546 | appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " |
8547 | "pg_catalog.pg_get_constraintdef(oid) AS consrc, " |
8548 | "true AS conislocal, true AS convalidated " |
8549 | "FROM pg_catalog.pg_constraint " |
8550 | "WHERE conrelid = '%u'::pg_catalog.oid " |
8551 | " AND contype = 'c' " |
8552 | "ORDER BY conname" , |
8553 | tbinfo->dobj.catId.oid); |
8554 | } |
8555 | |
8556 | res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); |
8557 | |
8558 | numConstrs = PQntuples(res); |
8559 | if (numConstrs != tbinfo->ncheck) |
8560 | { |
8561 | pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d" , |
8562 | "expected %d check constraints on table \"%s\" but found %d" , |
8563 | tbinfo->ncheck), |
8564 | tbinfo->ncheck, tbinfo->dobj.name, numConstrs); |
8565 | pg_log_error("(The system catalogs might be corrupted.)" ); |
8566 | exit_nicely(1); |
8567 | } |
8568 | |
8569 | constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo)); |
8570 | tbinfo->checkexprs = constrs; |
8571 | |
8572 | for (j = 0; j < numConstrs; j++) |
8573 | { |
8574 | bool validated = PQgetvalue(res, j, 5)[0] == 't'; |
8575 | |
8576 | constrs[j].dobj.objType = DO_CONSTRAINT; |
8577 | constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0)); |
8578 | constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1)); |
8579 | AssignDumpId(&constrs[j].dobj); |
8580 | constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, 2)); |
8581 | constrs[j].dobj.namespace = tbinfo->dobj.namespace; |
8582 | constrs[j].contable = tbinfo; |
8583 | constrs[j].condomain = NULL; |
8584 | constrs[j].contype = 'c'; |
8585 | constrs[j].condef = pg_strdup(PQgetvalue(res, j, 3)); |
8586 | constrs[j].confrelid = InvalidOid; |
8587 | constrs[j].conindex = 0; |
8588 | constrs[j].condeferrable = false; |
8589 | constrs[j].condeferred = false; |
8590 | constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't'); |
8591 | |
8592 | /* |
8593 | * An unvalidated constraint needs to be dumped separately, so |
8594 | * that potentially-violating existing data is loaded before |
8595 | * the constraint. |
8596 | */ |
8597 | constrs[j].separate = !validated; |
8598 | |
8599 | constrs[j].dobj.dump = tbinfo->dobj.dump; |
8600 | |
8601 | /* |
8602 | * Mark the constraint as needing to appear before the table |
8603 | * --- this is so that any other dependencies of the |
8604 | * constraint will be emitted before we try to create the |
8605 | * table. If the constraint is to be dumped separately, it |
8606 | * will be dumped after data is loaded anyway, so don't do it. |
8607 | * (There's an automatic dependency in the opposite direction |
8608 | * anyway, so don't need to add one manually here.) |
8609 | */ |
8610 | if (!constrs[j].separate) |
8611 | addObjectDependency(&tbinfo->dobj, |
8612 | constrs[j].dobj.dumpId); |
8613 | |
8614 | /* |
8615 | * If the constraint is inherited, this will be detected later |
8616 | * (in pre-8.4 databases). We also detect later if the |
8617 | * constraint must be split out from the table definition. |
8618 | */ |
8619 | } |
8620 | PQclear(res); |
8621 | } |
8622 | } |
8623 | |
8624 | destroyPQExpBuffer(q); |
8625 | } |
8626 | |
8627 | /* |
8628 | * Test whether a column should be printed as part of table's CREATE TABLE. |
8629 | * Column number is zero-based. |
8630 | * |
8631 | * Normally this is always true, but it's false for dropped columns, as well |
8632 | * as those that were inherited without any local definition. (If we print |
8633 | * such a column it will mistakenly get pg_attribute.attislocal set to true.) |
8634 | * For partitions, it's always true, because we want the partitions to be |
8635 | * created independently and ATTACH PARTITION used afterwards. |
8636 | * |
8637 | * In binary_upgrade mode, we must print all columns and fix the attislocal/ |
8638 | * attisdropped state later, so as to keep control of the physical column |
8639 | * order. |
8640 | * |
8641 | * This function exists because there are scattered nonobvious places that |
8642 | * must be kept in sync with this decision. |
8643 | */ |
8644 | bool |
8645 | shouldPrintColumn(DumpOptions *dopt, TableInfo *tbinfo, int colno) |
8646 | { |
8647 | if (dopt->binary_upgrade) |
8648 | return true; |
8649 | if (tbinfo->attisdropped[colno]) |
8650 | return false; |
8651 | return (tbinfo->attislocal[colno] || tbinfo->ispartition); |
8652 | } |
8653 | |
8654 | |
8655 | /* |
8656 | * getTSParsers: |
8657 | * read all text search parsers in the system catalogs and return them |
8658 | * in the TSParserInfo* structure |
8659 | * |
8660 | * numTSParsers is set to the number of parsers read in |
8661 | */ |
8662 | TSParserInfo * |
8663 | getTSParsers(Archive *fout, int *numTSParsers) |
8664 | { |
8665 | PGresult *res; |
8666 | int ntups; |
8667 | int i; |
8668 | PQExpBuffer query; |
8669 | TSParserInfo *prsinfo; |
8670 | int i_tableoid; |
8671 | int i_oid; |
8672 | int i_prsname; |
8673 | int i_prsnamespace; |
8674 | int ; |
8675 | int i_prstoken; |
8676 | int i_prsend; |
8677 | int i_prsheadline; |
8678 | int i_prslextype; |
8679 | |
8680 | /* Before 8.3, there is no built-in text search support */ |
8681 | if (fout->remoteVersion < 80300) |
8682 | { |
8683 | *numTSParsers = 0; |
8684 | return NULL; |
8685 | } |
8686 | |
8687 | query = createPQExpBuffer(); |
8688 | |
8689 | /* |
8690 | * find all text search objects, including builtin ones; we filter out |
8691 | * system-defined objects at dump-out time. |
8692 | */ |
8693 | |
8694 | appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, " |
8695 | "prsstart::oid, prstoken::oid, " |
8696 | "prsend::oid, prsheadline::oid, prslextype::oid " |
8697 | "FROM pg_ts_parser" ); |
8698 | |
8699 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
8700 | |
8701 | ntups = PQntuples(res); |
8702 | *numTSParsers = ntups; |
8703 | |
8704 | prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo)); |
8705 | |
8706 | i_tableoid = PQfnumber(res, "tableoid" ); |
8707 | i_oid = PQfnumber(res, "oid" ); |
8708 | i_prsname = PQfnumber(res, "prsname" ); |
8709 | i_prsnamespace = PQfnumber(res, "prsnamespace" ); |
8710 | i_prsstart = PQfnumber(res, "prsstart" ); |
8711 | i_prstoken = PQfnumber(res, "prstoken" ); |
8712 | i_prsend = PQfnumber(res, "prsend" ); |
8713 | i_prsheadline = PQfnumber(res, "prsheadline" ); |
8714 | i_prslextype = PQfnumber(res, "prslextype" ); |
8715 | |
8716 | for (i = 0; i < ntups; i++) |
8717 | { |
8718 | prsinfo[i].dobj.objType = DO_TSPARSER; |
8719 | prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
8720 | prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
8721 | AssignDumpId(&prsinfo[i].dobj); |
8722 | prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname)); |
8723 | prsinfo[i].dobj.namespace = |
8724 | findNamespace(fout, |
8725 | atooid(PQgetvalue(res, i, i_prsnamespace))); |
8726 | prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart)); |
8727 | prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken)); |
8728 | prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend)); |
8729 | prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline)); |
8730 | prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype)); |
8731 | |
8732 | /* Decide whether we want to dump it */ |
8733 | selectDumpableObject(&(prsinfo[i].dobj), fout); |
8734 | |
8735 | /* Text Search Parsers do not currently have ACLs. */ |
8736 | prsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
8737 | } |
8738 | |
8739 | PQclear(res); |
8740 | |
8741 | destroyPQExpBuffer(query); |
8742 | |
8743 | return prsinfo; |
8744 | } |
8745 | |
8746 | /* |
8747 | * getTSDictionaries: |
8748 | * read all text search dictionaries in the system catalogs and return them |
8749 | * in the TSDictInfo* structure |
8750 | * |
8751 | * numTSDicts is set to the number of dictionaries read in |
8752 | */ |
8753 | TSDictInfo * |
8754 | getTSDictionaries(Archive *fout, int *numTSDicts) |
8755 | { |
8756 | PGresult *res; |
8757 | int ntups; |
8758 | int i; |
8759 | PQExpBuffer query; |
8760 | TSDictInfo *dictinfo; |
8761 | int i_tableoid; |
8762 | int i_oid; |
8763 | int i_dictname; |
8764 | int i_dictnamespace; |
8765 | int i_rolname; |
8766 | int i_dicttemplate; |
8767 | int i_dictinitoption; |
8768 | |
8769 | /* Before 8.3, there is no built-in text search support */ |
8770 | if (fout->remoteVersion < 80300) |
8771 | { |
8772 | *numTSDicts = 0; |
8773 | return NULL; |
8774 | } |
8775 | |
8776 | query = createPQExpBuffer(); |
8777 | |
8778 | appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, " |
8779 | "dictnamespace, (%s dictowner) AS rolname, " |
8780 | "dicttemplate, dictinitoption " |
8781 | "FROM pg_ts_dict" , |
8782 | username_subquery); |
8783 | |
8784 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
8785 | |
8786 | ntups = PQntuples(res); |
8787 | *numTSDicts = ntups; |
8788 | |
8789 | dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo)); |
8790 | |
8791 | i_tableoid = PQfnumber(res, "tableoid" ); |
8792 | i_oid = PQfnumber(res, "oid" ); |
8793 | i_dictname = PQfnumber(res, "dictname" ); |
8794 | i_dictnamespace = PQfnumber(res, "dictnamespace" ); |
8795 | i_rolname = PQfnumber(res, "rolname" ); |
8796 | i_dictinitoption = PQfnumber(res, "dictinitoption" ); |
8797 | i_dicttemplate = PQfnumber(res, "dicttemplate" ); |
8798 | |
8799 | for (i = 0; i < ntups; i++) |
8800 | { |
8801 | dictinfo[i].dobj.objType = DO_TSDICT; |
8802 | dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
8803 | dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
8804 | AssignDumpId(&dictinfo[i].dobj); |
8805 | dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname)); |
8806 | dictinfo[i].dobj.namespace = |
8807 | findNamespace(fout, |
8808 | atooid(PQgetvalue(res, i, i_dictnamespace))); |
8809 | dictinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
8810 | dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate)); |
8811 | if (PQgetisnull(res, i, i_dictinitoption)) |
8812 | dictinfo[i].dictinitoption = NULL; |
8813 | else |
8814 | dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption)); |
8815 | |
8816 | /* Decide whether we want to dump it */ |
8817 | selectDumpableObject(&(dictinfo[i].dobj), fout); |
8818 | |
8819 | /* Text Search Dictionaries do not currently have ACLs. */ |
8820 | dictinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
8821 | } |
8822 | |
8823 | PQclear(res); |
8824 | |
8825 | destroyPQExpBuffer(query); |
8826 | |
8827 | return dictinfo; |
8828 | } |
8829 | |
8830 | /* |
8831 | * getTSTemplates: |
8832 | * read all text search templates in the system catalogs and return them |
8833 | * in the TSTemplateInfo* structure |
8834 | * |
8835 | * numTSTemplates is set to the number of templates read in |
8836 | */ |
8837 | TSTemplateInfo * |
8838 | getTSTemplates(Archive *fout, int *numTSTemplates) |
8839 | { |
8840 | PGresult *res; |
8841 | int ntups; |
8842 | int i; |
8843 | PQExpBuffer query; |
8844 | TSTemplateInfo *tmplinfo; |
8845 | int i_tableoid; |
8846 | int i_oid; |
8847 | int i_tmplname; |
8848 | int i_tmplnamespace; |
8849 | int i_tmplinit; |
8850 | int i_tmpllexize; |
8851 | |
8852 | /* Before 8.3, there is no built-in text search support */ |
8853 | if (fout->remoteVersion < 80300) |
8854 | { |
8855 | *numTSTemplates = 0; |
8856 | return NULL; |
8857 | } |
8858 | |
8859 | query = createPQExpBuffer(); |
8860 | |
8861 | appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, " |
8862 | "tmplnamespace, tmplinit::oid, tmpllexize::oid " |
8863 | "FROM pg_ts_template" ); |
8864 | |
8865 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
8866 | |
8867 | ntups = PQntuples(res); |
8868 | *numTSTemplates = ntups; |
8869 | |
8870 | tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo)); |
8871 | |
8872 | i_tableoid = PQfnumber(res, "tableoid" ); |
8873 | i_oid = PQfnumber(res, "oid" ); |
8874 | i_tmplname = PQfnumber(res, "tmplname" ); |
8875 | i_tmplnamespace = PQfnumber(res, "tmplnamespace" ); |
8876 | i_tmplinit = PQfnumber(res, "tmplinit" ); |
8877 | i_tmpllexize = PQfnumber(res, "tmpllexize" ); |
8878 | |
8879 | for (i = 0; i < ntups; i++) |
8880 | { |
8881 | tmplinfo[i].dobj.objType = DO_TSTEMPLATE; |
8882 | tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
8883 | tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
8884 | AssignDumpId(&tmplinfo[i].dobj); |
8885 | tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname)); |
8886 | tmplinfo[i].dobj.namespace = |
8887 | findNamespace(fout, |
8888 | atooid(PQgetvalue(res, i, i_tmplnamespace))); |
8889 | tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit)); |
8890 | tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize)); |
8891 | |
8892 | /* Decide whether we want to dump it */ |
8893 | selectDumpableObject(&(tmplinfo[i].dobj), fout); |
8894 | |
8895 | /* Text Search Templates do not currently have ACLs. */ |
8896 | tmplinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
8897 | } |
8898 | |
8899 | PQclear(res); |
8900 | |
8901 | destroyPQExpBuffer(query); |
8902 | |
8903 | return tmplinfo; |
8904 | } |
8905 | |
8906 | /* |
8907 | * getTSConfigurations: |
8908 | * read all text search configurations in the system catalogs and return |
8909 | * them in the TSConfigInfo* structure |
8910 | * |
8911 | * numTSConfigs is set to the number of configurations read in |
8912 | */ |
8913 | TSConfigInfo * |
8914 | getTSConfigurations(Archive *fout, int *numTSConfigs) |
8915 | { |
8916 | PGresult *res; |
8917 | int ntups; |
8918 | int i; |
8919 | PQExpBuffer query; |
8920 | TSConfigInfo *cfginfo; |
8921 | int i_tableoid; |
8922 | int i_oid; |
8923 | int i_cfgname; |
8924 | int i_cfgnamespace; |
8925 | int i_rolname; |
8926 | int i_cfgparser; |
8927 | |
8928 | /* Before 8.3, there is no built-in text search support */ |
8929 | if (fout->remoteVersion < 80300) |
8930 | { |
8931 | *numTSConfigs = 0; |
8932 | return NULL; |
8933 | } |
8934 | |
8935 | query = createPQExpBuffer(); |
8936 | |
8937 | appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, " |
8938 | "cfgnamespace, (%s cfgowner) AS rolname, cfgparser " |
8939 | "FROM pg_ts_config" , |
8940 | username_subquery); |
8941 | |
8942 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
8943 | |
8944 | ntups = PQntuples(res); |
8945 | *numTSConfigs = ntups; |
8946 | |
8947 | cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo)); |
8948 | |
8949 | i_tableoid = PQfnumber(res, "tableoid" ); |
8950 | i_oid = PQfnumber(res, "oid" ); |
8951 | i_cfgname = PQfnumber(res, "cfgname" ); |
8952 | i_cfgnamespace = PQfnumber(res, "cfgnamespace" ); |
8953 | i_rolname = PQfnumber(res, "rolname" ); |
8954 | i_cfgparser = PQfnumber(res, "cfgparser" ); |
8955 | |
8956 | for (i = 0; i < ntups; i++) |
8957 | { |
8958 | cfginfo[i].dobj.objType = DO_TSCONFIG; |
8959 | cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
8960 | cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
8961 | AssignDumpId(&cfginfo[i].dobj); |
8962 | cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname)); |
8963 | cfginfo[i].dobj.namespace = |
8964 | findNamespace(fout, |
8965 | atooid(PQgetvalue(res, i, i_cfgnamespace))); |
8966 | cfginfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
8967 | cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser)); |
8968 | |
8969 | /* Decide whether we want to dump it */ |
8970 | selectDumpableObject(&(cfginfo[i].dobj), fout); |
8971 | |
8972 | /* Text Search Configurations do not currently have ACLs. */ |
8973 | cfginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
8974 | } |
8975 | |
8976 | PQclear(res); |
8977 | |
8978 | destroyPQExpBuffer(query); |
8979 | |
8980 | return cfginfo; |
8981 | } |
8982 | |
8983 | /* |
8984 | * getForeignDataWrappers: |
8985 | * read all foreign-data wrappers in the system catalogs and return |
8986 | * them in the FdwInfo* structure |
8987 | * |
8988 | * numForeignDataWrappers is set to the number of fdws read in |
8989 | */ |
8990 | FdwInfo * |
8991 | getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) |
8992 | { |
8993 | DumpOptions *dopt = fout->dopt; |
8994 | PGresult *res; |
8995 | int ntups; |
8996 | int i; |
8997 | PQExpBuffer query; |
8998 | FdwInfo *fdwinfo; |
8999 | int i_tableoid; |
9000 | int i_oid; |
9001 | int i_fdwname; |
9002 | int i_rolname; |
9003 | int i_fdwhandler; |
9004 | int i_fdwvalidator; |
9005 | int i_fdwacl; |
9006 | int i_rfdwacl; |
9007 | int i_initfdwacl; |
9008 | int i_initrfdwacl; |
9009 | int i_fdwoptions; |
9010 | |
9011 | /* Before 8.4, there are no foreign-data wrappers */ |
9012 | if (fout->remoteVersion < 80400) |
9013 | { |
9014 | *numForeignDataWrappers = 0; |
9015 | return NULL; |
9016 | } |
9017 | |
9018 | query = createPQExpBuffer(); |
9019 | |
9020 | if (fout->remoteVersion >= 90600) |
9021 | { |
9022 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
9023 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
9024 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
9025 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
9026 | |
9027 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
9028 | initracl_subquery, "f.fdwacl" , "f.fdwowner" , "'F'" , |
9029 | dopt->binary_upgrade); |
9030 | |
9031 | appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.fdwname, " |
9032 | "(%s f.fdwowner) AS rolname, " |
9033 | "f.fdwhandler::pg_catalog.regproc, " |
9034 | "f.fdwvalidator::pg_catalog.regproc, " |
9035 | "%s AS fdwacl, " |
9036 | "%s AS rfdwacl, " |
9037 | "%s AS initfdwacl, " |
9038 | "%s AS initrfdwacl, " |
9039 | "array_to_string(ARRAY(" |
9040 | "SELECT quote_ident(option_name) || ' ' || " |
9041 | "quote_literal(option_value) " |
9042 | "FROM pg_options_to_table(f.fdwoptions) " |
9043 | "ORDER BY option_name" |
9044 | "), E',\n ') AS fdwoptions " |
9045 | "FROM pg_foreign_data_wrapper f " |
9046 | "LEFT JOIN pg_init_privs pip ON " |
9047 | "(f.oid = pip.objoid " |
9048 | "AND pip.classoid = 'pg_foreign_data_wrapper'::regclass " |
9049 | "AND pip.objsubid = 0) " , |
9050 | username_subquery, |
9051 | acl_subquery->data, |
9052 | racl_subquery->data, |
9053 | initacl_subquery->data, |
9054 | initracl_subquery->data); |
9055 | |
9056 | destroyPQExpBuffer(acl_subquery); |
9057 | destroyPQExpBuffer(racl_subquery); |
9058 | destroyPQExpBuffer(initacl_subquery); |
9059 | destroyPQExpBuffer(initracl_subquery); |
9060 | } |
9061 | else if (fout->remoteVersion >= 90100) |
9062 | { |
9063 | appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, " |
9064 | "(%s fdwowner) AS rolname, " |
9065 | "fdwhandler::pg_catalog.regproc, " |
9066 | "fdwvalidator::pg_catalog.regproc, fdwacl, " |
9067 | "NULL as rfdwacl, " |
9068 | "NULL as initfdwacl, NULL AS initrfdwacl, " |
9069 | "array_to_string(ARRAY(" |
9070 | "SELECT quote_ident(option_name) || ' ' || " |
9071 | "quote_literal(option_value) " |
9072 | "FROM pg_options_to_table(fdwoptions) " |
9073 | "ORDER BY option_name" |
9074 | "), E',\n ') AS fdwoptions " |
9075 | "FROM pg_foreign_data_wrapper" , |
9076 | username_subquery); |
9077 | } |
9078 | else |
9079 | { |
9080 | appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, " |
9081 | "(%s fdwowner) AS rolname, " |
9082 | "'-' AS fdwhandler, " |
9083 | "fdwvalidator::pg_catalog.regproc, fdwacl, " |
9084 | "NULL as rfdwacl, " |
9085 | "NULL as initfdwacl, NULL AS initrfdwacl, " |
9086 | "array_to_string(ARRAY(" |
9087 | "SELECT quote_ident(option_name) || ' ' || " |
9088 | "quote_literal(option_value) " |
9089 | "FROM pg_options_to_table(fdwoptions) " |
9090 | "ORDER BY option_name" |
9091 | "), E',\n ') AS fdwoptions " |
9092 | "FROM pg_foreign_data_wrapper" , |
9093 | username_subquery); |
9094 | } |
9095 | |
9096 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
9097 | |
9098 | ntups = PQntuples(res); |
9099 | *numForeignDataWrappers = ntups; |
9100 | |
9101 | fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo)); |
9102 | |
9103 | i_tableoid = PQfnumber(res, "tableoid" ); |
9104 | i_oid = PQfnumber(res, "oid" ); |
9105 | i_fdwname = PQfnumber(res, "fdwname" ); |
9106 | i_rolname = PQfnumber(res, "rolname" ); |
9107 | i_fdwhandler = PQfnumber(res, "fdwhandler" ); |
9108 | i_fdwvalidator = PQfnumber(res, "fdwvalidator" ); |
9109 | i_fdwacl = PQfnumber(res, "fdwacl" ); |
9110 | i_rfdwacl = PQfnumber(res, "rfdwacl" ); |
9111 | i_initfdwacl = PQfnumber(res, "initfdwacl" ); |
9112 | i_initrfdwacl = PQfnumber(res, "initrfdwacl" ); |
9113 | i_fdwoptions = PQfnumber(res, "fdwoptions" ); |
9114 | |
9115 | for (i = 0; i < ntups; i++) |
9116 | { |
9117 | fdwinfo[i].dobj.objType = DO_FDW; |
9118 | fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
9119 | fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
9120 | AssignDumpId(&fdwinfo[i].dobj); |
9121 | fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname)); |
9122 | fdwinfo[i].dobj.namespace = NULL; |
9123 | fdwinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
9124 | fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler)); |
9125 | fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator)); |
9126 | fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions)); |
9127 | fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl)); |
9128 | fdwinfo[i].rfdwacl = pg_strdup(PQgetvalue(res, i, i_rfdwacl)); |
9129 | fdwinfo[i].initfdwacl = pg_strdup(PQgetvalue(res, i, i_initfdwacl)); |
9130 | fdwinfo[i].initrfdwacl = pg_strdup(PQgetvalue(res, i, i_initrfdwacl)); |
9131 | |
9132 | /* Decide whether we want to dump it */ |
9133 | selectDumpableObject(&(fdwinfo[i].dobj), fout); |
9134 | |
9135 | /* Do not try to dump ACL if no ACL exists. */ |
9136 | if (PQgetisnull(res, i, i_fdwacl) && PQgetisnull(res, i, i_rfdwacl) && |
9137 | PQgetisnull(res, i, i_initfdwacl) && |
9138 | PQgetisnull(res, i, i_initrfdwacl)) |
9139 | fdwinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
9140 | } |
9141 | |
9142 | PQclear(res); |
9143 | |
9144 | destroyPQExpBuffer(query); |
9145 | |
9146 | return fdwinfo; |
9147 | } |
9148 | |
9149 | /* |
9150 | * getForeignServers: |
9151 | * read all foreign servers in the system catalogs and return |
9152 | * them in the ForeignServerInfo * structure |
9153 | * |
9154 | * numForeignServers is set to the number of servers read in |
9155 | */ |
9156 | ForeignServerInfo * |
9157 | getForeignServers(Archive *fout, int *numForeignServers) |
9158 | { |
9159 | DumpOptions *dopt = fout->dopt; |
9160 | PGresult *res; |
9161 | int ntups; |
9162 | int i; |
9163 | PQExpBuffer query; |
9164 | ForeignServerInfo *srvinfo; |
9165 | int i_tableoid; |
9166 | int i_oid; |
9167 | int i_srvname; |
9168 | int i_rolname; |
9169 | int i_srvfdw; |
9170 | int i_srvtype; |
9171 | int i_srvversion; |
9172 | int i_srvacl; |
9173 | int i_rsrvacl; |
9174 | int i_initsrvacl; |
9175 | int i_initrsrvacl; |
9176 | int i_srvoptions; |
9177 | |
9178 | /* Before 8.4, there are no foreign servers */ |
9179 | if (fout->remoteVersion < 80400) |
9180 | { |
9181 | *numForeignServers = 0; |
9182 | return NULL; |
9183 | } |
9184 | |
9185 | query = createPQExpBuffer(); |
9186 | |
9187 | if (fout->remoteVersion >= 90600) |
9188 | { |
9189 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
9190 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
9191 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
9192 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
9193 | |
9194 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
9195 | initracl_subquery, "f.srvacl" , "f.srvowner" , "'S'" , |
9196 | dopt->binary_upgrade); |
9197 | |
9198 | appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.srvname, " |
9199 | "(%s f.srvowner) AS rolname, " |
9200 | "f.srvfdw, f.srvtype, f.srvversion, " |
9201 | "%s AS srvacl, " |
9202 | "%s AS rsrvacl, " |
9203 | "%s AS initsrvacl, " |
9204 | "%s AS initrsrvacl, " |
9205 | "array_to_string(ARRAY(" |
9206 | "SELECT quote_ident(option_name) || ' ' || " |
9207 | "quote_literal(option_value) " |
9208 | "FROM pg_options_to_table(f.srvoptions) " |
9209 | "ORDER BY option_name" |
9210 | "), E',\n ') AS srvoptions " |
9211 | "FROM pg_foreign_server f " |
9212 | "LEFT JOIN pg_init_privs pip " |
9213 | "ON (f.oid = pip.objoid " |
9214 | "AND pip.classoid = 'pg_foreign_server'::regclass " |
9215 | "AND pip.objsubid = 0) " , |
9216 | username_subquery, |
9217 | acl_subquery->data, |
9218 | racl_subquery->data, |
9219 | initacl_subquery->data, |
9220 | initracl_subquery->data); |
9221 | |
9222 | destroyPQExpBuffer(acl_subquery); |
9223 | destroyPQExpBuffer(racl_subquery); |
9224 | destroyPQExpBuffer(initacl_subquery); |
9225 | destroyPQExpBuffer(initracl_subquery); |
9226 | } |
9227 | else |
9228 | { |
9229 | appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " |
9230 | "(%s srvowner) AS rolname, " |
9231 | "srvfdw, srvtype, srvversion, srvacl, " |
9232 | "NULL AS rsrvacl, " |
9233 | "NULL AS initsrvacl, NULL AS initrsrvacl, " |
9234 | "array_to_string(ARRAY(" |
9235 | "SELECT quote_ident(option_name) || ' ' || " |
9236 | "quote_literal(option_value) " |
9237 | "FROM pg_options_to_table(srvoptions) " |
9238 | "ORDER BY option_name" |
9239 | "), E',\n ') AS srvoptions " |
9240 | "FROM pg_foreign_server" , |
9241 | username_subquery); |
9242 | } |
9243 | |
9244 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
9245 | |
9246 | ntups = PQntuples(res); |
9247 | *numForeignServers = ntups; |
9248 | |
9249 | srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo)); |
9250 | |
9251 | i_tableoid = PQfnumber(res, "tableoid" ); |
9252 | i_oid = PQfnumber(res, "oid" ); |
9253 | i_srvname = PQfnumber(res, "srvname" ); |
9254 | i_rolname = PQfnumber(res, "rolname" ); |
9255 | i_srvfdw = PQfnumber(res, "srvfdw" ); |
9256 | i_srvtype = PQfnumber(res, "srvtype" ); |
9257 | i_srvversion = PQfnumber(res, "srvversion" ); |
9258 | i_srvacl = PQfnumber(res, "srvacl" ); |
9259 | i_rsrvacl = PQfnumber(res, "rsrvacl" ); |
9260 | i_initsrvacl = PQfnumber(res, "initsrvacl" ); |
9261 | i_initrsrvacl = PQfnumber(res, "initrsrvacl" ); |
9262 | i_srvoptions = PQfnumber(res, "srvoptions" ); |
9263 | |
9264 | for (i = 0; i < ntups; i++) |
9265 | { |
9266 | srvinfo[i].dobj.objType = DO_FOREIGN_SERVER; |
9267 | srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
9268 | srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
9269 | AssignDumpId(&srvinfo[i].dobj); |
9270 | srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname)); |
9271 | srvinfo[i].dobj.namespace = NULL; |
9272 | srvinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); |
9273 | srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw)); |
9274 | srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype)); |
9275 | srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion)); |
9276 | srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions)); |
9277 | srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl)); |
9278 | srvinfo[i].rsrvacl = pg_strdup(PQgetvalue(res, i, i_rsrvacl)); |
9279 | srvinfo[i].initsrvacl = pg_strdup(PQgetvalue(res, i, i_initsrvacl)); |
9280 | srvinfo[i].initrsrvacl = pg_strdup(PQgetvalue(res, i, i_initrsrvacl)); |
9281 | |
9282 | /* Decide whether we want to dump it */ |
9283 | selectDumpableObject(&(srvinfo[i].dobj), fout); |
9284 | |
9285 | /* Do not try to dump ACL if no ACL exists. */ |
9286 | if (PQgetisnull(res, i, i_srvacl) && PQgetisnull(res, i, i_rsrvacl) && |
9287 | PQgetisnull(res, i, i_initsrvacl) && |
9288 | PQgetisnull(res, i, i_initrsrvacl)) |
9289 | srvinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; |
9290 | } |
9291 | |
9292 | PQclear(res); |
9293 | |
9294 | destroyPQExpBuffer(query); |
9295 | |
9296 | return srvinfo; |
9297 | } |
9298 | |
9299 | /* |
9300 | * getDefaultACLs: |
9301 | * read all default ACL information in the system catalogs and return |
9302 | * them in the DefaultACLInfo structure |
9303 | * |
9304 | * numDefaultACLs is set to the number of ACLs read in |
9305 | */ |
9306 | DefaultACLInfo * |
9307 | getDefaultACLs(Archive *fout, int *numDefaultACLs) |
9308 | { |
9309 | DumpOptions *dopt = fout->dopt; |
9310 | DefaultACLInfo *daclinfo; |
9311 | PQExpBuffer query; |
9312 | PGresult *res; |
9313 | int i_oid; |
9314 | int i_tableoid; |
9315 | int i_defaclrole; |
9316 | int i_defaclnamespace; |
9317 | int i_defaclobjtype; |
9318 | int i_defaclacl; |
9319 | int i_rdefaclacl; |
9320 | int i_initdefaclacl; |
9321 | int i_initrdefaclacl; |
9322 | int i, |
9323 | ntups; |
9324 | |
9325 | if (fout->remoteVersion < 90000) |
9326 | { |
9327 | *numDefaultACLs = 0; |
9328 | return NULL; |
9329 | } |
9330 | |
9331 | query = createPQExpBuffer(); |
9332 | |
9333 | if (fout->remoteVersion >= 90600) |
9334 | { |
9335 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
9336 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
9337 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
9338 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
9339 | |
9340 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
9341 | initracl_subquery, "defaclacl" , "defaclrole" , |
9342 | "CASE WHEN defaclobjtype = 'S' THEN 's' ELSE defaclobjtype END::\"char\"" , |
9343 | dopt->binary_upgrade); |
9344 | |
9345 | appendPQExpBuffer(query, "SELECT d.oid, d.tableoid, " |
9346 | "(%s d.defaclrole) AS defaclrole, " |
9347 | "d.defaclnamespace, " |
9348 | "d.defaclobjtype, " |
9349 | "%s AS defaclacl, " |
9350 | "%s AS rdefaclacl, " |
9351 | "%s AS initdefaclacl, " |
9352 | "%s AS initrdefaclacl " |
9353 | "FROM pg_default_acl d " |
9354 | "LEFT JOIN pg_init_privs pip ON " |
9355 | "(d.oid = pip.objoid " |
9356 | "AND pip.classoid = 'pg_default_acl'::regclass " |
9357 | "AND pip.objsubid = 0) " , |
9358 | username_subquery, |
9359 | acl_subquery->data, |
9360 | racl_subquery->data, |
9361 | initacl_subquery->data, |
9362 | initracl_subquery->data); |
9363 | } |
9364 | else |
9365 | { |
9366 | appendPQExpBuffer(query, "SELECT oid, tableoid, " |
9367 | "(%s defaclrole) AS defaclrole, " |
9368 | "defaclnamespace, " |
9369 | "defaclobjtype, " |
9370 | "defaclacl, " |
9371 | "NULL AS rdefaclacl, " |
9372 | "NULL AS initdefaclacl, " |
9373 | "NULL AS initrdefaclacl " |
9374 | "FROM pg_default_acl" , |
9375 | username_subquery); |
9376 | } |
9377 | |
9378 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
9379 | |
9380 | ntups = PQntuples(res); |
9381 | *numDefaultACLs = ntups; |
9382 | |
9383 | daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo)); |
9384 | |
9385 | i_oid = PQfnumber(res, "oid" ); |
9386 | i_tableoid = PQfnumber(res, "tableoid" ); |
9387 | i_defaclrole = PQfnumber(res, "defaclrole" ); |
9388 | i_defaclnamespace = PQfnumber(res, "defaclnamespace" ); |
9389 | i_defaclobjtype = PQfnumber(res, "defaclobjtype" ); |
9390 | i_defaclacl = PQfnumber(res, "defaclacl" ); |
9391 | i_rdefaclacl = PQfnumber(res, "rdefaclacl" ); |
9392 | i_initdefaclacl = PQfnumber(res, "initdefaclacl" ); |
9393 | i_initrdefaclacl = PQfnumber(res, "initrdefaclacl" ); |
9394 | |
9395 | for (i = 0; i < ntups; i++) |
9396 | { |
9397 | Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace)); |
9398 | |
9399 | daclinfo[i].dobj.objType = DO_DEFAULT_ACL; |
9400 | daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); |
9401 | daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); |
9402 | AssignDumpId(&daclinfo[i].dobj); |
9403 | /* cheesy ... is it worth coming up with a better object name? */ |
9404 | daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype)); |
9405 | |
9406 | if (nspid != InvalidOid) |
9407 | daclinfo[i].dobj.namespace = findNamespace(fout, nspid); |
9408 | else |
9409 | daclinfo[i].dobj.namespace = NULL; |
9410 | |
9411 | daclinfo[i].defaclrole = pg_strdup(PQgetvalue(res, i, i_defaclrole)); |
9412 | daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype)); |
9413 | daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl)); |
9414 | daclinfo[i].rdefaclacl = pg_strdup(PQgetvalue(res, i, i_rdefaclacl)); |
9415 | daclinfo[i].initdefaclacl = pg_strdup(PQgetvalue(res, i, i_initdefaclacl)); |
9416 | daclinfo[i].initrdefaclacl = pg_strdup(PQgetvalue(res, i, i_initrdefaclacl)); |
9417 | |
9418 | /* Decide whether we want to dump it */ |
9419 | selectDumpableDefaultACL(&(daclinfo[i]), dopt); |
9420 | } |
9421 | |
9422 | PQclear(res); |
9423 | |
9424 | destroyPQExpBuffer(query); |
9425 | |
9426 | return daclinfo; |
9427 | } |
9428 | |
9429 | /* |
9430 | * dumpComment -- |
9431 | * |
9432 | * This routine is used to dump any comments associated with the |
9433 | * object handed to this routine. The routine takes the object type |
9434 | * and object name (ready to print, except for schema decoration), plus |
9435 | * the namespace and owner of the object (for labeling the ArchiveEntry), |
9436 | * plus catalog ID and subid which are the lookup key for pg_description, |
9437 | * plus the dump ID for the object (for setting a dependency). |
9438 | * If a matching pg_description entry is found, it is dumped. |
9439 | * |
9440 | * Note: in some cases, such as comments for triggers and rules, the "type" |
9441 | * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack |
9442 | * but it doesn't seem worth complicating the API for all callers to make |
9443 | * it cleaner. |
9444 | * |
9445 | * Note: although this routine takes a dumpId for dependency purposes, |
9446 | * that purpose is just to mark the dependency in the emitted dump file |
9447 | * for possible future use by pg_restore. We do NOT use it for determining |
9448 | * ordering of the comment in the dump file, because this routine is called |
9449 | * after dependency sorting occurs. This routine should be called just after |
9450 | * calling ArchiveEntry() for the specified object. |
9451 | */ |
9452 | static void |
9453 | (Archive *fout, const char *type, const char *name, |
9454 | const char *namespace, const char *owner, |
9455 | CatalogId catalogId, int subid, DumpId dumpId) |
9456 | { |
9457 | DumpOptions *dopt = fout->dopt; |
9458 | CommentItem *; |
9459 | int ; |
9460 | |
9461 | /* do nothing, if --no-comments is supplied */ |
9462 | if (dopt->no_comments) |
9463 | return; |
9464 | |
9465 | /* Comments are schema not data ... except blob comments are data */ |
9466 | if (strcmp(type, "LARGE OBJECT" ) != 0) |
9467 | { |
9468 | if (dopt->dataOnly) |
9469 | return; |
9470 | } |
9471 | else |
9472 | { |
9473 | /* We do dump blob comments in binary-upgrade mode */ |
9474 | if (dopt->schemaOnly && !dopt->binary_upgrade) |
9475 | return; |
9476 | } |
9477 | |
9478 | /* Search for comments associated with catalogId, using table */ |
9479 | ncomments = findComments(fout, catalogId.tableoid, catalogId.oid, |
9480 | &comments); |
9481 | |
9482 | /* Is there one matching the subid? */ |
9483 | while (ncomments > 0) |
9484 | { |
9485 | if (comments->objsubid == subid) |
9486 | break; |
9487 | comments++; |
9488 | ncomments--; |
9489 | } |
9490 | |
9491 | /* If a comment exists, build COMMENT ON statement */ |
9492 | if (ncomments > 0) |
9493 | { |
9494 | PQExpBuffer query = createPQExpBuffer(); |
9495 | PQExpBuffer tag = createPQExpBuffer(); |
9496 | |
9497 | appendPQExpBuffer(query, "COMMENT ON %s " , type); |
9498 | if (namespace && *namespace) |
9499 | appendPQExpBuffer(query, "%s." , fmtId(namespace)); |
9500 | appendPQExpBuffer(query, "%s IS " , name); |
9501 | appendStringLiteralAH(query, comments->descr, fout); |
9502 | appendPQExpBufferStr(query, ";\n" ); |
9503 | |
9504 | appendPQExpBuffer(tag, "%s %s" , type, name); |
9505 | |
9506 | /* |
9507 | * We mark comments as SECTION_NONE because they really belong in the |
9508 | * same section as their parent, whether that is pre-data or |
9509 | * post-data. |
9510 | */ |
9511 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
9512 | ARCHIVE_OPTS(.tag = tag->data, |
9513 | .namespace = namespace, |
9514 | .owner = owner, |
9515 | .description = "COMMENT" , |
9516 | .section = SECTION_NONE, |
9517 | .createStmt = query->data, |
9518 | .deps = &dumpId, |
9519 | .nDeps = 1)); |
9520 | |
9521 | destroyPQExpBuffer(query); |
9522 | destroyPQExpBuffer(tag); |
9523 | } |
9524 | } |
9525 | |
9526 | /* |
9527 | * dumpTableComment -- |
9528 | * |
9529 | * As above, but dump comments for both the specified table (or view) |
9530 | * and its columns. |
9531 | */ |
9532 | static void |
9533 | (Archive *fout, TableInfo *tbinfo, |
9534 | const char *reltypename) |
9535 | { |
9536 | DumpOptions *dopt = fout->dopt; |
9537 | CommentItem *; |
9538 | int ; |
9539 | PQExpBuffer query; |
9540 | PQExpBuffer tag; |
9541 | |
9542 | /* do nothing, if --no-comments is supplied */ |
9543 | if (dopt->no_comments) |
9544 | return; |
9545 | |
9546 | /* Comments are SCHEMA not data */ |
9547 | if (dopt->dataOnly) |
9548 | return; |
9549 | |
9550 | /* Search for comments associated with relation, using table */ |
9551 | ncomments = findComments(fout, |
9552 | tbinfo->dobj.catId.tableoid, |
9553 | tbinfo->dobj.catId.oid, |
9554 | &comments); |
9555 | |
9556 | /* If comments exist, build COMMENT ON statements */ |
9557 | if (ncomments <= 0) |
9558 | return; |
9559 | |
9560 | query = createPQExpBuffer(); |
9561 | tag = createPQExpBuffer(); |
9562 | |
9563 | while (ncomments > 0) |
9564 | { |
9565 | const char *descr = comments->descr; |
9566 | int objsubid = comments->objsubid; |
9567 | |
9568 | if (objsubid == 0) |
9569 | { |
9570 | resetPQExpBuffer(tag); |
9571 | appendPQExpBuffer(tag, "%s %s" , reltypename, |
9572 | fmtId(tbinfo->dobj.name)); |
9573 | |
9574 | resetPQExpBuffer(query); |
9575 | appendPQExpBuffer(query, "COMMENT ON %s %s IS " , reltypename, |
9576 | fmtQualifiedDumpable(tbinfo)); |
9577 | appendStringLiteralAH(query, descr, fout); |
9578 | appendPQExpBufferStr(query, ";\n" ); |
9579 | |
9580 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
9581 | ARCHIVE_OPTS(.tag = tag->data, |
9582 | .namespace = tbinfo->dobj.namespace->dobj.name, |
9583 | .owner = tbinfo->rolname, |
9584 | .description = "COMMENT" , |
9585 | .section = SECTION_NONE, |
9586 | .createStmt = query->data, |
9587 | .deps = &(tbinfo->dobj.dumpId), |
9588 | .nDeps = 1)); |
9589 | } |
9590 | else if (objsubid > 0 && objsubid <= tbinfo->numatts) |
9591 | { |
9592 | resetPQExpBuffer(tag); |
9593 | appendPQExpBuffer(tag, "COLUMN %s." , |
9594 | fmtId(tbinfo->dobj.name)); |
9595 | appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1])); |
9596 | |
9597 | resetPQExpBuffer(query); |
9598 | appendPQExpBuffer(query, "COMMENT ON COLUMN %s." , |
9599 | fmtQualifiedDumpable(tbinfo)); |
9600 | appendPQExpBuffer(query, "%s IS " , |
9601 | fmtId(tbinfo->attnames[objsubid - 1])); |
9602 | appendStringLiteralAH(query, descr, fout); |
9603 | appendPQExpBufferStr(query, ";\n" ); |
9604 | |
9605 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
9606 | ARCHIVE_OPTS(.tag = tag->data, |
9607 | .namespace = tbinfo->dobj.namespace->dobj.name, |
9608 | .owner = tbinfo->rolname, |
9609 | .description = "COMMENT" , |
9610 | .section = SECTION_NONE, |
9611 | .createStmt = query->data, |
9612 | .deps = &(tbinfo->dobj.dumpId), |
9613 | .nDeps = 1)); |
9614 | } |
9615 | |
9616 | comments++; |
9617 | ncomments--; |
9618 | } |
9619 | |
9620 | destroyPQExpBuffer(query); |
9621 | destroyPQExpBuffer(tag); |
9622 | } |
9623 | |
9624 | /* |
9625 | * findComments -- |
9626 | * |
9627 | * Find the comment(s), if any, associated with the given object. All the |
9628 | * objsubid values associated with the given classoid/objoid are found with |
9629 | * one search. |
9630 | */ |
9631 | static int |
9632 | (Archive *fout, Oid classoid, Oid objoid, |
9633 | CommentItem **items) |
9634 | { |
9635 | /* static storage for table of comments */ |
9636 | static CommentItem * = NULL; |
9637 | static int = -1; |
9638 | |
9639 | CommentItem *middle = NULL; |
9640 | CommentItem *low; |
9641 | CommentItem *high; |
9642 | int nmatch; |
9643 | |
9644 | /* Get comments if we didn't already */ |
9645 | if (ncomments < 0) |
9646 | ncomments = collectComments(fout, &comments); |
9647 | |
9648 | /* |
9649 | * Do binary search to find some item matching the object. |
9650 | */ |
9651 | low = &comments[0]; |
9652 | high = &comments[ncomments - 1]; |
9653 | while (low <= high) |
9654 | { |
9655 | middle = low + (high - low) / 2; |
9656 | |
9657 | if (classoid < middle->classoid) |
9658 | high = middle - 1; |
9659 | else if (classoid > middle->classoid) |
9660 | low = middle + 1; |
9661 | else if (objoid < middle->objoid) |
9662 | high = middle - 1; |
9663 | else if (objoid > middle->objoid) |
9664 | low = middle + 1; |
9665 | else |
9666 | break; /* found a match */ |
9667 | } |
9668 | |
9669 | if (low > high) /* no matches */ |
9670 | { |
9671 | *items = NULL; |
9672 | return 0; |
9673 | } |
9674 | |
9675 | /* |
9676 | * Now determine how many items match the object. The search loop |
9677 | * invariant still holds: only items between low and high inclusive could |
9678 | * match. |
9679 | */ |
9680 | nmatch = 1; |
9681 | while (middle > low) |
9682 | { |
9683 | if (classoid != middle[-1].classoid || |
9684 | objoid != middle[-1].objoid) |
9685 | break; |
9686 | middle--; |
9687 | nmatch++; |
9688 | } |
9689 | |
9690 | *items = middle; |
9691 | |
9692 | middle += nmatch; |
9693 | while (middle <= high) |
9694 | { |
9695 | if (classoid != middle->classoid || |
9696 | objoid != middle->objoid) |
9697 | break; |
9698 | middle++; |
9699 | nmatch++; |
9700 | } |
9701 | |
9702 | return nmatch; |
9703 | } |
9704 | |
9705 | /* |
9706 | * collectComments -- |
9707 | * |
9708 | * Construct a table of all comments available for database objects. |
9709 | * We used to do per-object queries for the comments, but it's much faster |
9710 | * to pull them all over at once, and on most databases the memory cost |
9711 | * isn't high. |
9712 | * |
9713 | * The table is sorted by classoid/objid/objsubid for speed in lookup. |
9714 | */ |
9715 | static int |
9716 | (Archive *fout, CommentItem **items) |
9717 | { |
9718 | PGresult *res; |
9719 | PQExpBuffer query; |
9720 | int i_description; |
9721 | int i_classoid; |
9722 | int i_objoid; |
9723 | int i_objsubid; |
9724 | int ntups; |
9725 | int i; |
9726 | CommentItem *; |
9727 | |
9728 | query = createPQExpBuffer(); |
9729 | |
9730 | appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid " |
9731 | "FROM pg_catalog.pg_description " |
9732 | "ORDER BY classoid, objoid, objsubid" ); |
9733 | |
9734 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
9735 | |
9736 | /* Construct lookup table containing OIDs in numeric form */ |
9737 | |
9738 | i_description = PQfnumber(res, "description" ); |
9739 | i_classoid = PQfnumber(res, "classoid" ); |
9740 | i_objoid = PQfnumber(res, "objoid" ); |
9741 | i_objsubid = PQfnumber(res, "objsubid" ); |
9742 | |
9743 | ntups = PQntuples(res); |
9744 | |
9745 | comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem)); |
9746 | |
9747 | for (i = 0; i < ntups; i++) |
9748 | { |
9749 | comments[i].descr = PQgetvalue(res, i, i_description); |
9750 | comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid)); |
9751 | comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid)); |
9752 | comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid)); |
9753 | } |
9754 | |
9755 | /* Do NOT free the PGresult since we are keeping pointers into it */ |
9756 | destroyPQExpBuffer(query); |
9757 | |
9758 | *items = comments; |
9759 | return ntups; |
9760 | } |
9761 | |
9762 | /* |
9763 | * dumpDumpableObject |
9764 | * |
9765 | * This routine and its subsidiaries are responsible for creating |
9766 | * ArchiveEntries (TOC objects) for each object to be dumped. |
9767 | */ |
9768 | static void |
9769 | dumpDumpableObject(Archive *fout, DumpableObject *dobj) |
9770 | { |
9771 | switch (dobj->objType) |
9772 | { |
9773 | case DO_NAMESPACE: |
9774 | dumpNamespace(fout, (NamespaceInfo *) dobj); |
9775 | break; |
9776 | case DO_EXTENSION: |
9777 | dumpExtension(fout, (ExtensionInfo *) dobj); |
9778 | break; |
9779 | case DO_TYPE: |
9780 | dumpType(fout, (TypeInfo *) dobj); |
9781 | break; |
9782 | case DO_SHELL_TYPE: |
9783 | dumpShellType(fout, (ShellTypeInfo *) dobj); |
9784 | break; |
9785 | case DO_FUNC: |
9786 | dumpFunc(fout, (FuncInfo *) dobj); |
9787 | break; |
9788 | case DO_AGG: |
9789 | dumpAgg(fout, (AggInfo *) dobj); |
9790 | break; |
9791 | case DO_OPERATOR: |
9792 | dumpOpr(fout, (OprInfo *) dobj); |
9793 | break; |
9794 | case DO_ACCESS_METHOD: |
9795 | dumpAccessMethod(fout, (AccessMethodInfo *) dobj); |
9796 | break; |
9797 | case DO_OPCLASS: |
9798 | dumpOpclass(fout, (OpclassInfo *) dobj); |
9799 | break; |
9800 | case DO_OPFAMILY: |
9801 | dumpOpfamily(fout, (OpfamilyInfo *) dobj); |
9802 | break; |
9803 | case DO_COLLATION: |
9804 | dumpCollation(fout, (CollInfo *) dobj); |
9805 | break; |
9806 | case DO_CONVERSION: |
9807 | dumpConversion(fout, (ConvInfo *) dobj); |
9808 | break; |
9809 | case DO_TABLE: |
9810 | dumpTable(fout, (TableInfo *) dobj); |
9811 | break; |
9812 | case DO_ATTRDEF: |
9813 | dumpAttrDef(fout, (AttrDefInfo *) dobj); |
9814 | break; |
9815 | case DO_INDEX: |
9816 | dumpIndex(fout, (IndxInfo *) dobj); |
9817 | break; |
9818 | case DO_INDEX_ATTACH: |
9819 | dumpIndexAttach(fout, (IndexAttachInfo *) dobj); |
9820 | break; |
9821 | case DO_STATSEXT: |
9822 | dumpStatisticsExt(fout, (StatsExtInfo *) dobj); |
9823 | break; |
9824 | case DO_REFRESH_MATVIEW: |
9825 | refreshMatViewData(fout, (TableDataInfo *) dobj); |
9826 | break; |
9827 | case DO_RULE: |
9828 | dumpRule(fout, (RuleInfo *) dobj); |
9829 | break; |
9830 | case DO_TRIGGER: |
9831 | dumpTrigger(fout, (TriggerInfo *) dobj); |
9832 | break; |
9833 | case DO_EVENT_TRIGGER: |
9834 | dumpEventTrigger(fout, (EventTriggerInfo *) dobj); |
9835 | break; |
9836 | case DO_CONSTRAINT: |
9837 | dumpConstraint(fout, (ConstraintInfo *) dobj); |
9838 | break; |
9839 | case DO_FK_CONSTRAINT: |
9840 | dumpConstraint(fout, (ConstraintInfo *) dobj); |
9841 | break; |
9842 | case DO_PROCLANG: |
9843 | dumpProcLang(fout, (ProcLangInfo *) dobj); |
9844 | break; |
9845 | case DO_CAST: |
9846 | dumpCast(fout, (CastInfo *) dobj); |
9847 | break; |
9848 | case DO_TRANSFORM: |
9849 | dumpTransform(fout, (TransformInfo *) dobj); |
9850 | break; |
9851 | case DO_SEQUENCE_SET: |
9852 | dumpSequenceData(fout, (TableDataInfo *) dobj); |
9853 | break; |
9854 | case DO_TABLE_DATA: |
9855 | dumpTableData(fout, (TableDataInfo *) dobj); |
9856 | break; |
9857 | case DO_DUMMY_TYPE: |
9858 | /* table rowtypes and array types are never dumped separately */ |
9859 | break; |
9860 | case DO_TSPARSER: |
9861 | dumpTSParser(fout, (TSParserInfo *) dobj); |
9862 | break; |
9863 | case DO_TSDICT: |
9864 | dumpTSDictionary(fout, (TSDictInfo *) dobj); |
9865 | break; |
9866 | case DO_TSTEMPLATE: |
9867 | dumpTSTemplate(fout, (TSTemplateInfo *) dobj); |
9868 | break; |
9869 | case DO_TSCONFIG: |
9870 | dumpTSConfig(fout, (TSConfigInfo *) dobj); |
9871 | break; |
9872 | case DO_FDW: |
9873 | dumpForeignDataWrapper(fout, (FdwInfo *) dobj); |
9874 | break; |
9875 | case DO_FOREIGN_SERVER: |
9876 | dumpForeignServer(fout, (ForeignServerInfo *) dobj); |
9877 | break; |
9878 | case DO_DEFAULT_ACL: |
9879 | dumpDefaultACL(fout, (DefaultACLInfo *) dobj); |
9880 | break; |
9881 | case DO_BLOB: |
9882 | dumpBlob(fout, (BlobInfo *) dobj); |
9883 | break; |
9884 | case DO_BLOB_DATA: |
9885 | if (dobj->dump & DUMP_COMPONENT_DATA) |
9886 | { |
9887 | TocEntry *te; |
9888 | |
9889 | te = ArchiveEntry(fout, dobj->catId, dobj->dumpId, |
9890 | ARCHIVE_OPTS(.tag = dobj->name, |
9891 | .description = "BLOBS" , |
9892 | .section = SECTION_DATA, |
9893 | .dumpFn = dumpBlobs)); |
9894 | |
9895 | /* |
9896 | * Set the TocEntry's dataLength in case we are doing a |
9897 | * parallel dump and want to order dump jobs by table size. |
9898 | * (We need some size estimate for every TocEntry with a |
9899 | * DataDumper function.) We don't currently have any cheap |
9900 | * way to estimate the size of blobs, but it doesn't matter; |
9901 | * let's just set the size to a large value so parallel dumps |
9902 | * will launch this job first. If there's lots of blobs, we |
9903 | * win, and if there aren't, we don't lose much. (If you want |
9904 | * to improve on this, really what you should be thinking |
9905 | * about is allowing blob dumping to be parallelized, not just |
9906 | * getting a smarter estimate for the single TOC entry.) |
9907 | */ |
9908 | te->dataLength = MaxBlockNumber; |
9909 | } |
9910 | break; |
9911 | case DO_POLICY: |
9912 | dumpPolicy(fout, (PolicyInfo *) dobj); |
9913 | break; |
9914 | case DO_PUBLICATION: |
9915 | dumpPublication(fout, (PublicationInfo *) dobj); |
9916 | break; |
9917 | case DO_PUBLICATION_REL: |
9918 | dumpPublicationTable(fout, (PublicationRelInfo *) dobj); |
9919 | break; |
9920 | case DO_SUBSCRIPTION: |
9921 | dumpSubscription(fout, (SubscriptionInfo *) dobj); |
9922 | break; |
9923 | case DO_PRE_DATA_BOUNDARY: |
9924 | case DO_POST_DATA_BOUNDARY: |
9925 | /* never dumped, nothing to do */ |
9926 | break; |
9927 | } |
9928 | } |
9929 | |
9930 | /* |
9931 | * dumpNamespace |
9932 | * writes out to fout the queries to recreate a user-defined namespace |
9933 | */ |
9934 | static void |
9935 | dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) |
9936 | { |
9937 | DumpOptions *dopt = fout->dopt; |
9938 | PQExpBuffer q; |
9939 | PQExpBuffer delq; |
9940 | char *qnspname; |
9941 | |
9942 | /* Skip if not to be dumped */ |
9943 | if (!nspinfo->dobj.dump || dopt->dataOnly) |
9944 | return; |
9945 | |
9946 | q = createPQExpBuffer(); |
9947 | delq = createPQExpBuffer(); |
9948 | |
9949 | qnspname = pg_strdup(fmtId(nspinfo->dobj.name)); |
9950 | |
9951 | appendPQExpBuffer(delq, "DROP SCHEMA %s;\n" , qnspname); |
9952 | |
9953 | appendPQExpBuffer(q, "CREATE SCHEMA %s;\n" , qnspname); |
9954 | |
9955 | if (dopt->binary_upgrade) |
9956 | binary_upgrade_extension_member(q, &nspinfo->dobj, |
9957 | "SCHEMA" , qnspname, NULL); |
9958 | |
9959 | if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
9960 | ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, |
9961 | ARCHIVE_OPTS(.tag = nspinfo->dobj.name, |
9962 | .owner = nspinfo->rolname, |
9963 | .description = "SCHEMA" , |
9964 | .section = SECTION_PRE_DATA, |
9965 | .createStmt = q->data, |
9966 | .dropStmt = delq->data)); |
9967 | |
9968 | /* Dump Schema Comments and Security Labels */ |
9969 | if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
9970 | dumpComment(fout, "SCHEMA" , qnspname, |
9971 | NULL, nspinfo->rolname, |
9972 | nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); |
9973 | |
9974 | if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
9975 | dumpSecLabel(fout, "SCHEMA" , qnspname, |
9976 | NULL, nspinfo->rolname, |
9977 | nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); |
9978 | |
9979 | if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL) |
9980 | dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA" , |
9981 | qnspname, NULL, NULL, |
9982 | nspinfo->rolname, nspinfo->nspacl, nspinfo->rnspacl, |
9983 | nspinfo->initnspacl, nspinfo->initrnspacl); |
9984 | |
9985 | free(qnspname); |
9986 | |
9987 | destroyPQExpBuffer(q); |
9988 | destroyPQExpBuffer(delq); |
9989 | } |
9990 | |
9991 | /* |
9992 | * dumpExtension |
9993 | * writes out to fout the queries to recreate an extension |
9994 | */ |
9995 | static void |
9996 | dumpExtension(Archive *fout, ExtensionInfo *extinfo) |
9997 | { |
9998 | DumpOptions *dopt = fout->dopt; |
9999 | PQExpBuffer q; |
10000 | PQExpBuffer delq; |
10001 | char *qextname; |
10002 | |
10003 | /* Skip if not to be dumped */ |
10004 | if (!extinfo->dobj.dump || dopt->dataOnly) |
10005 | return; |
10006 | |
10007 | q = createPQExpBuffer(); |
10008 | delq = createPQExpBuffer(); |
10009 | |
10010 | qextname = pg_strdup(fmtId(extinfo->dobj.name)); |
10011 | |
10012 | appendPQExpBuffer(delq, "DROP EXTENSION %s;\n" , qextname); |
10013 | |
10014 | if (!dopt->binary_upgrade) |
10015 | { |
10016 | /* |
10017 | * In a regular dump, we simply create the extension, intentionally |
10018 | * not specifying a version, so that the destination installation's |
10019 | * default version is used. |
10020 | * |
10021 | * Use of IF NOT EXISTS here is unlike our behavior for other object |
10022 | * types; but there are various scenarios in which it's convenient to |
10023 | * manually create the desired extension before restoring, so we |
10024 | * prefer to allow it to exist already. |
10025 | */ |
10026 | appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n" , |
10027 | qextname, fmtId(extinfo->namespace)); |
10028 | } |
10029 | else |
10030 | { |
10031 | /* |
10032 | * In binary-upgrade mode, it's critical to reproduce the state of the |
10033 | * database exactly, so our procedure is to create an empty extension, |
10034 | * restore all the contained objects normally, and add them to the |
10035 | * extension one by one. This function performs just the first of |
10036 | * those steps. binary_upgrade_extension_member() takes care of |
10037 | * adding member objects as they're created. |
10038 | */ |
10039 | int i; |
10040 | int n; |
10041 | |
10042 | appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n" ); |
10043 | |
10044 | /* |
10045 | * We unconditionally create the extension, so we must drop it if it |
10046 | * exists. This could happen if the user deleted 'plpgsql' and then |
10047 | * readded it, causing its oid to be greater than g_last_builtin_oid. |
10048 | */ |
10049 | appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n" , qextname); |
10050 | |
10051 | appendPQExpBufferStr(q, |
10052 | "SELECT pg_catalog.binary_upgrade_create_empty_extension(" ); |
10053 | appendStringLiteralAH(q, extinfo->dobj.name, fout); |
10054 | appendPQExpBufferStr(q, ", " ); |
10055 | appendStringLiteralAH(q, extinfo->namespace, fout); |
10056 | appendPQExpBufferStr(q, ", " ); |
10057 | appendPQExpBuffer(q, "%s, " , extinfo->relocatable ? "true" : "false" ); |
10058 | appendStringLiteralAH(q, extinfo->extversion, fout); |
10059 | appendPQExpBufferStr(q, ", " ); |
10060 | |
10061 | /* |
10062 | * Note that we're pushing extconfig (an OID array) back into |
10063 | * pg_extension exactly as-is. This is OK because pg_class OIDs are |
10064 | * preserved in binary upgrade. |
10065 | */ |
10066 | if (strlen(extinfo->extconfig) > 2) |
10067 | appendStringLiteralAH(q, extinfo->extconfig, fout); |
10068 | else |
10069 | appendPQExpBufferStr(q, "NULL" ); |
10070 | appendPQExpBufferStr(q, ", " ); |
10071 | if (strlen(extinfo->extcondition) > 2) |
10072 | appendStringLiteralAH(q, extinfo->extcondition, fout); |
10073 | else |
10074 | appendPQExpBufferStr(q, "NULL" ); |
10075 | appendPQExpBufferStr(q, ", " ); |
10076 | appendPQExpBufferStr(q, "ARRAY[" ); |
10077 | n = 0; |
10078 | for (i = 0; i < extinfo->dobj.nDeps; i++) |
10079 | { |
10080 | DumpableObject *extobj; |
10081 | |
10082 | extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]); |
10083 | if (extobj && extobj->objType == DO_EXTENSION) |
10084 | { |
10085 | if (n++ > 0) |
10086 | appendPQExpBufferChar(q, ','); |
10087 | appendStringLiteralAH(q, extobj->name, fout); |
10088 | } |
10089 | } |
10090 | appendPQExpBufferStr(q, "]::pg_catalog.text[]" ); |
10091 | appendPQExpBufferStr(q, ");\n" ); |
10092 | } |
10093 | |
10094 | if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
10095 | ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId, |
10096 | ARCHIVE_OPTS(.tag = extinfo->dobj.name, |
10097 | .description = "EXTENSION" , |
10098 | .section = SECTION_PRE_DATA, |
10099 | .createStmt = q->data, |
10100 | .dropStmt = delq->data)); |
10101 | |
10102 | /* Dump Extension Comments and Security Labels */ |
10103 | if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
10104 | dumpComment(fout, "EXTENSION" , qextname, |
10105 | NULL, "" , |
10106 | extinfo->dobj.catId, 0, extinfo->dobj.dumpId); |
10107 | |
10108 | if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
10109 | dumpSecLabel(fout, "EXTENSION" , qextname, |
10110 | NULL, "" , |
10111 | extinfo->dobj.catId, 0, extinfo->dobj.dumpId); |
10112 | |
10113 | free(qextname); |
10114 | |
10115 | destroyPQExpBuffer(q); |
10116 | destroyPQExpBuffer(delq); |
10117 | } |
10118 | |
10119 | /* |
10120 | * dumpType |
10121 | * writes out to fout the queries to recreate a user-defined type |
10122 | */ |
10123 | static void |
10124 | dumpType(Archive *fout, TypeInfo *tyinfo) |
10125 | { |
10126 | DumpOptions *dopt = fout->dopt; |
10127 | |
10128 | /* Skip if not to be dumped */ |
10129 | if (!tyinfo->dobj.dump || dopt->dataOnly) |
10130 | return; |
10131 | |
10132 | /* Dump out in proper style */ |
10133 | if (tyinfo->typtype == TYPTYPE_BASE) |
10134 | dumpBaseType(fout, tyinfo); |
10135 | else if (tyinfo->typtype == TYPTYPE_DOMAIN) |
10136 | dumpDomain(fout, tyinfo); |
10137 | else if (tyinfo->typtype == TYPTYPE_COMPOSITE) |
10138 | dumpCompositeType(fout, tyinfo); |
10139 | else if (tyinfo->typtype == TYPTYPE_ENUM) |
10140 | dumpEnumType(fout, tyinfo); |
10141 | else if (tyinfo->typtype == TYPTYPE_RANGE) |
10142 | dumpRangeType(fout, tyinfo); |
10143 | else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined) |
10144 | dumpUndefinedType(fout, tyinfo); |
10145 | else |
10146 | pg_log_warning("typtype of data type \"%s\" appears to be invalid" , |
10147 | tyinfo->dobj.name); |
10148 | } |
10149 | |
10150 | /* |
10151 | * dumpEnumType |
10152 | * writes out to fout the queries to recreate a user-defined enum type |
10153 | */ |
10154 | static void |
10155 | dumpEnumType(Archive *fout, TypeInfo *tyinfo) |
10156 | { |
10157 | DumpOptions *dopt = fout->dopt; |
10158 | PQExpBuffer q = createPQExpBuffer(); |
10159 | PQExpBuffer delq = createPQExpBuffer(); |
10160 | PQExpBuffer query = createPQExpBuffer(); |
10161 | PGresult *res; |
10162 | int num, |
10163 | i; |
10164 | Oid enum_oid; |
10165 | char *qtypname; |
10166 | char *qualtypname; |
10167 | char *label; |
10168 | |
10169 | if (fout->remoteVersion >= 90100) |
10170 | appendPQExpBuffer(query, "SELECT oid, enumlabel " |
10171 | "FROM pg_catalog.pg_enum " |
10172 | "WHERE enumtypid = '%u'" |
10173 | "ORDER BY enumsortorder" , |
10174 | tyinfo->dobj.catId.oid); |
10175 | else |
10176 | appendPQExpBuffer(query, "SELECT oid, enumlabel " |
10177 | "FROM pg_catalog.pg_enum " |
10178 | "WHERE enumtypid = '%u'" |
10179 | "ORDER BY oid" , |
10180 | tyinfo->dobj.catId.oid); |
10181 | |
10182 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
10183 | |
10184 | num = PQntuples(res); |
10185 | |
10186 | qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); |
10187 | qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo)); |
10188 | |
10189 | /* |
10190 | * CASCADE shouldn't be required here as for normal types since the I/O |
10191 | * functions are generic and do not get dropped. |
10192 | */ |
10193 | appendPQExpBuffer(delq, "DROP TYPE %s;\n" , qualtypname); |
10194 | |
10195 | if (dopt->binary_upgrade) |
10196 | binary_upgrade_set_type_oids_by_type_oid(fout, q, |
10197 | tyinfo->dobj.catId.oid, |
10198 | false); |
10199 | |
10200 | appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (" , |
10201 | qualtypname); |
10202 | |
10203 | if (!dopt->binary_upgrade) |
10204 | { |
10205 | /* Labels with server-assigned oids */ |
10206 | for (i = 0; i < num; i++) |
10207 | { |
10208 | label = PQgetvalue(res, i, PQfnumber(res, "enumlabel" )); |
10209 | if (i > 0) |
10210 | appendPQExpBufferChar(q, ','); |
10211 | appendPQExpBufferStr(q, "\n " ); |
10212 | appendStringLiteralAH(q, label, fout); |
10213 | } |
10214 | } |
10215 | |
10216 | appendPQExpBufferStr(q, "\n);\n" ); |
10217 | |
10218 | if (dopt->binary_upgrade) |
10219 | { |
10220 | /* Labels with dump-assigned (preserved) oids */ |
10221 | for (i = 0; i < num; i++) |
10222 | { |
10223 | enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid" ))); |
10224 | label = PQgetvalue(res, i, PQfnumber(res, "enumlabel" )); |
10225 | |
10226 | if (i == 0) |
10227 | appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n" ); |
10228 | appendPQExpBuffer(q, |
10229 | "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n" , |
10230 | enum_oid); |
10231 | appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE " , qualtypname); |
10232 | appendStringLiteralAH(q, label, fout); |
10233 | appendPQExpBufferStr(q, ";\n\n" ); |
10234 | } |
10235 | } |
10236 | |
10237 | if (dopt->binary_upgrade) |
10238 | binary_upgrade_extension_member(q, &tyinfo->dobj, |
10239 | "TYPE" , qtypname, |
10240 | tyinfo->dobj.namespace->dobj.name); |
10241 | |
10242 | if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
10243 | ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, |
10244 | ARCHIVE_OPTS(.tag = tyinfo->dobj.name, |
10245 | .namespace = tyinfo->dobj.namespace->dobj.name, |
10246 | .owner = tyinfo->rolname, |
10247 | .description = "TYPE" , |
10248 | .section = SECTION_PRE_DATA, |
10249 | .createStmt = q->data, |
10250 | .dropStmt = delq->data)); |
10251 | |
10252 | /* Dump Type Comments and Security Labels */ |
10253 | if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
10254 | dumpComment(fout, "TYPE" , qtypname, |
10255 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10256 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10257 | |
10258 | if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
10259 | dumpSecLabel(fout, "TYPE" , qtypname, |
10260 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10261 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10262 | |
10263 | if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL) |
10264 | dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE" , |
10265 | qtypname, NULL, |
10266 | tyinfo->dobj.namespace->dobj.name, |
10267 | tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, |
10268 | tyinfo->inittypacl, tyinfo->initrtypacl); |
10269 | |
10270 | PQclear(res); |
10271 | destroyPQExpBuffer(q); |
10272 | destroyPQExpBuffer(delq); |
10273 | destroyPQExpBuffer(query); |
10274 | free(qtypname); |
10275 | free(qualtypname); |
10276 | } |
10277 | |
10278 | /* |
10279 | * dumpRangeType |
10280 | * writes out to fout the queries to recreate a user-defined range type |
10281 | */ |
10282 | static void |
10283 | dumpRangeType(Archive *fout, TypeInfo *tyinfo) |
10284 | { |
10285 | DumpOptions *dopt = fout->dopt; |
10286 | PQExpBuffer q = createPQExpBuffer(); |
10287 | PQExpBuffer delq = createPQExpBuffer(); |
10288 | PQExpBuffer query = createPQExpBuffer(); |
10289 | PGresult *res; |
10290 | Oid collationOid; |
10291 | char *qtypname; |
10292 | char *qualtypname; |
10293 | char *procname; |
10294 | |
10295 | appendPQExpBuffer(query, |
10296 | "SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, " |
10297 | "opc.opcname AS opcname, " |
10298 | "(SELECT nspname FROM pg_catalog.pg_namespace nsp " |
10299 | " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, " |
10300 | "opc.opcdefault, " |
10301 | "CASE WHEN rngcollation = st.typcollation THEN 0 " |
10302 | " ELSE rngcollation END AS collation, " |
10303 | "rngcanonical, rngsubdiff " |
10304 | "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, " |
10305 | " pg_catalog.pg_opclass opc " |
10306 | "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND " |
10307 | "rngtypid = '%u'" , |
10308 | tyinfo->dobj.catId.oid); |
10309 | |
10310 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
10311 | |
10312 | qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); |
10313 | qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo)); |
10314 | |
10315 | /* |
10316 | * CASCADE shouldn't be required here as for normal types since the I/O |
10317 | * functions are generic and do not get dropped. |
10318 | */ |
10319 | appendPQExpBuffer(delq, "DROP TYPE %s;\n" , qualtypname); |
10320 | |
10321 | if (dopt->binary_upgrade) |
10322 | binary_upgrade_set_type_oids_by_type_oid(fout, q, |
10323 | tyinfo->dobj.catId.oid, |
10324 | false); |
10325 | |
10326 | appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (" , |
10327 | qualtypname); |
10328 | |
10329 | appendPQExpBuffer(q, "\n subtype = %s" , |
10330 | PQgetvalue(res, 0, PQfnumber(res, "rngsubtype" ))); |
10331 | |
10332 | /* print subtype_opclass only if not default for subtype */ |
10333 | if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault" ))[0] != 't') |
10334 | { |
10335 | char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname" )); |
10336 | char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp" )); |
10337 | |
10338 | appendPQExpBuffer(q, ",\n subtype_opclass = %s." , |
10339 | fmtId(nspname)); |
10340 | appendPQExpBufferStr(q, fmtId(opcname)); |
10341 | } |
10342 | |
10343 | collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation" ))); |
10344 | if (OidIsValid(collationOid)) |
10345 | { |
10346 | CollInfo *coll = findCollationByOid(collationOid); |
10347 | |
10348 | if (coll) |
10349 | appendPQExpBuffer(q, ",\n collation = %s" , |
10350 | fmtQualifiedDumpable(coll)); |
10351 | } |
10352 | |
10353 | procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical" )); |
10354 | if (strcmp(procname, "-" ) != 0) |
10355 | appendPQExpBuffer(q, ",\n canonical = %s" , procname); |
10356 | |
10357 | procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff" )); |
10358 | if (strcmp(procname, "-" ) != 0) |
10359 | appendPQExpBuffer(q, ",\n subtype_diff = %s" , procname); |
10360 | |
10361 | appendPQExpBufferStr(q, "\n);\n" ); |
10362 | |
10363 | if (dopt->binary_upgrade) |
10364 | binary_upgrade_extension_member(q, &tyinfo->dobj, |
10365 | "TYPE" , qtypname, |
10366 | tyinfo->dobj.namespace->dobj.name); |
10367 | |
10368 | if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
10369 | ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, |
10370 | ARCHIVE_OPTS(.tag = tyinfo->dobj.name, |
10371 | .namespace = tyinfo->dobj.namespace->dobj.name, |
10372 | .owner = tyinfo->rolname, |
10373 | .description = "TYPE" , |
10374 | .section = SECTION_PRE_DATA, |
10375 | .createStmt = q->data, |
10376 | .dropStmt = delq->data)); |
10377 | |
10378 | /* Dump Type Comments and Security Labels */ |
10379 | if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
10380 | dumpComment(fout, "TYPE" , qtypname, |
10381 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10382 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10383 | |
10384 | if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
10385 | dumpSecLabel(fout, "TYPE" , qtypname, |
10386 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10387 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10388 | |
10389 | if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL) |
10390 | dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE" , |
10391 | qtypname, NULL, |
10392 | tyinfo->dobj.namespace->dobj.name, |
10393 | tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, |
10394 | tyinfo->inittypacl, tyinfo->initrtypacl); |
10395 | |
10396 | PQclear(res); |
10397 | destroyPQExpBuffer(q); |
10398 | destroyPQExpBuffer(delq); |
10399 | destroyPQExpBuffer(query); |
10400 | free(qtypname); |
10401 | free(qualtypname); |
10402 | } |
10403 | |
10404 | /* |
10405 | * dumpUndefinedType |
10406 | * writes out to fout the queries to recreate a !typisdefined type |
10407 | * |
10408 | * This is a shell type, but we use different terminology to distinguish |
10409 | * this case from where we have to emit a shell type definition to break |
10410 | * circular dependencies. An undefined type shouldn't ever have anything |
10411 | * depending on it. |
10412 | */ |
10413 | static void |
10414 | dumpUndefinedType(Archive *fout, TypeInfo *tyinfo) |
10415 | { |
10416 | DumpOptions *dopt = fout->dopt; |
10417 | PQExpBuffer q = createPQExpBuffer(); |
10418 | PQExpBuffer delq = createPQExpBuffer(); |
10419 | char *qtypname; |
10420 | char *qualtypname; |
10421 | |
10422 | qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); |
10423 | qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo)); |
10424 | |
10425 | appendPQExpBuffer(delq, "DROP TYPE %s;\n" , qualtypname); |
10426 | |
10427 | if (dopt->binary_upgrade) |
10428 | binary_upgrade_set_type_oids_by_type_oid(fout, q, |
10429 | tyinfo->dobj.catId.oid, |
10430 | false); |
10431 | |
10432 | appendPQExpBuffer(q, "CREATE TYPE %s;\n" , |
10433 | qualtypname); |
10434 | |
10435 | if (dopt->binary_upgrade) |
10436 | binary_upgrade_extension_member(q, &tyinfo->dobj, |
10437 | "TYPE" , qtypname, |
10438 | tyinfo->dobj.namespace->dobj.name); |
10439 | |
10440 | if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
10441 | ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, |
10442 | ARCHIVE_OPTS(.tag = tyinfo->dobj.name, |
10443 | .namespace = tyinfo->dobj.namespace->dobj.name, |
10444 | .owner = tyinfo->rolname, |
10445 | .description = "TYPE" , |
10446 | .section = SECTION_PRE_DATA, |
10447 | .createStmt = q->data, |
10448 | .dropStmt = delq->data)); |
10449 | |
10450 | /* Dump Type Comments and Security Labels */ |
10451 | if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
10452 | dumpComment(fout, "TYPE" , qtypname, |
10453 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10454 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10455 | |
10456 | if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
10457 | dumpSecLabel(fout, "TYPE" , qtypname, |
10458 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10459 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10460 | |
10461 | if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL) |
10462 | dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE" , |
10463 | qtypname, NULL, |
10464 | tyinfo->dobj.namespace->dobj.name, |
10465 | tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, |
10466 | tyinfo->inittypacl, tyinfo->initrtypacl); |
10467 | |
10468 | destroyPQExpBuffer(q); |
10469 | destroyPQExpBuffer(delq); |
10470 | free(qtypname); |
10471 | free(qualtypname); |
10472 | } |
10473 | |
10474 | /* |
10475 | * dumpBaseType |
10476 | * writes out to fout the queries to recreate a user-defined base type |
10477 | */ |
10478 | static void |
10479 | dumpBaseType(Archive *fout, TypeInfo *tyinfo) |
10480 | { |
10481 | DumpOptions *dopt = fout->dopt; |
10482 | PQExpBuffer q = createPQExpBuffer(); |
10483 | PQExpBuffer delq = createPQExpBuffer(); |
10484 | PQExpBuffer query = createPQExpBuffer(); |
10485 | PGresult *res; |
10486 | char *qtypname; |
10487 | char *qualtypname; |
10488 | char *typlen; |
10489 | char *typinput; |
10490 | char *typoutput; |
10491 | char *typreceive; |
10492 | char *typsend; |
10493 | char *typmodin; |
10494 | char *typmodout; |
10495 | char *typanalyze; |
10496 | Oid typreceiveoid; |
10497 | Oid typsendoid; |
10498 | Oid typmodinoid; |
10499 | Oid typmodoutoid; |
10500 | Oid typanalyzeoid; |
10501 | char *typcategory; |
10502 | char *typispreferred; |
10503 | char *typdelim; |
10504 | char *typbyval; |
10505 | char *typalign; |
10506 | char *typstorage; |
10507 | char *typcollatable; |
10508 | char *typdefault; |
10509 | bool typdefault_is_literal = false; |
10510 | |
10511 | /* Fetch type-specific details */ |
10512 | if (fout->remoteVersion >= 90100) |
10513 | { |
10514 | appendPQExpBuffer(query, "SELECT typlen, " |
10515 | "typinput, typoutput, typreceive, typsend, " |
10516 | "typmodin, typmodout, typanalyze, " |
10517 | "typreceive::pg_catalog.oid AS typreceiveoid, " |
10518 | "typsend::pg_catalog.oid AS typsendoid, " |
10519 | "typmodin::pg_catalog.oid AS typmodinoid, " |
10520 | "typmodout::pg_catalog.oid AS typmodoutoid, " |
10521 | "typanalyze::pg_catalog.oid AS typanalyzeoid, " |
10522 | "typcategory, typispreferred, " |
10523 | "typdelim, typbyval, typalign, typstorage, " |
10524 | "(typcollation <> 0) AS typcollatable, " |
10525 | "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault " |
10526 | "FROM pg_catalog.pg_type " |
10527 | "WHERE oid = '%u'::pg_catalog.oid" , |
10528 | tyinfo->dobj.catId.oid); |
10529 | } |
10530 | else if (fout->remoteVersion >= 80400) |
10531 | { |
10532 | appendPQExpBuffer(query, "SELECT typlen, " |
10533 | "typinput, typoutput, typreceive, typsend, " |
10534 | "typmodin, typmodout, typanalyze, " |
10535 | "typreceive::pg_catalog.oid AS typreceiveoid, " |
10536 | "typsend::pg_catalog.oid AS typsendoid, " |
10537 | "typmodin::pg_catalog.oid AS typmodinoid, " |
10538 | "typmodout::pg_catalog.oid AS typmodoutoid, " |
10539 | "typanalyze::pg_catalog.oid AS typanalyzeoid, " |
10540 | "typcategory, typispreferred, " |
10541 | "typdelim, typbyval, typalign, typstorage, " |
10542 | "false AS typcollatable, " |
10543 | "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault " |
10544 | "FROM pg_catalog.pg_type " |
10545 | "WHERE oid = '%u'::pg_catalog.oid" , |
10546 | tyinfo->dobj.catId.oid); |
10547 | } |
10548 | else if (fout->remoteVersion >= 80300) |
10549 | { |
10550 | /* Before 8.4, pg_get_expr does not allow 0 for its second arg */ |
10551 | appendPQExpBuffer(query, "SELECT typlen, " |
10552 | "typinput, typoutput, typreceive, typsend, " |
10553 | "typmodin, typmodout, typanalyze, " |
10554 | "typreceive::pg_catalog.oid AS typreceiveoid, " |
10555 | "typsend::pg_catalog.oid AS typsendoid, " |
10556 | "typmodin::pg_catalog.oid AS typmodinoid, " |
10557 | "typmodout::pg_catalog.oid AS typmodoutoid, " |
10558 | "typanalyze::pg_catalog.oid AS typanalyzeoid, " |
10559 | "'U' AS typcategory, false AS typispreferred, " |
10560 | "typdelim, typbyval, typalign, typstorage, " |
10561 | "false AS typcollatable, " |
10562 | "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault " |
10563 | "FROM pg_catalog.pg_type " |
10564 | "WHERE oid = '%u'::pg_catalog.oid" , |
10565 | tyinfo->dobj.catId.oid); |
10566 | } |
10567 | else |
10568 | { |
10569 | appendPQExpBuffer(query, "SELECT typlen, " |
10570 | "typinput, typoutput, typreceive, typsend, " |
10571 | "'-' AS typmodin, '-' AS typmodout, " |
10572 | "typanalyze, " |
10573 | "typreceive::pg_catalog.oid AS typreceiveoid, " |
10574 | "typsend::pg_catalog.oid AS typsendoid, " |
10575 | "0 AS typmodinoid, 0 AS typmodoutoid, " |
10576 | "typanalyze::pg_catalog.oid AS typanalyzeoid, " |
10577 | "'U' AS typcategory, false AS typispreferred, " |
10578 | "typdelim, typbyval, typalign, typstorage, " |
10579 | "false AS typcollatable, " |
10580 | "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault " |
10581 | "FROM pg_catalog.pg_type " |
10582 | "WHERE oid = '%u'::pg_catalog.oid" , |
10583 | tyinfo->dobj.catId.oid); |
10584 | } |
10585 | |
10586 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
10587 | |
10588 | typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen" )); |
10589 | typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput" )); |
10590 | typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput" )); |
10591 | typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive" )); |
10592 | typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend" )); |
10593 | typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin" )); |
10594 | typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout" )); |
10595 | typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze" )); |
10596 | typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid" ))); |
10597 | typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid" ))); |
10598 | typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid" ))); |
10599 | typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid" ))); |
10600 | typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid" ))); |
10601 | typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory" )); |
10602 | typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred" )); |
10603 | typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim" )); |
10604 | typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval" )); |
10605 | typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign" )); |
10606 | typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage" )); |
10607 | typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable" )); |
10608 | if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin" ))) |
10609 | typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin" )); |
10610 | else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault" ))) |
10611 | { |
10612 | typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault" )); |
10613 | typdefault_is_literal = true; /* it needs quotes */ |
10614 | } |
10615 | else |
10616 | typdefault = NULL; |
10617 | |
10618 | qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); |
10619 | qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo)); |
10620 | |
10621 | /* |
10622 | * The reason we include CASCADE is that the circular dependency between |
10623 | * the type and its I/O functions makes it impossible to drop the type any |
10624 | * other way. |
10625 | */ |
10626 | appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n" , qualtypname); |
10627 | |
10628 | /* |
10629 | * We might already have a shell type, but setting pg_type_oid is |
10630 | * harmless, and in any case we'd better set the array type OID. |
10631 | */ |
10632 | if (dopt->binary_upgrade) |
10633 | binary_upgrade_set_type_oids_by_type_oid(fout, q, |
10634 | tyinfo->dobj.catId.oid, |
10635 | false); |
10636 | |
10637 | appendPQExpBuffer(q, |
10638 | "CREATE TYPE %s (\n" |
10639 | " INTERNALLENGTH = %s" , |
10640 | qualtypname, |
10641 | (strcmp(typlen, "-1" ) == 0) ? "variable" : typlen); |
10642 | |
10643 | /* regproc result is sufficiently quoted already */ |
10644 | appendPQExpBuffer(q, ",\n INPUT = %s" , typinput); |
10645 | appendPQExpBuffer(q, ",\n OUTPUT = %s" , typoutput); |
10646 | if (OidIsValid(typreceiveoid)) |
10647 | appendPQExpBuffer(q, ",\n RECEIVE = %s" , typreceive); |
10648 | if (OidIsValid(typsendoid)) |
10649 | appendPQExpBuffer(q, ",\n SEND = %s" , typsend); |
10650 | if (OidIsValid(typmodinoid)) |
10651 | appendPQExpBuffer(q, ",\n TYPMOD_IN = %s" , typmodin); |
10652 | if (OidIsValid(typmodoutoid)) |
10653 | appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s" , typmodout); |
10654 | if (OidIsValid(typanalyzeoid)) |
10655 | appendPQExpBuffer(q, ",\n ANALYZE = %s" , typanalyze); |
10656 | |
10657 | if (strcmp(typcollatable, "t" ) == 0) |
10658 | appendPQExpBufferStr(q, ",\n COLLATABLE = true" ); |
10659 | |
10660 | if (typdefault != NULL) |
10661 | { |
10662 | appendPQExpBufferStr(q, ",\n DEFAULT = " ); |
10663 | if (typdefault_is_literal) |
10664 | appendStringLiteralAH(q, typdefault, fout); |
10665 | else |
10666 | appendPQExpBufferStr(q, typdefault); |
10667 | } |
10668 | |
10669 | if (OidIsValid(tyinfo->typelem)) |
10670 | { |
10671 | char *elemType; |
10672 | |
10673 | elemType = getFormattedTypeName(fout, tyinfo->typelem, zeroAsOpaque); |
10674 | appendPQExpBuffer(q, ",\n ELEMENT = %s" , elemType); |
10675 | free(elemType); |
10676 | } |
10677 | |
10678 | if (strcmp(typcategory, "U" ) != 0) |
10679 | { |
10680 | appendPQExpBufferStr(q, ",\n CATEGORY = " ); |
10681 | appendStringLiteralAH(q, typcategory, fout); |
10682 | } |
10683 | |
10684 | if (strcmp(typispreferred, "t" ) == 0) |
10685 | appendPQExpBufferStr(q, ",\n PREFERRED = true" ); |
10686 | |
10687 | if (typdelim && strcmp(typdelim, "," ) != 0) |
10688 | { |
10689 | appendPQExpBufferStr(q, ",\n DELIMITER = " ); |
10690 | appendStringLiteralAH(q, typdelim, fout); |
10691 | } |
10692 | |
10693 | if (strcmp(typalign, "c" ) == 0) |
10694 | appendPQExpBufferStr(q, ",\n ALIGNMENT = char" ); |
10695 | else if (strcmp(typalign, "s" ) == 0) |
10696 | appendPQExpBufferStr(q, ",\n ALIGNMENT = int2" ); |
10697 | else if (strcmp(typalign, "i" ) == 0) |
10698 | appendPQExpBufferStr(q, ",\n ALIGNMENT = int4" ); |
10699 | else if (strcmp(typalign, "d" ) == 0) |
10700 | appendPQExpBufferStr(q, ",\n ALIGNMENT = double" ); |
10701 | |
10702 | if (strcmp(typstorage, "p" ) == 0) |
10703 | appendPQExpBufferStr(q, ",\n STORAGE = plain" ); |
10704 | else if (strcmp(typstorage, "e" ) == 0) |
10705 | appendPQExpBufferStr(q, ",\n STORAGE = external" ); |
10706 | else if (strcmp(typstorage, "x" ) == 0) |
10707 | appendPQExpBufferStr(q, ",\n STORAGE = extended" ); |
10708 | else if (strcmp(typstorage, "m" ) == 0) |
10709 | appendPQExpBufferStr(q, ",\n STORAGE = main" ); |
10710 | |
10711 | if (strcmp(typbyval, "t" ) == 0) |
10712 | appendPQExpBufferStr(q, ",\n PASSEDBYVALUE" ); |
10713 | |
10714 | appendPQExpBufferStr(q, "\n);\n" ); |
10715 | |
10716 | if (dopt->binary_upgrade) |
10717 | binary_upgrade_extension_member(q, &tyinfo->dobj, |
10718 | "TYPE" , qtypname, |
10719 | tyinfo->dobj.namespace->dobj.name); |
10720 | |
10721 | if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
10722 | ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, |
10723 | ARCHIVE_OPTS(.tag = tyinfo->dobj.name, |
10724 | .namespace = tyinfo->dobj.namespace->dobj.name, |
10725 | .owner = tyinfo->rolname, |
10726 | .description = "TYPE" , |
10727 | .section = SECTION_PRE_DATA, |
10728 | .createStmt = q->data, |
10729 | .dropStmt = delq->data)); |
10730 | |
10731 | /* Dump Type Comments and Security Labels */ |
10732 | if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
10733 | dumpComment(fout, "TYPE" , qtypname, |
10734 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10735 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10736 | |
10737 | if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
10738 | dumpSecLabel(fout, "TYPE" , qtypname, |
10739 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10740 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10741 | |
10742 | if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL) |
10743 | dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE" , |
10744 | qtypname, NULL, |
10745 | tyinfo->dobj.namespace->dobj.name, |
10746 | tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, |
10747 | tyinfo->inittypacl, tyinfo->initrtypacl); |
10748 | |
10749 | PQclear(res); |
10750 | destroyPQExpBuffer(q); |
10751 | destroyPQExpBuffer(delq); |
10752 | destroyPQExpBuffer(query); |
10753 | free(qtypname); |
10754 | free(qualtypname); |
10755 | } |
10756 | |
10757 | /* |
10758 | * dumpDomain |
10759 | * writes out to fout the queries to recreate a user-defined domain |
10760 | */ |
10761 | static void |
10762 | dumpDomain(Archive *fout, TypeInfo *tyinfo) |
10763 | { |
10764 | DumpOptions *dopt = fout->dopt; |
10765 | PQExpBuffer q = createPQExpBuffer(); |
10766 | PQExpBuffer delq = createPQExpBuffer(); |
10767 | PQExpBuffer query = createPQExpBuffer(); |
10768 | PGresult *res; |
10769 | int i; |
10770 | char *qtypname; |
10771 | char *qualtypname; |
10772 | char *typnotnull; |
10773 | char *typdefn; |
10774 | char *typdefault; |
10775 | Oid typcollation; |
10776 | bool typdefault_is_literal = false; |
10777 | |
10778 | /* Fetch domain specific details */ |
10779 | if (fout->remoteVersion >= 90100) |
10780 | { |
10781 | /* typcollation is new in 9.1 */ |
10782 | appendPQExpBuffer(query, "SELECT t.typnotnull, " |
10783 | "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, " |
10784 | "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " |
10785 | "t.typdefault, " |
10786 | "CASE WHEN t.typcollation <> u.typcollation " |
10787 | "THEN t.typcollation ELSE 0 END AS typcollation " |
10788 | "FROM pg_catalog.pg_type t " |
10789 | "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) " |
10790 | "WHERE t.oid = '%u'::pg_catalog.oid" , |
10791 | tyinfo->dobj.catId.oid); |
10792 | } |
10793 | else |
10794 | { |
10795 | appendPQExpBuffer(query, "SELECT typnotnull, " |
10796 | "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, " |
10797 | "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " |
10798 | "typdefault, 0 AS typcollation " |
10799 | "FROM pg_catalog.pg_type " |
10800 | "WHERE oid = '%u'::pg_catalog.oid" , |
10801 | tyinfo->dobj.catId.oid); |
10802 | } |
10803 | |
10804 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
10805 | |
10806 | typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull" )); |
10807 | typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn" )); |
10808 | if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin" ))) |
10809 | typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin" )); |
10810 | else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault" ))) |
10811 | { |
10812 | typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault" )); |
10813 | typdefault_is_literal = true; /* it needs quotes */ |
10814 | } |
10815 | else |
10816 | typdefault = NULL; |
10817 | typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation" ))); |
10818 | |
10819 | if (dopt->binary_upgrade) |
10820 | binary_upgrade_set_type_oids_by_type_oid(fout, q, |
10821 | tyinfo->dobj.catId.oid, |
10822 | true); /* force array type */ |
10823 | |
10824 | qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); |
10825 | qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo)); |
10826 | |
10827 | appendPQExpBuffer(q, |
10828 | "CREATE DOMAIN %s AS %s" , |
10829 | qualtypname, |
10830 | typdefn); |
10831 | |
10832 | /* Print collation only if different from base type's collation */ |
10833 | if (OidIsValid(typcollation)) |
10834 | { |
10835 | CollInfo *coll; |
10836 | |
10837 | coll = findCollationByOid(typcollation); |
10838 | if (coll) |
10839 | appendPQExpBuffer(q, " COLLATE %s" , fmtQualifiedDumpable(coll)); |
10840 | } |
10841 | |
10842 | if (typnotnull[0] == 't') |
10843 | appendPQExpBufferStr(q, " NOT NULL" ); |
10844 | |
10845 | if (typdefault != NULL) |
10846 | { |
10847 | appendPQExpBufferStr(q, " DEFAULT " ); |
10848 | if (typdefault_is_literal) |
10849 | appendStringLiteralAH(q, typdefault, fout); |
10850 | else |
10851 | appendPQExpBufferStr(q, typdefault); |
10852 | } |
10853 | |
10854 | PQclear(res); |
10855 | |
10856 | /* |
10857 | * Add any CHECK constraints for the domain |
10858 | */ |
10859 | for (i = 0; i < tyinfo->nDomChecks; i++) |
10860 | { |
10861 | ConstraintInfo *domcheck = &(tyinfo->domChecks[i]); |
10862 | |
10863 | if (!domcheck->separate) |
10864 | appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s" , |
10865 | fmtId(domcheck->dobj.name), domcheck->condef); |
10866 | } |
10867 | |
10868 | appendPQExpBufferStr(q, ";\n" ); |
10869 | |
10870 | appendPQExpBuffer(delq, "DROP DOMAIN %s;\n" , qualtypname); |
10871 | |
10872 | if (dopt->binary_upgrade) |
10873 | binary_upgrade_extension_member(q, &tyinfo->dobj, |
10874 | "DOMAIN" , qtypname, |
10875 | tyinfo->dobj.namespace->dobj.name); |
10876 | |
10877 | if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
10878 | ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, |
10879 | ARCHIVE_OPTS(.tag = tyinfo->dobj.name, |
10880 | .namespace = tyinfo->dobj.namespace->dobj.name, |
10881 | .owner = tyinfo->rolname, |
10882 | .description = "DOMAIN" , |
10883 | .section = SECTION_PRE_DATA, |
10884 | .createStmt = q->data, |
10885 | .dropStmt = delq->data)); |
10886 | |
10887 | /* Dump Domain Comments and Security Labels */ |
10888 | if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
10889 | dumpComment(fout, "DOMAIN" , qtypname, |
10890 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10891 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10892 | |
10893 | if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
10894 | dumpSecLabel(fout, "DOMAIN" , qtypname, |
10895 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
10896 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
10897 | |
10898 | if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL) |
10899 | dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE" , |
10900 | qtypname, NULL, |
10901 | tyinfo->dobj.namespace->dobj.name, |
10902 | tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, |
10903 | tyinfo->inittypacl, tyinfo->initrtypacl); |
10904 | |
10905 | /* Dump any per-constraint comments */ |
10906 | for (i = 0; i < tyinfo->nDomChecks; i++) |
10907 | { |
10908 | ConstraintInfo *domcheck = &(tyinfo->domChecks[i]); |
10909 | PQExpBuffer conprefix = createPQExpBuffer(); |
10910 | |
10911 | appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN" , |
10912 | fmtId(domcheck->dobj.name)); |
10913 | |
10914 | if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
10915 | dumpComment(fout, conprefix->data, qtypname, |
10916 | tyinfo->dobj.namespace->dobj.name, |
10917 | tyinfo->rolname, |
10918 | domcheck->dobj.catId, 0, tyinfo->dobj.dumpId); |
10919 | |
10920 | destroyPQExpBuffer(conprefix); |
10921 | } |
10922 | |
10923 | destroyPQExpBuffer(q); |
10924 | destroyPQExpBuffer(delq); |
10925 | destroyPQExpBuffer(query); |
10926 | free(qtypname); |
10927 | free(qualtypname); |
10928 | } |
10929 | |
10930 | /* |
10931 | * dumpCompositeType |
10932 | * writes out to fout the queries to recreate a user-defined stand-alone |
10933 | * composite type |
10934 | */ |
10935 | static void |
10936 | dumpCompositeType(Archive *fout, TypeInfo *tyinfo) |
10937 | { |
10938 | DumpOptions *dopt = fout->dopt; |
10939 | PQExpBuffer q = createPQExpBuffer(); |
10940 | PQExpBuffer dropped = createPQExpBuffer(); |
10941 | PQExpBuffer delq = createPQExpBuffer(); |
10942 | PQExpBuffer query = createPQExpBuffer(); |
10943 | PGresult *res; |
10944 | char *qtypname; |
10945 | char *qualtypname; |
10946 | int ntups; |
10947 | int i_attname; |
10948 | int i_atttypdefn; |
10949 | int i_attlen; |
10950 | int i_attalign; |
10951 | int i_attisdropped; |
10952 | int i_attcollation; |
10953 | int i; |
10954 | int actual_atts; |
10955 | |
10956 | /* Fetch type specific details */ |
10957 | if (fout->remoteVersion >= 90100) |
10958 | { |
10959 | /* |
10960 | * attcollation is new in 9.1. Since we only want to dump COLLATE |
10961 | * clauses for attributes whose collation is different from their |
10962 | * type's default, we use a CASE here to suppress uninteresting |
10963 | * attcollations cheaply. atttypid will be 0 for dropped columns; |
10964 | * collation does not matter for those. |
10965 | */ |
10966 | appendPQExpBuffer(query, "SELECT a.attname, " |
10967 | "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, " |
10968 | "a.attlen, a.attalign, a.attisdropped, " |
10969 | "CASE WHEN a.attcollation <> at.typcollation " |
10970 | "THEN a.attcollation ELSE 0 END AS attcollation " |
10971 | "FROM pg_catalog.pg_type ct " |
10972 | "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid " |
10973 | "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid " |
10974 | "WHERE ct.oid = '%u'::pg_catalog.oid " |
10975 | "ORDER BY a.attnum " , |
10976 | tyinfo->dobj.catId.oid); |
10977 | } |
10978 | else |
10979 | { |
10980 | /* |
10981 | * Since ALTER TYPE could not drop columns until 9.1, attisdropped |
10982 | * should always be false. |
10983 | */ |
10984 | appendPQExpBuffer(query, "SELECT a.attname, " |
10985 | "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, " |
10986 | "a.attlen, a.attalign, a.attisdropped, " |
10987 | "0 AS attcollation " |
10988 | "FROM pg_catalog.pg_type ct, pg_catalog.pg_attribute a " |
10989 | "WHERE ct.oid = '%u'::pg_catalog.oid " |
10990 | "AND a.attrelid = ct.typrelid " |
10991 | "ORDER BY a.attnum " , |
10992 | tyinfo->dobj.catId.oid); |
10993 | } |
10994 | |
10995 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
10996 | |
10997 | ntups = PQntuples(res); |
10998 | |
10999 | i_attname = PQfnumber(res, "attname" ); |
11000 | i_atttypdefn = PQfnumber(res, "atttypdefn" ); |
11001 | i_attlen = PQfnumber(res, "attlen" ); |
11002 | i_attalign = PQfnumber(res, "attalign" ); |
11003 | i_attisdropped = PQfnumber(res, "attisdropped" ); |
11004 | i_attcollation = PQfnumber(res, "attcollation" ); |
11005 | |
11006 | if (dopt->binary_upgrade) |
11007 | { |
11008 | binary_upgrade_set_type_oids_by_type_oid(fout, q, |
11009 | tyinfo->dobj.catId.oid, |
11010 | false); |
11011 | binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false); |
11012 | } |
11013 | |
11014 | qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); |
11015 | qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo)); |
11016 | |
11017 | appendPQExpBuffer(q, "CREATE TYPE %s AS (" , |
11018 | qualtypname); |
11019 | |
11020 | actual_atts = 0; |
11021 | for (i = 0; i < ntups; i++) |
11022 | { |
11023 | char *attname; |
11024 | char *atttypdefn; |
11025 | char *attlen; |
11026 | char *attalign; |
11027 | bool attisdropped; |
11028 | Oid attcollation; |
11029 | |
11030 | attname = PQgetvalue(res, i, i_attname); |
11031 | atttypdefn = PQgetvalue(res, i, i_atttypdefn); |
11032 | attlen = PQgetvalue(res, i, i_attlen); |
11033 | attalign = PQgetvalue(res, i, i_attalign); |
11034 | attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't'); |
11035 | attcollation = atooid(PQgetvalue(res, i, i_attcollation)); |
11036 | |
11037 | if (attisdropped && !dopt->binary_upgrade) |
11038 | continue; |
11039 | |
11040 | /* Format properly if not first attr */ |
11041 | if (actual_atts++ > 0) |
11042 | appendPQExpBufferChar(q, ','); |
11043 | appendPQExpBufferStr(q, "\n\t" ); |
11044 | |
11045 | if (!attisdropped) |
11046 | { |
11047 | appendPQExpBuffer(q, "%s %s" , fmtId(attname), atttypdefn); |
11048 | |
11049 | /* Add collation if not default for the column type */ |
11050 | if (OidIsValid(attcollation)) |
11051 | { |
11052 | CollInfo *coll; |
11053 | |
11054 | coll = findCollationByOid(attcollation); |
11055 | if (coll) |
11056 | appendPQExpBuffer(q, " COLLATE %s" , |
11057 | fmtQualifiedDumpable(coll)); |
11058 | } |
11059 | } |
11060 | else |
11061 | { |
11062 | /* |
11063 | * This is a dropped attribute and we're in binary_upgrade mode. |
11064 | * Insert a placeholder for it in the CREATE TYPE command, and set |
11065 | * length and alignment with direct UPDATE to the catalogs |
11066 | * afterwards. See similar code in dumpTableSchema(). |
11067 | */ |
11068 | appendPQExpBuffer(q, "%s INTEGER /* dummy */" , fmtId(attname)); |
11069 | |
11070 | /* stash separately for insertion after the CREATE TYPE */ |
11071 | appendPQExpBufferStr(dropped, |
11072 | "\n-- For binary upgrade, recreate dropped column.\n" ); |
11073 | appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n" |
11074 | "SET attlen = %s, " |
11075 | "attalign = '%s', attbyval = false\n" |
11076 | "WHERE attname = " , attlen, attalign); |
11077 | appendStringLiteralAH(dropped, attname, fout); |
11078 | appendPQExpBufferStr(dropped, "\n AND attrelid = " ); |
11079 | appendStringLiteralAH(dropped, qualtypname, fout); |
11080 | appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n" ); |
11081 | |
11082 | appendPQExpBuffer(dropped, "ALTER TYPE %s " , |
11083 | qualtypname); |
11084 | appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n" , |
11085 | fmtId(attname)); |
11086 | } |
11087 | } |
11088 | appendPQExpBufferStr(q, "\n);\n" ); |
11089 | appendPQExpBufferStr(q, dropped->data); |
11090 | |
11091 | appendPQExpBuffer(delq, "DROP TYPE %s;\n" , qualtypname); |
11092 | |
11093 | if (dopt->binary_upgrade) |
11094 | binary_upgrade_extension_member(q, &tyinfo->dobj, |
11095 | "TYPE" , qtypname, |
11096 | tyinfo->dobj.namespace->dobj.name); |
11097 | |
11098 | if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
11099 | ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, |
11100 | ARCHIVE_OPTS(.tag = tyinfo->dobj.name, |
11101 | .namespace = tyinfo->dobj.namespace->dobj.name, |
11102 | .owner = tyinfo->rolname, |
11103 | .description = "TYPE" , |
11104 | .section = SECTION_PRE_DATA, |
11105 | .createStmt = q->data, |
11106 | .dropStmt = delq->data)); |
11107 | |
11108 | |
11109 | /* Dump Type Comments and Security Labels */ |
11110 | if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
11111 | dumpComment(fout, "TYPE" , qtypname, |
11112 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
11113 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
11114 | |
11115 | if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
11116 | dumpSecLabel(fout, "TYPE" , qtypname, |
11117 | tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, |
11118 | tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); |
11119 | |
11120 | if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL) |
11121 | dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE" , |
11122 | qtypname, NULL, |
11123 | tyinfo->dobj.namespace->dobj.name, |
11124 | tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl, |
11125 | tyinfo->inittypacl, tyinfo->initrtypacl); |
11126 | |
11127 | PQclear(res); |
11128 | destroyPQExpBuffer(q); |
11129 | destroyPQExpBuffer(dropped); |
11130 | destroyPQExpBuffer(delq); |
11131 | destroyPQExpBuffer(query); |
11132 | free(qtypname); |
11133 | free(qualtypname); |
11134 | |
11135 | /* Dump any per-column comments */ |
11136 | if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
11137 | dumpCompositeTypeColComments(fout, tyinfo); |
11138 | } |
11139 | |
11140 | /* |
11141 | * dumpCompositeTypeColComments |
11142 | * writes out to fout the queries to recreate comments on the columns of |
11143 | * a user-defined stand-alone composite type |
11144 | */ |
11145 | static void |
11146 | (Archive *fout, TypeInfo *tyinfo) |
11147 | { |
11148 | CommentItem *; |
11149 | int ; |
11150 | PGresult *res; |
11151 | PQExpBuffer query; |
11152 | PQExpBuffer target; |
11153 | Oid pgClassOid; |
11154 | int i; |
11155 | int ntups; |
11156 | int i_attname; |
11157 | int i_attnum; |
11158 | |
11159 | /* do nothing, if --no-comments is supplied */ |
11160 | if (fout->dopt->no_comments) |
11161 | return; |
11162 | |
11163 | query = createPQExpBuffer(); |
11164 | |
11165 | appendPQExpBuffer(query, |
11166 | "SELECT c.tableoid, a.attname, a.attnum " |
11167 | "FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a " |
11168 | "WHERE c.oid = '%u' AND c.oid = a.attrelid " |
11169 | " AND NOT a.attisdropped " |
11170 | "ORDER BY a.attnum " , |
11171 | tyinfo->typrelid); |
11172 | |
11173 | /* Fetch column attnames */ |
11174 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
11175 | |
11176 | ntups = PQntuples(res); |
11177 | if (ntups < 1) |
11178 | { |
11179 | PQclear(res); |
11180 | destroyPQExpBuffer(query); |
11181 | return; |
11182 | } |
11183 | |
11184 | pgClassOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "tableoid" ))); |
11185 | |
11186 | /* Search for comments associated with type's pg_class OID */ |
11187 | ncomments = findComments(fout, |
11188 | pgClassOid, |
11189 | tyinfo->typrelid, |
11190 | &comments); |
11191 | |
11192 | /* If no comments exist, we're done */ |
11193 | if (ncomments <= 0) |
11194 | { |
11195 | PQclear(res); |
11196 | destroyPQExpBuffer(query); |
11197 | return; |
11198 | } |
11199 | |
11200 | /* Build COMMENT ON statements */ |
11201 | target = createPQExpBuffer(); |
11202 | |
11203 | i_attnum = PQfnumber(res, "attnum" ); |
11204 | i_attname = PQfnumber(res, "attname" ); |
11205 | while (ncomments > 0) |
11206 | { |
11207 | const char *attname; |
11208 | |
11209 | attname = NULL; |
11210 | for (i = 0; i < ntups; i++) |
11211 | { |
11212 | if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid) |
11213 | { |
11214 | attname = PQgetvalue(res, i, i_attname); |
11215 | break; |
11216 | } |
11217 | } |
11218 | if (attname) /* just in case we don't find it */ |
11219 | { |
11220 | const char *descr = comments->descr; |
11221 | |
11222 | resetPQExpBuffer(target); |
11223 | appendPQExpBuffer(target, "COLUMN %s." , |
11224 | fmtId(tyinfo->dobj.name)); |
11225 | appendPQExpBufferStr(target, fmtId(attname)); |
11226 | |
11227 | resetPQExpBuffer(query); |
11228 | appendPQExpBuffer(query, "COMMENT ON COLUMN %s." , |
11229 | fmtQualifiedDumpable(tyinfo)); |
11230 | appendPQExpBuffer(query, "%s IS " , fmtId(attname)); |
11231 | appendStringLiteralAH(query, descr, fout); |
11232 | appendPQExpBufferStr(query, ";\n" ); |
11233 | |
11234 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
11235 | ARCHIVE_OPTS(.tag = target->data, |
11236 | .namespace = tyinfo->dobj.namespace->dobj.name, |
11237 | .owner = tyinfo->rolname, |
11238 | .description = "COMMENT" , |
11239 | .section = SECTION_NONE, |
11240 | .createStmt = query->data, |
11241 | .deps = &(tyinfo->dobj.dumpId), |
11242 | .nDeps = 1)); |
11243 | } |
11244 | |
11245 | comments++; |
11246 | ncomments--; |
11247 | } |
11248 | |
11249 | PQclear(res); |
11250 | destroyPQExpBuffer(query); |
11251 | destroyPQExpBuffer(target); |
11252 | } |
11253 | |
11254 | /* |
11255 | * dumpShellType |
11256 | * writes out to fout the queries to create a shell type |
11257 | * |
11258 | * We dump a shell definition in advance of the I/O functions for the type. |
11259 | */ |
11260 | static void |
11261 | dumpShellType(Archive *fout, ShellTypeInfo *stinfo) |
11262 | { |
11263 | DumpOptions *dopt = fout->dopt; |
11264 | PQExpBuffer q; |
11265 | |
11266 | /* Skip if not to be dumped */ |
11267 | if (!stinfo->dobj.dump || dopt->dataOnly) |
11268 | return; |
11269 | |
11270 | q = createPQExpBuffer(); |
11271 | |
11272 | /* |
11273 | * Note the lack of a DROP command for the shell type; any required DROP |
11274 | * is driven off the base type entry, instead. This interacts with |
11275 | * _printTocEntry()'s use of the presence of a DROP command to decide |
11276 | * whether an entry needs an ALTER OWNER command. We don't want to alter |
11277 | * the shell type's owner immediately on creation; that should happen only |
11278 | * after it's filled in, otherwise the backend complains. |
11279 | */ |
11280 | |
11281 | if (dopt->binary_upgrade) |
11282 | binary_upgrade_set_type_oids_by_type_oid(fout, q, |
11283 | stinfo->baseType->dobj.catId.oid, |
11284 | false); |
11285 | |
11286 | appendPQExpBuffer(q, "CREATE TYPE %s;\n" , |
11287 | fmtQualifiedDumpable(stinfo)); |
11288 | |
11289 | if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
11290 | ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId, |
11291 | ARCHIVE_OPTS(.tag = stinfo->dobj.name, |
11292 | .namespace = stinfo->dobj.namespace->dobj.name, |
11293 | .owner = stinfo->baseType->rolname, |
11294 | .description = "SHELL TYPE" , |
11295 | .section = SECTION_PRE_DATA, |
11296 | .createStmt = q->data)); |
11297 | |
11298 | destroyPQExpBuffer(q); |
11299 | } |
11300 | |
11301 | /* |
11302 | * dumpProcLang |
11303 | * writes out to fout the queries to recreate a user-defined |
11304 | * procedural language |
11305 | */ |
11306 | static void |
11307 | dumpProcLang(Archive *fout, ProcLangInfo *plang) |
11308 | { |
11309 | DumpOptions *dopt = fout->dopt; |
11310 | PQExpBuffer defqry; |
11311 | PQExpBuffer delqry; |
11312 | bool useParams; |
11313 | char *qlanname; |
11314 | FuncInfo *funcInfo; |
11315 | FuncInfo *inlineInfo = NULL; |
11316 | FuncInfo *validatorInfo = NULL; |
11317 | |
11318 | /* Skip if not to be dumped */ |
11319 | if (!plang->dobj.dump || dopt->dataOnly) |
11320 | return; |
11321 | |
11322 | /* |
11323 | * Try to find the support function(s). It is not an error if we don't |
11324 | * find them --- if the functions are in the pg_catalog schema, as is |
11325 | * standard in 8.1 and up, then we won't have loaded them. (In this case |
11326 | * we will emit a parameterless CREATE LANGUAGE command, which will |
11327 | * require PL template knowledge in the backend to reload.) |
11328 | */ |
11329 | |
11330 | funcInfo = findFuncByOid(plang->lanplcallfoid); |
11331 | if (funcInfo != NULL && !funcInfo->dobj.dump) |
11332 | funcInfo = NULL; /* treat not-dumped same as not-found */ |
11333 | |
11334 | if (OidIsValid(plang->laninline)) |
11335 | { |
11336 | inlineInfo = findFuncByOid(plang->laninline); |
11337 | if (inlineInfo != NULL && !inlineInfo->dobj.dump) |
11338 | inlineInfo = NULL; |
11339 | } |
11340 | |
11341 | if (OidIsValid(plang->lanvalidator)) |
11342 | { |
11343 | validatorInfo = findFuncByOid(plang->lanvalidator); |
11344 | if (validatorInfo != NULL && !validatorInfo->dobj.dump) |
11345 | validatorInfo = NULL; |
11346 | } |
11347 | |
11348 | /* |
11349 | * If the functions are dumpable then emit a traditional CREATE LANGUAGE |
11350 | * with parameters. Otherwise, we'll write a parameterless command, which |
11351 | * will rely on data from pg_pltemplate. |
11352 | */ |
11353 | useParams = (funcInfo != NULL && |
11354 | (inlineInfo != NULL || !OidIsValid(plang->laninline)) && |
11355 | (validatorInfo != NULL || !OidIsValid(plang->lanvalidator))); |
11356 | |
11357 | defqry = createPQExpBuffer(); |
11358 | delqry = createPQExpBuffer(); |
11359 | |
11360 | qlanname = pg_strdup(fmtId(plang->dobj.name)); |
11361 | |
11362 | appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n" , |
11363 | qlanname); |
11364 | |
11365 | if (useParams) |
11366 | { |
11367 | appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s" , |
11368 | plang->lanpltrusted ? "TRUSTED " : "" , |
11369 | qlanname); |
11370 | appendPQExpBuffer(defqry, " HANDLER %s" , |
11371 | fmtQualifiedDumpable(funcInfo)); |
11372 | if (OidIsValid(plang->laninline)) |
11373 | appendPQExpBuffer(defqry, " INLINE %s" , |
11374 | fmtQualifiedDumpable(inlineInfo)); |
11375 | if (OidIsValid(plang->lanvalidator)) |
11376 | appendPQExpBuffer(defqry, " VALIDATOR %s" , |
11377 | fmtQualifiedDumpable(validatorInfo)); |
11378 | } |
11379 | else |
11380 | { |
11381 | /* |
11382 | * If not dumping parameters, then use CREATE OR REPLACE so that the |
11383 | * command will not fail if the language is preinstalled in the target |
11384 | * database. We restrict the use of REPLACE to this case so as to |
11385 | * eliminate the risk of replacing a language with incompatible |
11386 | * parameter settings: this command will only succeed at all if there |
11387 | * is a pg_pltemplate entry, and if there is one, the existing entry |
11388 | * must match it too. |
11389 | */ |
11390 | appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s" , |
11391 | qlanname); |
11392 | } |
11393 | appendPQExpBufferStr(defqry, ";\n" ); |
11394 | |
11395 | if (dopt->binary_upgrade) |
11396 | binary_upgrade_extension_member(defqry, &plang->dobj, |
11397 | "LANGUAGE" , qlanname, NULL); |
11398 | |
11399 | if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION) |
11400 | ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId, |
11401 | ARCHIVE_OPTS(.tag = plang->dobj.name, |
11402 | .owner = plang->lanowner, |
11403 | .description = "PROCEDURAL LANGUAGE" , |
11404 | .section = SECTION_PRE_DATA, |
11405 | .createStmt = defqry->data, |
11406 | .dropStmt = delqry->data, |
11407 | )); |
11408 | |
11409 | /* Dump Proc Lang Comments and Security Labels */ |
11410 | if (plang->dobj.dump & DUMP_COMPONENT_COMMENT) |
11411 | dumpComment(fout, "LANGUAGE" , qlanname, |
11412 | NULL, plang->lanowner, |
11413 | plang->dobj.catId, 0, plang->dobj.dumpId); |
11414 | |
11415 | if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL) |
11416 | dumpSecLabel(fout, "LANGUAGE" , qlanname, |
11417 | NULL, plang->lanowner, |
11418 | plang->dobj.catId, 0, plang->dobj.dumpId); |
11419 | |
11420 | if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL) |
11421 | dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE" , |
11422 | qlanname, NULL, NULL, |
11423 | plang->lanowner, plang->lanacl, plang->rlanacl, |
11424 | plang->initlanacl, plang->initrlanacl); |
11425 | |
11426 | free(qlanname); |
11427 | |
11428 | destroyPQExpBuffer(defqry); |
11429 | destroyPQExpBuffer(delqry); |
11430 | } |
11431 | |
11432 | /* |
11433 | * format_function_arguments: generate function name and argument list |
11434 | * |
11435 | * This is used when we can rely on pg_get_function_arguments to format |
11436 | * the argument list. Note, however, that pg_get_function_arguments |
11437 | * does not special-case zero-argument aggregates. |
11438 | */ |
11439 | static char * |
11440 | format_function_arguments(FuncInfo *finfo, char *funcargs, bool is_agg) |
11441 | { |
11442 | PQExpBufferData fn; |
11443 | |
11444 | initPQExpBuffer(&fn); |
11445 | appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name)); |
11446 | if (is_agg && finfo->nargs == 0) |
11447 | appendPQExpBufferStr(&fn, "(*)" ); |
11448 | else |
11449 | appendPQExpBuffer(&fn, "(%s)" , funcargs); |
11450 | return fn.data; |
11451 | } |
11452 | |
11453 | /* |
11454 | * format_function_arguments_old: generate function name and argument list |
11455 | * |
11456 | * The argument type names are qualified if needed. The function name |
11457 | * is never qualified. |
11458 | * |
11459 | * This is used only with pre-8.4 servers, so we aren't expecting to see |
11460 | * VARIADIC or TABLE arguments, nor are there any defaults for arguments. |
11461 | * |
11462 | * Any or all of allargtypes, argmodes, argnames may be NULL. |
11463 | */ |
11464 | static char * |
11465 | format_function_arguments_old(Archive *fout, |
11466 | FuncInfo *finfo, int nallargs, |
11467 | char **allargtypes, |
11468 | char **argmodes, |
11469 | char **argnames) |
11470 | { |
11471 | PQExpBufferData fn; |
11472 | int j; |
11473 | |
11474 | initPQExpBuffer(&fn); |
11475 | appendPQExpBuffer(&fn, "%s(" , fmtId(finfo->dobj.name)); |
11476 | for (j = 0; j < nallargs; j++) |
11477 | { |
11478 | Oid typid; |
11479 | char *typname; |
11480 | const char *argmode; |
11481 | const char *argname; |
11482 | |
11483 | typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j]; |
11484 | typname = getFormattedTypeName(fout, typid, zeroAsOpaque); |
11485 | |
11486 | if (argmodes) |
11487 | { |
11488 | switch (argmodes[j][0]) |
11489 | { |
11490 | case PROARGMODE_IN: |
11491 | argmode = "" ; |
11492 | break; |
11493 | case PROARGMODE_OUT: |
11494 | argmode = "OUT " ; |
11495 | break; |
11496 | case PROARGMODE_INOUT: |
11497 | argmode = "INOUT " ; |
11498 | break; |
11499 | default: |
11500 | pg_log_warning("bogus value in proargmodes array" ); |
11501 | argmode = "" ; |
11502 | break; |
11503 | } |
11504 | } |
11505 | else |
11506 | argmode = "" ; |
11507 | |
11508 | argname = argnames ? argnames[j] : (char *) NULL; |
11509 | if (argname && argname[0] == '\0') |
11510 | argname = NULL; |
11511 | |
11512 | appendPQExpBuffer(&fn, "%s%s%s%s%s" , |
11513 | (j > 0) ? ", " : "" , |
11514 | argmode, |
11515 | argname ? fmtId(argname) : "" , |
11516 | argname ? " " : "" , |
11517 | typname); |
11518 | free(typname); |
11519 | } |
11520 | appendPQExpBufferChar(&fn, ')'); |
11521 | return fn.data; |
11522 | } |
11523 | |
11524 | /* |
11525 | * format_function_signature: generate function name and argument list |
11526 | * |
11527 | * This is like format_function_arguments_old except that only a minimal |
11528 | * list of input argument types is generated; this is sufficient to |
11529 | * reference the function, but not to define it. |
11530 | * |
11531 | * If honor_quotes is false then the function name is never quoted. |
11532 | * This is appropriate for use in TOC tags, but not in SQL commands. |
11533 | */ |
11534 | static char * |
11535 | format_function_signature(Archive *fout, FuncInfo *finfo, bool honor_quotes) |
11536 | { |
11537 | PQExpBufferData fn; |
11538 | int j; |
11539 | |
11540 | initPQExpBuffer(&fn); |
11541 | if (honor_quotes) |
11542 | appendPQExpBuffer(&fn, "%s(" , fmtId(finfo->dobj.name)); |
11543 | else |
11544 | appendPQExpBuffer(&fn, "%s(" , finfo->dobj.name); |
11545 | for (j = 0; j < finfo->nargs; j++) |
11546 | { |
11547 | char *typname; |
11548 | |
11549 | if (j > 0) |
11550 | appendPQExpBufferStr(&fn, ", " ); |
11551 | |
11552 | typname = getFormattedTypeName(fout, finfo->argtypes[j], |
11553 | zeroAsOpaque); |
11554 | appendPQExpBufferStr(&fn, typname); |
11555 | free(typname); |
11556 | } |
11557 | appendPQExpBufferChar(&fn, ')'); |
11558 | return fn.data; |
11559 | } |
11560 | |
11561 | |
11562 | /* |
11563 | * dumpFunc: |
11564 | * dump out one function |
11565 | */ |
11566 | static void |
11567 | dumpFunc(Archive *fout, FuncInfo *finfo) |
11568 | { |
11569 | DumpOptions *dopt = fout->dopt; |
11570 | PQExpBuffer query; |
11571 | PQExpBuffer q; |
11572 | PQExpBuffer delqry; |
11573 | PQExpBuffer asPart; |
11574 | PGresult *res; |
11575 | char *funcsig; /* identity signature */ |
11576 | char *funcfullsig = NULL; /* full signature */ |
11577 | char *funcsig_tag; |
11578 | char *proretset; |
11579 | char *prosrc; |
11580 | char *probin; |
11581 | char *funcargs; |
11582 | char *funciargs; |
11583 | char *funcresult; |
11584 | char *proallargtypes; |
11585 | char *proargmodes; |
11586 | char *proargnames; |
11587 | char *protrftypes; |
11588 | char *prokind; |
11589 | char *provolatile; |
11590 | char *proisstrict; |
11591 | char *prosecdef; |
11592 | char *proleakproof; |
11593 | char *proconfig; |
11594 | char *procost; |
11595 | char *prorows; |
11596 | char *prosupport; |
11597 | char *proparallel; |
11598 | char *lanname; |
11599 | char *rettypename; |
11600 | int nallargs; |
11601 | char **allargtypes = NULL; |
11602 | char **argmodes = NULL; |
11603 | char **argnames = NULL; |
11604 | char **configitems = NULL; |
11605 | int nconfigitems = 0; |
11606 | const char *keyword; |
11607 | int i; |
11608 | |
11609 | /* Skip if not to be dumped */ |
11610 | if (!finfo->dobj.dump || dopt->dataOnly) |
11611 | return; |
11612 | |
11613 | query = createPQExpBuffer(); |
11614 | q = createPQExpBuffer(); |
11615 | delqry = createPQExpBuffer(); |
11616 | asPart = createPQExpBuffer(); |
11617 | |
11618 | /* Fetch function-specific details */ |
11619 | if (fout->remoteVersion >= 120000) |
11620 | { |
11621 | /* |
11622 | * prosupport was added in 12 |
11623 | */ |
11624 | appendPQExpBuffer(query, |
11625 | "SELECT proretset, prosrc, probin, " |
11626 | "pg_catalog.pg_get_function_arguments(oid) AS funcargs, " |
11627 | "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, " |
11628 | "pg_catalog.pg_get_function_result(oid) AS funcresult, " |
11629 | "array_to_string(protrftypes, ' ') AS protrftypes, " |
11630 | "prokind, provolatile, proisstrict, prosecdef, " |
11631 | "proleakproof, proconfig, procost, prorows, " |
11632 | "prosupport, proparallel, " |
11633 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11634 | "FROM pg_catalog.pg_proc " |
11635 | "WHERE oid = '%u'::pg_catalog.oid" , |
11636 | finfo->dobj.catId.oid); |
11637 | } |
11638 | else if (fout->remoteVersion >= 110000) |
11639 | { |
11640 | /* |
11641 | * prokind was added in 11 |
11642 | */ |
11643 | appendPQExpBuffer(query, |
11644 | "SELECT proretset, prosrc, probin, " |
11645 | "pg_catalog.pg_get_function_arguments(oid) AS funcargs, " |
11646 | "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, " |
11647 | "pg_catalog.pg_get_function_result(oid) AS funcresult, " |
11648 | "array_to_string(protrftypes, ' ') AS protrftypes, " |
11649 | "prokind, provolatile, proisstrict, prosecdef, " |
11650 | "proleakproof, proconfig, procost, prorows, " |
11651 | "'-' AS prosupport, proparallel, " |
11652 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11653 | "FROM pg_catalog.pg_proc " |
11654 | "WHERE oid = '%u'::pg_catalog.oid" , |
11655 | finfo->dobj.catId.oid); |
11656 | } |
11657 | else if (fout->remoteVersion >= 90600) |
11658 | { |
11659 | /* |
11660 | * proparallel was added in 9.6 |
11661 | */ |
11662 | appendPQExpBuffer(query, |
11663 | "SELECT proretset, prosrc, probin, " |
11664 | "pg_catalog.pg_get_function_arguments(oid) AS funcargs, " |
11665 | "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, " |
11666 | "pg_catalog.pg_get_function_result(oid) AS funcresult, " |
11667 | "array_to_string(protrftypes, ' ') AS protrftypes, " |
11668 | "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, " |
11669 | "provolatile, proisstrict, prosecdef, " |
11670 | "proleakproof, proconfig, procost, prorows, " |
11671 | "'-' AS prosupport, proparallel, " |
11672 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11673 | "FROM pg_catalog.pg_proc " |
11674 | "WHERE oid = '%u'::pg_catalog.oid" , |
11675 | finfo->dobj.catId.oid); |
11676 | } |
11677 | else if (fout->remoteVersion >= 90500) |
11678 | { |
11679 | /* |
11680 | * protrftypes was added in 9.5 |
11681 | */ |
11682 | appendPQExpBuffer(query, |
11683 | "SELECT proretset, prosrc, probin, " |
11684 | "pg_catalog.pg_get_function_arguments(oid) AS funcargs, " |
11685 | "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, " |
11686 | "pg_catalog.pg_get_function_result(oid) AS funcresult, " |
11687 | "array_to_string(protrftypes, ' ') AS protrftypes, " |
11688 | "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, " |
11689 | "provolatile, proisstrict, prosecdef, " |
11690 | "proleakproof, proconfig, procost, prorows, " |
11691 | "'-' AS prosupport, " |
11692 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11693 | "FROM pg_catalog.pg_proc " |
11694 | "WHERE oid = '%u'::pg_catalog.oid" , |
11695 | finfo->dobj.catId.oid); |
11696 | } |
11697 | else if (fout->remoteVersion >= 90200) |
11698 | { |
11699 | /* |
11700 | * proleakproof was added in 9.2 |
11701 | */ |
11702 | appendPQExpBuffer(query, |
11703 | "SELECT proretset, prosrc, probin, " |
11704 | "pg_catalog.pg_get_function_arguments(oid) AS funcargs, " |
11705 | "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, " |
11706 | "pg_catalog.pg_get_function_result(oid) AS funcresult, " |
11707 | "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, " |
11708 | "provolatile, proisstrict, prosecdef, " |
11709 | "proleakproof, proconfig, procost, prorows, " |
11710 | "'-' AS prosupport, " |
11711 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11712 | "FROM pg_catalog.pg_proc " |
11713 | "WHERE oid = '%u'::pg_catalog.oid" , |
11714 | finfo->dobj.catId.oid); |
11715 | } |
11716 | else if (fout->remoteVersion >= 80400) |
11717 | { |
11718 | /* |
11719 | * In 8.4 and up we rely on pg_get_function_arguments and |
11720 | * pg_get_function_result instead of examining proallargtypes etc. |
11721 | */ |
11722 | appendPQExpBuffer(query, |
11723 | "SELECT proretset, prosrc, probin, " |
11724 | "pg_catalog.pg_get_function_arguments(oid) AS funcargs, " |
11725 | "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, " |
11726 | "pg_catalog.pg_get_function_result(oid) AS funcresult, " |
11727 | "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, " |
11728 | "provolatile, proisstrict, prosecdef, " |
11729 | "false AS proleakproof, " |
11730 | " proconfig, procost, prorows, " |
11731 | "'-' AS prosupport, " |
11732 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11733 | "FROM pg_catalog.pg_proc " |
11734 | "WHERE oid = '%u'::pg_catalog.oid" , |
11735 | finfo->dobj.catId.oid); |
11736 | } |
11737 | else if (fout->remoteVersion >= 80300) |
11738 | { |
11739 | appendPQExpBuffer(query, |
11740 | "SELECT proretset, prosrc, probin, " |
11741 | "proallargtypes, proargmodes, proargnames, " |
11742 | "'f' AS prokind, " |
11743 | "provolatile, proisstrict, prosecdef, " |
11744 | "false AS proleakproof, " |
11745 | "proconfig, procost, prorows, " |
11746 | "'-' AS prosupport, " |
11747 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11748 | "FROM pg_catalog.pg_proc " |
11749 | "WHERE oid = '%u'::pg_catalog.oid" , |
11750 | finfo->dobj.catId.oid); |
11751 | } |
11752 | else if (fout->remoteVersion >= 80100) |
11753 | { |
11754 | appendPQExpBuffer(query, |
11755 | "SELECT proretset, prosrc, probin, " |
11756 | "proallargtypes, proargmodes, proargnames, " |
11757 | "'f' AS prokind, " |
11758 | "provolatile, proisstrict, prosecdef, " |
11759 | "false AS proleakproof, " |
11760 | "null AS proconfig, 0 AS procost, 0 AS prorows, " |
11761 | "'-' AS prosupport, " |
11762 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11763 | "FROM pg_catalog.pg_proc " |
11764 | "WHERE oid = '%u'::pg_catalog.oid" , |
11765 | finfo->dobj.catId.oid); |
11766 | } |
11767 | else |
11768 | { |
11769 | appendPQExpBuffer(query, |
11770 | "SELECT proretset, prosrc, probin, " |
11771 | "null AS proallargtypes, " |
11772 | "null AS proargmodes, " |
11773 | "proargnames, " |
11774 | "'f' AS prokind, " |
11775 | "provolatile, proisstrict, prosecdef, " |
11776 | "false AS proleakproof, " |
11777 | "null AS proconfig, 0 AS procost, 0 AS prorows, " |
11778 | "'-' AS prosupport, " |
11779 | "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " |
11780 | "FROM pg_catalog.pg_proc " |
11781 | "WHERE oid = '%u'::pg_catalog.oid" , |
11782 | finfo->dobj.catId.oid); |
11783 | } |
11784 | |
11785 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
11786 | |
11787 | proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset" )); |
11788 | prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc" )); |
11789 | probin = PQgetvalue(res, 0, PQfnumber(res, "probin" )); |
11790 | if (fout->remoteVersion >= 80400) |
11791 | { |
11792 | funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs" )); |
11793 | funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs" )); |
11794 | funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult" )); |
11795 | proallargtypes = proargmodes = proargnames = NULL; |
11796 | } |
11797 | else |
11798 | { |
11799 | proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes" )); |
11800 | proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes" )); |
11801 | proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames" )); |
11802 | funcargs = funciargs = funcresult = NULL; |
11803 | } |
11804 | if (PQfnumber(res, "protrftypes" ) != -1) |
11805 | protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes" )); |
11806 | else |
11807 | protrftypes = NULL; |
11808 | prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind" )); |
11809 | provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile" )); |
11810 | proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict" )); |
11811 | prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef" )); |
11812 | proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof" )); |
11813 | proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig" )); |
11814 | procost = PQgetvalue(res, 0, PQfnumber(res, "procost" )); |
11815 | prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows" )); |
11816 | prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport" )); |
11817 | |
11818 | if (PQfnumber(res, "proparallel" ) != -1) |
11819 | proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel" )); |
11820 | else |
11821 | proparallel = NULL; |
11822 | |
11823 | lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname" )); |
11824 | |
11825 | /* |
11826 | * See backend/commands/functioncmds.c for details of how the 'AS' clause |
11827 | * is used. In 8.4 and up, an unused probin is NULL (here ""); previous |
11828 | * versions would set it to "-". There are no known cases in which prosrc |
11829 | * is unused, so the tests below for "-" are probably useless. |
11830 | */ |
11831 | if (probin[0] != '\0' && strcmp(probin, "-" ) != 0) |
11832 | { |
11833 | appendPQExpBufferStr(asPart, "AS " ); |
11834 | appendStringLiteralAH(asPart, probin, fout); |
11835 | if (strcmp(prosrc, "-" ) != 0) |
11836 | { |
11837 | appendPQExpBufferStr(asPart, ", " ); |
11838 | |
11839 | /* |
11840 | * where we have bin, use dollar quoting if allowed and src |
11841 | * contains quote or backslash; else use regular quoting. |
11842 | */ |
11843 | if (dopt->disable_dollar_quoting || |
11844 | (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL)) |
11845 | appendStringLiteralAH(asPart, prosrc, fout); |
11846 | else |
11847 | appendStringLiteralDQ(asPart, prosrc, NULL); |
11848 | } |
11849 | } |
11850 | else |
11851 | { |
11852 | if (strcmp(prosrc, "-" ) != 0) |
11853 | { |
11854 | appendPQExpBufferStr(asPart, "AS " ); |
11855 | /* with no bin, dollar quote src unconditionally if allowed */ |
11856 | if (dopt->disable_dollar_quoting) |
11857 | appendStringLiteralAH(asPart, prosrc, fout); |
11858 | else |
11859 | appendStringLiteralDQ(asPart, prosrc, NULL); |
11860 | } |
11861 | } |
11862 | |
11863 | nallargs = finfo->nargs; /* unless we learn different from allargs */ |
11864 | |
11865 | if (proallargtypes && *proallargtypes) |
11866 | { |
11867 | int nitems = 0; |
11868 | |
11869 | if (!parsePGArray(proallargtypes, &allargtypes, &nitems) || |
11870 | nitems < finfo->nargs) |
11871 | { |
11872 | pg_log_warning("could not parse proallargtypes array" ); |
11873 | if (allargtypes) |
11874 | free(allargtypes); |
11875 | allargtypes = NULL; |
11876 | } |
11877 | else |
11878 | nallargs = nitems; |
11879 | } |
11880 | |
11881 | if (proargmodes && *proargmodes) |
11882 | { |
11883 | int nitems = 0; |
11884 | |
11885 | if (!parsePGArray(proargmodes, &argmodes, &nitems) || |
11886 | nitems != nallargs) |
11887 | { |
11888 | pg_log_warning("could not parse proargmodes array" ); |
11889 | if (argmodes) |
11890 | free(argmodes); |
11891 | argmodes = NULL; |
11892 | } |
11893 | } |
11894 | |
11895 | if (proargnames && *proargnames) |
11896 | { |
11897 | int nitems = 0; |
11898 | |
11899 | if (!parsePGArray(proargnames, &argnames, &nitems) || |
11900 | nitems != nallargs) |
11901 | { |
11902 | pg_log_warning("could not parse proargnames array" ); |
11903 | if (argnames) |
11904 | free(argnames); |
11905 | argnames = NULL; |
11906 | } |
11907 | } |
11908 | |
11909 | if (proconfig && *proconfig) |
11910 | { |
11911 | if (!parsePGArray(proconfig, &configitems, &nconfigitems)) |
11912 | { |
11913 | pg_log_warning("could not parse proconfig array" ); |
11914 | if (configitems) |
11915 | free(configitems); |
11916 | configitems = NULL; |
11917 | nconfigitems = 0; |
11918 | } |
11919 | } |
11920 | |
11921 | if (funcargs) |
11922 | { |
11923 | /* 8.4 or later; we rely on server-side code for most of the work */ |
11924 | funcfullsig = format_function_arguments(finfo, funcargs, false); |
11925 | funcsig = format_function_arguments(finfo, funciargs, false); |
11926 | } |
11927 | else |
11928 | /* pre-8.4, do it ourselves */ |
11929 | funcsig = format_function_arguments_old(fout, |
11930 | finfo, nallargs, allargtypes, |
11931 | argmodes, argnames); |
11932 | |
11933 | funcsig_tag = format_function_signature(fout, finfo, false); |
11934 | |
11935 | if (prokind[0] == PROKIND_PROCEDURE) |
11936 | keyword = "PROCEDURE" ; |
11937 | else |
11938 | keyword = "FUNCTION" ; /* works for window functions too */ |
11939 | |
11940 | appendPQExpBuffer(delqry, "DROP %s %s.%s;\n" , |
11941 | keyword, |
11942 | fmtId(finfo->dobj.namespace->dobj.name), |
11943 | funcsig); |
11944 | |
11945 | appendPQExpBuffer(q, "CREATE %s %s.%s" , |
11946 | keyword, |
11947 | fmtId(finfo->dobj.namespace->dobj.name), |
11948 | funcfullsig ? funcfullsig : |
11949 | funcsig); |
11950 | |
11951 | if (prokind[0] == PROKIND_PROCEDURE) |
11952 | /* no result type to output */ ; |
11953 | else if (funcresult) |
11954 | appendPQExpBuffer(q, " RETURNS %s" , funcresult); |
11955 | else |
11956 | { |
11957 | rettypename = getFormattedTypeName(fout, finfo->prorettype, |
11958 | zeroAsOpaque); |
11959 | appendPQExpBuffer(q, " RETURNS %s%s" , |
11960 | (proretset[0] == 't') ? "SETOF " : "" , |
11961 | rettypename); |
11962 | free(rettypename); |
11963 | } |
11964 | |
11965 | appendPQExpBuffer(q, "\n LANGUAGE %s" , fmtId(lanname)); |
11966 | |
11967 | if (protrftypes != NULL && strcmp(protrftypes, "" ) != 0) |
11968 | { |
11969 | Oid *typeids = palloc(FUNC_MAX_ARGS * sizeof(Oid)); |
11970 | int i; |
11971 | |
11972 | appendPQExpBufferStr(q, " TRANSFORM " ); |
11973 | parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS); |
11974 | for (i = 0; typeids[i]; i++) |
11975 | { |
11976 | if (i != 0) |
11977 | appendPQExpBufferStr(q, ", " ); |
11978 | appendPQExpBuffer(q, "FOR TYPE %s" , |
11979 | getFormattedTypeName(fout, typeids[i], zeroAsNone)); |
11980 | } |
11981 | } |
11982 | |
11983 | if (prokind[0] == PROKIND_WINDOW) |
11984 | appendPQExpBufferStr(q, " WINDOW" ); |
11985 | |
11986 | if (provolatile[0] != PROVOLATILE_VOLATILE) |
11987 | { |
11988 | if (provolatile[0] == PROVOLATILE_IMMUTABLE) |
11989 | appendPQExpBufferStr(q, " IMMUTABLE" ); |
11990 | else if (provolatile[0] == PROVOLATILE_STABLE) |
11991 | appendPQExpBufferStr(q, " STABLE" ); |
11992 | else if (provolatile[0] != PROVOLATILE_VOLATILE) |
11993 | fatal("unrecognized provolatile value for function \"%s\"" , |
11994 | finfo->dobj.name); |
11995 | } |
11996 | |
11997 | if (proisstrict[0] == 't') |
11998 | appendPQExpBufferStr(q, " STRICT" ); |
11999 | |
12000 | if (prosecdef[0] == 't') |
12001 | appendPQExpBufferStr(q, " SECURITY DEFINER" ); |
12002 | |
12003 | if (proleakproof[0] == 't') |
12004 | appendPQExpBufferStr(q, " LEAKPROOF" ); |
12005 | |
12006 | /* |
12007 | * COST and ROWS are emitted only if present and not default, so as not to |
12008 | * break backwards-compatibility of the dump without need. Keep this code |
12009 | * in sync with the defaults in functioncmds.c. |
12010 | */ |
12011 | if (strcmp(procost, "0" ) != 0) |
12012 | { |
12013 | if (strcmp(lanname, "internal" ) == 0 || strcmp(lanname, "c" ) == 0) |
12014 | { |
12015 | /* default cost is 1 */ |
12016 | if (strcmp(procost, "1" ) != 0) |
12017 | appendPQExpBuffer(q, " COST %s" , procost); |
12018 | } |
12019 | else |
12020 | { |
12021 | /* default cost is 100 */ |
12022 | if (strcmp(procost, "100" ) != 0) |
12023 | appendPQExpBuffer(q, " COST %s" , procost); |
12024 | } |
12025 | } |
12026 | if (proretset[0] == 't' && |
12027 | strcmp(prorows, "0" ) != 0 && strcmp(prorows, "1000" ) != 0) |
12028 | appendPQExpBuffer(q, " ROWS %s" , prorows); |
12029 | |
12030 | if (strcmp(prosupport, "-" ) != 0) |
12031 | { |
12032 | /* We rely on regprocout to provide quoting and qualification */ |
12033 | appendPQExpBuffer(q, " SUPPORT %s" , prosupport); |
12034 | } |
12035 | |
12036 | if (proparallel != NULL && proparallel[0] != PROPARALLEL_UNSAFE) |
12037 | { |
12038 | if (proparallel[0] == PROPARALLEL_SAFE) |
12039 | appendPQExpBufferStr(q, " PARALLEL SAFE" ); |
12040 | else if (proparallel[0] == PROPARALLEL_RESTRICTED) |
12041 | appendPQExpBufferStr(q, " PARALLEL RESTRICTED" ); |
12042 | else if (proparallel[0] != PROPARALLEL_UNSAFE) |
12043 | fatal("unrecognized proparallel value for function \"%s\"" , |
12044 | finfo->dobj.name); |
12045 | } |
12046 | |
12047 | for (i = 0; i < nconfigitems; i++) |
12048 | { |
12049 | /* we feel free to scribble on configitems[] here */ |
12050 | char *configitem = configitems[i]; |
12051 | char *pos; |
12052 | |
12053 | pos = strchr(configitem, '='); |
12054 | if (pos == NULL) |
12055 | continue; |
12056 | *pos++ = '\0'; |
12057 | appendPQExpBuffer(q, "\n SET %s TO " , fmtId(configitem)); |
12058 | |
12059 | /* |
12060 | * Variables that are marked GUC_LIST_QUOTE were already fully quoted |
12061 | * by flatten_set_variable_args() before they were put into the |
12062 | * proconfig array. However, because the quoting rules used there |
12063 | * aren't exactly like SQL's, we have to break the list value apart |
12064 | * and then quote the elements as string literals. (The elements may |
12065 | * be double-quoted as-is, but we can't just feed them to the SQL |
12066 | * parser; it would do the wrong thing with elements that are |
12067 | * zero-length or longer than NAMEDATALEN.) |
12068 | * |
12069 | * Variables that are not so marked should just be emitted as simple |
12070 | * string literals. If the variable is not known to |
12071 | * variable_is_guc_list_quote(), we'll do that; this makes it unsafe |
12072 | * to use GUC_LIST_QUOTE for extension variables. |
12073 | */ |
12074 | if (variable_is_guc_list_quote(configitem)) |
12075 | { |
12076 | char **namelist; |
12077 | char **nameptr; |
12078 | |
12079 | /* Parse string into list of identifiers */ |
12080 | /* this shouldn't fail really */ |
12081 | if (SplitGUCList(pos, ',', &namelist)) |
12082 | { |
12083 | for (nameptr = namelist; *nameptr; nameptr++) |
12084 | { |
12085 | if (nameptr != namelist) |
12086 | appendPQExpBufferStr(q, ", " ); |
12087 | appendStringLiteralAH(q, *nameptr, fout); |
12088 | } |
12089 | } |
12090 | pg_free(namelist); |
12091 | } |
12092 | else |
12093 | appendStringLiteralAH(q, pos, fout); |
12094 | } |
12095 | |
12096 | appendPQExpBuffer(q, "\n %s;\n" , asPart->data); |
12097 | |
12098 | if (dopt->binary_upgrade) |
12099 | binary_upgrade_extension_member(q, &finfo->dobj, |
12100 | keyword, funcsig, |
12101 | finfo->dobj.namespace->dobj.name); |
12102 | |
12103 | if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
12104 | ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId, |
12105 | ARCHIVE_OPTS(.tag = funcsig_tag, |
12106 | .namespace = finfo->dobj.namespace->dobj.name, |
12107 | .owner = finfo->rolname, |
12108 | .description = keyword, |
12109 | .section = SECTION_PRE_DATA, |
12110 | .createStmt = q->data, |
12111 | .dropStmt = delqry->data)); |
12112 | |
12113 | /* Dump Function Comments and Security Labels */ |
12114 | if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
12115 | dumpComment(fout, keyword, funcsig, |
12116 | finfo->dobj.namespace->dobj.name, finfo->rolname, |
12117 | finfo->dobj.catId, 0, finfo->dobj.dumpId); |
12118 | |
12119 | if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
12120 | dumpSecLabel(fout, keyword, funcsig, |
12121 | finfo->dobj.namespace->dobj.name, finfo->rolname, |
12122 | finfo->dobj.catId, 0, finfo->dobj.dumpId); |
12123 | |
12124 | if (finfo->dobj.dump & DUMP_COMPONENT_ACL) |
12125 | dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, keyword, |
12126 | funcsig, NULL, |
12127 | finfo->dobj.namespace->dobj.name, |
12128 | finfo->rolname, finfo->proacl, finfo->rproacl, |
12129 | finfo->initproacl, finfo->initrproacl); |
12130 | |
12131 | PQclear(res); |
12132 | |
12133 | destroyPQExpBuffer(query); |
12134 | destroyPQExpBuffer(q); |
12135 | destroyPQExpBuffer(delqry); |
12136 | destroyPQExpBuffer(asPart); |
12137 | free(funcsig); |
12138 | if (funcfullsig) |
12139 | free(funcfullsig); |
12140 | free(funcsig_tag); |
12141 | if (allargtypes) |
12142 | free(allargtypes); |
12143 | if (argmodes) |
12144 | free(argmodes); |
12145 | if (argnames) |
12146 | free(argnames); |
12147 | if (configitems) |
12148 | free(configitems); |
12149 | } |
12150 | |
12151 | |
12152 | /* |
12153 | * Dump a user-defined cast |
12154 | */ |
12155 | static void |
12156 | dumpCast(Archive *fout, CastInfo *cast) |
12157 | { |
12158 | DumpOptions *dopt = fout->dopt; |
12159 | PQExpBuffer defqry; |
12160 | PQExpBuffer delqry; |
12161 | PQExpBuffer labelq; |
12162 | PQExpBuffer castargs; |
12163 | FuncInfo *funcInfo = NULL; |
12164 | char *sourceType; |
12165 | char *targetType; |
12166 | |
12167 | /* Skip if not to be dumped */ |
12168 | if (!cast->dobj.dump || dopt->dataOnly) |
12169 | return; |
12170 | |
12171 | /* Cannot dump if we don't have the cast function's info */ |
12172 | if (OidIsValid(cast->castfunc)) |
12173 | { |
12174 | funcInfo = findFuncByOid(cast->castfunc); |
12175 | if (funcInfo == NULL) |
12176 | fatal("could not find function definition for function with OID %u" , |
12177 | cast->castfunc); |
12178 | } |
12179 | |
12180 | defqry = createPQExpBuffer(); |
12181 | delqry = createPQExpBuffer(); |
12182 | labelq = createPQExpBuffer(); |
12183 | castargs = createPQExpBuffer(); |
12184 | |
12185 | sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone); |
12186 | targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone); |
12187 | appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n" , |
12188 | sourceType, targetType); |
12189 | |
12190 | appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) " , |
12191 | sourceType, targetType); |
12192 | |
12193 | switch (cast->castmethod) |
12194 | { |
12195 | case COERCION_METHOD_BINARY: |
12196 | appendPQExpBufferStr(defqry, "WITHOUT FUNCTION" ); |
12197 | break; |
12198 | case COERCION_METHOD_INOUT: |
12199 | appendPQExpBufferStr(defqry, "WITH INOUT" ); |
12200 | break; |
12201 | case COERCION_METHOD_FUNCTION: |
12202 | if (funcInfo) |
12203 | { |
12204 | char *fsig = format_function_signature(fout, funcInfo, true); |
12205 | |
12206 | /* |
12207 | * Always qualify the function name (format_function_signature |
12208 | * won't qualify it). |
12209 | */ |
12210 | appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s" , |
12211 | fmtId(funcInfo->dobj.namespace->dobj.name), fsig); |
12212 | free(fsig); |
12213 | } |
12214 | else |
12215 | pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field" ); |
12216 | break; |
12217 | default: |
12218 | pg_log_warning("bogus value in pg_cast.castmethod field" ); |
12219 | } |
12220 | |
12221 | if (cast->castcontext == 'a') |
12222 | appendPQExpBufferStr(defqry, " AS ASSIGNMENT" ); |
12223 | else if (cast->castcontext == 'i') |
12224 | appendPQExpBufferStr(defqry, " AS IMPLICIT" ); |
12225 | appendPQExpBufferStr(defqry, ";\n" ); |
12226 | |
12227 | appendPQExpBuffer(labelq, "CAST (%s AS %s)" , |
12228 | sourceType, targetType); |
12229 | |
12230 | appendPQExpBuffer(castargs, "(%s AS %s)" , |
12231 | sourceType, targetType); |
12232 | |
12233 | if (dopt->binary_upgrade) |
12234 | binary_upgrade_extension_member(defqry, &cast->dobj, |
12235 | "CAST" , castargs->data, NULL); |
12236 | |
12237 | if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION) |
12238 | ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId, |
12239 | ARCHIVE_OPTS(.tag = labelq->data, |
12240 | .description = "CAST" , |
12241 | .section = SECTION_PRE_DATA, |
12242 | .createStmt = defqry->data, |
12243 | .dropStmt = delqry->data)); |
12244 | |
12245 | /* Dump Cast Comments */ |
12246 | if (cast->dobj.dump & DUMP_COMPONENT_COMMENT) |
12247 | dumpComment(fout, "CAST" , castargs->data, |
12248 | NULL, "" , |
12249 | cast->dobj.catId, 0, cast->dobj.dumpId); |
12250 | |
12251 | free(sourceType); |
12252 | free(targetType); |
12253 | |
12254 | destroyPQExpBuffer(defqry); |
12255 | destroyPQExpBuffer(delqry); |
12256 | destroyPQExpBuffer(labelq); |
12257 | destroyPQExpBuffer(castargs); |
12258 | } |
12259 | |
12260 | /* |
12261 | * Dump a transform |
12262 | */ |
12263 | static void |
12264 | dumpTransform(Archive *fout, TransformInfo *transform) |
12265 | { |
12266 | DumpOptions *dopt = fout->dopt; |
12267 | PQExpBuffer defqry; |
12268 | PQExpBuffer delqry; |
12269 | PQExpBuffer labelq; |
12270 | PQExpBuffer transformargs; |
12271 | FuncInfo *fromsqlFuncInfo = NULL; |
12272 | FuncInfo *tosqlFuncInfo = NULL; |
12273 | char *lanname; |
12274 | char *transformType; |
12275 | |
12276 | /* Skip if not to be dumped */ |
12277 | if (!transform->dobj.dump || dopt->dataOnly) |
12278 | return; |
12279 | |
12280 | /* Cannot dump if we don't have the transform functions' info */ |
12281 | if (OidIsValid(transform->trffromsql)) |
12282 | { |
12283 | fromsqlFuncInfo = findFuncByOid(transform->trffromsql); |
12284 | if (fromsqlFuncInfo == NULL) |
12285 | fatal("could not find function definition for function with OID %u" , |
12286 | transform->trffromsql); |
12287 | } |
12288 | if (OidIsValid(transform->trftosql)) |
12289 | { |
12290 | tosqlFuncInfo = findFuncByOid(transform->trftosql); |
12291 | if (tosqlFuncInfo == NULL) |
12292 | fatal("could not find function definition for function with OID %u" , |
12293 | transform->trftosql); |
12294 | } |
12295 | |
12296 | defqry = createPQExpBuffer(); |
12297 | delqry = createPQExpBuffer(); |
12298 | labelq = createPQExpBuffer(); |
12299 | transformargs = createPQExpBuffer(); |
12300 | |
12301 | lanname = get_language_name(fout, transform->trflang); |
12302 | transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone); |
12303 | |
12304 | appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n" , |
12305 | transformType, lanname); |
12306 | |
12307 | appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (" , |
12308 | transformType, lanname); |
12309 | |
12310 | if (!transform->trffromsql && !transform->trftosql) |
12311 | pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero" ); |
12312 | |
12313 | if (transform->trffromsql) |
12314 | { |
12315 | if (fromsqlFuncInfo) |
12316 | { |
12317 | char *fsig = format_function_signature(fout, fromsqlFuncInfo, true); |
12318 | |
12319 | /* |
12320 | * Always qualify the function name (format_function_signature |
12321 | * won't qualify it). |
12322 | */ |
12323 | appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s" , |
12324 | fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig); |
12325 | free(fsig); |
12326 | } |
12327 | else |
12328 | pg_log_warning("bogus value in pg_transform.trffromsql field" ); |
12329 | } |
12330 | |
12331 | if (transform->trftosql) |
12332 | { |
12333 | if (transform->trffromsql) |
12334 | appendPQExpBuffer(defqry, ", " ); |
12335 | |
12336 | if (tosqlFuncInfo) |
12337 | { |
12338 | char *fsig = format_function_signature(fout, tosqlFuncInfo, true); |
12339 | |
12340 | /* |
12341 | * Always qualify the function name (format_function_signature |
12342 | * won't qualify it). |
12343 | */ |
12344 | appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s" , |
12345 | fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig); |
12346 | free(fsig); |
12347 | } |
12348 | else |
12349 | pg_log_warning("bogus value in pg_transform.trftosql field" ); |
12350 | } |
12351 | |
12352 | appendPQExpBuffer(defqry, ");\n" ); |
12353 | |
12354 | appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s" , |
12355 | transformType, lanname); |
12356 | |
12357 | appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s" , |
12358 | transformType, lanname); |
12359 | |
12360 | if (dopt->binary_upgrade) |
12361 | binary_upgrade_extension_member(defqry, &transform->dobj, |
12362 | "TRANSFORM" , transformargs->data, NULL); |
12363 | |
12364 | if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION) |
12365 | ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId, |
12366 | ARCHIVE_OPTS(.tag = labelq->data, |
12367 | .description = "TRANSFORM" , |
12368 | .section = SECTION_PRE_DATA, |
12369 | .createStmt = defqry->data, |
12370 | .dropStmt = delqry->data, |
12371 | .deps = transform->dobj.dependencies, |
12372 | .nDeps = transform->dobj.nDeps)); |
12373 | |
12374 | /* Dump Transform Comments */ |
12375 | if (transform->dobj.dump & DUMP_COMPONENT_COMMENT) |
12376 | dumpComment(fout, "TRANSFORM" , transformargs->data, |
12377 | NULL, "" , |
12378 | transform->dobj.catId, 0, transform->dobj.dumpId); |
12379 | |
12380 | free(lanname); |
12381 | free(transformType); |
12382 | destroyPQExpBuffer(defqry); |
12383 | destroyPQExpBuffer(delqry); |
12384 | destroyPQExpBuffer(labelq); |
12385 | destroyPQExpBuffer(transformargs); |
12386 | } |
12387 | |
12388 | |
12389 | /* |
12390 | * dumpOpr |
12391 | * write out a single operator definition |
12392 | */ |
12393 | static void |
12394 | dumpOpr(Archive *fout, OprInfo *oprinfo) |
12395 | { |
12396 | DumpOptions *dopt = fout->dopt; |
12397 | PQExpBuffer query; |
12398 | PQExpBuffer q; |
12399 | PQExpBuffer delq; |
12400 | PQExpBuffer oprid; |
12401 | PQExpBuffer details; |
12402 | PGresult *res; |
12403 | int i_oprkind; |
12404 | int i_oprcode; |
12405 | int i_oprleft; |
12406 | int i_oprright; |
12407 | int i_oprcom; |
12408 | int i_oprnegate; |
12409 | int i_oprrest; |
12410 | int i_oprjoin; |
12411 | int i_oprcanmerge; |
12412 | int i_oprcanhash; |
12413 | char *oprkind; |
12414 | char *oprcode; |
12415 | char *oprleft; |
12416 | char *oprright; |
12417 | char *oprcom; |
12418 | char *oprnegate; |
12419 | char *oprrest; |
12420 | char *oprjoin; |
12421 | char *oprcanmerge; |
12422 | char *oprcanhash; |
12423 | char *oprregproc; |
12424 | char *oprref; |
12425 | |
12426 | /* Skip if not to be dumped */ |
12427 | if (!oprinfo->dobj.dump || dopt->dataOnly) |
12428 | return; |
12429 | |
12430 | /* |
12431 | * some operators are invalid because they were the result of user |
12432 | * defining operators before commutators exist |
12433 | */ |
12434 | if (!OidIsValid(oprinfo->oprcode)) |
12435 | return; |
12436 | |
12437 | query = createPQExpBuffer(); |
12438 | q = createPQExpBuffer(); |
12439 | delq = createPQExpBuffer(); |
12440 | oprid = createPQExpBuffer(); |
12441 | details = createPQExpBuffer(); |
12442 | |
12443 | if (fout->remoteVersion >= 80300) |
12444 | { |
12445 | appendPQExpBuffer(query, "SELECT oprkind, " |
12446 | "oprcode::pg_catalog.regprocedure, " |
12447 | "oprleft::pg_catalog.regtype, " |
12448 | "oprright::pg_catalog.regtype, " |
12449 | "oprcom, " |
12450 | "oprnegate, " |
12451 | "oprrest::pg_catalog.regprocedure, " |
12452 | "oprjoin::pg_catalog.regprocedure, " |
12453 | "oprcanmerge, oprcanhash " |
12454 | "FROM pg_catalog.pg_operator " |
12455 | "WHERE oid = '%u'::pg_catalog.oid" , |
12456 | oprinfo->dobj.catId.oid); |
12457 | } |
12458 | else |
12459 | { |
12460 | appendPQExpBuffer(query, "SELECT oprkind, " |
12461 | "oprcode::pg_catalog.regprocedure, " |
12462 | "oprleft::pg_catalog.regtype, " |
12463 | "oprright::pg_catalog.regtype, " |
12464 | "oprcom, " |
12465 | "oprnegate, " |
12466 | "oprrest::pg_catalog.regprocedure, " |
12467 | "oprjoin::pg_catalog.regprocedure, " |
12468 | "(oprlsortop != 0) AS oprcanmerge, " |
12469 | "oprcanhash " |
12470 | "FROM pg_catalog.pg_operator " |
12471 | "WHERE oid = '%u'::pg_catalog.oid" , |
12472 | oprinfo->dobj.catId.oid); |
12473 | } |
12474 | |
12475 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
12476 | |
12477 | i_oprkind = PQfnumber(res, "oprkind" ); |
12478 | i_oprcode = PQfnumber(res, "oprcode" ); |
12479 | i_oprleft = PQfnumber(res, "oprleft" ); |
12480 | i_oprright = PQfnumber(res, "oprright" ); |
12481 | i_oprcom = PQfnumber(res, "oprcom" ); |
12482 | i_oprnegate = PQfnumber(res, "oprnegate" ); |
12483 | i_oprrest = PQfnumber(res, "oprrest" ); |
12484 | i_oprjoin = PQfnumber(res, "oprjoin" ); |
12485 | i_oprcanmerge = PQfnumber(res, "oprcanmerge" ); |
12486 | i_oprcanhash = PQfnumber(res, "oprcanhash" ); |
12487 | |
12488 | oprkind = PQgetvalue(res, 0, i_oprkind); |
12489 | oprcode = PQgetvalue(res, 0, i_oprcode); |
12490 | oprleft = PQgetvalue(res, 0, i_oprleft); |
12491 | oprright = PQgetvalue(res, 0, i_oprright); |
12492 | oprcom = PQgetvalue(res, 0, i_oprcom); |
12493 | oprnegate = PQgetvalue(res, 0, i_oprnegate); |
12494 | oprrest = PQgetvalue(res, 0, i_oprrest); |
12495 | oprjoin = PQgetvalue(res, 0, i_oprjoin); |
12496 | oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge); |
12497 | oprcanhash = PQgetvalue(res, 0, i_oprcanhash); |
12498 | |
12499 | oprregproc = convertRegProcReference(fout, oprcode); |
12500 | if (oprregproc) |
12501 | { |
12502 | appendPQExpBuffer(details, " FUNCTION = %s" , oprregproc); |
12503 | free(oprregproc); |
12504 | } |
12505 | |
12506 | appendPQExpBuffer(oprid, "%s (" , |
12507 | oprinfo->dobj.name); |
12508 | |
12509 | /* |
12510 | * right unary means there's a left arg and left unary means there's a |
12511 | * right arg |
12512 | */ |
12513 | if (strcmp(oprkind, "r" ) == 0 || |
12514 | strcmp(oprkind, "b" ) == 0) |
12515 | { |
12516 | appendPQExpBuffer(details, ",\n LEFTARG = %s" , oprleft); |
12517 | appendPQExpBufferStr(oprid, oprleft); |
12518 | } |
12519 | else |
12520 | appendPQExpBufferStr(oprid, "NONE" ); |
12521 | |
12522 | if (strcmp(oprkind, "l" ) == 0 || |
12523 | strcmp(oprkind, "b" ) == 0) |
12524 | { |
12525 | appendPQExpBuffer(details, ",\n RIGHTARG = %s" , oprright); |
12526 | appendPQExpBuffer(oprid, ", %s)" , oprright); |
12527 | } |
12528 | else |
12529 | appendPQExpBufferStr(oprid, ", NONE)" ); |
12530 | |
12531 | oprref = getFormattedOperatorName(fout, oprcom); |
12532 | if (oprref) |
12533 | { |
12534 | appendPQExpBuffer(details, ",\n COMMUTATOR = %s" , oprref); |
12535 | free(oprref); |
12536 | } |
12537 | |
12538 | oprref = getFormattedOperatorName(fout, oprnegate); |
12539 | if (oprref) |
12540 | { |
12541 | appendPQExpBuffer(details, ",\n NEGATOR = %s" , oprref); |
12542 | free(oprref); |
12543 | } |
12544 | |
12545 | if (strcmp(oprcanmerge, "t" ) == 0) |
12546 | appendPQExpBufferStr(details, ",\n MERGES" ); |
12547 | |
12548 | if (strcmp(oprcanhash, "t" ) == 0) |
12549 | appendPQExpBufferStr(details, ",\n HASHES" ); |
12550 | |
12551 | oprregproc = convertRegProcReference(fout, oprrest); |
12552 | if (oprregproc) |
12553 | { |
12554 | appendPQExpBuffer(details, ",\n RESTRICT = %s" , oprregproc); |
12555 | free(oprregproc); |
12556 | } |
12557 | |
12558 | oprregproc = convertRegProcReference(fout, oprjoin); |
12559 | if (oprregproc) |
12560 | { |
12561 | appendPQExpBuffer(details, ",\n JOIN = %s" , oprregproc); |
12562 | free(oprregproc); |
12563 | } |
12564 | |
12565 | appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n" , |
12566 | fmtId(oprinfo->dobj.namespace->dobj.name), |
12567 | oprid->data); |
12568 | |
12569 | appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n" , |
12570 | fmtId(oprinfo->dobj.namespace->dobj.name), |
12571 | oprinfo->dobj.name, details->data); |
12572 | |
12573 | if (dopt->binary_upgrade) |
12574 | binary_upgrade_extension_member(q, &oprinfo->dobj, |
12575 | "OPERATOR" , oprid->data, |
12576 | oprinfo->dobj.namespace->dobj.name); |
12577 | |
12578 | if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
12579 | ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId, |
12580 | ARCHIVE_OPTS(.tag = oprinfo->dobj.name, |
12581 | .namespace = oprinfo->dobj.namespace->dobj.name, |
12582 | .owner = oprinfo->rolname, |
12583 | .description = "OPERATOR" , |
12584 | .section = SECTION_PRE_DATA, |
12585 | .createStmt = q->data, |
12586 | .dropStmt = delq->data)); |
12587 | |
12588 | /* Dump Operator Comments */ |
12589 | if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
12590 | dumpComment(fout, "OPERATOR" , oprid->data, |
12591 | oprinfo->dobj.namespace->dobj.name, oprinfo->rolname, |
12592 | oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId); |
12593 | |
12594 | PQclear(res); |
12595 | |
12596 | destroyPQExpBuffer(query); |
12597 | destroyPQExpBuffer(q); |
12598 | destroyPQExpBuffer(delq); |
12599 | destroyPQExpBuffer(oprid); |
12600 | destroyPQExpBuffer(details); |
12601 | } |
12602 | |
12603 | /* |
12604 | * Convert a function reference obtained from pg_operator |
12605 | * |
12606 | * Returns allocated string of what to print, or NULL if function references |
12607 | * is InvalidOid. Returned string is expected to be free'd by the caller. |
12608 | * |
12609 | * The input is a REGPROCEDURE display; we have to strip the argument-types |
12610 | * part. |
12611 | */ |
12612 | static char * |
12613 | convertRegProcReference(Archive *fout, const char *proc) |
12614 | { |
12615 | char *name; |
12616 | char *paren; |
12617 | bool inquote; |
12618 | |
12619 | /* In all cases "-" means a null reference */ |
12620 | if (strcmp(proc, "-" ) == 0) |
12621 | return NULL; |
12622 | |
12623 | name = pg_strdup(proc); |
12624 | /* find non-double-quoted left paren */ |
12625 | inquote = false; |
12626 | for (paren = name; *paren; paren++) |
12627 | { |
12628 | if (*paren == '(' && !inquote) |
12629 | { |
12630 | *paren = '\0'; |
12631 | break; |
12632 | } |
12633 | if (*paren == '"') |
12634 | inquote = !inquote; |
12635 | } |
12636 | return name; |
12637 | } |
12638 | |
12639 | /* |
12640 | * getFormattedOperatorName - retrieve the operator name for the |
12641 | * given operator OID (presented in string form). |
12642 | * |
12643 | * Returns an allocated string, or NULL if the given OID is invalid. |
12644 | * Caller is responsible for free'ing result string. |
12645 | * |
12646 | * What we produce has the format "OPERATOR(schema.oprname)". This is only |
12647 | * useful in commands where the operator's argument types can be inferred from |
12648 | * context. We always schema-qualify the name, though. The predecessor to |
12649 | * this code tried to skip the schema qualification if possible, but that led |
12650 | * to wrong results in corner cases, such as if an operator and its negator |
12651 | * are in different schemas. |
12652 | */ |
12653 | static char * |
12654 | getFormattedOperatorName(Archive *fout, const char *oproid) |
12655 | { |
12656 | OprInfo *oprInfo; |
12657 | |
12658 | /* In all cases "0" means a null reference */ |
12659 | if (strcmp(oproid, "0" ) == 0) |
12660 | return NULL; |
12661 | |
12662 | oprInfo = findOprByOid(atooid(oproid)); |
12663 | if (oprInfo == NULL) |
12664 | { |
12665 | pg_log_warning("could not find operator with OID %s" , |
12666 | oproid); |
12667 | return NULL; |
12668 | } |
12669 | |
12670 | return psprintf("OPERATOR(%s.%s)" , |
12671 | fmtId(oprInfo->dobj.namespace->dobj.name), |
12672 | oprInfo->dobj.name); |
12673 | } |
12674 | |
12675 | /* |
12676 | * Convert a function OID obtained from pg_ts_parser or pg_ts_template |
12677 | * |
12678 | * It is sufficient to use REGPROC rather than REGPROCEDURE, since the |
12679 | * argument lists of these functions are predetermined. Note that the |
12680 | * caller should ensure we are in the proper schema, because the results |
12681 | * are search path dependent! |
12682 | */ |
12683 | static char * |
12684 | convertTSFunction(Archive *fout, Oid funcOid) |
12685 | { |
12686 | char *result; |
12687 | char query[128]; |
12688 | PGresult *res; |
12689 | |
12690 | snprintf(query, sizeof(query), |
12691 | "SELECT '%u'::pg_catalog.regproc" , funcOid); |
12692 | res = ExecuteSqlQueryForSingleRow(fout, query); |
12693 | |
12694 | result = pg_strdup(PQgetvalue(res, 0, 0)); |
12695 | |
12696 | PQclear(res); |
12697 | |
12698 | return result; |
12699 | } |
12700 | |
12701 | /* |
12702 | * dumpAccessMethod |
12703 | * write out a single access method definition |
12704 | */ |
12705 | static void |
12706 | dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo) |
12707 | { |
12708 | DumpOptions *dopt = fout->dopt; |
12709 | PQExpBuffer q; |
12710 | PQExpBuffer delq; |
12711 | char *qamname; |
12712 | |
12713 | /* Skip if not to be dumped */ |
12714 | if (!aminfo->dobj.dump || dopt->dataOnly) |
12715 | return; |
12716 | |
12717 | q = createPQExpBuffer(); |
12718 | delq = createPQExpBuffer(); |
12719 | |
12720 | qamname = pg_strdup(fmtId(aminfo->dobj.name)); |
12721 | |
12722 | appendPQExpBuffer(q, "CREATE ACCESS METHOD %s " , qamname); |
12723 | |
12724 | switch (aminfo->amtype) |
12725 | { |
12726 | case AMTYPE_INDEX: |
12727 | appendPQExpBuffer(q, "TYPE INDEX " ); |
12728 | break; |
12729 | case AMTYPE_TABLE: |
12730 | appendPQExpBuffer(q, "TYPE TABLE " ); |
12731 | break; |
12732 | default: |
12733 | pg_log_warning("invalid type \"%c\" of access method \"%s\"" , |
12734 | aminfo->amtype, qamname); |
12735 | destroyPQExpBuffer(q); |
12736 | destroyPQExpBuffer(delq); |
12737 | free(qamname); |
12738 | return; |
12739 | } |
12740 | |
12741 | appendPQExpBuffer(q, "HANDLER %s;\n" , aminfo->amhandler); |
12742 | |
12743 | appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n" , |
12744 | qamname); |
12745 | |
12746 | if (dopt->binary_upgrade) |
12747 | binary_upgrade_extension_member(q, &aminfo->dobj, |
12748 | "ACCESS METHOD" , qamname, NULL); |
12749 | |
12750 | if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
12751 | ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId, |
12752 | ARCHIVE_OPTS(.tag = aminfo->dobj.name, |
12753 | .description = "ACCESS METHOD" , |
12754 | .section = SECTION_PRE_DATA, |
12755 | .createStmt = q->data, |
12756 | .dropStmt = delq->data)); |
12757 | |
12758 | /* Dump Access Method Comments */ |
12759 | if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
12760 | dumpComment(fout, "ACCESS METHOD" , qamname, |
12761 | NULL, "" , |
12762 | aminfo->dobj.catId, 0, aminfo->dobj.dumpId); |
12763 | |
12764 | destroyPQExpBuffer(q); |
12765 | destroyPQExpBuffer(delq); |
12766 | free(qamname); |
12767 | } |
12768 | |
12769 | /* |
12770 | * dumpOpclass |
12771 | * write out a single operator class definition |
12772 | */ |
12773 | static void |
12774 | dumpOpclass(Archive *fout, OpclassInfo *opcinfo) |
12775 | { |
12776 | DumpOptions *dopt = fout->dopt; |
12777 | PQExpBuffer query; |
12778 | PQExpBuffer q; |
12779 | PQExpBuffer delq; |
12780 | PQExpBuffer nameusing; |
12781 | PGresult *res; |
12782 | int ntups; |
12783 | int i_opcintype; |
12784 | int i_opckeytype; |
12785 | int i_opcdefault; |
12786 | int i_opcfamily; |
12787 | int i_opcfamilyname; |
12788 | int i_opcfamilynsp; |
12789 | int i_amname; |
12790 | int i_amopstrategy; |
12791 | int i_amopreqcheck; |
12792 | int i_amopopr; |
12793 | int i_sortfamily; |
12794 | int i_sortfamilynsp; |
12795 | int i_amprocnum; |
12796 | int i_amproc; |
12797 | int i_amproclefttype; |
12798 | int i_amprocrighttype; |
12799 | char *opcintype; |
12800 | char *opckeytype; |
12801 | char *opcdefault; |
12802 | char *opcfamily; |
12803 | char *opcfamilyname; |
12804 | char *opcfamilynsp; |
12805 | char *amname; |
12806 | char *amopstrategy; |
12807 | char *amopreqcheck; |
12808 | char *amopopr; |
12809 | char *sortfamily; |
12810 | char *sortfamilynsp; |
12811 | char *amprocnum; |
12812 | char *amproc; |
12813 | char *amproclefttype; |
12814 | char *amprocrighttype; |
12815 | bool needComma; |
12816 | int i; |
12817 | |
12818 | /* Skip if not to be dumped */ |
12819 | if (!opcinfo->dobj.dump || dopt->dataOnly) |
12820 | return; |
12821 | |
12822 | query = createPQExpBuffer(); |
12823 | q = createPQExpBuffer(); |
12824 | delq = createPQExpBuffer(); |
12825 | nameusing = createPQExpBuffer(); |
12826 | |
12827 | /* Get additional fields from the pg_opclass row */ |
12828 | if (fout->remoteVersion >= 80300) |
12829 | { |
12830 | appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, " |
12831 | "opckeytype::pg_catalog.regtype, " |
12832 | "opcdefault, opcfamily, " |
12833 | "opfname AS opcfamilyname, " |
12834 | "nspname AS opcfamilynsp, " |
12835 | "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname " |
12836 | "FROM pg_catalog.pg_opclass c " |
12837 | "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily " |
12838 | "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace " |
12839 | "WHERE c.oid = '%u'::pg_catalog.oid" , |
12840 | opcinfo->dobj.catId.oid); |
12841 | } |
12842 | else |
12843 | { |
12844 | appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, " |
12845 | "opckeytype::pg_catalog.regtype, " |
12846 | "opcdefault, NULL AS opcfamily, " |
12847 | "NULL AS opcfamilyname, " |
12848 | "NULL AS opcfamilynsp, " |
12849 | "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname " |
12850 | "FROM pg_catalog.pg_opclass " |
12851 | "WHERE oid = '%u'::pg_catalog.oid" , |
12852 | opcinfo->dobj.catId.oid); |
12853 | } |
12854 | |
12855 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
12856 | |
12857 | i_opcintype = PQfnumber(res, "opcintype" ); |
12858 | i_opckeytype = PQfnumber(res, "opckeytype" ); |
12859 | i_opcdefault = PQfnumber(res, "opcdefault" ); |
12860 | i_opcfamily = PQfnumber(res, "opcfamily" ); |
12861 | i_opcfamilyname = PQfnumber(res, "opcfamilyname" ); |
12862 | i_opcfamilynsp = PQfnumber(res, "opcfamilynsp" ); |
12863 | i_amname = PQfnumber(res, "amname" ); |
12864 | |
12865 | /* opcintype may still be needed after we PQclear res */ |
12866 | opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype)); |
12867 | opckeytype = PQgetvalue(res, 0, i_opckeytype); |
12868 | opcdefault = PQgetvalue(res, 0, i_opcdefault); |
12869 | /* opcfamily will still be needed after we PQclear res */ |
12870 | opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily)); |
12871 | opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname); |
12872 | opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp); |
12873 | /* amname will still be needed after we PQclear res */ |
12874 | amname = pg_strdup(PQgetvalue(res, 0, i_amname)); |
12875 | |
12876 | appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s" , |
12877 | fmtQualifiedDumpable(opcinfo)); |
12878 | appendPQExpBuffer(delq, " USING %s;\n" , |
12879 | fmtId(amname)); |
12880 | |
12881 | /* Build the fixed portion of the CREATE command */ |
12882 | appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n " , |
12883 | fmtQualifiedDumpable(opcinfo)); |
12884 | if (strcmp(opcdefault, "t" ) == 0) |
12885 | appendPQExpBufferStr(q, "DEFAULT " ); |
12886 | appendPQExpBuffer(q, "FOR TYPE %s USING %s" , |
12887 | opcintype, |
12888 | fmtId(amname)); |
12889 | if (strlen(opcfamilyname) > 0) |
12890 | { |
12891 | appendPQExpBufferStr(q, " FAMILY " ); |
12892 | appendPQExpBuffer(q, "%s." , fmtId(opcfamilynsp)); |
12893 | appendPQExpBufferStr(q, fmtId(opcfamilyname)); |
12894 | } |
12895 | appendPQExpBufferStr(q, " AS\n " ); |
12896 | |
12897 | needComma = false; |
12898 | |
12899 | if (strcmp(opckeytype, "-" ) != 0) |
12900 | { |
12901 | appendPQExpBuffer(q, "STORAGE %s" , |
12902 | opckeytype); |
12903 | needComma = true; |
12904 | } |
12905 | |
12906 | PQclear(res); |
12907 | |
12908 | /* |
12909 | * Now fetch and print the OPERATOR entries (pg_amop rows). |
12910 | * |
12911 | * Print only those opfamily members that are tied to the opclass by |
12912 | * pg_depend entries. |
12913 | * |
12914 | * XXX RECHECK is gone as of 8.4, but we'll still print it if dumping an |
12915 | * older server's opclass in which it is used. This is to avoid |
12916 | * hard-to-detect breakage if a newer pg_dump is used to dump from an |
12917 | * older server and then reload into that old version. This can go away |
12918 | * once 8.3 is so old as to not be of interest to anyone. |
12919 | */ |
12920 | resetPQExpBuffer(query); |
12921 | |
12922 | if (fout->remoteVersion >= 90100) |
12923 | { |
12924 | appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, " |
12925 | "amopopr::pg_catalog.regoperator, " |
12926 | "opfname AS sortfamily, " |
12927 | "nspname AS sortfamilynsp " |
12928 | "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON " |
12929 | "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) " |
12930 | "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily " |
12931 | "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace " |
12932 | "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass " |
12933 | "AND refobjid = '%u'::pg_catalog.oid " |
12934 | "AND amopfamily = '%s'::pg_catalog.oid " |
12935 | "ORDER BY amopstrategy" , |
12936 | opcinfo->dobj.catId.oid, |
12937 | opcfamily); |
12938 | } |
12939 | else if (fout->remoteVersion >= 80400) |
12940 | { |
12941 | appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, " |
12942 | "amopopr::pg_catalog.regoperator, " |
12943 | "NULL AS sortfamily, " |
12944 | "NULL AS sortfamilynsp " |
12945 | "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend " |
12946 | "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass " |
12947 | "AND refobjid = '%u'::pg_catalog.oid " |
12948 | "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass " |
12949 | "AND objid = ao.oid " |
12950 | "ORDER BY amopstrategy" , |
12951 | opcinfo->dobj.catId.oid); |
12952 | } |
12953 | else if (fout->remoteVersion >= 80300) |
12954 | { |
12955 | appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, " |
12956 | "amopopr::pg_catalog.regoperator, " |
12957 | "NULL AS sortfamily, " |
12958 | "NULL AS sortfamilynsp " |
12959 | "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend " |
12960 | "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass " |
12961 | "AND refobjid = '%u'::pg_catalog.oid " |
12962 | "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass " |
12963 | "AND objid = ao.oid " |
12964 | "ORDER BY amopstrategy" , |
12965 | opcinfo->dobj.catId.oid); |
12966 | } |
12967 | else |
12968 | { |
12969 | /* |
12970 | * Here, we print all entries since there are no opfamilies and hence |
12971 | * no loose operators to worry about. |
12972 | */ |
12973 | appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, " |
12974 | "amopopr::pg_catalog.regoperator, " |
12975 | "NULL AS sortfamily, " |
12976 | "NULL AS sortfamilynsp " |
12977 | "FROM pg_catalog.pg_amop " |
12978 | "WHERE amopclaid = '%u'::pg_catalog.oid " |
12979 | "ORDER BY amopstrategy" , |
12980 | opcinfo->dobj.catId.oid); |
12981 | } |
12982 | |
12983 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
12984 | |
12985 | ntups = PQntuples(res); |
12986 | |
12987 | i_amopstrategy = PQfnumber(res, "amopstrategy" ); |
12988 | i_amopreqcheck = PQfnumber(res, "amopreqcheck" ); |
12989 | i_amopopr = PQfnumber(res, "amopopr" ); |
12990 | i_sortfamily = PQfnumber(res, "sortfamily" ); |
12991 | i_sortfamilynsp = PQfnumber(res, "sortfamilynsp" ); |
12992 | |
12993 | for (i = 0; i < ntups; i++) |
12994 | { |
12995 | amopstrategy = PQgetvalue(res, i, i_amopstrategy); |
12996 | amopreqcheck = PQgetvalue(res, i, i_amopreqcheck); |
12997 | amopopr = PQgetvalue(res, i, i_amopopr); |
12998 | sortfamily = PQgetvalue(res, i, i_sortfamily); |
12999 | sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp); |
13000 | |
13001 | if (needComma) |
13002 | appendPQExpBufferStr(q, " ,\n " ); |
13003 | |
13004 | appendPQExpBuffer(q, "OPERATOR %s %s" , |
13005 | amopstrategy, amopopr); |
13006 | |
13007 | if (strlen(sortfamily) > 0) |
13008 | { |
13009 | appendPQExpBufferStr(q, " FOR ORDER BY " ); |
13010 | appendPQExpBuffer(q, "%s." , fmtId(sortfamilynsp)); |
13011 | appendPQExpBufferStr(q, fmtId(sortfamily)); |
13012 | } |
13013 | |
13014 | if (strcmp(amopreqcheck, "t" ) == 0) |
13015 | appendPQExpBufferStr(q, " RECHECK" ); |
13016 | |
13017 | needComma = true; |
13018 | } |
13019 | |
13020 | PQclear(res); |
13021 | |
13022 | /* |
13023 | * Now fetch and print the FUNCTION entries (pg_amproc rows). |
13024 | * |
13025 | * Print only those opfamily members that are tied to the opclass by |
13026 | * pg_depend entries. |
13027 | * |
13028 | * We print the amproclefttype/amprocrighttype even though in most cases |
13029 | * the backend could deduce the right values, because of the corner case |
13030 | * of a btree sort support function for a cross-type comparison. That's |
13031 | * only allowed in 9.2 and later, but for simplicity print them in all |
13032 | * versions that have the columns. |
13033 | */ |
13034 | resetPQExpBuffer(query); |
13035 | |
13036 | if (fout->remoteVersion >= 80300) |
13037 | { |
13038 | appendPQExpBuffer(query, "SELECT amprocnum, " |
13039 | "amproc::pg_catalog.regprocedure, " |
13040 | "amproclefttype::pg_catalog.regtype, " |
13041 | "amprocrighttype::pg_catalog.regtype " |
13042 | "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend " |
13043 | "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass " |
13044 | "AND refobjid = '%u'::pg_catalog.oid " |
13045 | "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass " |
13046 | "AND objid = ap.oid " |
13047 | "ORDER BY amprocnum" , |
13048 | opcinfo->dobj.catId.oid); |
13049 | } |
13050 | else |
13051 | { |
13052 | appendPQExpBuffer(query, "SELECT amprocnum, " |
13053 | "amproc::pg_catalog.regprocedure, " |
13054 | "'' AS amproclefttype, " |
13055 | "'' AS amprocrighttype " |
13056 | "FROM pg_catalog.pg_amproc " |
13057 | "WHERE amopclaid = '%u'::pg_catalog.oid " |
13058 | "ORDER BY amprocnum" , |
13059 | opcinfo->dobj.catId.oid); |
13060 | } |
13061 | |
13062 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
13063 | |
13064 | ntups = PQntuples(res); |
13065 | |
13066 | i_amprocnum = PQfnumber(res, "amprocnum" ); |
13067 | i_amproc = PQfnumber(res, "amproc" ); |
13068 | i_amproclefttype = PQfnumber(res, "amproclefttype" ); |
13069 | i_amprocrighttype = PQfnumber(res, "amprocrighttype" ); |
13070 | |
13071 | for (i = 0; i < ntups; i++) |
13072 | { |
13073 | amprocnum = PQgetvalue(res, i, i_amprocnum); |
13074 | amproc = PQgetvalue(res, i, i_amproc); |
13075 | amproclefttype = PQgetvalue(res, i, i_amproclefttype); |
13076 | amprocrighttype = PQgetvalue(res, i, i_amprocrighttype); |
13077 | |
13078 | if (needComma) |
13079 | appendPQExpBufferStr(q, " ,\n " ); |
13080 | |
13081 | appendPQExpBuffer(q, "FUNCTION %s" , amprocnum); |
13082 | |
13083 | if (*amproclefttype && *amprocrighttype) |
13084 | appendPQExpBuffer(q, " (%s, %s)" , amproclefttype, amprocrighttype); |
13085 | |
13086 | appendPQExpBuffer(q, " %s" , amproc); |
13087 | |
13088 | needComma = true; |
13089 | } |
13090 | |
13091 | PQclear(res); |
13092 | |
13093 | /* |
13094 | * If needComma is still false it means we haven't added anything after |
13095 | * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE |
13096 | * clause with the same datatype. This isn't sanctioned by the |
13097 | * documentation, but actually DefineOpClass will treat it as a no-op. |
13098 | */ |
13099 | if (!needComma) |
13100 | appendPQExpBuffer(q, "STORAGE %s" , opcintype); |
13101 | |
13102 | appendPQExpBufferStr(q, ";\n" ); |
13103 | |
13104 | appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name)); |
13105 | appendPQExpBuffer(nameusing, " USING %s" , |
13106 | fmtId(amname)); |
13107 | |
13108 | if (dopt->binary_upgrade) |
13109 | binary_upgrade_extension_member(q, &opcinfo->dobj, |
13110 | "OPERATOR CLASS" , nameusing->data, |
13111 | opcinfo->dobj.namespace->dobj.name); |
13112 | |
13113 | if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
13114 | ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId, |
13115 | ARCHIVE_OPTS(.tag = opcinfo->dobj.name, |
13116 | .namespace = opcinfo->dobj.namespace->dobj.name, |
13117 | .owner = opcinfo->rolname, |
13118 | .description = "OPERATOR CLASS" , |
13119 | .section = SECTION_PRE_DATA, |
13120 | .createStmt = q->data, |
13121 | .dropStmt = delq->data)); |
13122 | |
13123 | /* Dump Operator Class Comments */ |
13124 | if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
13125 | dumpComment(fout, "OPERATOR CLASS" , nameusing->data, |
13126 | opcinfo->dobj.namespace->dobj.name, opcinfo->rolname, |
13127 | opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId); |
13128 | |
13129 | free(opcintype); |
13130 | free(opcfamily); |
13131 | free(amname); |
13132 | destroyPQExpBuffer(query); |
13133 | destroyPQExpBuffer(q); |
13134 | destroyPQExpBuffer(delq); |
13135 | destroyPQExpBuffer(nameusing); |
13136 | } |
13137 | |
13138 | /* |
13139 | * dumpOpfamily |
13140 | * write out a single operator family definition |
13141 | * |
13142 | * Note: this also dumps any "loose" operator members that aren't bound to a |
13143 | * specific opclass within the opfamily. |
13144 | */ |
13145 | static void |
13146 | dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) |
13147 | { |
13148 | DumpOptions *dopt = fout->dopt; |
13149 | PQExpBuffer query; |
13150 | PQExpBuffer q; |
13151 | PQExpBuffer delq; |
13152 | PQExpBuffer nameusing; |
13153 | PGresult *res; |
13154 | PGresult *res_ops; |
13155 | PGresult *res_procs; |
13156 | int ntups; |
13157 | int i_amname; |
13158 | int i_amopstrategy; |
13159 | int i_amopreqcheck; |
13160 | int i_amopopr; |
13161 | int i_sortfamily; |
13162 | int i_sortfamilynsp; |
13163 | int i_amprocnum; |
13164 | int i_amproc; |
13165 | int i_amproclefttype; |
13166 | int i_amprocrighttype; |
13167 | char *amname; |
13168 | char *amopstrategy; |
13169 | char *amopreqcheck; |
13170 | char *amopopr; |
13171 | char *sortfamily; |
13172 | char *sortfamilynsp; |
13173 | char *amprocnum; |
13174 | char *amproc; |
13175 | char *amproclefttype; |
13176 | char *amprocrighttype; |
13177 | bool needComma; |
13178 | int i; |
13179 | |
13180 | /* Skip if not to be dumped */ |
13181 | if (!opfinfo->dobj.dump || dopt->dataOnly) |
13182 | return; |
13183 | |
13184 | query = createPQExpBuffer(); |
13185 | q = createPQExpBuffer(); |
13186 | delq = createPQExpBuffer(); |
13187 | nameusing = createPQExpBuffer(); |
13188 | |
13189 | /* |
13190 | * Fetch only those opfamily members that are tied directly to the |
13191 | * opfamily by pg_depend entries. |
13192 | * |
13193 | * XXX RECHECK is gone as of 8.4, but we'll still print it if dumping an |
13194 | * older server's opclass in which it is used. This is to avoid |
13195 | * hard-to-detect breakage if a newer pg_dump is used to dump from an |
13196 | * older server and then reload into that old version. This can go away |
13197 | * once 8.3 is so old as to not be of interest to anyone. |
13198 | */ |
13199 | if (fout->remoteVersion >= 90100) |
13200 | { |
13201 | appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, " |
13202 | "amopopr::pg_catalog.regoperator, " |
13203 | "opfname AS sortfamily, " |
13204 | "nspname AS sortfamilynsp " |
13205 | "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON " |
13206 | "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) " |
13207 | "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily " |
13208 | "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace " |
13209 | "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass " |
13210 | "AND refobjid = '%u'::pg_catalog.oid " |
13211 | "AND amopfamily = '%u'::pg_catalog.oid " |
13212 | "ORDER BY amopstrategy" , |
13213 | opfinfo->dobj.catId.oid, |
13214 | opfinfo->dobj.catId.oid); |
13215 | } |
13216 | else if (fout->remoteVersion >= 80400) |
13217 | { |
13218 | appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, " |
13219 | "amopopr::pg_catalog.regoperator, " |
13220 | "NULL AS sortfamily, " |
13221 | "NULL AS sortfamilynsp " |
13222 | "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend " |
13223 | "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass " |
13224 | "AND refobjid = '%u'::pg_catalog.oid " |
13225 | "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass " |
13226 | "AND objid = ao.oid " |
13227 | "ORDER BY amopstrategy" , |
13228 | opfinfo->dobj.catId.oid); |
13229 | } |
13230 | else |
13231 | { |
13232 | appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, " |
13233 | "amopopr::pg_catalog.regoperator, " |
13234 | "NULL AS sortfamily, " |
13235 | "NULL AS sortfamilynsp " |
13236 | "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend " |
13237 | "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass " |
13238 | "AND refobjid = '%u'::pg_catalog.oid " |
13239 | "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass " |
13240 | "AND objid = ao.oid " |
13241 | "ORDER BY amopstrategy" , |
13242 | opfinfo->dobj.catId.oid); |
13243 | } |
13244 | |
13245 | res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
13246 | |
13247 | resetPQExpBuffer(query); |
13248 | |
13249 | appendPQExpBuffer(query, "SELECT amprocnum, " |
13250 | "amproc::pg_catalog.regprocedure, " |
13251 | "amproclefttype::pg_catalog.regtype, " |
13252 | "amprocrighttype::pg_catalog.regtype " |
13253 | "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend " |
13254 | "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass " |
13255 | "AND refobjid = '%u'::pg_catalog.oid " |
13256 | "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass " |
13257 | "AND objid = ap.oid " |
13258 | "ORDER BY amprocnum" , |
13259 | opfinfo->dobj.catId.oid); |
13260 | |
13261 | res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
13262 | |
13263 | /* Get additional fields from the pg_opfamily row */ |
13264 | resetPQExpBuffer(query); |
13265 | |
13266 | appendPQExpBuffer(query, "SELECT " |
13267 | "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname " |
13268 | "FROM pg_catalog.pg_opfamily " |
13269 | "WHERE oid = '%u'::pg_catalog.oid" , |
13270 | opfinfo->dobj.catId.oid); |
13271 | |
13272 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
13273 | |
13274 | i_amname = PQfnumber(res, "amname" ); |
13275 | |
13276 | /* amname will still be needed after we PQclear res */ |
13277 | amname = pg_strdup(PQgetvalue(res, 0, i_amname)); |
13278 | |
13279 | appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s" , |
13280 | fmtQualifiedDumpable(opfinfo)); |
13281 | appendPQExpBuffer(delq, " USING %s;\n" , |
13282 | fmtId(amname)); |
13283 | |
13284 | /* Build the fixed portion of the CREATE command */ |
13285 | appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s" , |
13286 | fmtQualifiedDumpable(opfinfo)); |
13287 | appendPQExpBuffer(q, " USING %s;\n" , |
13288 | fmtId(amname)); |
13289 | |
13290 | PQclear(res); |
13291 | |
13292 | /* Do we need an ALTER to add loose members? */ |
13293 | if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0) |
13294 | { |
13295 | appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s" , |
13296 | fmtQualifiedDumpable(opfinfo)); |
13297 | appendPQExpBuffer(q, " USING %s ADD\n " , |
13298 | fmtId(amname)); |
13299 | |
13300 | needComma = false; |
13301 | |
13302 | /* |
13303 | * Now fetch and print the OPERATOR entries (pg_amop rows). |
13304 | */ |
13305 | ntups = PQntuples(res_ops); |
13306 | |
13307 | i_amopstrategy = PQfnumber(res_ops, "amopstrategy" ); |
13308 | i_amopreqcheck = PQfnumber(res_ops, "amopreqcheck" ); |
13309 | i_amopopr = PQfnumber(res_ops, "amopopr" ); |
13310 | i_sortfamily = PQfnumber(res_ops, "sortfamily" ); |
13311 | i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp" ); |
13312 | |
13313 | for (i = 0; i < ntups; i++) |
13314 | { |
13315 | amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy); |
13316 | amopreqcheck = PQgetvalue(res_ops, i, i_amopreqcheck); |
13317 | amopopr = PQgetvalue(res_ops, i, i_amopopr); |
13318 | sortfamily = PQgetvalue(res_ops, i, i_sortfamily); |
13319 | sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp); |
13320 | |
13321 | if (needComma) |
13322 | appendPQExpBufferStr(q, " ,\n " ); |
13323 | |
13324 | appendPQExpBuffer(q, "OPERATOR %s %s" , |
13325 | amopstrategy, amopopr); |
13326 | |
13327 | if (strlen(sortfamily) > 0) |
13328 | { |
13329 | appendPQExpBufferStr(q, " FOR ORDER BY " ); |
13330 | appendPQExpBuffer(q, "%s." , fmtId(sortfamilynsp)); |
13331 | appendPQExpBufferStr(q, fmtId(sortfamily)); |
13332 | } |
13333 | |
13334 | if (strcmp(amopreqcheck, "t" ) == 0) |
13335 | appendPQExpBufferStr(q, " RECHECK" ); |
13336 | |
13337 | needComma = true; |
13338 | } |
13339 | |
13340 | /* |
13341 | * Now fetch and print the FUNCTION entries (pg_amproc rows). |
13342 | */ |
13343 | ntups = PQntuples(res_procs); |
13344 | |
13345 | i_amprocnum = PQfnumber(res_procs, "amprocnum" ); |
13346 | i_amproc = PQfnumber(res_procs, "amproc" ); |
13347 | i_amproclefttype = PQfnumber(res_procs, "amproclefttype" ); |
13348 | i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype" ); |
13349 | |
13350 | for (i = 0; i < ntups; i++) |
13351 | { |
13352 | amprocnum = PQgetvalue(res_procs, i, i_amprocnum); |
13353 | amproc = PQgetvalue(res_procs, i, i_amproc); |
13354 | amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype); |
13355 | amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype); |
13356 | |
13357 | if (needComma) |
13358 | appendPQExpBufferStr(q, " ,\n " ); |
13359 | |
13360 | appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s" , |
13361 | amprocnum, amproclefttype, amprocrighttype, |
13362 | amproc); |
13363 | |
13364 | needComma = true; |
13365 | } |
13366 | |
13367 | appendPQExpBufferStr(q, ";\n" ); |
13368 | } |
13369 | |
13370 | appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name)); |
13371 | appendPQExpBuffer(nameusing, " USING %s" , |
13372 | fmtId(amname)); |
13373 | |
13374 | if (dopt->binary_upgrade) |
13375 | binary_upgrade_extension_member(q, &opfinfo->dobj, |
13376 | "OPERATOR FAMILY" , nameusing->data, |
13377 | opfinfo->dobj.namespace->dobj.name); |
13378 | |
13379 | if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
13380 | ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId, |
13381 | ARCHIVE_OPTS(.tag = opfinfo->dobj.name, |
13382 | .namespace = opfinfo->dobj.namespace->dobj.name, |
13383 | .owner = opfinfo->rolname, |
13384 | .description = "OPERATOR FAMILY" , |
13385 | .section = SECTION_PRE_DATA, |
13386 | .createStmt = q->data, |
13387 | .dropStmt = delq->data)); |
13388 | |
13389 | /* Dump Operator Family Comments */ |
13390 | if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
13391 | dumpComment(fout, "OPERATOR FAMILY" , nameusing->data, |
13392 | opfinfo->dobj.namespace->dobj.name, opfinfo->rolname, |
13393 | opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId); |
13394 | |
13395 | free(amname); |
13396 | PQclear(res_ops); |
13397 | PQclear(res_procs); |
13398 | destroyPQExpBuffer(query); |
13399 | destroyPQExpBuffer(q); |
13400 | destroyPQExpBuffer(delq); |
13401 | destroyPQExpBuffer(nameusing); |
13402 | } |
13403 | |
13404 | /* |
13405 | * dumpCollation |
13406 | * write out a single collation definition |
13407 | */ |
13408 | static void |
13409 | dumpCollation(Archive *fout, CollInfo *collinfo) |
13410 | { |
13411 | DumpOptions *dopt = fout->dopt; |
13412 | PQExpBuffer query; |
13413 | PQExpBuffer q; |
13414 | PQExpBuffer delq; |
13415 | char *qcollname; |
13416 | PGresult *res; |
13417 | int i_collprovider; |
13418 | int i_collisdeterministic; |
13419 | int i_collcollate; |
13420 | int i_collctype; |
13421 | const char *collprovider; |
13422 | const char *collcollate; |
13423 | const char *collctype; |
13424 | |
13425 | /* Skip if not to be dumped */ |
13426 | if (!collinfo->dobj.dump || dopt->dataOnly) |
13427 | return; |
13428 | |
13429 | query = createPQExpBuffer(); |
13430 | q = createPQExpBuffer(); |
13431 | delq = createPQExpBuffer(); |
13432 | |
13433 | qcollname = pg_strdup(fmtId(collinfo->dobj.name)); |
13434 | |
13435 | /* Get collation-specific details */ |
13436 | appendPQExpBuffer(query, "SELECT " ); |
13437 | |
13438 | if (fout->remoteVersion >= 100000) |
13439 | appendPQExpBuffer(query, |
13440 | "collprovider, " |
13441 | "collversion, " ); |
13442 | else |
13443 | appendPQExpBuffer(query, |
13444 | "'c' AS collprovider, " |
13445 | "NULL AS collversion, " ); |
13446 | |
13447 | if (fout->remoteVersion >= 120000) |
13448 | appendPQExpBuffer(query, |
13449 | "collisdeterministic, " ); |
13450 | else |
13451 | appendPQExpBuffer(query, |
13452 | "true AS collisdeterministic, " ); |
13453 | |
13454 | appendPQExpBuffer(query, |
13455 | "collcollate, " |
13456 | "collctype " |
13457 | "FROM pg_catalog.pg_collation c " |
13458 | "WHERE c.oid = '%u'::pg_catalog.oid" , |
13459 | collinfo->dobj.catId.oid); |
13460 | |
13461 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
13462 | |
13463 | i_collprovider = PQfnumber(res, "collprovider" ); |
13464 | i_collisdeterministic = PQfnumber(res, "collisdeterministic" ); |
13465 | i_collcollate = PQfnumber(res, "collcollate" ); |
13466 | i_collctype = PQfnumber(res, "collctype" ); |
13467 | |
13468 | collprovider = PQgetvalue(res, 0, i_collprovider); |
13469 | collcollate = PQgetvalue(res, 0, i_collcollate); |
13470 | collctype = PQgetvalue(res, 0, i_collctype); |
13471 | |
13472 | appendPQExpBuffer(delq, "DROP COLLATION %s;\n" , |
13473 | fmtQualifiedDumpable(collinfo)); |
13474 | |
13475 | appendPQExpBuffer(q, "CREATE COLLATION %s (" , |
13476 | fmtQualifiedDumpable(collinfo)); |
13477 | |
13478 | appendPQExpBufferStr(q, "provider = " ); |
13479 | if (collprovider[0] == 'c') |
13480 | appendPQExpBufferStr(q, "libc" ); |
13481 | else if (collprovider[0] == 'i') |
13482 | appendPQExpBufferStr(q, "icu" ); |
13483 | else if (collprovider[0] == 'd') |
13484 | /* to allow dumping pg_catalog; not accepted on input */ |
13485 | appendPQExpBufferStr(q, "default" ); |
13486 | else |
13487 | fatal("unrecognized collation provider: %s" , |
13488 | collprovider); |
13489 | |
13490 | if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f" ) == 0) |
13491 | appendPQExpBufferStr(q, ", deterministic = false" ); |
13492 | |
13493 | if (strcmp(collcollate, collctype) == 0) |
13494 | { |
13495 | appendPQExpBufferStr(q, ", locale = " ); |
13496 | appendStringLiteralAH(q, collcollate, fout); |
13497 | } |
13498 | else |
13499 | { |
13500 | appendPQExpBufferStr(q, ", lc_collate = " ); |
13501 | appendStringLiteralAH(q, collcollate, fout); |
13502 | appendPQExpBufferStr(q, ", lc_ctype = " ); |
13503 | appendStringLiteralAH(q, collctype, fout); |
13504 | } |
13505 | |
13506 | /* |
13507 | * For binary upgrade, carry over the collation version. For normal |
13508 | * dump/restore, omit the version, so that it is computed upon restore. |
13509 | */ |
13510 | if (dopt->binary_upgrade) |
13511 | { |
13512 | int i_collversion; |
13513 | |
13514 | i_collversion = PQfnumber(res, "collversion" ); |
13515 | if (!PQgetisnull(res, 0, i_collversion)) |
13516 | { |
13517 | appendPQExpBufferStr(q, ", version = " ); |
13518 | appendStringLiteralAH(q, |
13519 | PQgetvalue(res, 0, i_collversion), |
13520 | fout); |
13521 | } |
13522 | } |
13523 | |
13524 | appendPQExpBufferStr(q, ");\n" ); |
13525 | |
13526 | if (dopt->binary_upgrade) |
13527 | binary_upgrade_extension_member(q, &collinfo->dobj, |
13528 | "COLLATION" , qcollname, |
13529 | collinfo->dobj.namespace->dobj.name); |
13530 | |
13531 | if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
13532 | ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId, |
13533 | ARCHIVE_OPTS(.tag = collinfo->dobj.name, |
13534 | .namespace = collinfo->dobj.namespace->dobj.name, |
13535 | .owner = collinfo->rolname, |
13536 | .description = "COLLATION" , |
13537 | .section = SECTION_PRE_DATA, |
13538 | .createStmt = q->data, |
13539 | .dropStmt = delq->data)); |
13540 | |
13541 | /* Dump Collation Comments */ |
13542 | if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
13543 | dumpComment(fout, "COLLATION" , qcollname, |
13544 | collinfo->dobj.namespace->dobj.name, collinfo->rolname, |
13545 | collinfo->dobj.catId, 0, collinfo->dobj.dumpId); |
13546 | |
13547 | PQclear(res); |
13548 | |
13549 | destroyPQExpBuffer(query); |
13550 | destroyPQExpBuffer(q); |
13551 | destroyPQExpBuffer(delq); |
13552 | free(qcollname); |
13553 | } |
13554 | |
13555 | /* |
13556 | * dumpConversion |
13557 | * write out a single conversion definition |
13558 | */ |
13559 | static void |
13560 | dumpConversion(Archive *fout, ConvInfo *convinfo) |
13561 | { |
13562 | DumpOptions *dopt = fout->dopt; |
13563 | PQExpBuffer query; |
13564 | PQExpBuffer q; |
13565 | PQExpBuffer delq; |
13566 | char *qconvname; |
13567 | PGresult *res; |
13568 | int i_conforencoding; |
13569 | int i_contoencoding; |
13570 | int i_conproc; |
13571 | int i_condefault; |
13572 | const char *conforencoding; |
13573 | const char *contoencoding; |
13574 | const char *conproc; |
13575 | bool condefault; |
13576 | |
13577 | /* Skip if not to be dumped */ |
13578 | if (!convinfo->dobj.dump || dopt->dataOnly) |
13579 | return; |
13580 | |
13581 | query = createPQExpBuffer(); |
13582 | q = createPQExpBuffer(); |
13583 | delq = createPQExpBuffer(); |
13584 | |
13585 | qconvname = pg_strdup(fmtId(convinfo->dobj.name)); |
13586 | |
13587 | /* Get conversion-specific details */ |
13588 | appendPQExpBuffer(query, "SELECT " |
13589 | "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, " |
13590 | "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, " |
13591 | "conproc, condefault " |
13592 | "FROM pg_catalog.pg_conversion c " |
13593 | "WHERE c.oid = '%u'::pg_catalog.oid" , |
13594 | convinfo->dobj.catId.oid); |
13595 | |
13596 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
13597 | |
13598 | i_conforencoding = PQfnumber(res, "conforencoding" ); |
13599 | i_contoencoding = PQfnumber(res, "contoencoding" ); |
13600 | i_conproc = PQfnumber(res, "conproc" ); |
13601 | i_condefault = PQfnumber(res, "condefault" ); |
13602 | |
13603 | conforencoding = PQgetvalue(res, 0, i_conforencoding); |
13604 | contoencoding = PQgetvalue(res, 0, i_contoencoding); |
13605 | conproc = PQgetvalue(res, 0, i_conproc); |
13606 | condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't'); |
13607 | |
13608 | appendPQExpBuffer(delq, "DROP CONVERSION %s;\n" , |
13609 | fmtQualifiedDumpable(convinfo)); |
13610 | |
13611 | appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR " , |
13612 | (condefault) ? "DEFAULT " : "" , |
13613 | fmtQualifiedDumpable(convinfo)); |
13614 | appendStringLiteralAH(q, conforencoding, fout); |
13615 | appendPQExpBufferStr(q, " TO " ); |
13616 | appendStringLiteralAH(q, contoencoding, fout); |
13617 | /* regproc output is already sufficiently quoted */ |
13618 | appendPQExpBuffer(q, " FROM %s;\n" , conproc); |
13619 | |
13620 | if (dopt->binary_upgrade) |
13621 | binary_upgrade_extension_member(q, &convinfo->dobj, |
13622 | "CONVERSION" , qconvname, |
13623 | convinfo->dobj.namespace->dobj.name); |
13624 | |
13625 | if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
13626 | ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId, |
13627 | ARCHIVE_OPTS(.tag = convinfo->dobj.name, |
13628 | .namespace = convinfo->dobj.namespace->dobj.name, |
13629 | .owner = convinfo->rolname, |
13630 | .description = "CONVERSION" , |
13631 | .section = SECTION_PRE_DATA, |
13632 | .createStmt = q->data, |
13633 | .dropStmt = delq->data)); |
13634 | |
13635 | /* Dump Conversion Comments */ |
13636 | if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
13637 | dumpComment(fout, "CONVERSION" , qconvname, |
13638 | convinfo->dobj.namespace->dobj.name, convinfo->rolname, |
13639 | convinfo->dobj.catId, 0, convinfo->dobj.dumpId); |
13640 | |
13641 | PQclear(res); |
13642 | |
13643 | destroyPQExpBuffer(query); |
13644 | destroyPQExpBuffer(q); |
13645 | destroyPQExpBuffer(delq); |
13646 | free(qconvname); |
13647 | } |
13648 | |
13649 | /* |
13650 | * format_aggregate_signature: generate aggregate name and argument list |
13651 | * |
13652 | * The argument type names are qualified if needed. The aggregate name |
13653 | * is never qualified. |
13654 | */ |
13655 | static char * |
13656 | format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes) |
13657 | { |
13658 | PQExpBufferData buf; |
13659 | int j; |
13660 | |
13661 | initPQExpBuffer(&buf); |
13662 | if (honor_quotes) |
13663 | appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name)); |
13664 | else |
13665 | appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name); |
13666 | |
13667 | if (agginfo->aggfn.nargs == 0) |
13668 | appendPQExpBuffer(&buf, "(*)" ); |
13669 | else |
13670 | { |
13671 | appendPQExpBufferChar(&buf, '('); |
13672 | for (j = 0; j < agginfo->aggfn.nargs; j++) |
13673 | { |
13674 | char *typname; |
13675 | |
13676 | typname = getFormattedTypeName(fout, agginfo->aggfn.argtypes[j], |
13677 | zeroAsOpaque); |
13678 | |
13679 | appendPQExpBuffer(&buf, "%s%s" , |
13680 | (j > 0) ? ", " : "" , |
13681 | typname); |
13682 | free(typname); |
13683 | } |
13684 | appendPQExpBufferChar(&buf, ')'); |
13685 | } |
13686 | return buf.data; |
13687 | } |
13688 | |
13689 | /* |
13690 | * dumpAgg |
13691 | * write out a single aggregate definition |
13692 | */ |
13693 | static void |
13694 | dumpAgg(Archive *fout, AggInfo *agginfo) |
13695 | { |
13696 | DumpOptions *dopt = fout->dopt; |
13697 | PQExpBuffer query; |
13698 | PQExpBuffer q; |
13699 | PQExpBuffer delq; |
13700 | PQExpBuffer details; |
13701 | char *aggsig; /* identity signature */ |
13702 | char *aggfullsig = NULL; /* full signature */ |
13703 | char *aggsig_tag; |
13704 | PGresult *res; |
13705 | int i_aggtransfn; |
13706 | int i_aggfinalfn; |
13707 | int i_aggcombinefn; |
13708 | int i_aggserialfn; |
13709 | int i_aggdeserialfn; |
13710 | int i_aggmtransfn; |
13711 | int i_aggminvtransfn; |
13712 | int i_aggmfinalfn; |
13713 | int ; |
13714 | int ; |
13715 | int i_aggfinalmodify; |
13716 | int i_aggmfinalmodify; |
13717 | int i_aggsortop; |
13718 | int i_aggkind; |
13719 | int i_aggtranstype; |
13720 | int i_aggtransspace; |
13721 | int i_aggmtranstype; |
13722 | int i_aggmtransspace; |
13723 | int i_agginitval; |
13724 | int i_aggminitval; |
13725 | int i_convertok; |
13726 | int i_proparallel; |
13727 | const char *aggtransfn; |
13728 | const char *aggfinalfn; |
13729 | const char *aggcombinefn; |
13730 | const char *aggserialfn; |
13731 | const char *aggdeserialfn; |
13732 | const char *aggmtransfn; |
13733 | const char *aggminvtransfn; |
13734 | const char *aggmfinalfn; |
13735 | bool ; |
13736 | bool ; |
13737 | char aggfinalmodify; |
13738 | char aggmfinalmodify; |
13739 | const char *aggsortop; |
13740 | char *aggsortconvop; |
13741 | char aggkind; |
13742 | const char *aggtranstype; |
13743 | const char *aggtransspace; |
13744 | const char *aggmtranstype; |
13745 | const char *aggmtransspace; |
13746 | const char *agginitval; |
13747 | const char *aggminitval; |
13748 | bool convertok; |
13749 | const char *proparallel; |
13750 | char defaultfinalmodify; |
13751 | |
13752 | /* Skip if not to be dumped */ |
13753 | if (!agginfo->aggfn.dobj.dump || dopt->dataOnly) |
13754 | return; |
13755 | |
13756 | query = createPQExpBuffer(); |
13757 | q = createPQExpBuffer(); |
13758 | delq = createPQExpBuffer(); |
13759 | details = createPQExpBuffer(); |
13760 | |
13761 | /* Get aggregate-specific details */ |
13762 | if (fout->remoteVersion >= 110000) |
13763 | { |
13764 | appendPQExpBuffer(query, "SELECT aggtransfn, " |
13765 | "aggfinalfn, aggtranstype::pg_catalog.regtype, " |
13766 | "aggcombinefn, aggserialfn, aggdeserialfn, aggmtransfn, " |
13767 | "aggminvtransfn, aggmfinalfn, aggmtranstype::pg_catalog.regtype, " |
13768 | "aggfinalextra, aggmfinalextra, " |
13769 | "aggfinalmodify, aggmfinalmodify, " |
13770 | "aggsortop, " |
13771 | "aggkind, " |
13772 | "aggtransspace, agginitval, " |
13773 | "aggmtransspace, aggminitval, " |
13774 | "true AS convertok, " |
13775 | "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, " |
13776 | "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs, " |
13777 | "p.proparallel " |
13778 | "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " |
13779 | "WHERE a.aggfnoid = p.oid " |
13780 | "AND p.oid = '%u'::pg_catalog.oid" , |
13781 | agginfo->aggfn.dobj.catId.oid); |
13782 | } |
13783 | else if (fout->remoteVersion >= 90600) |
13784 | { |
13785 | appendPQExpBuffer(query, "SELECT aggtransfn, " |
13786 | "aggfinalfn, aggtranstype::pg_catalog.regtype, " |
13787 | "aggcombinefn, aggserialfn, aggdeserialfn, aggmtransfn, " |
13788 | "aggminvtransfn, aggmfinalfn, aggmtranstype::pg_catalog.regtype, " |
13789 | "aggfinalextra, aggmfinalextra, " |
13790 | "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, " |
13791 | "aggsortop, " |
13792 | "aggkind, " |
13793 | "aggtransspace, agginitval, " |
13794 | "aggmtransspace, aggminitval, " |
13795 | "true AS convertok, " |
13796 | "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, " |
13797 | "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs, " |
13798 | "p.proparallel " |
13799 | "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " |
13800 | "WHERE a.aggfnoid = p.oid " |
13801 | "AND p.oid = '%u'::pg_catalog.oid" , |
13802 | agginfo->aggfn.dobj.catId.oid); |
13803 | } |
13804 | else if (fout->remoteVersion >= 90400) |
13805 | { |
13806 | appendPQExpBuffer(query, "SELECT aggtransfn, " |
13807 | "aggfinalfn, aggtranstype::pg_catalog.regtype, " |
13808 | "'-' AS aggcombinefn, '-' AS aggserialfn, " |
13809 | "'-' AS aggdeserialfn, aggmtransfn, aggminvtransfn, " |
13810 | "aggmfinalfn, aggmtranstype::pg_catalog.regtype, " |
13811 | "aggfinalextra, aggmfinalextra, " |
13812 | "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, " |
13813 | "aggsortop, " |
13814 | "aggkind, " |
13815 | "aggtransspace, agginitval, " |
13816 | "aggmtransspace, aggminitval, " |
13817 | "true AS convertok, " |
13818 | "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, " |
13819 | "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs " |
13820 | "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " |
13821 | "WHERE a.aggfnoid = p.oid " |
13822 | "AND p.oid = '%u'::pg_catalog.oid" , |
13823 | agginfo->aggfn.dobj.catId.oid); |
13824 | } |
13825 | else if (fout->remoteVersion >= 80400) |
13826 | { |
13827 | appendPQExpBuffer(query, "SELECT aggtransfn, " |
13828 | "aggfinalfn, aggtranstype::pg_catalog.regtype, " |
13829 | "'-' AS aggcombinefn, '-' AS aggserialfn, " |
13830 | "'-' AS aggdeserialfn, '-' AS aggmtransfn, " |
13831 | "'-' AS aggminvtransfn, '-' AS aggmfinalfn, " |
13832 | "0 AS aggmtranstype, false AS aggfinalextra, " |
13833 | "false AS aggmfinalextra, " |
13834 | "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, " |
13835 | "aggsortop, " |
13836 | "'n' AS aggkind, " |
13837 | "0 AS aggtransspace, agginitval, " |
13838 | "0 AS aggmtransspace, NULL AS aggminitval, " |
13839 | "true AS convertok, " |
13840 | "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, " |
13841 | "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs " |
13842 | "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " |
13843 | "WHERE a.aggfnoid = p.oid " |
13844 | "AND p.oid = '%u'::pg_catalog.oid" , |
13845 | agginfo->aggfn.dobj.catId.oid); |
13846 | } |
13847 | else if (fout->remoteVersion >= 80100) |
13848 | { |
13849 | appendPQExpBuffer(query, "SELECT aggtransfn, " |
13850 | "aggfinalfn, aggtranstype::pg_catalog.regtype, " |
13851 | "'-' AS aggcombinefn, '-' AS aggserialfn, " |
13852 | "'-' AS aggdeserialfn, '-' AS aggmtransfn, " |
13853 | "'-' AS aggminvtransfn, '-' AS aggmfinalfn, " |
13854 | "0 AS aggmtranstype, false AS aggfinalextra, " |
13855 | "false AS aggmfinalextra, " |
13856 | "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, " |
13857 | "aggsortop, " |
13858 | "'n' AS aggkind, " |
13859 | "0 AS aggtransspace, agginitval, " |
13860 | "0 AS aggmtransspace, NULL AS aggminitval, " |
13861 | "true AS convertok " |
13862 | "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " |
13863 | "WHERE a.aggfnoid = p.oid " |
13864 | "AND p.oid = '%u'::pg_catalog.oid" , |
13865 | agginfo->aggfn.dobj.catId.oid); |
13866 | } |
13867 | else |
13868 | { |
13869 | appendPQExpBuffer(query, "SELECT aggtransfn, " |
13870 | "aggfinalfn, aggtranstype::pg_catalog.regtype, " |
13871 | "'-' AS aggcombinefn, '-' AS aggserialfn, " |
13872 | "'-' AS aggdeserialfn, '-' AS aggmtransfn, " |
13873 | "'-' AS aggminvtransfn, '-' AS aggmfinalfn, " |
13874 | "0 AS aggmtranstype, false AS aggfinalextra, " |
13875 | "false AS aggmfinalextra, " |
13876 | "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, " |
13877 | "0 AS aggsortop, " |
13878 | "'n' AS aggkind, " |
13879 | "0 AS aggtransspace, agginitval, " |
13880 | "0 AS aggmtransspace, NULL AS aggminitval, " |
13881 | "true AS convertok " |
13882 | "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " |
13883 | "WHERE a.aggfnoid = p.oid " |
13884 | "AND p.oid = '%u'::pg_catalog.oid" , |
13885 | agginfo->aggfn.dobj.catId.oid); |
13886 | } |
13887 | |
13888 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
13889 | |
13890 | i_aggtransfn = PQfnumber(res, "aggtransfn" ); |
13891 | i_aggfinalfn = PQfnumber(res, "aggfinalfn" ); |
13892 | i_aggcombinefn = PQfnumber(res, "aggcombinefn" ); |
13893 | i_aggserialfn = PQfnumber(res, "aggserialfn" ); |
13894 | i_aggdeserialfn = PQfnumber(res, "aggdeserialfn" ); |
13895 | i_aggmtransfn = PQfnumber(res, "aggmtransfn" ); |
13896 | i_aggminvtransfn = PQfnumber(res, "aggminvtransfn" ); |
13897 | i_aggmfinalfn = PQfnumber(res, "aggmfinalfn" ); |
13898 | i_aggfinalextra = PQfnumber(res, "aggfinalextra" ); |
13899 | i_aggmfinalextra = PQfnumber(res, "aggmfinalextra" ); |
13900 | i_aggfinalmodify = PQfnumber(res, "aggfinalmodify" ); |
13901 | i_aggmfinalmodify = PQfnumber(res, "aggmfinalmodify" ); |
13902 | i_aggsortop = PQfnumber(res, "aggsortop" ); |
13903 | i_aggkind = PQfnumber(res, "aggkind" ); |
13904 | i_aggtranstype = PQfnumber(res, "aggtranstype" ); |
13905 | i_aggtransspace = PQfnumber(res, "aggtransspace" ); |
13906 | i_aggmtranstype = PQfnumber(res, "aggmtranstype" ); |
13907 | i_aggmtransspace = PQfnumber(res, "aggmtransspace" ); |
13908 | i_agginitval = PQfnumber(res, "agginitval" ); |
13909 | i_aggminitval = PQfnumber(res, "aggminitval" ); |
13910 | i_convertok = PQfnumber(res, "convertok" ); |
13911 | i_proparallel = PQfnumber(res, "proparallel" ); |
13912 | |
13913 | aggtransfn = PQgetvalue(res, 0, i_aggtransfn); |
13914 | aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); |
13915 | aggcombinefn = PQgetvalue(res, 0, i_aggcombinefn); |
13916 | aggserialfn = PQgetvalue(res, 0, i_aggserialfn); |
13917 | aggdeserialfn = PQgetvalue(res, 0, i_aggdeserialfn); |
13918 | aggmtransfn = PQgetvalue(res, 0, i_aggmtransfn); |
13919 | aggminvtransfn = PQgetvalue(res, 0, i_aggminvtransfn); |
13920 | aggmfinalfn = PQgetvalue(res, 0, i_aggmfinalfn); |
13921 | aggfinalextra = (PQgetvalue(res, 0, i_aggfinalextra)[0] == 't'); |
13922 | aggmfinalextra = (PQgetvalue(res, 0, i_aggmfinalextra)[0] == 't'); |
13923 | aggfinalmodify = PQgetvalue(res, 0, i_aggfinalmodify)[0]; |
13924 | aggmfinalmodify = PQgetvalue(res, 0, i_aggmfinalmodify)[0]; |
13925 | aggsortop = PQgetvalue(res, 0, i_aggsortop); |
13926 | aggkind = PQgetvalue(res, 0, i_aggkind)[0]; |
13927 | aggtranstype = PQgetvalue(res, 0, i_aggtranstype); |
13928 | aggtransspace = PQgetvalue(res, 0, i_aggtransspace); |
13929 | aggmtranstype = PQgetvalue(res, 0, i_aggmtranstype); |
13930 | aggmtransspace = PQgetvalue(res, 0, i_aggmtransspace); |
13931 | agginitval = PQgetvalue(res, 0, i_agginitval); |
13932 | aggminitval = PQgetvalue(res, 0, i_aggminitval); |
13933 | convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); |
13934 | |
13935 | if (fout->remoteVersion >= 80400) |
13936 | { |
13937 | /* 8.4 or later; we rely on server-side code for most of the work */ |
13938 | char *funcargs; |
13939 | char *funciargs; |
13940 | |
13941 | funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs" )); |
13942 | funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs" )); |
13943 | aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true); |
13944 | aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true); |
13945 | } |
13946 | else |
13947 | /* pre-8.4, do it ourselves */ |
13948 | aggsig = format_aggregate_signature(agginfo, fout, true); |
13949 | |
13950 | aggsig_tag = format_aggregate_signature(agginfo, fout, false); |
13951 | |
13952 | if (i_proparallel != -1) |
13953 | proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel" )); |
13954 | else |
13955 | proparallel = NULL; |
13956 | |
13957 | if (!convertok) |
13958 | { |
13959 | pg_log_warning("aggregate function %s could not be dumped correctly for this database version; ignored" , |
13960 | aggsig); |
13961 | |
13962 | if (aggfullsig) |
13963 | free(aggfullsig); |
13964 | |
13965 | free(aggsig); |
13966 | |
13967 | return; |
13968 | } |
13969 | |
13970 | /* identify default modify flag for aggkind (must match DefineAggregate) */ |
13971 | defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE; |
13972 | /* replace omitted flags for old versions */ |
13973 | if (aggfinalmodify == '0') |
13974 | aggfinalmodify = defaultfinalmodify; |
13975 | if (aggmfinalmodify == '0') |
13976 | aggmfinalmodify = defaultfinalmodify; |
13977 | |
13978 | /* regproc and regtype output is already sufficiently quoted */ |
13979 | appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s" , |
13980 | aggtransfn, aggtranstype); |
13981 | |
13982 | if (strcmp(aggtransspace, "0" ) != 0) |
13983 | { |
13984 | appendPQExpBuffer(details, ",\n SSPACE = %s" , |
13985 | aggtransspace); |
13986 | } |
13987 | |
13988 | if (!PQgetisnull(res, 0, i_agginitval)) |
13989 | { |
13990 | appendPQExpBufferStr(details, ",\n INITCOND = " ); |
13991 | appendStringLiteralAH(details, agginitval, fout); |
13992 | } |
13993 | |
13994 | if (strcmp(aggfinalfn, "-" ) != 0) |
13995 | { |
13996 | appendPQExpBuffer(details, ",\n FINALFUNC = %s" , |
13997 | aggfinalfn); |
13998 | if (aggfinalextra) |
13999 | appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA" ); |
14000 | if (aggfinalmodify != defaultfinalmodify) |
14001 | { |
14002 | switch (aggfinalmodify) |
14003 | { |
14004 | case AGGMODIFY_READ_ONLY: |
14005 | appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY" ); |
14006 | break; |
14007 | case AGGMODIFY_SHAREABLE: |
14008 | appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE" ); |
14009 | break; |
14010 | case AGGMODIFY_READ_WRITE: |
14011 | appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE" ); |
14012 | break; |
14013 | default: |
14014 | fatal("unrecognized aggfinalmodify value for aggregate \"%s\"" , |
14015 | agginfo->aggfn.dobj.name); |
14016 | break; |
14017 | } |
14018 | } |
14019 | } |
14020 | |
14021 | if (strcmp(aggcombinefn, "-" ) != 0) |
14022 | appendPQExpBuffer(details, ",\n COMBINEFUNC = %s" , aggcombinefn); |
14023 | |
14024 | if (strcmp(aggserialfn, "-" ) != 0) |
14025 | appendPQExpBuffer(details, ",\n SERIALFUNC = %s" , aggserialfn); |
14026 | |
14027 | if (strcmp(aggdeserialfn, "-" ) != 0) |
14028 | appendPQExpBuffer(details, ",\n DESERIALFUNC = %s" , aggdeserialfn); |
14029 | |
14030 | if (strcmp(aggmtransfn, "-" ) != 0) |
14031 | { |
14032 | appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s" , |
14033 | aggmtransfn, |
14034 | aggminvtransfn, |
14035 | aggmtranstype); |
14036 | } |
14037 | |
14038 | if (strcmp(aggmtransspace, "0" ) != 0) |
14039 | { |
14040 | appendPQExpBuffer(details, ",\n MSSPACE = %s" , |
14041 | aggmtransspace); |
14042 | } |
14043 | |
14044 | if (!PQgetisnull(res, 0, i_aggminitval)) |
14045 | { |
14046 | appendPQExpBufferStr(details, ",\n MINITCOND = " ); |
14047 | appendStringLiteralAH(details, aggminitval, fout); |
14048 | } |
14049 | |
14050 | if (strcmp(aggmfinalfn, "-" ) != 0) |
14051 | { |
14052 | appendPQExpBuffer(details, ",\n MFINALFUNC = %s" , |
14053 | aggmfinalfn); |
14054 | if (aggmfinalextra) |
14055 | appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA" ); |
14056 | if (aggmfinalmodify != defaultfinalmodify) |
14057 | { |
14058 | switch (aggmfinalmodify) |
14059 | { |
14060 | case AGGMODIFY_READ_ONLY: |
14061 | appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY" ); |
14062 | break; |
14063 | case AGGMODIFY_SHAREABLE: |
14064 | appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE" ); |
14065 | break; |
14066 | case AGGMODIFY_READ_WRITE: |
14067 | appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE" ); |
14068 | break; |
14069 | default: |
14070 | fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"" , |
14071 | agginfo->aggfn.dobj.name); |
14072 | break; |
14073 | } |
14074 | } |
14075 | } |
14076 | |
14077 | aggsortconvop = getFormattedOperatorName(fout, aggsortop); |
14078 | if (aggsortconvop) |
14079 | { |
14080 | appendPQExpBuffer(details, ",\n SORTOP = %s" , |
14081 | aggsortconvop); |
14082 | free(aggsortconvop); |
14083 | } |
14084 | |
14085 | if (aggkind == AGGKIND_HYPOTHETICAL) |
14086 | appendPQExpBufferStr(details, ",\n HYPOTHETICAL" ); |
14087 | |
14088 | if (proparallel != NULL && proparallel[0] != PROPARALLEL_UNSAFE) |
14089 | { |
14090 | if (proparallel[0] == PROPARALLEL_SAFE) |
14091 | appendPQExpBufferStr(details, ",\n PARALLEL = safe" ); |
14092 | else if (proparallel[0] == PROPARALLEL_RESTRICTED) |
14093 | appendPQExpBufferStr(details, ",\n PARALLEL = restricted" ); |
14094 | else if (proparallel[0] != PROPARALLEL_UNSAFE) |
14095 | fatal("unrecognized proparallel value for function \"%s\"" , |
14096 | agginfo->aggfn.dobj.name); |
14097 | } |
14098 | |
14099 | appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n" , |
14100 | fmtId(agginfo->aggfn.dobj.namespace->dobj.name), |
14101 | aggsig); |
14102 | |
14103 | appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n" , |
14104 | fmtId(agginfo->aggfn.dobj.namespace->dobj.name), |
14105 | aggfullsig ? aggfullsig : aggsig, details->data); |
14106 | |
14107 | if (dopt->binary_upgrade) |
14108 | binary_upgrade_extension_member(q, &agginfo->aggfn.dobj, |
14109 | "AGGREGATE" , aggsig, |
14110 | agginfo->aggfn.dobj.namespace->dobj.name); |
14111 | |
14112 | if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION) |
14113 | ArchiveEntry(fout, agginfo->aggfn.dobj.catId, |
14114 | agginfo->aggfn.dobj.dumpId, |
14115 | ARCHIVE_OPTS(.tag = aggsig_tag, |
14116 | .namespace = agginfo->aggfn.dobj.namespace->dobj.name, |
14117 | .owner = agginfo->aggfn.rolname, |
14118 | .description = "AGGREGATE" , |
14119 | .section = SECTION_PRE_DATA, |
14120 | .createStmt = q->data, |
14121 | .dropStmt = delq->data)); |
14122 | |
14123 | /* Dump Aggregate Comments */ |
14124 | if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT) |
14125 | dumpComment(fout, "AGGREGATE" , aggsig, |
14126 | agginfo->aggfn.dobj.namespace->dobj.name, |
14127 | agginfo->aggfn.rolname, |
14128 | agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); |
14129 | |
14130 | if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL) |
14131 | dumpSecLabel(fout, "AGGREGATE" , aggsig, |
14132 | agginfo->aggfn.dobj.namespace->dobj.name, |
14133 | agginfo->aggfn.rolname, |
14134 | agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); |
14135 | |
14136 | /* |
14137 | * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL |
14138 | * command look like a function's GRANT; in particular this affects the |
14139 | * syntax for zero-argument aggregates and ordered-set aggregates. |
14140 | */ |
14141 | free(aggsig); |
14142 | |
14143 | aggsig = format_function_signature(fout, &agginfo->aggfn, true); |
14144 | |
14145 | if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL) |
14146 | dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId, |
14147 | "FUNCTION" , aggsig, NULL, |
14148 | agginfo->aggfn.dobj.namespace->dobj.name, |
14149 | agginfo->aggfn.rolname, agginfo->aggfn.proacl, |
14150 | agginfo->aggfn.rproacl, |
14151 | agginfo->aggfn.initproacl, agginfo->aggfn.initrproacl); |
14152 | |
14153 | free(aggsig); |
14154 | if (aggfullsig) |
14155 | free(aggfullsig); |
14156 | free(aggsig_tag); |
14157 | |
14158 | PQclear(res); |
14159 | |
14160 | destroyPQExpBuffer(query); |
14161 | destroyPQExpBuffer(q); |
14162 | destroyPQExpBuffer(delq); |
14163 | destroyPQExpBuffer(details); |
14164 | } |
14165 | |
14166 | /* |
14167 | * dumpTSParser |
14168 | * write out a single text search parser |
14169 | */ |
14170 | static void |
14171 | dumpTSParser(Archive *fout, TSParserInfo *prsinfo) |
14172 | { |
14173 | DumpOptions *dopt = fout->dopt; |
14174 | PQExpBuffer q; |
14175 | PQExpBuffer delq; |
14176 | char *qprsname; |
14177 | |
14178 | /* Skip if not to be dumped */ |
14179 | if (!prsinfo->dobj.dump || dopt->dataOnly) |
14180 | return; |
14181 | |
14182 | q = createPQExpBuffer(); |
14183 | delq = createPQExpBuffer(); |
14184 | |
14185 | qprsname = pg_strdup(fmtId(prsinfo->dobj.name)); |
14186 | |
14187 | appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n" , |
14188 | fmtQualifiedDumpable(prsinfo)); |
14189 | |
14190 | appendPQExpBuffer(q, " START = %s,\n" , |
14191 | convertTSFunction(fout, prsinfo->prsstart)); |
14192 | appendPQExpBuffer(q, " GETTOKEN = %s,\n" , |
14193 | convertTSFunction(fout, prsinfo->prstoken)); |
14194 | appendPQExpBuffer(q, " END = %s,\n" , |
14195 | convertTSFunction(fout, prsinfo->prsend)); |
14196 | if (prsinfo->prsheadline != InvalidOid) |
14197 | appendPQExpBuffer(q, " HEADLINE = %s,\n" , |
14198 | convertTSFunction(fout, prsinfo->prsheadline)); |
14199 | appendPQExpBuffer(q, " LEXTYPES = %s );\n" , |
14200 | convertTSFunction(fout, prsinfo->prslextype)); |
14201 | |
14202 | appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n" , |
14203 | fmtQualifiedDumpable(prsinfo)); |
14204 | |
14205 | if (dopt->binary_upgrade) |
14206 | binary_upgrade_extension_member(q, &prsinfo->dobj, |
14207 | "TEXT SEARCH PARSER" , qprsname, |
14208 | prsinfo->dobj.namespace->dobj.name); |
14209 | |
14210 | if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
14211 | ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId, |
14212 | ARCHIVE_OPTS(.tag = prsinfo->dobj.name, |
14213 | .namespace = prsinfo->dobj.namespace->dobj.name, |
14214 | .description = "TEXT SEARCH PARSER" , |
14215 | .section = SECTION_PRE_DATA, |
14216 | .createStmt = q->data, |
14217 | .dropStmt = delq->data)); |
14218 | |
14219 | /* Dump Parser Comments */ |
14220 | if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
14221 | dumpComment(fout, "TEXT SEARCH PARSER" , qprsname, |
14222 | prsinfo->dobj.namespace->dobj.name, "" , |
14223 | prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId); |
14224 | |
14225 | destroyPQExpBuffer(q); |
14226 | destroyPQExpBuffer(delq); |
14227 | free(qprsname); |
14228 | } |
14229 | |
14230 | /* |
14231 | * dumpTSDictionary |
14232 | * write out a single text search dictionary |
14233 | */ |
14234 | static void |
14235 | dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) |
14236 | { |
14237 | DumpOptions *dopt = fout->dopt; |
14238 | PQExpBuffer q; |
14239 | PQExpBuffer delq; |
14240 | PQExpBuffer query; |
14241 | char *qdictname; |
14242 | PGresult *res; |
14243 | char *nspname; |
14244 | char *tmplname; |
14245 | |
14246 | /* Skip if not to be dumped */ |
14247 | if (!dictinfo->dobj.dump || dopt->dataOnly) |
14248 | return; |
14249 | |
14250 | q = createPQExpBuffer(); |
14251 | delq = createPQExpBuffer(); |
14252 | query = createPQExpBuffer(); |
14253 | |
14254 | qdictname = pg_strdup(fmtId(dictinfo->dobj.name)); |
14255 | |
14256 | /* Fetch name and namespace of the dictionary's template */ |
14257 | appendPQExpBuffer(query, "SELECT nspname, tmplname " |
14258 | "FROM pg_ts_template p, pg_namespace n " |
14259 | "WHERE p.oid = '%u' AND n.oid = tmplnamespace" , |
14260 | dictinfo->dicttemplate); |
14261 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
14262 | nspname = PQgetvalue(res, 0, 0); |
14263 | tmplname = PQgetvalue(res, 0, 1); |
14264 | |
14265 | appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n" , |
14266 | fmtQualifiedDumpable(dictinfo)); |
14267 | |
14268 | appendPQExpBufferStr(q, " TEMPLATE = " ); |
14269 | appendPQExpBuffer(q, "%s." , fmtId(nspname)); |
14270 | appendPQExpBufferStr(q, fmtId(tmplname)); |
14271 | |
14272 | PQclear(res); |
14273 | |
14274 | /* the dictinitoption can be dumped straight into the command */ |
14275 | if (dictinfo->dictinitoption) |
14276 | appendPQExpBuffer(q, ",\n %s" , dictinfo->dictinitoption); |
14277 | |
14278 | appendPQExpBufferStr(q, " );\n" ); |
14279 | |
14280 | appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n" , |
14281 | fmtQualifiedDumpable(dictinfo)); |
14282 | |
14283 | if (dopt->binary_upgrade) |
14284 | binary_upgrade_extension_member(q, &dictinfo->dobj, |
14285 | "TEXT SEARCH DICTIONARY" , qdictname, |
14286 | dictinfo->dobj.namespace->dobj.name); |
14287 | |
14288 | if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
14289 | ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId, |
14290 | ARCHIVE_OPTS(.tag = dictinfo->dobj.name, |
14291 | .namespace = dictinfo->dobj.namespace->dobj.name, |
14292 | .owner = dictinfo->rolname, |
14293 | .description = "TEXT SEARCH DICTIONARY" , |
14294 | .section = SECTION_PRE_DATA, |
14295 | .createStmt = q->data, |
14296 | .dropStmt = delq->data)); |
14297 | |
14298 | /* Dump Dictionary Comments */ |
14299 | if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
14300 | dumpComment(fout, "TEXT SEARCH DICTIONARY" , qdictname, |
14301 | dictinfo->dobj.namespace->dobj.name, dictinfo->rolname, |
14302 | dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId); |
14303 | |
14304 | destroyPQExpBuffer(q); |
14305 | destroyPQExpBuffer(delq); |
14306 | destroyPQExpBuffer(query); |
14307 | free(qdictname); |
14308 | } |
14309 | |
14310 | /* |
14311 | * dumpTSTemplate |
14312 | * write out a single text search template |
14313 | */ |
14314 | static void |
14315 | dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) |
14316 | { |
14317 | DumpOptions *dopt = fout->dopt; |
14318 | PQExpBuffer q; |
14319 | PQExpBuffer delq; |
14320 | char *qtmplname; |
14321 | |
14322 | /* Skip if not to be dumped */ |
14323 | if (!tmplinfo->dobj.dump || dopt->dataOnly) |
14324 | return; |
14325 | |
14326 | q = createPQExpBuffer(); |
14327 | delq = createPQExpBuffer(); |
14328 | |
14329 | qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name)); |
14330 | |
14331 | appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n" , |
14332 | fmtQualifiedDumpable(tmplinfo)); |
14333 | |
14334 | if (tmplinfo->tmplinit != InvalidOid) |
14335 | appendPQExpBuffer(q, " INIT = %s,\n" , |
14336 | convertTSFunction(fout, tmplinfo->tmplinit)); |
14337 | appendPQExpBuffer(q, " LEXIZE = %s );\n" , |
14338 | convertTSFunction(fout, tmplinfo->tmpllexize)); |
14339 | |
14340 | appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n" , |
14341 | fmtQualifiedDumpable(tmplinfo)); |
14342 | |
14343 | if (dopt->binary_upgrade) |
14344 | binary_upgrade_extension_member(q, &tmplinfo->dobj, |
14345 | "TEXT SEARCH TEMPLATE" , qtmplname, |
14346 | tmplinfo->dobj.namespace->dobj.name); |
14347 | |
14348 | if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
14349 | ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId, |
14350 | ARCHIVE_OPTS(.tag = tmplinfo->dobj.name, |
14351 | .namespace = tmplinfo->dobj.namespace->dobj.name, |
14352 | .description = "TEXT SEARCH TEMPLATE" , |
14353 | .section = SECTION_PRE_DATA, |
14354 | .createStmt = q->data, |
14355 | .dropStmt = delq->data)); |
14356 | |
14357 | /* Dump Template Comments */ |
14358 | if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
14359 | dumpComment(fout, "TEXT SEARCH TEMPLATE" , qtmplname, |
14360 | tmplinfo->dobj.namespace->dobj.name, "" , |
14361 | tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId); |
14362 | |
14363 | destroyPQExpBuffer(q); |
14364 | destroyPQExpBuffer(delq); |
14365 | free(qtmplname); |
14366 | } |
14367 | |
14368 | /* |
14369 | * dumpTSConfig |
14370 | * write out a single text search configuration |
14371 | */ |
14372 | static void |
14373 | dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) |
14374 | { |
14375 | DumpOptions *dopt = fout->dopt; |
14376 | PQExpBuffer q; |
14377 | PQExpBuffer delq; |
14378 | PQExpBuffer query; |
14379 | char *qcfgname; |
14380 | PGresult *res; |
14381 | char *nspname; |
14382 | char *prsname; |
14383 | int ntups, |
14384 | i; |
14385 | int i_tokenname; |
14386 | int i_dictname; |
14387 | |
14388 | /* Skip if not to be dumped */ |
14389 | if (!cfginfo->dobj.dump || dopt->dataOnly) |
14390 | return; |
14391 | |
14392 | q = createPQExpBuffer(); |
14393 | delq = createPQExpBuffer(); |
14394 | query = createPQExpBuffer(); |
14395 | |
14396 | qcfgname = pg_strdup(fmtId(cfginfo->dobj.name)); |
14397 | |
14398 | /* Fetch name and namespace of the config's parser */ |
14399 | appendPQExpBuffer(query, "SELECT nspname, prsname " |
14400 | "FROM pg_ts_parser p, pg_namespace n " |
14401 | "WHERE p.oid = '%u' AND n.oid = prsnamespace" , |
14402 | cfginfo->cfgparser); |
14403 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
14404 | nspname = PQgetvalue(res, 0, 0); |
14405 | prsname = PQgetvalue(res, 0, 1); |
14406 | |
14407 | appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n" , |
14408 | fmtQualifiedDumpable(cfginfo)); |
14409 | |
14410 | appendPQExpBuffer(q, " PARSER = %s." , fmtId(nspname)); |
14411 | appendPQExpBuffer(q, "%s );\n" , fmtId(prsname)); |
14412 | |
14413 | PQclear(res); |
14414 | |
14415 | resetPQExpBuffer(query); |
14416 | appendPQExpBuffer(query, |
14417 | "SELECT\n" |
14418 | " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n" |
14419 | " WHERE t.tokid = m.maptokentype ) AS tokenname,\n" |
14420 | " m.mapdict::pg_catalog.regdictionary AS dictname\n" |
14421 | "FROM pg_catalog.pg_ts_config_map AS m\n" |
14422 | "WHERE m.mapcfg = '%u'\n" |
14423 | "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno" , |
14424 | cfginfo->cfgparser, cfginfo->dobj.catId.oid); |
14425 | |
14426 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
14427 | ntups = PQntuples(res); |
14428 | |
14429 | i_tokenname = PQfnumber(res, "tokenname" ); |
14430 | i_dictname = PQfnumber(res, "dictname" ); |
14431 | |
14432 | for (i = 0; i < ntups; i++) |
14433 | { |
14434 | char *tokenname = PQgetvalue(res, i, i_tokenname); |
14435 | char *dictname = PQgetvalue(res, i, i_dictname); |
14436 | |
14437 | if (i == 0 || |
14438 | strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0) |
14439 | { |
14440 | /* starting a new token type, so start a new command */ |
14441 | if (i > 0) |
14442 | appendPQExpBufferStr(q, ";\n" ); |
14443 | appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n" , |
14444 | fmtQualifiedDumpable(cfginfo)); |
14445 | /* tokenname needs quoting, dictname does NOT */ |
14446 | appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s" , |
14447 | fmtId(tokenname), dictname); |
14448 | } |
14449 | else |
14450 | appendPQExpBuffer(q, ", %s" , dictname); |
14451 | } |
14452 | |
14453 | if (ntups > 0) |
14454 | appendPQExpBufferStr(q, ";\n" ); |
14455 | |
14456 | PQclear(res); |
14457 | |
14458 | appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n" , |
14459 | fmtQualifiedDumpable(cfginfo)); |
14460 | |
14461 | if (dopt->binary_upgrade) |
14462 | binary_upgrade_extension_member(q, &cfginfo->dobj, |
14463 | "TEXT SEARCH CONFIGURATION" , qcfgname, |
14464 | cfginfo->dobj.namespace->dobj.name); |
14465 | |
14466 | if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
14467 | ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId, |
14468 | ARCHIVE_OPTS(.tag = cfginfo->dobj.name, |
14469 | .namespace = cfginfo->dobj.namespace->dobj.name, |
14470 | .owner = cfginfo->rolname, |
14471 | .description = "TEXT SEARCH CONFIGURATION" , |
14472 | .section = SECTION_PRE_DATA, |
14473 | .createStmt = q->data, |
14474 | .dropStmt = delq->data)); |
14475 | |
14476 | /* Dump Configuration Comments */ |
14477 | if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
14478 | dumpComment(fout, "TEXT SEARCH CONFIGURATION" , qcfgname, |
14479 | cfginfo->dobj.namespace->dobj.name, cfginfo->rolname, |
14480 | cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId); |
14481 | |
14482 | destroyPQExpBuffer(q); |
14483 | destroyPQExpBuffer(delq); |
14484 | destroyPQExpBuffer(query); |
14485 | free(qcfgname); |
14486 | } |
14487 | |
14488 | /* |
14489 | * dumpForeignDataWrapper |
14490 | * write out a single foreign-data wrapper definition |
14491 | */ |
14492 | static void |
14493 | dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) |
14494 | { |
14495 | DumpOptions *dopt = fout->dopt; |
14496 | PQExpBuffer q; |
14497 | PQExpBuffer delq; |
14498 | char *qfdwname; |
14499 | |
14500 | /* Skip if not to be dumped */ |
14501 | if (!fdwinfo->dobj.dump || dopt->dataOnly) |
14502 | return; |
14503 | |
14504 | q = createPQExpBuffer(); |
14505 | delq = createPQExpBuffer(); |
14506 | |
14507 | qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name)); |
14508 | |
14509 | appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s" , |
14510 | qfdwname); |
14511 | |
14512 | if (strcmp(fdwinfo->fdwhandler, "-" ) != 0) |
14513 | appendPQExpBuffer(q, " HANDLER %s" , fdwinfo->fdwhandler); |
14514 | |
14515 | if (strcmp(fdwinfo->fdwvalidator, "-" ) != 0) |
14516 | appendPQExpBuffer(q, " VALIDATOR %s" , fdwinfo->fdwvalidator); |
14517 | |
14518 | if (strlen(fdwinfo->fdwoptions) > 0) |
14519 | appendPQExpBuffer(q, " OPTIONS (\n %s\n)" , fdwinfo->fdwoptions); |
14520 | |
14521 | appendPQExpBufferStr(q, ";\n" ); |
14522 | |
14523 | appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n" , |
14524 | qfdwname); |
14525 | |
14526 | if (dopt->binary_upgrade) |
14527 | binary_upgrade_extension_member(q, &fdwinfo->dobj, |
14528 | "FOREIGN DATA WRAPPER" , qfdwname, |
14529 | NULL); |
14530 | |
14531 | if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
14532 | ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId, |
14533 | ARCHIVE_OPTS(.tag = fdwinfo->dobj.name, |
14534 | .owner = fdwinfo->rolname, |
14535 | .description = "FOREIGN DATA WRAPPER" , |
14536 | .section = SECTION_PRE_DATA, |
14537 | .createStmt = q->data, |
14538 | .dropStmt = delq->data)); |
14539 | |
14540 | /* Dump Foreign Data Wrapper Comments */ |
14541 | if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
14542 | dumpComment(fout, "FOREIGN DATA WRAPPER" , qfdwname, |
14543 | NULL, fdwinfo->rolname, |
14544 | fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId); |
14545 | |
14546 | /* Handle the ACL */ |
14547 | if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL) |
14548 | dumpACL(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId, |
14549 | "FOREIGN DATA WRAPPER" , qfdwname, NULL, |
14550 | NULL, fdwinfo->rolname, |
14551 | fdwinfo->fdwacl, fdwinfo->rfdwacl, |
14552 | fdwinfo->initfdwacl, fdwinfo->initrfdwacl); |
14553 | |
14554 | free(qfdwname); |
14555 | |
14556 | destroyPQExpBuffer(q); |
14557 | destroyPQExpBuffer(delq); |
14558 | } |
14559 | |
14560 | /* |
14561 | * dumpForeignServer |
14562 | * write out a foreign server definition |
14563 | */ |
14564 | static void |
14565 | dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) |
14566 | { |
14567 | DumpOptions *dopt = fout->dopt; |
14568 | PQExpBuffer q; |
14569 | PQExpBuffer delq; |
14570 | PQExpBuffer query; |
14571 | PGresult *res; |
14572 | char *qsrvname; |
14573 | char *fdwname; |
14574 | |
14575 | /* Skip if not to be dumped */ |
14576 | if (!srvinfo->dobj.dump || dopt->dataOnly) |
14577 | return; |
14578 | |
14579 | q = createPQExpBuffer(); |
14580 | delq = createPQExpBuffer(); |
14581 | query = createPQExpBuffer(); |
14582 | |
14583 | qsrvname = pg_strdup(fmtId(srvinfo->dobj.name)); |
14584 | |
14585 | /* look up the foreign-data wrapper */ |
14586 | appendPQExpBuffer(query, "SELECT fdwname " |
14587 | "FROM pg_foreign_data_wrapper w " |
14588 | "WHERE w.oid = '%u'" , |
14589 | srvinfo->srvfdw); |
14590 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
14591 | fdwname = PQgetvalue(res, 0, 0); |
14592 | |
14593 | appendPQExpBuffer(q, "CREATE SERVER %s" , qsrvname); |
14594 | if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0) |
14595 | { |
14596 | appendPQExpBufferStr(q, " TYPE " ); |
14597 | appendStringLiteralAH(q, srvinfo->srvtype, fout); |
14598 | } |
14599 | if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0) |
14600 | { |
14601 | appendPQExpBufferStr(q, " VERSION " ); |
14602 | appendStringLiteralAH(q, srvinfo->srvversion, fout); |
14603 | } |
14604 | |
14605 | appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER " ); |
14606 | appendPQExpBufferStr(q, fmtId(fdwname)); |
14607 | |
14608 | if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0) |
14609 | appendPQExpBuffer(q, " OPTIONS (\n %s\n)" , srvinfo->srvoptions); |
14610 | |
14611 | appendPQExpBufferStr(q, ";\n" ); |
14612 | |
14613 | appendPQExpBuffer(delq, "DROP SERVER %s;\n" , |
14614 | qsrvname); |
14615 | |
14616 | if (dopt->binary_upgrade) |
14617 | binary_upgrade_extension_member(q, &srvinfo->dobj, |
14618 | "SERVER" , qsrvname, NULL); |
14619 | |
14620 | if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
14621 | ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId, |
14622 | ARCHIVE_OPTS(.tag = srvinfo->dobj.name, |
14623 | .owner = srvinfo->rolname, |
14624 | .description = "SERVER" , |
14625 | .section = SECTION_PRE_DATA, |
14626 | .createStmt = q->data, |
14627 | .dropStmt = delq->data)); |
14628 | |
14629 | /* Dump Foreign Server Comments */ |
14630 | if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
14631 | dumpComment(fout, "SERVER" , qsrvname, |
14632 | NULL, srvinfo->rolname, |
14633 | srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId); |
14634 | |
14635 | /* Handle the ACL */ |
14636 | if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL) |
14637 | dumpACL(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId, |
14638 | "FOREIGN SERVER" , qsrvname, NULL, |
14639 | NULL, srvinfo->rolname, |
14640 | srvinfo->srvacl, srvinfo->rsrvacl, |
14641 | srvinfo->initsrvacl, srvinfo->initrsrvacl); |
14642 | |
14643 | /* Dump user mappings */ |
14644 | if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP) |
14645 | dumpUserMappings(fout, |
14646 | srvinfo->dobj.name, NULL, |
14647 | srvinfo->rolname, |
14648 | srvinfo->dobj.catId, srvinfo->dobj.dumpId); |
14649 | |
14650 | free(qsrvname); |
14651 | |
14652 | destroyPQExpBuffer(q); |
14653 | destroyPQExpBuffer(delq); |
14654 | destroyPQExpBuffer(query); |
14655 | } |
14656 | |
14657 | /* |
14658 | * dumpUserMappings |
14659 | * |
14660 | * This routine is used to dump any user mappings associated with the |
14661 | * server handed to this routine. Should be called after ArchiveEntry() |
14662 | * for the server. |
14663 | */ |
14664 | static void |
14665 | dumpUserMappings(Archive *fout, |
14666 | const char *servername, const char *namespace, |
14667 | const char *owner, |
14668 | CatalogId catalogId, DumpId dumpId) |
14669 | { |
14670 | PQExpBuffer q; |
14671 | PQExpBuffer delq; |
14672 | PQExpBuffer query; |
14673 | PQExpBuffer tag; |
14674 | PGresult *res; |
14675 | int ntups; |
14676 | int i_usename; |
14677 | int i_umoptions; |
14678 | int i; |
14679 | |
14680 | q = createPQExpBuffer(); |
14681 | tag = createPQExpBuffer(); |
14682 | delq = createPQExpBuffer(); |
14683 | query = createPQExpBuffer(); |
14684 | |
14685 | /* |
14686 | * We read from the publicly accessible view pg_user_mappings, so as not |
14687 | * to fail if run by a non-superuser. Note that the view will show |
14688 | * umoptions as null if the user hasn't got privileges for the associated |
14689 | * server; this means that pg_dump will dump such a mapping, but with no |
14690 | * OPTIONS clause. A possible alternative is to skip such mappings |
14691 | * altogether, but it's not clear that that's an improvement. |
14692 | */ |
14693 | appendPQExpBuffer(query, |
14694 | "SELECT usename, " |
14695 | "array_to_string(ARRAY(" |
14696 | "SELECT quote_ident(option_name) || ' ' || " |
14697 | "quote_literal(option_value) " |
14698 | "FROM pg_options_to_table(umoptions) " |
14699 | "ORDER BY option_name" |
14700 | "), E',\n ') AS umoptions " |
14701 | "FROM pg_user_mappings " |
14702 | "WHERE srvid = '%u' " |
14703 | "ORDER BY usename" , |
14704 | catalogId.oid); |
14705 | |
14706 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
14707 | |
14708 | ntups = PQntuples(res); |
14709 | i_usename = PQfnumber(res, "usename" ); |
14710 | i_umoptions = PQfnumber(res, "umoptions" ); |
14711 | |
14712 | for (i = 0; i < ntups; i++) |
14713 | { |
14714 | char *usename; |
14715 | char *umoptions; |
14716 | |
14717 | usename = PQgetvalue(res, i, i_usename); |
14718 | umoptions = PQgetvalue(res, i, i_umoptions); |
14719 | |
14720 | resetPQExpBuffer(q); |
14721 | appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s" , fmtId(usename)); |
14722 | appendPQExpBuffer(q, " SERVER %s" , fmtId(servername)); |
14723 | |
14724 | if (umoptions && strlen(umoptions) > 0) |
14725 | appendPQExpBuffer(q, " OPTIONS (\n %s\n)" , umoptions); |
14726 | |
14727 | appendPQExpBufferStr(q, ";\n" ); |
14728 | |
14729 | resetPQExpBuffer(delq); |
14730 | appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s" , fmtId(usename)); |
14731 | appendPQExpBuffer(delq, " SERVER %s;\n" , fmtId(servername)); |
14732 | |
14733 | resetPQExpBuffer(tag); |
14734 | appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s" , |
14735 | usename, servername); |
14736 | |
14737 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
14738 | ARCHIVE_OPTS(.tag = tag->data, |
14739 | .namespace = namespace, |
14740 | .owner = owner, |
14741 | .description = "USER MAPPING" , |
14742 | .section = SECTION_PRE_DATA, |
14743 | .createStmt = q->data, |
14744 | .dropStmt = delq->data)); |
14745 | } |
14746 | |
14747 | PQclear(res); |
14748 | |
14749 | destroyPQExpBuffer(query); |
14750 | destroyPQExpBuffer(delq); |
14751 | destroyPQExpBuffer(tag); |
14752 | destroyPQExpBuffer(q); |
14753 | } |
14754 | |
14755 | /* |
14756 | * Write out default privileges information |
14757 | */ |
14758 | static void |
14759 | dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) |
14760 | { |
14761 | DumpOptions *dopt = fout->dopt; |
14762 | PQExpBuffer q; |
14763 | PQExpBuffer tag; |
14764 | const char *type; |
14765 | |
14766 | /* Skip if not to be dumped */ |
14767 | if (!daclinfo->dobj.dump || dopt->dataOnly || dopt->aclsSkip) |
14768 | return; |
14769 | |
14770 | q = createPQExpBuffer(); |
14771 | tag = createPQExpBuffer(); |
14772 | |
14773 | switch (daclinfo->defaclobjtype) |
14774 | { |
14775 | case DEFACLOBJ_RELATION: |
14776 | type = "TABLES" ; |
14777 | break; |
14778 | case DEFACLOBJ_SEQUENCE: |
14779 | type = "SEQUENCES" ; |
14780 | break; |
14781 | case DEFACLOBJ_FUNCTION: |
14782 | type = "FUNCTIONS" ; |
14783 | break; |
14784 | case DEFACLOBJ_TYPE: |
14785 | type = "TYPES" ; |
14786 | break; |
14787 | case DEFACLOBJ_NAMESPACE: |
14788 | type = "SCHEMAS" ; |
14789 | break; |
14790 | default: |
14791 | /* shouldn't get here */ |
14792 | fatal("unrecognized object type in default privileges: %d" , |
14793 | (int) daclinfo->defaclobjtype); |
14794 | type = "" ; /* keep compiler quiet */ |
14795 | } |
14796 | |
14797 | appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s" , type); |
14798 | |
14799 | /* build the actual command(s) for this tuple */ |
14800 | if (!buildDefaultACLCommands(type, |
14801 | daclinfo->dobj.namespace != NULL ? |
14802 | daclinfo->dobj.namespace->dobj.name : NULL, |
14803 | daclinfo->defaclacl, |
14804 | daclinfo->rdefaclacl, |
14805 | daclinfo->initdefaclacl, |
14806 | daclinfo->initrdefaclacl, |
14807 | daclinfo->defaclrole, |
14808 | fout->remoteVersion, |
14809 | q)) |
14810 | fatal("could not parse default ACL list (%s)" , |
14811 | daclinfo->defaclacl); |
14812 | |
14813 | if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL) |
14814 | ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId, |
14815 | ARCHIVE_OPTS(.tag = tag->data, |
14816 | .namespace = daclinfo->dobj.namespace ? |
14817 | daclinfo->dobj.namespace->dobj.name : NULL, |
14818 | .owner = daclinfo->defaclrole, |
14819 | .description = "DEFAULT ACL" , |
14820 | .section = SECTION_POST_DATA, |
14821 | .createStmt = q->data)); |
14822 | |
14823 | destroyPQExpBuffer(tag); |
14824 | destroyPQExpBuffer(q); |
14825 | } |
14826 | |
14827 | /*---------- |
14828 | * Write out grant/revoke information |
14829 | * |
14830 | * 'objCatId' is the catalog ID of the underlying object. |
14831 | * 'objDumpId' is the dump ID of the underlying object. |
14832 | * 'type' must be one of |
14833 | * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE, |
14834 | * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT. |
14835 | * 'name' is the formatted name of the object. Must be quoted etc. already. |
14836 | * 'subname' is the formatted name of the sub-object, if any. Must be quoted. |
14837 | * (Currently we assume that subname is only provided for table columns.) |
14838 | * 'nspname' is the namespace the object is in (NULL if none). |
14839 | * 'owner' is the owner, NULL if there is no owner (for languages). |
14840 | * 'acls' contains the ACL string of the object from the appropriate system |
14841 | * catalog field; it will be passed to buildACLCommands for building the |
14842 | * appropriate GRANT commands. |
14843 | * 'racls' contains the ACL string of any initial-but-now-revoked ACLs of the |
14844 | * object; it will be passed to buildACLCommands for building the |
14845 | * appropriate REVOKE commands. |
14846 | * 'initacls' In binary-upgrade mode, ACL string of the object's initial |
14847 | * privileges, to be recorded into pg_init_privs |
14848 | * 'initracls' In binary-upgrade mode, ACL string of the object's |
14849 | * revoked-from-default privileges, to be recorded into pg_init_privs |
14850 | * |
14851 | * NB: initacls/initracls are needed because extensions can set privileges on |
14852 | * an object during the extension's script file and we record those into |
14853 | * pg_init_privs as that object's initial privileges. |
14854 | *---------- |
14855 | */ |
14856 | static void |
14857 | dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, |
14858 | const char *type, const char *name, const char *subname, |
14859 | const char *nspname, const char *owner, |
14860 | const char *acls, const char *racls, |
14861 | const char *initacls, const char *initracls) |
14862 | { |
14863 | DumpOptions *dopt = fout->dopt; |
14864 | PQExpBuffer sql; |
14865 | |
14866 | /* Do nothing if ACL dump is not enabled */ |
14867 | if (dopt->aclsSkip) |
14868 | return; |
14869 | |
14870 | /* --data-only skips ACLs *except* BLOB ACLs */ |
14871 | if (dopt->dataOnly && strcmp(type, "LARGE OBJECT" ) != 0) |
14872 | return; |
14873 | |
14874 | sql = createPQExpBuffer(); |
14875 | |
14876 | /* |
14877 | * Check to see if this object has had any initial ACLs included for it. |
14878 | * If so, we are in binary upgrade mode and these are the ACLs to turn |
14879 | * into GRANT and REVOKE statements to set and record the initial |
14880 | * privileges for an extension object. Let the backend know that these |
14881 | * are to be recorded by calling binary_upgrade_set_record_init_privs() |
14882 | * before and after. |
14883 | */ |
14884 | if (strlen(initacls) != 0 || strlen(initracls) != 0) |
14885 | { |
14886 | appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n" ); |
14887 | if (!buildACLCommands(name, subname, nspname, type, |
14888 | initacls, initracls, owner, |
14889 | "" , fout->remoteVersion, sql)) |
14890 | fatal("could not parse initial GRANT ACL list (%s) or initial REVOKE ACL list (%s) for object \"%s\" (%s)" , |
14891 | initacls, initracls, name, type); |
14892 | appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n" ); |
14893 | } |
14894 | |
14895 | if (!buildACLCommands(name, subname, nspname, type, |
14896 | acls, racls, owner, |
14897 | "" , fout->remoteVersion, sql)) |
14898 | fatal("could not parse GRANT ACL list (%s) or REVOKE ACL list (%s) for object \"%s\" (%s)" , |
14899 | acls, racls, name, type); |
14900 | |
14901 | if (sql->len > 0) |
14902 | { |
14903 | PQExpBuffer tag = createPQExpBuffer(); |
14904 | |
14905 | if (subname) |
14906 | appendPQExpBuffer(tag, "COLUMN %s.%s" , name, subname); |
14907 | else |
14908 | appendPQExpBuffer(tag, "%s %s" , type, name); |
14909 | |
14910 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
14911 | ARCHIVE_OPTS(.tag = tag->data, |
14912 | .namespace = nspname, |
14913 | .owner = owner, |
14914 | .description = "ACL" , |
14915 | .section = SECTION_NONE, |
14916 | .createStmt = sql->data, |
14917 | .deps = &objDumpId, |
14918 | .nDeps = 1)); |
14919 | destroyPQExpBuffer(tag); |
14920 | } |
14921 | |
14922 | destroyPQExpBuffer(sql); |
14923 | } |
14924 | |
14925 | /* |
14926 | * dumpSecLabel |
14927 | * |
14928 | * This routine is used to dump any security labels associated with the |
14929 | * object handed to this routine. The routine takes the object type |
14930 | * and object name (ready to print, except for schema decoration), plus |
14931 | * the namespace and owner of the object (for labeling the ArchiveEntry), |
14932 | * plus catalog ID and subid which are the lookup key for pg_seclabel, |
14933 | * plus the dump ID for the object (for setting a dependency). |
14934 | * If a matching pg_seclabel entry is found, it is dumped. |
14935 | * |
14936 | * Note: although this routine takes a dumpId for dependency purposes, |
14937 | * that purpose is just to mark the dependency in the emitted dump file |
14938 | * for possible future use by pg_restore. We do NOT use it for determining |
14939 | * ordering of the label in the dump file, because this routine is called |
14940 | * after dependency sorting occurs. This routine should be called just after |
14941 | * calling ArchiveEntry() for the specified object. |
14942 | */ |
14943 | static void |
14944 | dumpSecLabel(Archive *fout, const char *type, const char *name, |
14945 | const char *namespace, const char *owner, |
14946 | CatalogId catalogId, int subid, DumpId dumpId) |
14947 | { |
14948 | DumpOptions *dopt = fout->dopt; |
14949 | SecLabelItem *labels; |
14950 | int nlabels; |
14951 | int i; |
14952 | PQExpBuffer query; |
14953 | |
14954 | /* do nothing, if --no-security-labels is supplied */ |
14955 | if (dopt->no_security_labels) |
14956 | return; |
14957 | |
14958 | /* Security labels are schema not data ... except blob labels are data */ |
14959 | if (strcmp(type, "LARGE OBJECT" ) != 0) |
14960 | { |
14961 | if (dopt->dataOnly) |
14962 | return; |
14963 | } |
14964 | else |
14965 | { |
14966 | /* We do dump blob security labels in binary-upgrade mode */ |
14967 | if (dopt->schemaOnly && !dopt->binary_upgrade) |
14968 | return; |
14969 | } |
14970 | |
14971 | /* Search for security labels associated with catalogId, using table */ |
14972 | nlabels = findSecLabels(fout, catalogId.tableoid, catalogId.oid, &labels); |
14973 | |
14974 | query = createPQExpBuffer(); |
14975 | |
14976 | for (i = 0; i < nlabels; i++) |
14977 | { |
14978 | /* |
14979 | * Ignore label entries for which the subid doesn't match. |
14980 | */ |
14981 | if (labels[i].objsubid != subid) |
14982 | continue; |
14983 | |
14984 | appendPQExpBuffer(query, |
14985 | "SECURITY LABEL FOR %s ON %s " , |
14986 | fmtId(labels[i].provider), type); |
14987 | if (namespace && *namespace) |
14988 | appendPQExpBuffer(query, "%s." , fmtId(namespace)); |
14989 | appendPQExpBuffer(query, "%s IS " , name); |
14990 | appendStringLiteralAH(query, labels[i].label, fout); |
14991 | appendPQExpBufferStr(query, ";\n" ); |
14992 | } |
14993 | |
14994 | if (query->len > 0) |
14995 | { |
14996 | PQExpBuffer tag = createPQExpBuffer(); |
14997 | |
14998 | appendPQExpBuffer(tag, "%s %s" , type, name); |
14999 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
15000 | ARCHIVE_OPTS(.tag = tag->data, |
15001 | .namespace = namespace, |
15002 | .owner = owner, |
15003 | .description = "SECURITY LABEL" , |
15004 | .section = SECTION_NONE, |
15005 | .createStmt = query->data, |
15006 | .deps = &dumpId, |
15007 | .nDeps = 1)); |
15008 | destroyPQExpBuffer(tag); |
15009 | } |
15010 | |
15011 | destroyPQExpBuffer(query); |
15012 | } |
15013 | |
15014 | /* |
15015 | * dumpTableSecLabel |
15016 | * |
15017 | * As above, but dump security label for both the specified table (or view) |
15018 | * and its columns. |
15019 | */ |
15020 | static void |
15021 | dumpTableSecLabel(Archive *fout, TableInfo *tbinfo, const char *reltypename) |
15022 | { |
15023 | DumpOptions *dopt = fout->dopt; |
15024 | SecLabelItem *labels; |
15025 | int nlabels; |
15026 | int i; |
15027 | PQExpBuffer query; |
15028 | PQExpBuffer target; |
15029 | |
15030 | /* do nothing, if --no-security-labels is supplied */ |
15031 | if (dopt->no_security_labels) |
15032 | return; |
15033 | |
15034 | /* SecLabel are SCHEMA not data */ |
15035 | if (dopt->dataOnly) |
15036 | return; |
15037 | |
15038 | /* Search for comments associated with relation, using table */ |
15039 | nlabels = findSecLabels(fout, |
15040 | tbinfo->dobj.catId.tableoid, |
15041 | tbinfo->dobj.catId.oid, |
15042 | &labels); |
15043 | |
15044 | /* If security labels exist, build SECURITY LABEL statements */ |
15045 | if (nlabels <= 0) |
15046 | return; |
15047 | |
15048 | query = createPQExpBuffer(); |
15049 | target = createPQExpBuffer(); |
15050 | |
15051 | for (i = 0; i < nlabels; i++) |
15052 | { |
15053 | const char *colname; |
15054 | const char *provider = labels[i].provider; |
15055 | const char *label = labels[i].label; |
15056 | int objsubid = labels[i].objsubid; |
15057 | |
15058 | resetPQExpBuffer(target); |
15059 | if (objsubid == 0) |
15060 | { |
15061 | appendPQExpBuffer(target, "%s %s" , reltypename, |
15062 | fmtQualifiedDumpable(tbinfo)); |
15063 | } |
15064 | else |
15065 | { |
15066 | colname = getAttrName(objsubid, tbinfo); |
15067 | /* first fmtXXX result must be consumed before calling again */ |
15068 | appendPQExpBuffer(target, "COLUMN %s" , |
15069 | fmtQualifiedDumpable(tbinfo)); |
15070 | appendPQExpBuffer(target, ".%s" , fmtId(colname)); |
15071 | } |
15072 | appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS " , |
15073 | fmtId(provider), target->data); |
15074 | appendStringLiteralAH(query, label, fout); |
15075 | appendPQExpBufferStr(query, ";\n" ); |
15076 | } |
15077 | if (query->len > 0) |
15078 | { |
15079 | resetPQExpBuffer(target); |
15080 | appendPQExpBuffer(target, "%s %s" , reltypename, |
15081 | fmtId(tbinfo->dobj.name)); |
15082 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
15083 | ARCHIVE_OPTS(.tag = target->data, |
15084 | .namespace = tbinfo->dobj.namespace->dobj.name, |
15085 | .owner = tbinfo->rolname, |
15086 | .description = "SECURITY LABEL" , |
15087 | .section = SECTION_NONE, |
15088 | .createStmt = query->data, |
15089 | .deps = &(tbinfo->dobj.dumpId), |
15090 | .nDeps = 1)); |
15091 | } |
15092 | destroyPQExpBuffer(query); |
15093 | destroyPQExpBuffer(target); |
15094 | } |
15095 | |
15096 | /* |
15097 | * findSecLabels |
15098 | * |
15099 | * Find the security label(s), if any, associated with the given object. |
15100 | * All the objsubid values associated with the given classoid/objoid are |
15101 | * found with one search. |
15102 | */ |
15103 | static int |
15104 | findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items) |
15105 | { |
15106 | /* static storage for table of security labels */ |
15107 | static SecLabelItem *labels = NULL; |
15108 | static int nlabels = -1; |
15109 | |
15110 | SecLabelItem *middle = NULL; |
15111 | SecLabelItem *low; |
15112 | SecLabelItem *high; |
15113 | int nmatch; |
15114 | |
15115 | /* Get security labels if we didn't already */ |
15116 | if (nlabels < 0) |
15117 | nlabels = collectSecLabels(fout, &labels); |
15118 | |
15119 | if (nlabels <= 0) /* no labels, so no match is possible */ |
15120 | { |
15121 | *items = NULL; |
15122 | return 0; |
15123 | } |
15124 | |
15125 | /* |
15126 | * Do binary search to find some item matching the object. |
15127 | */ |
15128 | low = &labels[0]; |
15129 | high = &labels[nlabels - 1]; |
15130 | while (low <= high) |
15131 | { |
15132 | middle = low + (high - low) / 2; |
15133 | |
15134 | if (classoid < middle->classoid) |
15135 | high = middle - 1; |
15136 | else if (classoid > middle->classoid) |
15137 | low = middle + 1; |
15138 | else if (objoid < middle->objoid) |
15139 | high = middle - 1; |
15140 | else if (objoid > middle->objoid) |
15141 | low = middle + 1; |
15142 | else |
15143 | break; /* found a match */ |
15144 | } |
15145 | |
15146 | if (low > high) /* no matches */ |
15147 | { |
15148 | *items = NULL; |
15149 | return 0; |
15150 | } |
15151 | |
15152 | /* |
15153 | * Now determine how many items match the object. The search loop |
15154 | * invariant still holds: only items between low and high inclusive could |
15155 | * match. |
15156 | */ |
15157 | nmatch = 1; |
15158 | while (middle > low) |
15159 | { |
15160 | if (classoid != middle[-1].classoid || |
15161 | objoid != middle[-1].objoid) |
15162 | break; |
15163 | middle--; |
15164 | nmatch++; |
15165 | } |
15166 | |
15167 | *items = middle; |
15168 | |
15169 | middle += nmatch; |
15170 | while (middle <= high) |
15171 | { |
15172 | if (classoid != middle->classoid || |
15173 | objoid != middle->objoid) |
15174 | break; |
15175 | middle++; |
15176 | nmatch++; |
15177 | } |
15178 | |
15179 | return nmatch; |
15180 | } |
15181 | |
15182 | /* |
15183 | * collectSecLabels |
15184 | * |
15185 | * Construct a table of all security labels available for database objects. |
15186 | * It's much faster to pull them all at once. |
15187 | * |
15188 | * The table is sorted by classoid/objid/objsubid for speed in lookup. |
15189 | */ |
15190 | static int |
15191 | collectSecLabels(Archive *fout, SecLabelItem **items) |
15192 | { |
15193 | PGresult *res; |
15194 | PQExpBuffer query; |
15195 | int i_label; |
15196 | int i_provider; |
15197 | int i_classoid; |
15198 | int i_objoid; |
15199 | int i_objsubid; |
15200 | int ntups; |
15201 | int i; |
15202 | SecLabelItem *labels; |
15203 | |
15204 | query = createPQExpBuffer(); |
15205 | |
15206 | appendPQExpBufferStr(query, |
15207 | "SELECT label, provider, classoid, objoid, objsubid " |
15208 | "FROM pg_catalog.pg_seclabel " |
15209 | "ORDER BY classoid, objoid, objsubid" ); |
15210 | |
15211 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
15212 | |
15213 | /* Construct lookup table containing OIDs in numeric form */ |
15214 | i_label = PQfnumber(res, "label" ); |
15215 | i_provider = PQfnumber(res, "provider" ); |
15216 | i_classoid = PQfnumber(res, "classoid" ); |
15217 | i_objoid = PQfnumber(res, "objoid" ); |
15218 | i_objsubid = PQfnumber(res, "objsubid" ); |
15219 | |
15220 | ntups = PQntuples(res); |
15221 | |
15222 | labels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem)); |
15223 | |
15224 | for (i = 0; i < ntups; i++) |
15225 | { |
15226 | labels[i].label = PQgetvalue(res, i, i_label); |
15227 | labels[i].provider = PQgetvalue(res, i, i_provider); |
15228 | labels[i].classoid = atooid(PQgetvalue(res, i, i_classoid)); |
15229 | labels[i].objoid = atooid(PQgetvalue(res, i, i_objoid)); |
15230 | labels[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid)); |
15231 | } |
15232 | |
15233 | /* Do NOT free the PGresult since we are keeping pointers into it */ |
15234 | destroyPQExpBuffer(query); |
15235 | |
15236 | *items = labels; |
15237 | return ntups; |
15238 | } |
15239 | |
15240 | /* |
15241 | * dumpTable |
15242 | * write out to fout the declarations (not data) of a user-defined table |
15243 | */ |
15244 | static void |
15245 | dumpTable(Archive *fout, TableInfo *tbinfo) |
15246 | { |
15247 | DumpOptions *dopt = fout->dopt; |
15248 | char *namecopy; |
15249 | |
15250 | /* |
15251 | * noop if we are not dumping anything about this table, or if we are |
15252 | * doing a data-only dump |
15253 | */ |
15254 | if (!tbinfo->dobj.dump || dopt->dataOnly) |
15255 | return; |
15256 | |
15257 | if (tbinfo->relkind == RELKIND_SEQUENCE) |
15258 | dumpSequence(fout, tbinfo); |
15259 | else |
15260 | dumpTableSchema(fout, tbinfo); |
15261 | |
15262 | /* Handle the ACL here */ |
15263 | namecopy = pg_strdup(fmtId(tbinfo->dobj.name)); |
15264 | if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL) |
15265 | { |
15266 | const char *objtype = |
15267 | (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE" ; |
15268 | |
15269 | dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, |
15270 | objtype, namecopy, NULL, |
15271 | tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, |
15272 | tbinfo->relacl, tbinfo->rrelacl, |
15273 | tbinfo->initrelacl, tbinfo->initrrelacl); |
15274 | } |
15275 | |
15276 | /* |
15277 | * Handle column ACLs, if any. Note: we pull these with a separate query |
15278 | * rather than trying to fetch them during getTableAttrs, so that we won't |
15279 | * miss ACLs on system columns. |
15280 | */ |
15281 | if (fout->remoteVersion >= 80400 && tbinfo->dobj.dump & DUMP_COMPONENT_ACL) |
15282 | { |
15283 | PQExpBuffer query = createPQExpBuffer(); |
15284 | PGresult *res; |
15285 | int i; |
15286 | |
15287 | if (fout->remoteVersion >= 90600) |
15288 | { |
15289 | PQExpBuffer acl_subquery = createPQExpBuffer(); |
15290 | PQExpBuffer racl_subquery = createPQExpBuffer(); |
15291 | PQExpBuffer initacl_subquery = createPQExpBuffer(); |
15292 | PQExpBuffer initracl_subquery = createPQExpBuffer(); |
15293 | |
15294 | buildACLQueries(acl_subquery, racl_subquery, initacl_subquery, |
15295 | initracl_subquery, "at.attacl" , "c.relowner" , "'c'" , |
15296 | dopt->binary_upgrade); |
15297 | |
15298 | appendPQExpBuffer(query, |
15299 | "SELECT at.attname, " |
15300 | "%s AS attacl, " |
15301 | "%s AS rattacl, " |
15302 | "%s AS initattacl, " |
15303 | "%s AS initrattacl " |
15304 | "FROM pg_catalog.pg_attribute at " |
15305 | "JOIN pg_catalog.pg_class c ON (at.attrelid = c.oid) " |
15306 | "LEFT JOIN pg_catalog.pg_init_privs pip ON " |
15307 | "(at.attrelid = pip.objoid " |
15308 | "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass " |
15309 | "AND at.attnum = pip.objsubid) " |
15310 | "WHERE at.attrelid = '%u'::pg_catalog.oid AND " |
15311 | "NOT at.attisdropped " |
15312 | "AND (" |
15313 | "%s IS NOT NULL OR " |
15314 | "%s IS NOT NULL OR " |
15315 | "%s IS NOT NULL OR " |
15316 | "%s IS NOT NULL)" |
15317 | "ORDER BY at.attnum" , |
15318 | acl_subquery->data, |
15319 | racl_subquery->data, |
15320 | initacl_subquery->data, |
15321 | initracl_subquery->data, |
15322 | tbinfo->dobj.catId.oid, |
15323 | acl_subquery->data, |
15324 | racl_subquery->data, |
15325 | initacl_subquery->data, |
15326 | initracl_subquery->data); |
15327 | |
15328 | destroyPQExpBuffer(acl_subquery); |
15329 | destroyPQExpBuffer(racl_subquery); |
15330 | destroyPQExpBuffer(initacl_subquery); |
15331 | destroyPQExpBuffer(initracl_subquery); |
15332 | } |
15333 | else |
15334 | { |
15335 | appendPQExpBuffer(query, |
15336 | "SELECT attname, attacl, NULL as rattacl, " |
15337 | "NULL AS initattacl, NULL AS initrattacl " |
15338 | "FROM pg_catalog.pg_attribute " |
15339 | "WHERE attrelid = '%u'::pg_catalog.oid AND NOT attisdropped " |
15340 | "AND attacl IS NOT NULL " |
15341 | "ORDER BY attnum" , |
15342 | tbinfo->dobj.catId.oid); |
15343 | } |
15344 | |
15345 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
15346 | |
15347 | for (i = 0; i < PQntuples(res); i++) |
15348 | { |
15349 | char *attname = PQgetvalue(res, i, 0); |
15350 | char *attacl = PQgetvalue(res, i, 1); |
15351 | char *rattacl = PQgetvalue(res, i, 2); |
15352 | char *initattacl = PQgetvalue(res, i, 3); |
15353 | char *initrattacl = PQgetvalue(res, i, 4); |
15354 | char *attnamecopy; |
15355 | |
15356 | attnamecopy = pg_strdup(fmtId(attname)); |
15357 | /* Column's GRANT type is always TABLE */ |
15358 | dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, |
15359 | "TABLE" , namecopy, attnamecopy, |
15360 | tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, |
15361 | attacl, rattacl, initattacl, initrattacl); |
15362 | free(attnamecopy); |
15363 | } |
15364 | PQclear(res); |
15365 | destroyPQExpBuffer(query); |
15366 | } |
15367 | |
15368 | free(namecopy); |
15369 | |
15370 | return; |
15371 | } |
15372 | |
15373 | /* |
15374 | * Create the AS clause for a view or materialized view. The semicolon is |
15375 | * stripped because a materialized view must add a WITH NO DATA clause. |
15376 | * |
15377 | * This returns a new buffer which must be freed by the caller. |
15378 | */ |
15379 | static PQExpBuffer |
15380 | createViewAsClause(Archive *fout, TableInfo *tbinfo) |
15381 | { |
15382 | PQExpBuffer query = createPQExpBuffer(); |
15383 | PQExpBuffer result = createPQExpBuffer(); |
15384 | PGresult *res; |
15385 | int len; |
15386 | |
15387 | /* Fetch the view definition */ |
15388 | appendPQExpBuffer(query, |
15389 | "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef" , |
15390 | tbinfo->dobj.catId.oid); |
15391 | |
15392 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
15393 | |
15394 | if (PQntuples(res) != 1) |
15395 | { |
15396 | if (PQntuples(res) < 1) |
15397 | fatal("query to obtain definition of view \"%s\" returned no data" , |
15398 | tbinfo->dobj.name); |
15399 | else |
15400 | fatal("query to obtain definition of view \"%s\" returned more than one definition" , |
15401 | tbinfo->dobj.name); |
15402 | } |
15403 | |
15404 | len = PQgetlength(res, 0, 0); |
15405 | |
15406 | if (len == 0) |
15407 | fatal("definition of view \"%s\" appears to be empty (length zero)" , |
15408 | tbinfo->dobj.name); |
15409 | |
15410 | /* Strip off the trailing semicolon so that other things may follow. */ |
15411 | Assert(PQgetvalue(res, 0, 0)[len - 1] == ';'); |
15412 | appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1); |
15413 | |
15414 | PQclear(res); |
15415 | destroyPQExpBuffer(query); |
15416 | |
15417 | return result; |
15418 | } |
15419 | |
15420 | /* |
15421 | * Create a dummy AS clause for a view. This is used when the real view |
15422 | * definition has to be postponed because of circular dependencies. |
15423 | * We must duplicate the view's external properties -- column names and types |
15424 | * (including collation) -- so that it works for subsequent references. |
15425 | * |
15426 | * This returns a new buffer which must be freed by the caller. |
15427 | */ |
15428 | static PQExpBuffer |
15429 | createDummyViewAsClause(Archive *fout, TableInfo *tbinfo) |
15430 | { |
15431 | PQExpBuffer result = createPQExpBuffer(); |
15432 | int j; |
15433 | |
15434 | appendPQExpBufferStr(result, "SELECT" ); |
15435 | |
15436 | for (j = 0; j < tbinfo->numatts; j++) |
15437 | { |
15438 | if (j > 0) |
15439 | appendPQExpBufferChar(result, ','); |
15440 | appendPQExpBufferStr(result, "\n " ); |
15441 | |
15442 | appendPQExpBuffer(result, "NULL::%s" , tbinfo->atttypnames[j]); |
15443 | |
15444 | /* |
15445 | * Must add collation if not default for the type, because CREATE OR |
15446 | * REPLACE VIEW won't change it |
15447 | */ |
15448 | if (OidIsValid(tbinfo->attcollation[j])) |
15449 | { |
15450 | CollInfo *coll; |
15451 | |
15452 | coll = findCollationByOid(tbinfo->attcollation[j]); |
15453 | if (coll) |
15454 | appendPQExpBuffer(result, " COLLATE %s" , |
15455 | fmtQualifiedDumpable(coll)); |
15456 | } |
15457 | |
15458 | appendPQExpBuffer(result, " AS %s" , fmtId(tbinfo->attnames[j])); |
15459 | } |
15460 | |
15461 | return result; |
15462 | } |
15463 | |
15464 | /* |
15465 | * dumpTableSchema |
15466 | * write the declaration (not data) of one user-defined table or view |
15467 | */ |
15468 | static void |
15469 | dumpTableSchema(Archive *fout, TableInfo *tbinfo) |
15470 | { |
15471 | DumpOptions *dopt = fout->dopt; |
15472 | PQExpBuffer q = createPQExpBuffer(); |
15473 | PQExpBuffer delq = createPQExpBuffer(); |
15474 | char *qrelname; |
15475 | char *qualrelname; |
15476 | int numParents; |
15477 | TableInfo **parents; |
15478 | int actual_atts; /* number of attrs in this CREATE statement */ |
15479 | const char *reltypename; |
15480 | char *storage; |
15481 | int j, |
15482 | k; |
15483 | |
15484 | qrelname = pg_strdup(fmtId(tbinfo->dobj.name)); |
15485 | qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo)); |
15486 | |
15487 | |
15488 | if (tbinfo->hasoids) |
15489 | pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")" , |
15490 | qrelname); |
15491 | |
15492 | if (dopt->binary_upgrade) |
15493 | binary_upgrade_set_type_oids_by_rel_oid(fout, q, |
15494 | tbinfo->dobj.catId.oid); |
15495 | |
15496 | /* Is it a table or a view? */ |
15497 | if (tbinfo->relkind == RELKIND_VIEW) |
15498 | { |
15499 | PQExpBuffer result; |
15500 | |
15501 | /* |
15502 | * Note: keep this code in sync with the is_view case in dumpRule() |
15503 | */ |
15504 | |
15505 | reltypename = "VIEW" ; |
15506 | |
15507 | appendPQExpBuffer(delq, "DROP VIEW %s;\n" , qualrelname); |
15508 | |
15509 | if (dopt->binary_upgrade) |
15510 | binary_upgrade_set_pg_class_oids(fout, q, |
15511 | tbinfo->dobj.catId.oid, false); |
15512 | |
15513 | appendPQExpBuffer(q, "CREATE VIEW %s" , qualrelname); |
15514 | |
15515 | if (tbinfo->dummy_view) |
15516 | result = createDummyViewAsClause(fout, tbinfo); |
15517 | else |
15518 | { |
15519 | if (nonemptyReloptions(tbinfo->reloptions)) |
15520 | { |
15521 | appendPQExpBufferStr(q, " WITH (" ); |
15522 | appendReloptionsArrayAH(q, tbinfo->reloptions, "" , fout); |
15523 | appendPQExpBufferChar(q, ')'); |
15524 | } |
15525 | result = createViewAsClause(fout, tbinfo); |
15526 | } |
15527 | appendPQExpBuffer(q, " AS\n%s" , result->data); |
15528 | destroyPQExpBuffer(result); |
15529 | |
15530 | if (tbinfo->checkoption != NULL && !tbinfo->dummy_view) |
15531 | appendPQExpBuffer(q, "\n WITH %s CHECK OPTION" , tbinfo->checkoption); |
15532 | appendPQExpBufferStr(q, ";\n" ); |
15533 | } |
15534 | else |
15535 | { |
15536 | char *ftoptions = NULL; |
15537 | char *srvname = NULL; |
15538 | |
15539 | switch (tbinfo->relkind) |
15540 | { |
15541 | case RELKIND_FOREIGN_TABLE: |
15542 | { |
15543 | PQExpBuffer query = createPQExpBuffer(); |
15544 | PGresult *res; |
15545 | int i_srvname; |
15546 | int i_ftoptions; |
15547 | |
15548 | reltypename = "FOREIGN TABLE" ; |
15549 | |
15550 | /* retrieve name of foreign server and generic options */ |
15551 | appendPQExpBuffer(query, |
15552 | "SELECT fs.srvname, " |
15553 | "pg_catalog.array_to_string(ARRAY(" |
15554 | "SELECT pg_catalog.quote_ident(option_name) || " |
15555 | "' ' || pg_catalog.quote_literal(option_value) " |
15556 | "FROM pg_catalog.pg_options_to_table(ftoptions) " |
15557 | "ORDER BY option_name" |
15558 | "), E',\n ') AS ftoptions " |
15559 | "FROM pg_catalog.pg_foreign_table ft " |
15560 | "JOIN pg_catalog.pg_foreign_server fs " |
15561 | "ON (fs.oid = ft.ftserver) " |
15562 | "WHERE ft.ftrelid = '%u'" , |
15563 | tbinfo->dobj.catId.oid); |
15564 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
15565 | i_srvname = PQfnumber(res, "srvname" ); |
15566 | i_ftoptions = PQfnumber(res, "ftoptions" ); |
15567 | srvname = pg_strdup(PQgetvalue(res, 0, i_srvname)); |
15568 | ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions)); |
15569 | PQclear(res); |
15570 | destroyPQExpBuffer(query); |
15571 | break; |
15572 | } |
15573 | case RELKIND_MATVIEW: |
15574 | reltypename = "MATERIALIZED VIEW" ; |
15575 | break; |
15576 | default: |
15577 | reltypename = "TABLE" ; |
15578 | } |
15579 | |
15580 | numParents = tbinfo->numParents; |
15581 | parents = tbinfo->parents; |
15582 | |
15583 | appendPQExpBuffer(delq, "DROP %s %s;\n" , reltypename, qualrelname); |
15584 | |
15585 | if (dopt->binary_upgrade) |
15586 | binary_upgrade_set_pg_class_oids(fout, q, |
15587 | tbinfo->dobj.catId.oid, false); |
15588 | |
15589 | appendPQExpBuffer(q, "CREATE %s%s %s" , |
15590 | tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ? |
15591 | "UNLOGGED " : "" , |
15592 | reltypename, |
15593 | qualrelname); |
15594 | |
15595 | /* |
15596 | * Attach to type, if reloftype; except in case of a binary upgrade, |
15597 | * we dump the table normally and attach it to the type afterward. |
15598 | */ |
15599 | if (tbinfo->reloftype && !dopt->binary_upgrade) |
15600 | appendPQExpBuffer(q, " OF %s" , tbinfo->reloftype); |
15601 | |
15602 | if (tbinfo->relkind != RELKIND_MATVIEW) |
15603 | { |
15604 | /* Dump the attributes */ |
15605 | actual_atts = 0; |
15606 | for (j = 0; j < tbinfo->numatts; j++) |
15607 | { |
15608 | /* |
15609 | * Normally, dump if it's locally defined in this table, and |
15610 | * not dropped. But for binary upgrade, we'll dump all the |
15611 | * columns, and then fix up the dropped and nonlocal cases |
15612 | * below. |
15613 | */ |
15614 | if (shouldPrintColumn(dopt, tbinfo, j)) |
15615 | { |
15616 | bool print_default; |
15617 | bool print_notnull; |
15618 | |
15619 | /* |
15620 | * Default value --- suppress if to be printed separately. |
15621 | */ |
15622 | print_default = (tbinfo->attrdefs[j] != NULL && |
15623 | !tbinfo->attrdefs[j]->separate); |
15624 | |
15625 | /* |
15626 | * Not Null constraint --- suppress if inherited, except |
15627 | * if partition, or in binary-upgrade case where that |
15628 | * won't work. |
15629 | */ |
15630 | print_notnull = (tbinfo->notnull[j] && |
15631 | (!tbinfo->inhNotNull[j] || |
15632 | tbinfo->ispartition || dopt->binary_upgrade)); |
15633 | |
15634 | /* |
15635 | * Skip column if fully defined by reloftype, except in |
15636 | * binary upgrade |
15637 | */ |
15638 | if (tbinfo->reloftype && !print_default && !print_notnull && |
15639 | !dopt->binary_upgrade) |
15640 | continue; |
15641 | |
15642 | /* Format properly if not first attr */ |
15643 | if (actual_atts == 0) |
15644 | appendPQExpBufferStr(q, " (" ); |
15645 | else |
15646 | appendPQExpBufferChar(q, ','); |
15647 | appendPQExpBufferStr(q, "\n " ); |
15648 | actual_atts++; |
15649 | |
15650 | /* Attribute name */ |
15651 | appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j])); |
15652 | |
15653 | if (tbinfo->attisdropped[j]) |
15654 | { |
15655 | /* |
15656 | * ALTER TABLE DROP COLUMN clears |
15657 | * pg_attribute.atttypid, so we will not have gotten a |
15658 | * valid type name; insert INTEGER as a stopgap. We'll |
15659 | * clean things up later. |
15660 | */ |
15661 | appendPQExpBufferStr(q, " INTEGER /* dummy */" ); |
15662 | /* and skip to the next column */ |
15663 | continue; |
15664 | } |
15665 | |
15666 | /* |
15667 | * Attribute type; print it except when creating a typed |
15668 | * table ('OF type_name'), but in binary-upgrade mode, |
15669 | * print it in that case too. |
15670 | */ |
15671 | if (dopt->binary_upgrade || !tbinfo->reloftype) |
15672 | { |
15673 | appendPQExpBuffer(q, " %s" , |
15674 | tbinfo->atttypnames[j]); |
15675 | } |
15676 | |
15677 | if (print_default) |
15678 | { |
15679 | if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED) |
15680 | appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED" , |
15681 | tbinfo->attrdefs[j]->adef_expr); |
15682 | else |
15683 | appendPQExpBuffer(q, " DEFAULT %s" , |
15684 | tbinfo->attrdefs[j]->adef_expr); |
15685 | } |
15686 | |
15687 | |
15688 | if (print_notnull) |
15689 | appendPQExpBufferStr(q, " NOT NULL" ); |
15690 | |
15691 | /* Add collation if not default for the type */ |
15692 | if (OidIsValid(tbinfo->attcollation[j])) |
15693 | { |
15694 | CollInfo *coll; |
15695 | |
15696 | coll = findCollationByOid(tbinfo->attcollation[j]); |
15697 | if (coll) |
15698 | appendPQExpBuffer(q, " COLLATE %s" , |
15699 | fmtQualifiedDumpable(coll)); |
15700 | } |
15701 | } |
15702 | } |
15703 | |
15704 | /* |
15705 | * Add non-inherited CHECK constraints, if any. |
15706 | * |
15707 | * For partitions, we need to include check constraints even if |
15708 | * they're not defined locally, because the ALTER TABLE ATTACH |
15709 | * PARTITION that we'll emit later expects the constraint to be |
15710 | * there. (No need to fix conislocal: ATTACH PARTITION does that) |
15711 | */ |
15712 | for (j = 0; j < tbinfo->ncheck; j++) |
15713 | { |
15714 | ConstraintInfo *constr = &(tbinfo->checkexprs[j]); |
15715 | |
15716 | if (constr->separate || |
15717 | (!constr->conislocal && !tbinfo->ispartition)) |
15718 | continue; |
15719 | |
15720 | if (actual_atts == 0) |
15721 | appendPQExpBufferStr(q, " (\n " ); |
15722 | else |
15723 | appendPQExpBufferStr(q, ",\n " ); |
15724 | |
15725 | appendPQExpBuffer(q, "CONSTRAINT %s " , |
15726 | fmtId(constr->dobj.name)); |
15727 | appendPQExpBufferStr(q, constr->condef); |
15728 | |
15729 | actual_atts++; |
15730 | } |
15731 | |
15732 | if (actual_atts) |
15733 | appendPQExpBufferStr(q, "\n)" ); |
15734 | else if (!(tbinfo->reloftype && !dopt->binary_upgrade)) |
15735 | { |
15736 | /* |
15737 | * No attributes? we must have a parenthesized attribute list, |
15738 | * even though empty, when not using the OF TYPE syntax. |
15739 | */ |
15740 | appendPQExpBufferStr(q, " (\n)" ); |
15741 | } |
15742 | |
15743 | /* |
15744 | * Emit the INHERITS clause (not for partitions), except in |
15745 | * binary-upgrade mode. |
15746 | */ |
15747 | if (numParents > 0 && !tbinfo->ispartition && |
15748 | !dopt->binary_upgrade) |
15749 | { |
15750 | appendPQExpBufferStr(q, "\nINHERITS (" ); |
15751 | for (k = 0; k < numParents; k++) |
15752 | { |
15753 | TableInfo *parentRel = parents[k]; |
15754 | |
15755 | if (k > 0) |
15756 | appendPQExpBufferStr(q, ", " ); |
15757 | appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel)); |
15758 | } |
15759 | appendPQExpBufferChar(q, ')'); |
15760 | } |
15761 | |
15762 | if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) |
15763 | appendPQExpBuffer(q, "\nPARTITION BY %s" , tbinfo->partkeydef); |
15764 | |
15765 | if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) |
15766 | appendPQExpBuffer(q, "\nSERVER %s" , fmtId(srvname)); |
15767 | } |
15768 | |
15769 | if (nonemptyReloptions(tbinfo->reloptions) || |
15770 | nonemptyReloptions(tbinfo->toast_reloptions)) |
15771 | { |
15772 | bool addcomma = false; |
15773 | |
15774 | appendPQExpBufferStr(q, "\nWITH (" ); |
15775 | if (nonemptyReloptions(tbinfo->reloptions)) |
15776 | { |
15777 | addcomma = true; |
15778 | appendReloptionsArrayAH(q, tbinfo->reloptions, "" , fout); |
15779 | } |
15780 | if (nonemptyReloptions(tbinfo->toast_reloptions)) |
15781 | { |
15782 | if (addcomma) |
15783 | appendPQExpBufferStr(q, ", " ); |
15784 | appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast." , |
15785 | fout); |
15786 | } |
15787 | appendPQExpBufferChar(q, ')'); |
15788 | } |
15789 | |
15790 | /* Dump generic options if any */ |
15791 | if (ftoptions && ftoptions[0]) |
15792 | appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)" , ftoptions); |
15793 | |
15794 | /* |
15795 | * For materialized views, create the AS clause just like a view. At |
15796 | * this point, we always mark the view as not populated. |
15797 | */ |
15798 | if (tbinfo->relkind == RELKIND_MATVIEW) |
15799 | { |
15800 | PQExpBuffer result; |
15801 | |
15802 | result = createViewAsClause(fout, tbinfo); |
15803 | appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n" , |
15804 | result->data); |
15805 | destroyPQExpBuffer(result); |
15806 | } |
15807 | else |
15808 | appendPQExpBufferStr(q, ";\n" ); |
15809 | |
15810 | /* |
15811 | * in binary upgrade mode, update the catalog with any missing values |
15812 | * that might be present. |
15813 | */ |
15814 | if (dopt->binary_upgrade) |
15815 | { |
15816 | for (j = 0; j < tbinfo->numatts; j++) |
15817 | { |
15818 | if (tbinfo->attmissingval[j][0] != '\0') |
15819 | { |
15820 | appendPQExpBufferStr(q, "\n-- set missing value.\n" ); |
15821 | appendPQExpBufferStr(q, |
15822 | "SELECT pg_catalog.binary_upgrade_set_missing_value(" ); |
15823 | appendStringLiteralAH(q, qualrelname, fout); |
15824 | appendPQExpBufferStr(q, "::pg_catalog.regclass," ); |
15825 | appendStringLiteralAH(q, tbinfo->attnames[j], fout); |
15826 | appendPQExpBufferStr(q, "," ); |
15827 | appendStringLiteralAH(q, tbinfo->attmissingval[j], fout); |
15828 | appendPQExpBufferStr(q, ");\n\n" ); |
15829 | } |
15830 | } |
15831 | } |
15832 | |
15833 | /* |
15834 | * To create binary-compatible heap files, we have to ensure the same |
15835 | * physical column order, including dropped columns, as in the |
15836 | * original. Therefore, we create dropped columns above and drop them |
15837 | * here, also updating their attlen/attalign values so that the |
15838 | * dropped column can be skipped properly. (We do not bother with |
15839 | * restoring the original attbyval setting.) Also, inheritance |
15840 | * relationships are set up by doing ALTER TABLE INHERIT rather than |
15841 | * using an INHERITS clause --- the latter would possibly mess up the |
15842 | * column order. That also means we have to take care about setting |
15843 | * attislocal correctly, plus fix up any inherited CHECK constraints. |
15844 | * Analogously, we set up typed tables using ALTER TABLE / OF here. |
15845 | * |
15846 | * We process foreign and partitioned tables here, even though they |
15847 | * lack heap storage, because they can participate in inheritance |
15848 | * relationships and we want this stuff to be consistent across the |
15849 | * inheritance tree. We can exclude indexes, toast tables, sequences |
15850 | * and matviews, even though they have storage, because we don't |
15851 | * support altering or dropping columns in them, nor can they be part |
15852 | * of inheritance trees. |
15853 | */ |
15854 | if (dopt->binary_upgrade && |
15855 | (tbinfo->relkind == RELKIND_RELATION || |
15856 | tbinfo->relkind == RELKIND_FOREIGN_TABLE || |
15857 | tbinfo->relkind == RELKIND_PARTITIONED_TABLE)) |
15858 | { |
15859 | for (j = 0; j < tbinfo->numatts; j++) |
15860 | { |
15861 | if (tbinfo->attisdropped[j]) |
15862 | { |
15863 | appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n" ); |
15864 | appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n" |
15865 | "SET attlen = %d, " |
15866 | "attalign = '%c', attbyval = false\n" |
15867 | "WHERE attname = " , |
15868 | tbinfo->attlen[j], |
15869 | tbinfo->attalign[j]); |
15870 | appendStringLiteralAH(q, tbinfo->attnames[j], fout); |
15871 | appendPQExpBufferStr(q, "\n AND attrelid = " ); |
15872 | appendStringLiteralAH(q, qualrelname, fout); |
15873 | appendPQExpBufferStr(q, "::pg_catalog.regclass;\n" ); |
15874 | |
15875 | if (tbinfo->relkind == RELKIND_RELATION || |
15876 | tbinfo->relkind == RELKIND_PARTITIONED_TABLE) |
15877 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s " , |
15878 | qualrelname); |
15879 | else |
15880 | appendPQExpBuffer(q, "ALTER FOREIGN TABLE ONLY %s " , |
15881 | qualrelname); |
15882 | appendPQExpBuffer(q, "DROP COLUMN %s;\n" , |
15883 | fmtId(tbinfo->attnames[j])); |
15884 | } |
15885 | else if (!tbinfo->attislocal[j]) |
15886 | { |
15887 | appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n" ); |
15888 | appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n" |
15889 | "SET attislocal = false\n" |
15890 | "WHERE attname = " ); |
15891 | appendStringLiteralAH(q, tbinfo->attnames[j], fout); |
15892 | appendPQExpBufferStr(q, "\n AND attrelid = " ); |
15893 | appendStringLiteralAH(q, qualrelname, fout); |
15894 | appendPQExpBufferStr(q, "::pg_catalog.regclass;\n" ); |
15895 | } |
15896 | } |
15897 | |
15898 | /* |
15899 | * Add inherited CHECK constraints, if any. |
15900 | * |
15901 | * For partitions, they were already dumped, and conislocal |
15902 | * doesn't need fixing. |
15903 | */ |
15904 | for (k = 0; k < tbinfo->ncheck; k++) |
15905 | { |
15906 | ConstraintInfo *constr = &(tbinfo->checkexprs[k]); |
15907 | |
15908 | if (constr->separate || constr->conislocal || tbinfo->ispartition) |
15909 | continue; |
15910 | |
15911 | appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n" ); |
15912 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s " , |
15913 | qualrelname); |
15914 | appendPQExpBuffer(q, " ADD CONSTRAINT %s " , |
15915 | fmtId(constr->dobj.name)); |
15916 | appendPQExpBuffer(q, "%s;\n" , constr->condef); |
15917 | appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n" |
15918 | "SET conislocal = false\n" |
15919 | "WHERE contype = 'c' AND conname = " ); |
15920 | appendStringLiteralAH(q, constr->dobj.name, fout); |
15921 | appendPQExpBufferStr(q, "\n AND conrelid = " ); |
15922 | appendStringLiteralAH(q, qualrelname, fout); |
15923 | appendPQExpBufferStr(q, "::pg_catalog.regclass;\n" ); |
15924 | } |
15925 | |
15926 | if (numParents > 0 && !tbinfo->ispartition) |
15927 | { |
15928 | appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n" ); |
15929 | for (k = 0; k < numParents; k++) |
15930 | { |
15931 | TableInfo *parentRel = parents[k]; |
15932 | |
15933 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s INHERIT %s;\n" , |
15934 | qualrelname, |
15935 | fmtQualifiedDumpable(parentRel)); |
15936 | } |
15937 | } |
15938 | |
15939 | if (tbinfo->reloftype) |
15940 | { |
15941 | appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n" ); |
15942 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n" , |
15943 | qualrelname, |
15944 | tbinfo->reloftype); |
15945 | } |
15946 | } |
15947 | |
15948 | /* |
15949 | * For partitioned tables, emit the ATTACH PARTITION clause. Note |
15950 | * that we always want to create partitions this way instead of using |
15951 | * CREATE TABLE .. PARTITION OF, mainly to preserve a possible column |
15952 | * layout discrepancy with the parent, but also to ensure it gets the |
15953 | * correct tablespace setting if it differs from the parent's. |
15954 | */ |
15955 | if (tbinfo->ispartition) |
15956 | { |
15957 | /* With partitions there can only be one parent */ |
15958 | if (tbinfo->numParents != 1) |
15959 | fatal("invalid number of parents %d for table \"%s\"" , |
15960 | tbinfo->numParents, tbinfo->dobj.name); |
15961 | |
15962 | /* Perform ALTER TABLE on the parent */ |
15963 | appendPQExpBuffer(q, |
15964 | "ALTER TABLE ONLY %s ATTACH PARTITION %s %s;\n" , |
15965 | fmtQualifiedDumpable(parents[0]), |
15966 | qualrelname, tbinfo->partbound); |
15967 | } |
15968 | |
15969 | /* |
15970 | * In binary_upgrade mode, arrange to restore the old relfrozenxid and |
15971 | * relminmxid of all vacuumable relations. (While vacuum.c processes |
15972 | * TOAST tables semi-independently, here we see them only as children |
15973 | * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the |
15974 | * child toast table is handled below.) |
15975 | */ |
15976 | if (dopt->binary_upgrade && |
15977 | (tbinfo->relkind == RELKIND_RELATION || |
15978 | tbinfo->relkind == RELKIND_MATVIEW)) |
15979 | { |
15980 | appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n" ); |
15981 | appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n" |
15982 | "SET relfrozenxid = '%u', relminmxid = '%u'\n" |
15983 | "WHERE oid = " , |
15984 | tbinfo->frozenxid, tbinfo->minmxid); |
15985 | appendStringLiteralAH(q, qualrelname, fout); |
15986 | appendPQExpBufferStr(q, "::pg_catalog.regclass;\n" ); |
15987 | |
15988 | if (tbinfo->toast_oid) |
15989 | { |
15990 | /* |
15991 | * The toast table will have the same OID at restore, so we |
15992 | * can safely target it by OID. |
15993 | */ |
15994 | appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n" ); |
15995 | appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n" |
15996 | "SET relfrozenxid = '%u', relminmxid = '%u'\n" |
15997 | "WHERE oid = '%u';\n" , |
15998 | tbinfo->toast_frozenxid, |
15999 | tbinfo->toast_minmxid, tbinfo->toast_oid); |
16000 | } |
16001 | } |
16002 | |
16003 | /* |
16004 | * In binary_upgrade mode, restore matviews' populated status by |
16005 | * poking pg_class directly. This is pretty ugly, but we can't use |
16006 | * REFRESH MATERIALIZED VIEW since it's possible that some underlying |
16007 | * matview is not populated even though this matview is; in any case, |
16008 | * we want to transfer the matview's heap storage, not run REFRESH. |
16009 | */ |
16010 | if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW && |
16011 | tbinfo->relispopulated) |
16012 | { |
16013 | appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n" ); |
16014 | appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n" |
16015 | "SET relispopulated = 't'\n" |
16016 | "WHERE oid = " ); |
16017 | appendStringLiteralAH(q, qualrelname, fout); |
16018 | appendPQExpBufferStr(q, "::pg_catalog.regclass;\n" ); |
16019 | } |
16020 | |
16021 | /* |
16022 | * Dump additional per-column properties that we can't handle in the |
16023 | * main CREATE TABLE command. |
16024 | */ |
16025 | for (j = 0; j < tbinfo->numatts; j++) |
16026 | { |
16027 | /* None of this applies to dropped columns */ |
16028 | if (tbinfo->attisdropped[j]) |
16029 | continue; |
16030 | |
16031 | /* |
16032 | * If we didn't dump the column definition explicitly above, and |
16033 | * it is NOT NULL and did not inherit that property from a parent, |
16034 | * we have to mark it separately. |
16035 | */ |
16036 | if (!shouldPrintColumn(dopt, tbinfo, j) && |
16037 | tbinfo->notnull[j] && !tbinfo->inhNotNull[j]) |
16038 | { |
16039 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s " , |
16040 | qualrelname); |
16041 | appendPQExpBuffer(q, "ALTER COLUMN %s SET NOT NULL;\n" , |
16042 | fmtId(tbinfo->attnames[j])); |
16043 | } |
16044 | |
16045 | /* |
16046 | * Dump per-column statistics information. We only issue an ALTER |
16047 | * TABLE statement if the attstattarget entry for this column is |
16048 | * non-negative (i.e. it's not the default value) |
16049 | */ |
16050 | if (tbinfo->attstattarget[j] >= 0) |
16051 | { |
16052 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s " , |
16053 | qualrelname); |
16054 | appendPQExpBuffer(q, "ALTER COLUMN %s " , |
16055 | fmtId(tbinfo->attnames[j])); |
16056 | appendPQExpBuffer(q, "SET STATISTICS %d;\n" , |
16057 | tbinfo->attstattarget[j]); |
16058 | } |
16059 | |
16060 | /* |
16061 | * Dump per-column storage information. The statement is only |
16062 | * dumped if the storage has been changed from the type's default. |
16063 | */ |
16064 | if (tbinfo->attstorage[j] != tbinfo->typstorage[j]) |
16065 | { |
16066 | switch (tbinfo->attstorage[j]) |
16067 | { |
16068 | case 'p': |
16069 | storage = "PLAIN" ; |
16070 | break; |
16071 | case 'e': |
16072 | storage = "EXTERNAL" ; |
16073 | break; |
16074 | case 'm': |
16075 | storage = "MAIN" ; |
16076 | break; |
16077 | case 'x': |
16078 | storage = "EXTENDED" ; |
16079 | break; |
16080 | default: |
16081 | storage = NULL; |
16082 | } |
16083 | |
16084 | /* |
16085 | * Only dump the statement if it's a storage type we recognize |
16086 | */ |
16087 | if (storage != NULL) |
16088 | { |
16089 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s " , |
16090 | qualrelname); |
16091 | appendPQExpBuffer(q, "ALTER COLUMN %s " , |
16092 | fmtId(tbinfo->attnames[j])); |
16093 | appendPQExpBuffer(q, "SET STORAGE %s;\n" , |
16094 | storage); |
16095 | } |
16096 | } |
16097 | |
16098 | /* |
16099 | * Dump per-column attributes. |
16100 | */ |
16101 | if (tbinfo->attoptions[j][0] != '\0') |
16102 | { |
16103 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s " , |
16104 | qualrelname); |
16105 | appendPQExpBuffer(q, "ALTER COLUMN %s " , |
16106 | fmtId(tbinfo->attnames[j])); |
16107 | appendPQExpBuffer(q, "SET (%s);\n" , |
16108 | tbinfo->attoptions[j]); |
16109 | } |
16110 | |
16111 | /* |
16112 | * Dump per-column fdw options. |
16113 | */ |
16114 | if (tbinfo->relkind == RELKIND_FOREIGN_TABLE && |
16115 | tbinfo->attfdwoptions[j][0] != '\0') |
16116 | { |
16117 | appendPQExpBuffer(q, "ALTER FOREIGN TABLE %s " , |
16118 | qualrelname); |
16119 | appendPQExpBuffer(q, "ALTER COLUMN %s " , |
16120 | fmtId(tbinfo->attnames[j])); |
16121 | appendPQExpBuffer(q, "OPTIONS (\n %s\n);\n" , |
16122 | tbinfo->attfdwoptions[j]); |
16123 | } |
16124 | } |
16125 | |
16126 | if (ftoptions) |
16127 | free(ftoptions); |
16128 | if (srvname) |
16129 | free(srvname); |
16130 | } |
16131 | |
16132 | /* |
16133 | * dump properties we only have ALTER TABLE syntax for |
16134 | */ |
16135 | if ((tbinfo->relkind == RELKIND_RELATION || |
16136 | tbinfo->relkind == RELKIND_PARTITIONED_TABLE || |
16137 | tbinfo->relkind == RELKIND_MATVIEW) && |
16138 | tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) |
16139 | { |
16140 | if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX) |
16141 | { |
16142 | /* nothing to do, will be set when the index is dumped */ |
16143 | } |
16144 | else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING) |
16145 | { |
16146 | appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n" , |
16147 | qualrelname); |
16148 | } |
16149 | else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL) |
16150 | { |
16151 | appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n" , |
16152 | qualrelname); |
16153 | } |
16154 | } |
16155 | |
16156 | if (tbinfo->forcerowsec) |
16157 | appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n" , |
16158 | qualrelname); |
16159 | |
16160 | if (dopt->binary_upgrade) |
16161 | binary_upgrade_extension_member(q, &tbinfo->dobj, |
16162 | reltypename, qrelname, |
16163 | tbinfo->dobj.namespace->dobj.name); |
16164 | |
16165 | if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16166 | { |
16167 | char *tableam = NULL; |
16168 | |
16169 | if (tbinfo->relkind == RELKIND_RELATION || |
16170 | tbinfo->relkind == RELKIND_MATVIEW) |
16171 | tableam = tbinfo->amname; |
16172 | |
16173 | ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, |
16174 | ARCHIVE_OPTS(.tag = tbinfo->dobj.name, |
16175 | .namespace = tbinfo->dobj.namespace->dobj.name, |
16176 | .tablespace = (tbinfo->relkind == RELKIND_VIEW) ? |
16177 | NULL : tbinfo->reltablespace, |
16178 | .tableam = tableam, |
16179 | .owner = tbinfo->rolname, |
16180 | .description = reltypename, |
16181 | .section = tbinfo->postponed_def ? |
16182 | SECTION_POST_DATA : SECTION_PRE_DATA, |
16183 | .createStmt = q->data, |
16184 | .dropStmt = delq->data)); |
16185 | } |
16186 | |
16187 | /* Dump Table Comments */ |
16188 | if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
16189 | dumpTableComment(fout, tbinfo, reltypename); |
16190 | |
16191 | /* Dump Table Security Labels */ |
16192 | if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
16193 | dumpTableSecLabel(fout, tbinfo, reltypename); |
16194 | |
16195 | /* Dump comments on inlined table constraints */ |
16196 | for (j = 0; j < tbinfo->ncheck; j++) |
16197 | { |
16198 | ConstraintInfo *constr = &(tbinfo->checkexprs[j]); |
16199 | |
16200 | if (constr->separate || !constr->conislocal) |
16201 | continue; |
16202 | |
16203 | if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
16204 | dumpTableConstraintComment(fout, constr); |
16205 | } |
16206 | |
16207 | destroyPQExpBuffer(q); |
16208 | destroyPQExpBuffer(delq); |
16209 | free(qrelname); |
16210 | free(qualrelname); |
16211 | } |
16212 | |
16213 | /* |
16214 | * dumpAttrDef --- dump an attribute's default-value declaration |
16215 | */ |
16216 | static void |
16217 | dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) |
16218 | { |
16219 | DumpOptions *dopt = fout->dopt; |
16220 | TableInfo *tbinfo = adinfo->adtable; |
16221 | int adnum = adinfo->adnum; |
16222 | PQExpBuffer q; |
16223 | PQExpBuffer delq; |
16224 | char *qualrelname; |
16225 | char *tag; |
16226 | |
16227 | /* Skip if table definition not to be dumped */ |
16228 | if (!tbinfo->dobj.dump || dopt->dataOnly) |
16229 | return; |
16230 | |
16231 | /* Skip if not "separate"; it was dumped in the table's definition */ |
16232 | if (!adinfo->separate) |
16233 | return; |
16234 | |
16235 | q = createPQExpBuffer(); |
16236 | delq = createPQExpBuffer(); |
16237 | |
16238 | qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo)); |
16239 | |
16240 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s " , |
16241 | qualrelname); |
16242 | appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n" , |
16243 | fmtId(tbinfo->attnames[adnum - 1]), |
16244 | adinfo->adef_expr); |
16245 | |
16246 | appendPQExpBuffer(delq, "ALTER TABLE %s " , |
16247 | qualrelname); |
16248 | appendPQExpBuffer(delq, "ALTER COLUMN %s DROP DEFAULT;\n" , |
16249 | fmtId(tbinfo->attnames[adnum - 1])); |
16250 | |
16251 | tag = psprintf("%s %s" , tbinfo->dobj.name, tbinfo->attnames[adnum - 1]); |
16252 | |
16253 | if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16254 | ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId, |
16255 | ARCHIVE_OPTS(.tag = tag, |
16256 | .namespace = tbinfo->dobj.namespace->dobj.name, |
16257 | .owner = tbinfo->rolname, |
16258 | .description = "DEFAULT" , |
16259 | .section = SECTION_PRE_DATA, |
16260 | .createStmt = q->data, |
16261 | .dropStmt = delq->data)); |
16262 | |
16263 | free(tag); |
16264 | destroyPQExpBuffer(q); |
16265 | destroyPQExpBuffer(delq); |
16266 | free(qualrelname); |
16267 | } |
16268 | |
16269 | /* |
16270 | * getAttrName: extract the correct name for an attribute |
16271 | * |
16272 | * The array tblInfo->attnames[] only provides names of user attributes; |
16273 | * if a system attribute number is supplied, we have to fake it. |
16274 | * We also do a little bit of bounds checking for safety's sake. |
16275 | */ |
16276 | static const char * |
16277 | getAttrName(int attrnum, TableInfo *tblInfo) |
16278 | { |
16279 | if (attrnum > 0 && attrnum <= tblInfo->numatts) |
16280 | return tblInfo->attnames[attrnum - 1]; |
16281 | switch (attrnum) |
16282 | { |
16283 | case SelfItemPointerAttributeNumber: |
16284 | return "ctid" ; |
16285 | case MinTransactionIdAttributeNumber: |
16286 | return "xmin" ; |
16287 | case MinCommandIdAttributeNumber: |
16288 | return "cmin" ; |
16289 | case MaxTransactionIdAttributeNumber: |
16290 | return "xmax" ; |
16291 | case MaxCommandIdAttributeNumber: |
16292 | return "cmax" ; |
16293 | case TableOidAttributeNumber: |
16294 | return "tableoid" ; |
16295 | } |
16296 | fatal("invalid column number %d for table \"%s\"" , |
16297 | attrnum, tblInfo->dobj.name); |
16298 | return NULL; /* keep compiler quiet */ |
16299 | } |
16300 | |
16301 | /* |
16302 | * dumpIndex |
16303 | * write out to fout a user-defined index |
16304 | */ |
16305 | static void |
16306 | dumpIndex(Archive *fout, IndxInfo *indxinfo) |
16307 | { |
16308 | DumpOptions *dopt = fout->dopt; |
16309 | TableInfo *tbinfo = indxinfo->indextable; |
16310 | bool is_constraint = (indxinfo->indexconstraint != 0); |
16311 | PQExpBuffer q; |
16312 | PQExpBuffer delq; |
16313 | char *qindxname; |
16314 | |
16315 | if (dopt->dataOnly) |
16316 | return; |
16317 | |
16318 | q = createPQExpBuffer(); |
16319 | delq = createPQExpBuffer(); |
16320 | |
16321 | qindxname = pg_strdup(fmtId(indxinfo->dobj.name)); |
16322 | |
16323 | /* |
16324 | * If there's an associated constraint, don't dump the index per se, but |
16325 | * do dump any comment for it. (This is safe because dependency ordering |
16326 | * will have ensured the constraint is emitted first.) Note that the |
16327 | * emitted comment has to be shown as depending on the constraint, not the |
16328 | * index, in such cases. |
16329 | */ |
16330 | if (!is_constraint) |
16331 | { |
16332 | char *indstatcols = indxinfo->indstatcols; |
16333 | char *indstatvals = indxinfo->indstatvals; |
16334 | char **indstatcolsarray = NULL; |
16335 | char **indstatvalsarray = NULL; |
16336 | int nstatcols; |
16337 | int nstatvals; |
16338 | |
16339 | if (dopt->binary_upgrade) |
16340 | binary_upgrade_set_pg_class_oids(fout, q, |
16341 | indxinfo->dobj.catId.oid, true); |
16342 | |
16343 | /* Plain secondary index */ |
16344 | appendPQExpBuffer(q, "%s;\n" , indxinfo->indexdef); |
16345 | |
16346 | /* |
16347 | * Append ALTER TABLE commands as needed to set properties that we |
16348 | * only have ALTER TABLE syntax for. Keep this in sync with the |
16349 | * similar code in dumpConstraint! |
16350 | */ |
16351 | |
16352 | /* If the index is clustered, we need to record that. */ |
16353 | if (indxinfo->indisclustered) |
16354 | { |
16355 | appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER" , |
16356 | fmtQualifiedDumpable(tbinfo)); |
16357 | /* index name is not qualified in this syntax */ |
16358 | appendPQExpBuffer(q, " ON %s;\n" , |
16359 | qindxname); |
16360 | } |
16361 | |
16362 | /* |
16363 | * If the index has any statistics on some of its columns, generate |
16364 | * the associated ALTER INDEX queries. |
16365 | */ |
16366 | if (parsePGArray(indstatcols, &indstatcolsarray, &nstatcols) && |
16367 | parsePGArray(indstatvals, &indstatvalsarray, &nstatvals) && |
16368 | nstatcols == nstatvals) |
16369 | { |
16370 | int j; |
16371 | |
16372 | for (j = 0; j < nstatcols; j++) |
16373 | { |
16374 | appendPQExpBuffer(q, "ALTER INDEX %s " , |
16375 | fmtQualifiedDumpable(indxinfo)); |
16376 | |
16377 | /* |
16378 | * Note that this is a column number, so no quotes should be |
16379 | * used. |
16380 | */ |
16381 | appendPQExpBuffer(q, "ALTER COLUMN %s " , |
16382 | indstatcolsarray[j]); |
16383 | appendPQExpBuffer(q, "SET STATISTICS %s;\n" , |
16384 | indstatvalsarray[j]); |
16385 | } |
16386 | } |
16387 | |
16388 | /* If the index defines identity, we need to record that. */ |
16389 | if (indxinfo->indisreplident) |
16390 | { |
16391 | appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING" , |
16392 | fmtQualifiedDumpable(tbinfo)); |
16393 | /* index name is not qualified in this syntax */ |
16394 | appendPQExpBuffer(q, " INDEX %s;\n" , |
16395 | qindxname); |
16396 | } |
16397 | |
16398 | appendPQExpBuffer(delq, "DROP INDEX %s;\n" , |
16399 | fmtQualifiedDumpable(indxinfo)); |
16400 | |
16401 | if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16402 | ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId, |
16403 | ARCHIVE_OPTS(.tag = indxinfo->dobj.name, |
16404 | .namespace = tbinfo->dobj.namespace->dobj.name, |
16405 | .tablespace = indxinfo->tablespace, |
16406 | .owner = tbinfo->rolname, |
16407 | .description = "INDEX" , |
16408 | .section = SECTION_POST_DATA, |
16409 | .createStmt = q->data, |
16410 | .dropStmt = delq->data)); |
16411 | |
16412 | if (indstatcolsarray) |
16413 | free(indstatcolsarray); |
16414 | if (indstatvalsarray) |
16415 | free(indstatvalsarray); |
16416 | } |
16417 | |
16418 | /* Dump Index Comments */ |
16419 | if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
16420 | dumpComment(fout, "INDEX" , qindxname, |
16421 | tbinfo->dobj.namespace->dobj.name, |
16422 | tbinfo->rolname, |
16423 | indxinfo->dobj.catId, 0, |
16424 | is_constraint ? indxinfo->indexconstraint : |
16425 | indxinfo->dobj.dumpId); |
16426 | |
16427 | destroyPQExpBuffer(q); |
16428 | destroyPQExpBuffer(delq); |
16429 | free(qindxname); |
16430 | } |
16431 | |
16432 | /* |
16433 | * dumpIndexAttach |
16434 | * write out to fout a partitioned-index attachment clause |
16435 | */ |
16436 | static void |
16437 | dumpIndexAttach(Archive *fout, IndexAttachInfo *attachinfo) |
16438 | { |
16439 | if (fout->dopt->dataOnly) |
16440 | return; |
16441 | |
16442 | if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16443 | { |
16444 | PQExpBuffer q = createPQExpBuffer(); |
16445 | |
16446 | appendPQExpBuffer(q, "ALTER INDEX %s " , |
16447 | fmtQualifiedDumpable(attachinfo->parentIdx)); |
16448 | appendPQExpBuffer(q, "ATTACH PARTITION %s;\n" , |
16449 | fmtQualifiedDumpable(attachinfo->partitionIdx)); |
16450 | |
16451 | ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId, |
16452 | ARCHIVE_OPTS(.tag = attachinfo->dobj.name, |
16453 | .namespace = attachinfo->dobj.namespace->dobj.name, |
16454 | .description = "INDEX ATTACH" , |
16455 | .section = SECTION_POST_DATA, |
16456 | .createStmt = q->data)); |
16457 | |
16458 | destroyPQExpBuffer(q); |
16459 | } |
16460 | } |
16461 | |
16462 | /* |
16463 | * dumpStatisticsExt |
16464 | * write out to fout an extended statistics object |
16465 | */ |
16466 | static void |
16467 | dumpStatisticsExt(Archive *fout, StatsExtInfo *statsextinfo) |
16468 | { |
16469 | DumpOptions *dopt = fout->dopt; |
16470 | PQExpBuffer q; |
16471 | PQExpBuffer delq; |
16472 | PQExpBuffer query; |
16473 | char *qstatsextname; |
16474 | PGresult *res; |
16475 | char *stxdef; |
16476 | |
16477 | /* Skip if not to be dumped */ |
16478 | if (!statsextinfo->dobj.dump || dopt->dataOnly) |
16479 | return; |
16480 | |
16481 | q = createPQExpBuffer(); |
16482 | delq = createPQExpBuffer(); |
16483 | query = createPQExpBuffer(); |
16484 | |
16485 | qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name)); |
16486 | |
16487 | appendPQExpBuffer(query, "SELECT " |
16488 | "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)" , |
16489 | statsextinfo->dobj.catId.oid); |
16490 | |
16491 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
16492 | |
16493 | stxdef = PQgetvalue(res, 0, 0); |
16494 | |
16495 | /* Result of pg_get_statisticsobjdef is complete except for semicolon */ |
16496 | appendPQExpBuffer(q, "%s;\n" , stxdef); |
16497 | |
16498 | appendPQExpBuffer(delq, "DROP STATISTICS %s;\n" , |
16499 | fmtQualifiedDumpable(statsextinfo)); |
16500 | |
16501 | if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16502 | ArchiveEntry(fout, statsextinfo->dobj.catId, |
16503 | statsextinfo->dobj.dumpId, |
16504 | ARCHIVE_OPTS(.tag = statsextinfo->dobj.name, |
16505 | .namespace = statsextinfo->dobj.namespace->dobj.name, |
16506 | .owner = statsextinfo->rolname, |
16507 | .description = "STATISTICS" , |
16508 | .section = SECTION_POST_DATA, |
16509 | .createStmt = q->data, |
16510 | .dropStmt = delq->data)); |
16511 | |
16512 | /* Dump Statistics Comments */ |
16513 | if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
16514 | dumpComment(fout, "STATISTICS" , qstatsextname, |
16515 | statsextinfo->dobj.namespace->dobj.name, |
16516 | statsextinfo->rolname, |
16517 | statsextinfo->dobj.catId, 0, |
16518 | statsextinfo->dobj.dumpId); |
16519 | |
16520 | PQclear(res); |
16521 | destroyPQExpBuffer(q); |
16522 | destroyPQExpBuffer(delq); |
16523 | destroyPQExpBuffer(query); |
16524 | free(qstatsextname); |
16525 | } |
16526 | |
16527 | /* |
16528 | * dumpConstraint |
16529 | * write out to fout a user-defined constraint |
16530 | */ |
16531 | static void |
16532 | dumpConstraint(Archive *fout, ConstraintInfo *coninfo) |
16533 | { |
16534 | DumpOptions *dopt = fout->dopt; |
16535 | TableInfo *tbinfo = coninfo->contable; |
16536 | PQExpBuffer q; |
16537 | PQExpBuffer delq; |
16538 | char *tag = NULL; |
16539 | |
16540 | /* Skip if not to be dumped */ |
16541 | if (!coninfo->dobj.dump || dopt->dataOnly) |
16542 | return; |
16543 | |
16544 | q = createPQExpBuffer(); |
16545 | delq = createPQExpBuffer(); |
16546 | |
16547 | if (coninfo->contype == 'p' || |
16548 | coninfo->contype == 'u' || |
16549 | coninfo->contype == 'x') |
16550 | { |
16551 | /* Index-related constraint */ |
16552 | IndxInfo *indxinfo; |
16553 | int k; |
16554 | |
16555 | indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex); |
16556 | |
16557 | if (indxinfo == NULL) |
16558 | fatal("missing index for constraint \"%s\"" , |
16559 | coninfo->dobj.name); |
16560 | |
16561 | if (dopt->binary_upgrade) |
16562 | binary_upgrade_set_pg_class_oids(fout, q, |
16563 | indxinfo->dobj.catId.oid, true); |
16564 | |
16565 | appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n" , |
16566 | fmtQualifiedDumpable(tbinfo)); |
16567 | appendPQExpBuffer(q, " ADD CONSTRAINT %s " , |
16568 | fmtId(coninfo->dobj.name)); |
16569 | |
16570 | if (coninfo->condef) |
16571 | { |
16572 | /* pg_get_constraintdef should have provided everything */ |
16573 | appendPQExpBuffer(q, "%s;\n" , coninfo->condef); |
16574 | } |
16575 | else |
16576 | { |
16577 | appendPQExpBuffer(q, "%s (" , |
16578 | coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE" ); |
16579 | for (k = 0; k < indxinfo->indnkeyattrs; k++) |
16580 | { |
16581 | int indkey = (int) indxinfo->indkeys[k]; |
16582 | const char *attname; |
16583 | |
16584 | if (indkey == InvalidAttrNumber) |
16585 | break; |
16586 | attname = getAttrName(indkey, tbinfo); |
16587 | |
16588 | appendPQExpBuffer(q, "%s%s" , |
16589 | (k == 0) ? "" : ", " , |
16590 | fmtId(attname)); |
16591 | } |
16592 | |
16593 | if (indxinfo->indnkeyattrs < indxinfo->indnattrs) |
16594 | appendPQExpBuffer(q, ") INCLUDE (" ); |
16595 | |
16596 | for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++) |
16597 | { |
16598 | int indkey = (int) indxinfo->indkeys[k]; |
16599 | const char *attname; |
16600 | |
16601 | if (indkey == InvalidAttrNumber) |
16602 | break; |
16603 | attname = getAttrName(indkey, tbinfo); |
16604 | |
16605 | appendPQExpBuffer(q, "%s%s" , |
16606 | (k == indxinfo->indnkeyattrs) ? "" : ", " , |
16607 | fmtId(attname)); |
16608 | } |
16609 | |
16610 | appendPQExpBufferChar(q, ')'); |
16611 | |
16612 | if (nonemptyReloptions(indxinfo->indreloptions)) |
16613 | { |
16614 | appendPQExpBufferStr(q, " WITH (" ); |
16615 | appendReloptionsArrayAH(q, indxinfo->indreloptions, "" , fout); |
16616 | appendPQExpBufferChar(q, ')'); |
16617 | } |
16618 | |
16619 | if (coninfo->condeferrable) |
16620 | { |
16621 | appendPQExpBufferStr(q, " DEFERRABLE" ); |
16622 | if (coninfo->condeferred) |
16623 | appendPQExpBufferStr(q, " INITIALLY DEFERRED" ); |
16624 | } |
16625 | |
16626 | appendPQExpBufferStr(q, ";\n" ); |
16627 | } |
16628 | |
16629 | /* |
16630 | * Append ALTER TABLE commands as needed to set properties that we |
16631 | * only have ALTER TABLE syntax for. Keep this in sync with the |
16632 | * similar code in dumpIndex! |
16633 | */ |
16634 | |
16635 | /* If the index is clustered, we need to record that. */ |
16636 | if (indxinfo->indisclustered) |
16637 | { |
16638 | appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER" , |
16639 | fmtQualifiedDumpable(tbinfo)); |
16640 | /* index name is not qualified in this syntax */ |
16641 | appendPQExpBuffer(q, " ON %s;\n" , |
16642 | fmtId(indxinfo->dobj.name)); |
16643 | } |
16644 | |
16645 | /* If the index defines identity, we need to record that. */ |
16646 | if (indxinfo->indisreplident) |
16647 | { |
16648 | appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING" , |
16649 | fmtQualifiedDumpable(tbinfo)); |
16650 | /* index name is not qualified in this syntax */ |
16651 | appendPQExpBuffer(q, " INDEX %s;\n" , |
16652 | fmtId(indxinfo->dobj.name)); |
16653 | } |
16654 | |
16655 | appendPQExpBuffer(delq, "ALTER TABLE ONLY %s " , |
16656 | fmtQualifiedDumpable(tbinfo)); |
16657 | appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n" , |
16658 | fmtId(coninfo->dobj.name)); |
16659 | |
16660 | tag = psprintf("%s %s" , tbinfo->dobj.name, coninfo->dobj.name); |
16661 | |
16662 | if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16663 | ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId, |
16664 | ARCHIVE_OPTS(.tag = tag, |
16665 | .namespace = tbinfo->dobj.namespace->dobj.name, |
16666 | .tablespace = indxinfo->tablespace, |
16667 | .owner = tbinfo->rolname, |
16668 | .description = "CONSTRAINT" , |
16669 | .section = SECTION_POST_DATA, |
16670 | .createStmt = q->data, |
16671 | .dropStmt = delq->data)); |
16672 | } |
16673 | else if (coninfo->contype == 'f') |
16674 | { |
16675 | char *only; |
16676 | |
16677 | /* |
16678 | * Foreign keys on partitioned tables are always declared as |
16679 | * inheriting to partitions; for all other cases, emit them as |
16680 | * applying ONLY directly to the named table, because that's how they |
16681 | * work for regular inherited tables. |
16682 | */ |
16683 | only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY " ; |
16684 | |
16685 | /* |
16686 | * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the |
16687 | * current table data is not processed |
16688 | */ |
16689 | appendPQExpBuffer(q, "ALTER TABLE %s%s\n" , |
16690 | only, fmtQualifiedDumpable(tbinfo)); |
16691 | appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n" , |
16692 | fmtId(coninfo->dobj.name), |
16693 | coninfo->condef); |
16694 | |
16695 | appendPQExpBuffer(delq, "ALTER TABLE %s%s " , |
16696 | only, fmtQualifiedDumpable(tbinfo)); |
16697 | appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n" , |
16698 | fmtId(coninfo->dobj.name)); |
16699 | |
16700 | tag = psprintf("%s %s" , tbinfo->dobj.name, coninfo->dobj.name); |
16701 | |
16702 | if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16703 | ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId, |
16704 | ARCHIVE_OPTS(.tag = tag, |
16705 | .namespace = tbinfo->dobj.namespace->dobj.name, |
16706 | .owner = tbinfo->rolname, |
16707 | .description = "FK CONSTRAINT" , |
16708 | .section = SECTION_POST_DATA, |
16709 | .createStmt = q->data, |
16710 | .dropStmt = delq->data)); |
16711 | } |
16712 | else if (coninfo->contype == 'c' && tbinfo) |
16713 | { |
16714 | /* CHECK constraint on a table */ |
16715 | |
16716 | /* Ignore if not to be dumped separately, or if it was inherited */ |
16717 | if (coninfo->separate && coninfo->conislocal) |
16718 | { |
16719 | /* not ONLY since we want it to propagate to children */ |
16720 | appendPQExpBuffer(q, "ALTER TABLE %s\n" , |
16721 | fmtQualifiedDumpable(tbinfo)); |
16722 | appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n" , |
16723 | fmtId(coninfo->dobj.name), |
16724 | coninfo->condef); |
16725 | |
16726 | appendPQExpBuffer(delq, "ALTER TABLE %s " , |
16727 | fmtQualifiedDumpable(tbinfo)); |
16728 | appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n" , |
16729 | fmtId(coninfo->dobj.name)); |
16730 | |
16731 | tag = psprintf("%s %s" , tbinfo->dobj.name, coninfo->dobj.name); |
16732 | |
16733 | if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16734 | ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId, |
16735 | ARCHIVE_OPTS(.tag = tag, |
16736 | .namespace = tbinfo->dobj.namespace->dobj.name, |
16737 | .owner = tbinfo->rolname, |
16738 | .description = "CHECK CONSTRAINT" , |
16739 | .section = SECTION_POST_DATA, |
16740 | .createStmt = q->data, |
16741 | .dropStmt = delq->data)); |
16742 | } |
16743 | } |
16744 | else if (coninfo->contype == 'c' && tbinfo == NULL) |
16745 | { |
16746 | /* CHECK constraint on a domain */ |
16747 | TypeInfo *tyinfo = coninfo->condomain; |
16748 | |
16749 | /* Ignore if not to be dumped separately */ |
16750 | if (coninfo->separate) |
16751 | { |
16752 | appendPQExpBuffer(q, "ALTER DOMAIN %s\n" , |
16753 | fmtQualifiedDumpable(tyinfo)); |
16754 | appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n" , |
16755 | fmtId(coninfo->dobj.name), |
16756 | coninfo->condef); |
16757 | |
16758 | appendPQExpBuffer(delq, "ALTER DOMAIN %s " , |
16759 | fmtQualifiedDumpable(tyinfo)); |
16760 | appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n" , |
16761 | fmtId(coninfo->dobj.name)); |
16762 | |
16763 | tag = psprintf("%s %s" , tyinfo->dobj.name, coninfo->dobj.name); |
16764 | |
16765 | if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
16766 | ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId, |
16767 | ARCHIVE_OPTS(.tag = tag, |
16768 | .namespace = tyinfo->dobj.namespace->dobj.name, |
16769 | .owner = tyinfo->rolname, |
16770 | .description = "CHECK CONSTRAINT" , |
16771 | .section = SECTION_POST_DATA, |
16772 | .createStmt = q->data, |
16773 | .dropStmt = delq->data)); |
16774 | } |
16775 | } |
16776 | else |
16777 | { |
16778 | fatal("unrecognized constraint type: %c" , |
16779 | coninfo->contype); |
16780 | } |
16781 | |
16782 | /* Dump Constraint Comments --- only works for table constraints */ |
16783 | if (tbinfo && coninfo->separate && |
16784 | coninfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
16785 | dumpTableConstraintComment(fout, coninfo); |
16786 | |
16787 | free(tag); |
16788 | destroyPQExpBuffer(q); |
16789 | destroyPQExpBuffer(delq); |
16790 | } |
16791 | |
16792 | /* |
16793 | * dumpTableConstraintComment --- dump a constraint's comment if any |
16794 | * |
16795 | * This is split out because we need the function in two different places |
16796 | * depending on whether the constraint is dumped as part of CREATE TABLE |
16797 | * or as a separate ALTER command. |
16798 | */ |
16799 | static void |
16800 | (Archive *fout, ConstraintInfo *coninfo) |
16801 | { |
16802 | TableInfo *tbinfo = coninfo->contable; |
16803 | PQExpBuffer conprefix = createPQExpBuffer(); |
16804 | char *qtabname; |
16805 | |
16806 | qtabname = pg_strdup(fmtId(tbinfo->dobj.name)); |
16807 | |
16808 | appendPQExpBuffer(conprefix, "CONSTRAINT %s ON" , |
16809 | fmtId(coninfo->dobj.name)); |
16810 | |
16811 | if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
16812 | dumpComment(fout, conprefix->data, qtabname, |
16813 | tbinfo->dobj.namespace->dobj.name, |
16814 | tbinfo->rolname, |
16815 | coninfo->dobj.catId, 0, |
16816 | coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId); |
16817 | |
16818 | destroyPQExpBuffer(conprefix); |
16819 | free(qtabname); |
16820 | } |
16821 | |
16822 | /* |
16823 | * findLastBuiltinOid_V71 - |
16824 | * |
16825 | * find the last built in oid |
16826 | * |
16827 | * For 7.1 through 8.0, we do this by retrieving datlastsysoid from the |
16828 | * pg_database entry for the current database. (Note: current_database() |
16829 | * requires 7.3; pg_dump requires 8.0 now.) |
16830 | */ |
16831 | static Oid |
16832 | findLastBuiltinOid_V71(Archive *fout) |
16833 | { |
16834 | PGresult *res; |
16835 | Oid last_oid; |
16836 | |
16837 | res = ExecuteSqlQueryForSingleRow(fout, |
16838 | "SELECT datlastsysoid FROM pg_database WHERE datname = current_database()" ); |
16839 | last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid" ))); |
16840 | PQclear(res); |
16841 | |
16842 | return last_oid; |
16843 | } |
16844 | |
16845 | /* |
16846 | * dumpSequence |
16847 | * write the declaration (not data) of one user-defined sequence |
16848 | */ |
16849 | static void |
16850 | dumpSequence(Archive *fout, TableInfo *tbinfo) |
16851 | { |
16852 | DumpOptions *dopt = fout->dopt; |
16853 | PGresult *res; |
16854 | char *startv, |
16855 | *incby, |
16856 | *maxv, |
16857 | *minv, |
16858 | *cache, |
16859 | *seqtype; |
16860 | bool cycled; |
16861 | bool is_ascending; |
16862 | int64 default_minv, |
16863 | default_maxv; |
16864 | char bufm[32], |
16865 | bufx[32]; |
16866 | PQExpBuffer query = createPQExpBuffer(); |
16867 | PQExpBuffer delqry = createPQExpBuffer(); |
16868 | char *qseqname; |
16869 | |
16870 | qseqname = pg_strdup(fmtId(tbinfo->dobj.name)); |
16871 | |
16872 | if (fout->remoteVersion >= 100000) |
16873 | { |
16874 | appendPQExpBuffer(query, |
16875 | "SELECT format_type(seqtypid, NULL), " |
16876 | "seqstart, seqincrement, " |
16877 | "seqmax, seqmin, " |
16878 | "seqcache, seqcycle " |
16879 | "FROM pg_catalog.pg_sequence " |
16880 | "WHERE seqrelid = '%u'::oid" , |
16881 | tbinfo->dobj.catId.oid); |
16882 | } |
16883 | else if (fout->remoteVersion >= 80400) |
16884 | { |
16885 | /* |
16886 | * Before PostgreSQL 10, sequence metadata is in the sequence itself. |
16887 | * |
16888 | * Note: it might seem that 'bigint' potentially needs to be |
16889 | * schema-qualified, but actually that's a keyword. |
16890 | */ |
16891 | appendPQExpBuffer(query, |
16892 | "SELECT 'bigint' AS sequence_type, " |
16893 | "start_value, increment_by, max_value, min_value, " |
16894 | "cache_value, is_cycled FROM %s" , |
16895 | fmtQualifiedDumpable(tbinfo)); |
16896 | } |
16897 | else |
16898 | { |
16899 | appendPQExpBuffer(query, |
16900 | "SELECT 'bigint' AS sequence_type, " |
16901 | "0 AS start_value, increment_by, max_value, min_value, " |
16902 | "cache_value, is_cycled FROM %s" , |
16903 | fmtQualifiedDumpable(tbinfo)); |
16904 | } |
16905 | |
16906 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
16907 | |
16908 | if (PQntuples(res) != 1) |
16909 | { |
16910 | pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)" , |
16911 | "query to get data of sequence \"%s\" returned %d rows (expected 1)" , |
16912 | PQntuples(res)), |
16913 | tbinfo->dobj.name, PQntuples(res)); |
16914 | exit_nicely(1); |
16915 | } |
16916 | |
16917 | seqtype = PQgetvalue(res, 0, 0); |
16918 | startv = PQgetvalue(res, 0, 1); |
16919 | incby = PQgetvalue(res, 0, 2); |
16920 | maxv = PQgetvalue(res, 0, 3); |
16921 | minv = PQgetvalue(res, 0, 4); |
16922 | cache = PQgetvalue(res, 0, 5); |
16923 | cycled = (strcmp(PQgetvalue(res, 0, 6), "t" ) == 0); |
16924 | |
16925 | /* Calculate default limits for a sequence of this type */ |
16926 | is_ascending = (incby[0] != '-'); |
16927 | if (strcmp(seqtype, "smallint" ) == 0) |
16928 | { |
16929 | default_minv = is_ascending ? 1 : PG_INT16_MIN; |
16930 | default_maxv = is_ascending ? PG_INT16_MAX : -1; |
16931 | } |
16932 | else if (strcmp(seqtype, "integer" ) == 0) |
16933 | { |
16934 | default_minv = is_ascending ? 1 : PG_INT32_MIN; |
16935 | default_maxv = is_ascending ? PG_INT32_MAX : -1; |
16936 | } |
16937 | else if (strcmp(seqtype, "bigint" ) == 0) |
16938 | { |
16939 | default_minv = is_ascending ? 1 : PG_INT64_MIN; |
16940 | default_maxv = is_ascending ? PG_INT64_MAX : -1; |
16941 | } |
16942 | else |
16943 | { |
16944 | fatal("unrecognized sequence type: %s" , seqtype); |
16945 | default_minv = default_maxv = 0; /* keep compiler quiet */ |
16946 | } |
16947 | |
16948 | /* |
16949 | * 64-bit strtol() isn't very portable, so convert the limits to strings |
16950 | * and compare that way. |
16951 | */ |
16952 | snprintf(bufm, sizeof(bufm), INT64_FORMAT, default_minv); |
16953 | snprintf(bufx, sizeof(bufx), INT64_FORMAT, default_maxv); |
16954 | |
16955 | /* Don't print minv/maxv if they match the respective default limit */ |
16956 | if (strcmp(minv, bufm) == 0) |
16957 | minv = NULL; |
16958 | if (strcmp(maxv, bufx) == 0) |
16959 | maxv = NULL; |
16960 | |
16961 | /* |
16962 | * Identity sequences are not to be dropped separately. |
16963 | */ |
16964 | if (!tbinfo->is_identity_sequence) |
16965 | { |
16966 | appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n" , |
16967 | fmtQualifiedDumpable(tbinfo)); |
16968 | } |
16969 | |
16970 | resetPQExpBuffer(query); |
16971 | |
16972 | if (dopt->binary_upgrade) |
16973 | { |
16974 | binary_upgrade_set_pg_class_oids(fout, query, |
16975 | tbinfo->dobj.catId.oid, false); |
16976 | binary_upgrade_set_type_oids_by_rel_oid(fout, query, |
16977 | tbinfo->dobj.catId.oid); |
16978 | } |
16979 | |
16980 | if (tbinfo->is_identity_sequence) |
16981 | { |
16982 | TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab); |
16983 | |
16984 | appendPQExpBuffer(query, |
16985 | "ALTER TABLE %s " , |
16986 | fmtQualifiedDumpable(owning_tab)); |
16987 | appendPQExpBuffer(query, |
16988 | "ALTER COLUMN %s ADD GENERATED " , |
16989 | fmtId(owning_tab->attnames[tbinfo->owning_col - 1])); |
16990 | if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS) |
16991 | appendPQExpBuffer(query, "ALWAYS" ); |
16992 | else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT) |
16993 | appendPQExpBuffer(query, "BY DEFAULT" ); |
16994 | appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n" , |
16995 | fmtQualifiedDumpable(tbinfo)); |
16996 | } |
16997 | else |
16998 | { |
16999 | appendPQExpBuffer(query, |
17000 | "CREATE SEQUENCE %s\n" , |
17001 | fmtQualifiedDumpable(tbinfo)); |
17002 | |
17003 | if (strcmp(seqtype, "bigint" ) != 0) |
17004 | appendPQExpBuffer(query, " AS %s\n" , seqtype); |
17005 | } |
17006 | |
17007 | if (fout->remoteVersion >= 80400) |
17008 | appendPQExpBuffer(query, " START WITH %s\n" , startv); |
17009 | |
17010 | appendPQExpBuffer(query, " INCREMENT BY %s\n" , incby); |
17011 | |
17012 | if (minv) |
17013 | appendPQExpBuffer(query, " MINVALUE %s\n" , minv); |
17014 | else |
17015 | appendPQExpBufferStr(query, " NO MINVALUE\n" ); |
17016 | |
17017 | if (maxv) |
17018 | appendPQExpBuffer(query, " MAXVALUE %s\n" , maxv); |
17019 | else |
17020 | appendPQExpBufferStr(query, " NO MAXVALUE\n" ); |
17021 | |
17022 | appendPQExpBuffer(query, |
17023 | " CACHE %s%s" , |
17024 | cache, (cycled ? "\n CYCLE" : "" )); |
17025 | |
17026 | if (tbinfo->is_identity_sequence) |
17027 | appendPQExpBufferStr(query, "\n);\n" ); |
17028 | else |
17029 | appendPQExpBufferStr(query, ";\n" ); |
17030 | |
17031 | /* binary_upgrade: no need to clear TOAST table oid */ |
17032 | |
17033 | if (dopt->binary_upgrade) |
17034 | binary_upgrade_extension_member(query, &tbinfo->dobj, |
17035 | "SEQUENCE" , qseqname, |
17036 | tbinfo->dobj.namespace->dobj.name); |
17037 | |
17038 | if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
17039 | ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, |
17040 | ARCHIVE_OPTS(.tag = tbinfo->dobj.name, |
17041 | .namespace = tbinfo->dobj.namespace->dobj.name, |
17042 | .owner = tbinfo->rolname, |
17043 | .description = "SEQUENCE" , |
17044 | .section = SECTION_PRE_DATA, |
17045 | .createStmt = query->data, |
17046 | .dropStmt = delqry->data)); |
17047 | |
17048 | /* |
17049 | * If the sequence is owned by a table column, emit the ALTER for it as a |
17050 | * separate TOC entry immediately following the sequence's own entry. It's |
17051 | * OK to do this rather than using full sorting logic, because the |
17052 | * dependency that tells us it's owned will have forced the table to be |
17053 | * created first. We can't just include the ALTER in the TOC entry |
17054 | * because it will fail if we haven't reassigned the sequence owner to |
17055 | * match the table's owner. |
17056 | * |
17057 | * We need not schema-qualify the table reference because both sequence |
17058 | * and table must be in the same schema. |
17059 | */ |
17060 | if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence) |
17061 | { |
17062 | TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab); |
17063 | |
17064 | if (owning_tab == NULL) |
17065 | fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found" , |
17066 | tbinfo->owning_tab, tbinfo->dobj.catId.oid); |
17067 | |
17068 | if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION) |
17069 | { |
17070 | resetPQExpBuffer(query); |
17071 | appendPQExpBuffer(query, "ALTER SEQUENCE %s" , |
17072 | fmtQualifiedDumpable(tbinfo)); |
17073 | appendPQExpBuffer(query, " OWNED BY %s" , |
17074 | fmtQualifiedDumpable(owning_tab)); |
17075 | appendPQExpBuffer(query, ".%s;\n" , |
17076 | fmtId(owning_tab->attnames[tbinfo->owning_col - 1])); |
17077 | |
17078 | if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
17079 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
17080 | ARCHIVE_OPTS(.tag = tbinfo->dobj.name, |
17081 | .namespace = tbinfo->dobj.namespace->dobj.name, |
17082 | .owner = tbinfo->rolname, |
17083 | .description = "SEQUENCE OWNED BY" , |
17084 | .section = SECTION_PRE_DATA, |
17085 | .createStmt = query->data, |
17086 | .deps = &(tbinfo->dobj.dumpId), |
17087 | .nDeps = 1)); |
17088 | } |
17089 | } |
17090 | |
17091 | /* Dump Sequence Comments and Security Labels */ |
17092 | if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
17093 | dumpComment(fout, "SEQUENCE" , qseqname, |
17094 | tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, |
17095 | tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); |
17096 | |
17097 | if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL) |
17098 | dumpSecLabel(fout, "SEQUENCE" , qseqname, |
17099 | tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, |
17100 | tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); |
17101 | |
17102 | PQclear(res); |
17103 | |
17104 | destroyPQExpBuffer(query); |
17105 | destroyPQExpBuffer(delqry); |
17106 | free(qseqname); |
17107 | } |
17108 | |
17109 | /* |
17110 | * dumpSequenceData |
17111 | * write the data of one user-defined sequence |
17112 | */ |
17113 | static void |
17114 | dumpSequenceData(Archive *fout, TableDataInfo *tdinfo) |
17115 | { |
17116 | TableInfo *tbinfo = tdinfo->tdtable; |
17117 | PGresult *res; |
17118 | char *last; |
17119 | bool called; |
17120 | PQExpBuffer query = createPQExpBuffer(); |
17121 | |
17122 | appendPQExpBuffer(query, |
17123 | "SELECT last_value, is_called FROM %s" , |
17124 | fmtQualifiedDumpable(tbinfo)); |
17125 | |
17126 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
17127 | |
17128 | if (PQntuples(res) != 1) |
17129 | { |
17130 | pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)" , |
17131 | "query to get data of sequence \"%s\" returned %d rows (expected 1)" , |
17132 | PQntuples(res)), |
17133 | tbinfo->dobj.name, PQntuples(res)); |
17134 | exit_nicely(1); |
17135 | } |
17136 | |
17137 | last = PQgetvalue(res, 0, 0); |
17138 | called = (strcmp(PQgetvalue(res, 0, 1), "t" ) == 0); |
17139 | |
17140 | resetPQExpBuffer(query); |
17141 | appendPQExpBufferStr(query, "SELECT pg_catalog.setval(" ); |
17142 | appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout); |
17143 | appendPQExpBuffer(query, ", %s, %s);\n" , |
17144 | last, (called ? "true" : "false" )); |
17145 | |
17146 | if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA) |
17147 | ArchiveEntry(fout, nilCatalogId, createDumpId(), |
17148 | ARCHIVE_OPTS(.tag = tbinfo->dobj.name, |
17149 | .namespace = tbinfo->dobj.namespace->dobj.name, |
17150 | .owner = tbinfo->rolname, |
17151 | .description = "SEQUENCE SET" , |
17152 | .section = SECTION_DATA, |
17153 | .createStmt = query->data, |
17154 | .deps = &(tbinfo->dobj.dumpId), |
17155 | .nDeps = 1)); |
17156 | |
17157 | PQclear(res); |
17158 | |
17159 | destroyPQExpBuffer(query); |
17160 | } |
17161 | |
17162 | /* |
17163 | * dumpTrigger |
17164 | * write the declaration of one user-defined table trigger |
17165 | */ |
17166 | static void |
17167 | dumpTrigger(Archive *fout, TriggerInfo *tginfo) |
17168 | { |
17169 | DumpOptions *dopt = fout->dopt; |
17170 | TableInfo *tbinfo = tginfo->tgtable; |
17171 | PQExpBuffer query; |
17172 | PQExpBuffer delqry; |
17173 | PQExpBuffer trigprefix; |
17174 | char *qtabname; |
17175 | char *tgargs; |
17176 | size_t lentgargs; |
17177 | const char *p; |
17178 | int findx; |
17179 | char *tag; |
17180 | |
17181 | /* |
17182 | * we needn't check dobj.dump because TriggerInfo wouldn't have been |
17183 | * created in the first place for non-dumpable triggers |
17184 | */ |
17185 | if (dopt->dataOnly) |
17186 | return; |
17187 | |
17188 | query = createPQExpBuffer(); |
17189 | delqry = createPQExpBuffer(); |
17190 | trigprefix = createPQExpBuffer(); |
17191 | |
17192 | qtabname = pg_strdup(fmtId(tbinfo->dobj.name)); |
17193 | |
17194 | appendPQExpBuffer(delqry, "DROP TRIGGER %s " , |
17195 | fmtId(tginfo->dobj.name)); |
17196 | appendPQExpBuffer(delqry, "ON %s;\n" , |
17197 | fmtQualifiedDumpable(tbinfo)); |
17198 | |
17199 | if (tginfo->tgdef) |
17200 | { |
17201 | appendPQExpBuffer(query, "%s;\n" , tginfo->tgdef); |
17202 | } |
17203 | else |
17204 | { |
17205 | if (tginfo->tgisconstraint) |
17206 | { |
17207 | appendPQExpBufferStr(query, "CREATE CONSTRAINT TRIGGER " ); |
17208 | appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname)); |
17209 | } |
17210 | else |
17211 | { |
17212 | appendPQExpBufferStr(query, "CREATE TRIGGER " ); |
17213 | appendPQExpBufferStr(query, fmtId(tginfo->dobj.name)); |
17214 | } |
17215 | appendPQExpBufferStr(query, "\n " ); |
17216 | |
17217 | /* Trigger type */ |
17218 | if (TRIGGER_FOR_BEFORE(tginfo->tgtype)) |
17219 | appendPQExpBufferStr(query, "BEFORE" ); |
17220 | else if (TRIGGER_FOR_AFTER(tginfo->tgtype)) |
17221 | appendPQExpBufferStr(query, "AFTER" ); |
17222 | else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype)) |
17223 | appendPQExpBufferStr(query, "INSTEAD OF" ); |
17224 | else |
17225 | { |
17226 | pg_log_error("unexpected tgtype value: %d" , tginfo->tgtype); |
17227 | exit_nicely(1); |
17228 | } |
17229 | |
17230 | findx = 0; |
17231 | if (TRIGGER_FOR_INSERT(tginfo->tgtype)) |
17232 | { |
17233 | appendPQExpBufferStr(query, " INSERT" ); |
17234 | findx++; |
17235 | } |
17236 | if (TRIGGER_FOR_DELETE(tginfo->tgtype)) |
17237 | { |
17238 | if (findx > 0) |
17239 | appendPQExpBufferStr(query, " OR DELETE" ); |
17240 | else |
17241 | appendPQExpBufferStr(query, " DELETE" ); |
17242 | findx++; |
17243 | } |
17244 | if (TRIGGER_FOR_UPDATE(tginfo->tgtype)) |
17245 | { |
17246 | if (findx > 0) |
17247 | appendPQExpBufferStr(query, " OR UPDATE" ); |
17248 | else |
17249 | appendPQExpBufferStr(query, " UPDATE" ); |
17250 | findx++; |
17251 | } |
17252 | if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype)) |
17253 | { |
17254 | if (findx > 0) |
17255 | appendPQExpBufferStr(query, " OR TRUNCATE" ); |
17256 | else |
17257 | appendPQExpBufferStr(query, " TRUNCATE" ); |
17258 | findx++; |
17259 | } |
17260 | appendPQExpBuffer(query, " ON %s\n" , |
17261 | fmtQualifiedDumpable(tbinfo)); |
17262 | |
17263 | if (tginfo->tgisconstraint) |
17264 | { |
17265 | if (OidIsValid(tginfo->tgconstrrelid)) |
17266 | { |
17267 | /* regclass output is already quoted */ |
17268 | appendPQExpBuffer(query, " FROM %s\n " , |
17269 | tginfo->tgconstrrelname); |
17270 | } |
17271 | if (!tginfo->tgdeferrable) |
17272 | appendPQExpBufferStr(query, "NOT " ); |
17273 | appendPQExpBufferStr(query, "DEFERRABLE INITIALLY " ); |
17274 | if (tginfo->tginitdeferred) |
17275 | appendPQExpBufferStr(query, "DEFERRED\n" ); |
17276 | else |
17277 | appendPQExpBufferStr(query, "IMMEDIATE\n" ); |
17278 | } |
17279 | |
17280 | if (TRIGGER_FOR_ROW(tginfo->tgtype)) |
17281 | appendPQExpBufferStr(query, " FOR EACH ROW\n " ); |
17282 | else |
17283 | appendPQExpBufferStr(query, " FOR EACH STATEMENT\n " ); |
17284 | |
17285 | /* regproc output is already sufficiently quoted */ |
17286 | appendPQExpBuffer(query, "EXECUTE FUNCTION %s(" , |
17287 | tginfo->tgfname); |
17288 | |
17289 | tgargs = (char *) PQunescapeBytea((unsigned char *) tginfo->tgargs, |
17290 | &lentgargs); |
17291 | p = tgargs; |
17292 | for (findx = 0; findx < tginfo->tgnargs; findx++) |
17293 | { |
17294 | /* find the embedded null that terminates this trigger argument */ |
17295 | size_t tlen = strlen(p); |
17296 | |
17297 | if (p + tlen >= tgargs + lentgargs) |
17298 | { |
17299 | /* hm, not found before end of bytea value... */ |
17300 | pg_log_error("invalid argument string (%s) for trigger \"%s\" on table \"%s\"" , |
17301 | tginfo->tgargs, |
17302 | tginfo->dobj.name, |
17303 | tbinfo->dobj.name); |
17304 | exit_nicely(1); |
17305 | } |
17306 | |
17307 | if (findx > 0) |
17308 | appendPQExpBufferStr(query, ", " ); |
17309 | appendStringLiteralAH(query, p, fout); |
17310 | p += tlen + 1; |
17311 | } |
17312 | free(tgargs); |
17313 | appendPQExpBufferStr(query, ");\n" ); |
17314 | } |
17315 | |
17316 | if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O') |
17317 | { |
17318 | appendPQExpBuffer(query, "\nALTER TABLE %s " , |
17319 | fmtQualifiedDumpable(tbinfo)); |
17320 | switch (tginfo->tgenabled) |
17321 | { |
17322 | case 'D': |
17323 | case 'f': |
17324 | appendPQExpBufferStr(query, "DISABLE" ); |
17325 | break; |
17326 | case 'A': |
17327 | appendPQExpBufferStr(query, "ENABLE ALWAYS" ); |
17328 | break; |
17329 | case 'R': |
17330 | appendPQExpBufferStr(query, "ENABLE REPLICA" ); |
17331 | break; |
17332 | default: |
17333 | appendPQExpBufferStr(query, "ENABLE" ); |
17334 | break; |
17335 | } |
17336 | appendPQExpBuffer(query, " TRIGGER %s;\n" , |
17337 | fmtId(tginfo->dobj.name)); |
17338 | } |
17339 | |
17340 | appendPQExpBuffer(trigprefix, "TRIGGER %s ON" , |
17341 | fmtId(tginfo->dobj.name)); |
17342 | |
17343 | tag = psprintf("%s %s" , tbinfo->dobj.name, tginfo->dobj.name); |
17344 | |
17345 | if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
17346 | ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId, |
17347 | ARCHIVE_OPTS(.tag = tag, |
17348 | .namespace = tbinfo->dobj.namespace->dobj.name, |
17349 | .owner = tbinfo->rolname, |
17350 | .description = "TRIGGER" , |
17351 | .section = SECTION_POST_DATA, |
17352 | .createStmt = query->data, |
17353 | .dropStmt = delqry->data)); |
17354 | |
17355 | if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
17356 | dumpComment(fout, trigprefix->data, qtabname, |
17357 | tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, |
17358 | tginfo->dobj.catId, 0, tginfo->dobj.dumpId); |
17359 | |
17360 | free(tag); |
17361 | destroyPQExpBuffer(query); |
17362 | destroyPQExpBuffer(delqry); |
17363 | destroyPQExpBuffer(trigprefix); |
17364 | free(qtabname); |
17365 | } |
17366 | |
17367 | /* |
17368 | * dumpEventTrigger |
17369 | * write the declaration of one user-defined event trigger |
17370 | */ |
17371 | static void |
17372 | dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo) |
17373 | { |
17374 | DumpOptions *dopt = fout->dopt; |
17375 | PQExpBuffer query; |
17376 | PQExpBuffer delqry; |
17377 | char *qevtname; |
17378 | |
17379 | /* Skip if not to be dumped */ |
17380 | if (!evtinfo->dobj.dump || dopt->dataOnly) |
17381 | return; |
17382 | |
17383 | query = createPQExpBuffer(); |
17384 | delqry = createPQExpBuffer(); |
17385 | |
17386 | qevtname = pg_strdup(fmtId(evtinfo->dobj.name)); |
17387 | |
17388 | appendPQExpBufferStr(query, "CREATE EVENT TRIGGER " ); |
17389 | appendPQExpBufferStr(query, qevtname); |
17390 | appendPQExpBufferStr(query, " ON " ); |
17391 | appendPQExpBufferStr(query, fmtId(evtinfo->evtevent)); |
17392 | |
17393 | if (strcmp("" , evtinfo->evttags) != 0) |
17394 | { |
17395 | appendPQExpBufferStr(query, "\n WHEN TAG IN (" ); |
17396 | appendPQExpBufferStr(query, evtinfo->evttags); |
17397 | appendPQExpBufferChar(query, ')'); |
17398 | } |
17399 | |
17400 | appendPQExpBufferStr(query, "\n EXECUTE FUNCTION " ); |
17401 | appendPQExpBufferStr(query, evtinfo->evtfname); |
17402 | appendPQExpBufferStr(query, "();\n" ); |
17403 | |
17404 | if (evtinfo->evtenabled != 'O') |
17405 | { |
17406 | appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s " , |
17407 | qevtname); |
17408 | switch (evtinfo->evtenabled) |
17409 | { |
17410 | case 'D': |
17411 | appendPQExpBufferStr(query, "DISABLE" ); |
17412 | break; |
17413 | case 'A': |
17414 | appendPQExpBufferStr(query, "ENABLE ALWAYS" ); |
17415 | break; |
17416 | case 'R': |
17417 | appendPQExpBufferStr(query, "ENABLE REPLICA" ); |
17418 | break; |
17419 | default: |
17420 | appendPQExpBufferStr(query, "ENABLE" ); |
17421 | break; |
17422 | } |
17423 | appendPQExpBufferStr(query, ";\n" ); |
17424 | } |
17425 | |
17426 | appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n" , |
17427 | qevtname); |
17428 | |
17429 | if (dopt->binary_upgrade) |
17430 | binary_upgrade_extension_member(query, &evtinfo->dobj, |
17431 | "EVENT TRIGGER" , qevtname, NULL); |
17432 | |
17433 | if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
17434 | ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId, |
17435 | ARCHIVE_OPTS(.tag = evtinfo->dobj.name, |
17436 | .owner = evtinfo->evtowner, |
17437 | .description = "EVENT TRIGGER" , |
17438 | .section = SECTION_POST_DATA, |
17439 | .createStmt = query->data, |
17440 | .dropStmt = delqry->data)); |
17441 | |
17442 | if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
17443 | dumpComment(fout, "EVENT TRIGGER" , qevtname, |
17444 | NULL, evtinfo->evtowner, |
17445 | evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId); |
17446 | |
17447 | destroyPQExpBuffer(query); |
17448 | destroyPQExpBuffer(delqry); |
17449 | free(qevtname); |
17450 | } |
17451 | |
17452 | /* |
17453 | * dumpRule |
17454 | * Dump a rule |
17455 | */ |
17456 | static void |
17457 | dumpRule(Archive *fout, RuleInfo *rinfo) |
17458 | { |
17459 | DumpOptions *dopt = fout->dopt; |
17460 | TableInfo *tbinfo = rinfo->ruletable; |
17461 | bool is_view; |
17462 | PQExpBuffer query; |
17463 | PQExpBuffer cmd; |
17464 | PQExpBuffer delcmd; |
17465 | PQExpBuffer ruleprefix; |
17466 | char *qtabname; |
17467 | PGresult *res; |
17468 | char *tag; |
17469 | |
17470 | /* Skip if not to be dumped */ |
17471 | if (!rinfo->dobj.dump || dopt->dataOnly) |
17472 | return; |
17473 | |
17474 | /* |
17475 | * If it is an ON SELECT rule that is created implicitly by CREATE VIEW, |
17476 | * we do not want to dump it as a separate object. |
17477 | */ |
17478 | if (!rinfo->separate) |
17479 | return; |
17480 | |
17481 | /* |
17482 | * If it's an ON SELECT rule, we want to print it as a view definition, |
17483 | * instead of a rule. |
17484 | */ |
17485 | is_view = (rinfo->ev_type == '1' && rinfo->is_instead); |
17486 | |
17487 | query = createPQExpBuffer(); |
17488 | cmd = createPQExpBuffer(); |
17489 | delcmd = createPQExpBuffer(); |
17490 | ruleprefix = createPQExpBuffer(); |
17491 | |
17492 | qtabname = pg_strdup(fmtId(tbinfo->dobj.name)); |
17493 | |
17494 | if (is_view) |
17495 | { |
17496 | PQExpBuffer result; |
17497 | |
17498 | /* |
17499 | * We need OR REPLACE here because we'll be replacing a dummy view. |
17500 | * Otherwise this should look largely like the regular view dump code. |
17501 | */ |
17502 | appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s" , |
17503 | fmtQualifiedDumpable(tbinfo)); |
17504 | if (nonemptyReloptions(tbinfo->reloptions)) |
17505 | { |
17506 | appendPQExpBufferStr(cmd, " WITH (" ); |
17507 | appendReloptionsArrayAH(cmd, tbinfo->reloptions, "" , fout); |
17508 | appendPQExpBufferChar(cmd, ')'); |
17509 | } |
17510 | result = createViewAsClause(fout, tbinfo); |
17511 | appendPQExpBuffer(cmd, " AS\n%s" , result->data); |
17512 | destroyPQExpBuffer(result); |
17513 | if (tbinfo->checkoption != NULL) |
17514 | appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION" , |
17515 | tbinfo->checkoption); |
17516 | appendPQExpBufferStr(cmd, ";\n" ); |
17517 | } |
17518 | else |
17519 | { |
17520 | /* In the rule case, just print pg_get_ruledef's result verbatim */ |
17521 | appendPQExpBuffer(query, |
17522 | "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)" , |
17523 | rinfo->dobj.catId.oid); |
17524 | |
17525 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
17526 | |
17527 | if (PQntuples(res) != 1) |
17528 | { |
17529 | pg_log_error("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned" , |
17530 | rinfo->dobj.name, tbinfo->dobj.name); |
17531 | exit_nicely(1); |
17532 | } |
17533 | |
17534 | printfPQExpBuffer(cmd, "%s\n" , PQgetvalue(res, 0, 0)); |
17535 | |
17536 | PQclear(res); |
17537 | } |
17538 | |
17539 | /* |
17540 | * Add the command to alter the rules replication firing semantics if it |
17541 | * differs from the default. |
17542 | */ |
17543 | if (rinfo->ev_enabled != 'O') |
17544 | { |
17545 | appendPQExpBuffer(cmd, "ALTER TABLE %s " , fmtQualifiedDumpable(tbinfo)); |
17546 | switch (rinfo->ev_enabled) |
17547 | { |
17548 | case 'A': |
17549 | appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n" , |
17550 | fmtId(rinfo->dobj.name)); |
17551 | break; |
17552 | case 'R': |
17553 | appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n" , |
17554 | fmtId(rinfo->dobj.name)); |
17555 | break; |
17556 | case 'D': |
17557 | appendPQExpBuffer(cmd, "DISABLE RULE %s;\n" , |
17558 | fmtId(rinfo->dobj.name)); |
17559 | break; |
17560 | } |
17561 | } |
17562 | |
17563 | if (is_view) |
17564 | { |
17565 | /* |
17566 | * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR |
17567 | * REPLACE VIEW to replace the rule with something with minimal |
17568 | * dependencies. |
17569 | */ |
17570 | PQExpBuffer result; |
17571 | |
17572 | appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s" , |
17573 | fmtQualifiedDumpable(tbinfo)); |
17574 | result = createDummyViewAsClause(fout, tbinfo); |
17575 | appendPQExpBuffer(delcmd, " AS\n%s;\n" , result->data); |
17576 | destroyPQExpBuffer(result); |
17577 | } |
17578 | else |
17579 | { |
17580 | appendPQExpBuffer(delcmd, "DROP RULE %s " , |
17581 | fmtId(rinfo->dobj.name)); |
17582 | appendPQExpBuffer(delcmd, "ON %s;\n" , |
17583 | fmtQualifiedDumpable(tbinfo)); |
17584 | } |
17585 | |
17586 | appendPQExpBuffer(ruleprefix, "RULE %s ON" , |
17587 | fmtId(rinfo->dobj.name)); |
17588 | |
17589 | tag = psprintf("%s %s" , tbinfo->dobj.name, rinfo->dobj.name); |
17590 | |
17591 | if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) |
17592 | ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId, |
17593 | ARCHIVE_OPTS(.tag = tag, |
17594 | .namespace = tbinfo->dobj.namespace->dobj.name, |
17595 | .owner = tbinfo->rolname, |
17596 | .description = "RULE" , |
17597 | .section = SECTION_POST_DATA, |
17598 | .createStmt = cmd->data, |
17599 | .dropStmt = delcmd->data)); |
17600 | |
17601 | /* Dump rule comments */ |
17602 | if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT) |
17603 | dumpComment(fout, ruleprefix->data, qtabname, |
17604 | tbinfo->dobj.namespace->dobj.name, |
17605 | tbinfo->rolname, |
17606 | rinfo->dobj.catId, 0, rinfo->dobj.dumpId); |
17607 | |
17608 | free(tag); |
17609 | destroyPQExpBuffer(query); |
17610 | destroyPQExpBuffer(cmd); |
17611 | destroyPQExpBuffer(delcmd); |
17612 | destroyPQExpBuffer(ruleprefix); |
17613 | free(qtabname); |
17614 | } |
17615 | |
17616 | /* |
17617 | * getExtensionMembership --- obtain extension membership data |
17618 | * |
17619 | * We need to identify objects that are extension members as soon as they're |
17620 | * loaded, so that we can correctly determine whether they need to be dumped. |
17621 | * Generally speaking, extension member objects will get marked as *not* to |
17622 | * be dumped, as they will be recreated by the single CREATE EXTENSION |
17623 | * command. However, in binary upgrade mode we still need to dump the members |
17624 | * individually. |
17625 | */ |
17626 | void |
17627 | getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], |
17628 | int numExtensions) |
17629 | { |
17630 | PQExpBuffer query; |
17631 | PGresult *res; |
17632 | int ntups, |
17633 | nextmembers, |
17634 | i; |
17635 | int i_classid, |
17636 | i_objid, |
17637 | i_refobjid; |
17638 | ExtensionMemberId *extmembers; |
17639 | ExtensionInfo *ext; |
17640 | |
17641 | /* Nothing to do if no extensions */ |
17642 | if (numExtensions == 0) |
17643 | return; |
17644 | |
17645 | query = createPQExpBuffer(); |
17646 | |
17647 | /* refclassid constraint is redundant but may speed the search */ |
17648 | appendPQExpBufferStr(query, "SELECT " |
17649 | "classid, objid, refobjid " |
17650 | "FROM pg_depend " |
17651 | "WHERE refclassid = 'pg_extension'::regclass " |
17652 | "AND deptype = 'e' " |
17653 | "ORDER BY 3" ); |
17654 | |
17655 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
17656 | |
17657 | ntups = PQntuples(res); |
17658 | |
17659 | i_classid = PQfnumber(res, "classid" ); |
17660 | i_objid = PQfnumber(res, "objid" ); |
17661 | i_refobjid = PQfnumber(res, "refobjid" ); |
17662 | |
17663 | extmembers = (ExtensionMemberId *) pg_malloc(ntups * sizeof(ExtensionMemberId)); |
17664 | nextmembers = 0; |
17665 | |
17666 | /* |
17667 | * Accumulate data into extmembers[]. |
17668 | * |
17669 | * Since we ordered the SELECT by referenced ID, we can expect that |
17670 | * multiple entries for the same extension will appear together; this |
17671 | * saves on searches. |
17672 | */ |
17673 | ext = NULL; |
17674 | |
17675 | for (i = 0; i < ntups; i++) |
17676 | { |
17677 | CatalogId objId; |
17678 | Oid extId; |
17679 | |
17680 | objId.tableoid = atooid(PQgetvalue(res, i, i_classid)); |
17681 | objId.oid = atooid(PQgetvalue(res, i, i_objid)); |
17682 | extId = atooid(PQgetvalue(res, i, i_refobjid)); |
17683 | |
17684 | if (ext == NULL || |
17685 | ext->dobj.catId.oid != extId) |
17686 | ext = findExtensionByOid(extId); |
17687 | |
17688 | if (ext == NULL) |
17689 | { |
17690 | /* shouldn't happen */ |
17691 | pg_log_warning("could not find referenced extension %u" , extId); |
17692 | continue; |
17693 | } |
17694 | |
17695 | extmembers[nextmembers].catId = objId; |
17696 | extmembers[nextmembers].ext = ext; |
17697 | nextmembers++; |
17698 | } |
17699 | |
17700 | PQclear(res); |
17701 | |
17702 | /* Remember the data for use later */ |
17703 | setExtensionMembership(extmembers, nextmembers); |
17704 | |
17705 | destroyPQExpBuffer(query); |
17706 | } |
17707 | |
17708 | /* |
17709 | * processExtensionTables --- deal with extension configuration tables |
17710 | * |
17711 | * There are two parts to this process: |
17712 | * |
17713 | * 1. Identify and create dump records for extension configuration tables. |
17714 | * |
17715 | * Extensions can mark tables as "configuration", which means that the user |
17716 | * is able and expected to modify those tables after the extension has been |
17717 | * loaded. For these tables, we dump out only the data- the structure is |
17718 | * expected to be handled at CREATE EXTENSION time, including any indexes or |
17719 | * foreign keys, which brings us to- |
17720 | * |
17721 | * 2. Record FK dependencies between configuration tables. |
17722 | * |
17723 | * Due to the FKs being created at CREATE EXTENSION time and therefore before |
17724 | * the data is loaded, we have to work out what the best order for reloading |
17725 | * the data is, to avoid FK violations when the tables are restored. This is |
17726 | * not perfect- we can't handle circular dependencies and if any exist they |
17727 | * will cause an invalid dump to be produced (though at least all of the data |
17728 | * is included for a user to manually restore). This is currently documented |
17729 | * but perhaps we can provide a better solution in the future. |
17730 | */ |
17731 | void |
17732 | processExtensionTables(Archive *fout, ExtensionInfo extinfo[], |
17733 | int numExtensions) |
17734 | { |
17735 | DumpOptions *dopt = fout->dopt; |
17736 | PQExpBuffer query; |
17737 | PGresult *res; |
17738 | int ntups, |
17739 | i; |
17740 | int i_conrelid, |
17741 | i_confrelid; |
17742 | |
17743 | /* Nothing to do if no extensions */ |
17744 | if (numExtensions == 0) |
17745 | return; |
17746 | |
17747 | /* |
17748 | * Identify extension configuration tables and create TableDataInfo |
17749 | * objects for them, ensuring their data will be dumped even though the |
17750 | * tables themselves won't be. |
17751 | * |
17752 | * Note that we create TableDataInfo objects even in schemaOnly mode, ie, |
17753 | * user data in a configuration table is treated like schema data. This |
17754 | * seems appropriate since system data in a config table would get |
17755 | * reloaded by CREATE EXTENSION. |
17756 | */ |
17757 | for (i = 0; i < numExtensions; i++) |
17758 | { |
17759 | ExtensionInfo *curext = &(extinfo[i]); |
17760 | char *extconfig = curext->extconfig; |
17761 | char *extcondition = curext->extcondition; |
17762 | char **extconfigarray = NULL; |
17763 | char **extconditionarray = NULL; |
17764 | int nconfigitems; |
17765 | int nconditionitems; |
17766 | |
17767 | if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) && |
17768 | parsePGArray(extcondition, &extconditionarray, &nconditionitems) && |
17769 | nconfigitems == nconditionitems) |
17770 | { |
17771 | int j; |
17772 | |
17773 | for (j = 0; j < nconfigitems; j++) |
17774 | { |
17775 | TableInfo *configtbl; |
17776 | Oid configtbloid = atooid(extconfigarray[j]); |
17777 | bool dumpobj = |
17778 | curext->dobj.dump & DUMP_COMPONENT_DEFINITION; |
17779 | |
17780 | configtbl = findTableByOid(configtbloid); |
17781 | if (configtbl == NULL) |
17782 | continue; |
17783 | |
17784 | /* |
17785 | * Tables of not-to-be-dumped extensions shouldn't be dumped |
17786 | * unless the table or its schema is explicitly included |
17787 | */ |
17788 | if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION)) |
17789 | { |
17790 | /* check table explicitly requested */ |
17791 | if (table_include_oids.head != NULL && |
17792 | simple_oid_list_member(&table_include_oids, |
17793 | configtbloid)) |
17794 | dumpobj = true; |
17795 | |
17796 | /* check table's schema explicitly requested */ |
17797 | if (configtbl->dobj.namespace->dobj.dump & |
17798 | DUMP_COMPONENT_DATA) |
17799 | dumpobj = true; |
17800 | } |
17801 | |
17802 | /* check table excluded by an exclusion switch */ |
17803 | if (table_exclude_oids.head != NULL && |
17804 | simple_oid_list_member(&table_exclude_oids, |
17805 | configtbloid)) |
17806 | dumpobj = false; |
17807 | |
17808 | /* check schema excluded by an exclusion switch */ |
17809 | if (simple_oid_list_member(&schema_exclude_oids, |
17810 | configtbl->dobj.namespace->dobj.catId.oid)) |
17811 | dumpobj = false; |
17812 | |
17813 | if (dumpobj) |
17814 | { |
17815 | makeTableDataInfo(dopt, configtbl); |
17816 | if (configtbl->dataObj != NULL) |
17817 | { |
17818 | if (strlen(extconditionarray[j]) > 0) |
17819 | configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]); |
17820 | } |
17821 | } |
17822 | } |
17823 | } |
17824 | if (extconfigarray) |
17825 | free(extconfigarray); |
17826 | if (extconditionarray) |
17827 | free(extconditionarray); |
17828 | } |
17829 | |
17830 | /* |
17831 | * Now that all the TableInfoData objects have been created for all the |
17832 | * extensions, check their FK dependencies and register them to try and |
17833 | * dump the data out in an order that they can be restored in. |
17834 | * |
17835 | * Note that this is not a problem for user tables as their FKs are |
17836 | * recreated after the data has been loaded. |
17837 | */ |
17838 | |
17839 | query = createPQExpBuffer(); |
17840 | |
17841 | printfPQExpBuffer(query, |
17842 | "SELECT conrelid, confrelid " |
17843 | "FROM pg_constraint " |
17844 | "JOIN pg_depend ON (objid = confrelid) " |
17845 | "WHERE contype = 'f' " |
17846 | "AND refclassid = 'pg_extension'::regclass " |
17847 | "AND classid = 'pg_class'::regclass;" ); |
17848 | |
17849 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
17850 | ntups = PQntuples(res); |
17851 | |
17852 | i_conrelid = PQfnumber(res, "conrelid" ); |
17853 | i_confrelid = PQfnumber(res, "confrelid" ); |
17854 | |
17855 | /* Now get the dependencies and register them */ |
17856 | for (i = 0; i < ntups; i++) |
17857 | { |
17858 | Oid conrelid, |
17859 | confrelid; |
17860 | TableInfo *reftable, |
17861 | *contable; |
17862 | |
17863 | conrelid = atooid(PQgetvalue(res, i, i_conrelid)); |
17864 | confrelid = atooid(PQgetvalue(res, i, i_confrelid)); |
17865 | contable = findTableByOid(conrelid); |
17866 | reftable = findTableByOid(confrelid); |
17867 | |
17868 | if (reftable == NULL || |
17869 | reftable->dataObj == NULL || |
17870 | contable == NULL || |
17871 | contable->dataObj == NULL) |
17872 | continue; |
17873 | |
17874 | /* |
17875 | * Make referencing TABLE_DATA object depend on the referenced table's |
17876 | * TABLE_DATA object. |
17877 | */ |
17878 | addObjectDependency(&contable->dataObj->dobj, |
17879 | reftable->dataObj->dobj.dumpId); |
17880 | } |
17881 | PQclear(res); |
17882 | destroyPQExpBuffer(query); |
17883 | } |
17884 | |
17885 | /* |
17886 | * getDependencies --- obtain available dependency data |
17887 | */ |
17888 | static void |
17889 | getDependencies(Archive *fout) |
17890 | { |
17891 | PQExpBuffer query; |
17892 | PGresult *res; |
17893 | int ntups, |
17894 | i; |
17895 | int i_classid, |
17896 | i_objid, |
17897 | i_refclassid, |
17898 | i_refobjid, |
17899 | i_deptype; |
17900 | DumpableObject *dobj, |
17901 | *refdobj; |
17902 | |
17903 | pg_log_info("reading dependency data" ); |
17904 | |
17905 | query = createPQExpBuffer(); |
17906 | |
17907 | /* |
17908 | * Messy query to collect the dependency data we need. Note that we |
17909 | * ignore the sub-object column, so that dependencies of or on a column |
17910 | * look the same as dependencies of or on a whole table. |
17911 | * |
17912 | * PIN dependencies aren't interesting, and EXTENSION dependencies were |
17913 | * already processed by getExtensionMembership. |
17914 | */ |
17915 | appendPQExpBufferStr(query, "SELECT " |
17916 | "classid, objid, refclassid, refobjid, deptype " |
17917 | "FROM pg_depend " |
17918 | "WHERE deptype != 'p' AND deptype != 'e'\n" ); |
17919 | |
17920 | /* |
17921 | * Since we don't treat pg_amop entries as separate DumpableObjects, we |
17922 | * have to translate their dependencies into dependencies of their parent |
17923 | * opfamily. Ignore internal dependencies though, as those will point to |
17924 | * their parent opclass, which we needn't consider here (and if we did, |
17925 | * it'd just result in circular dependencies). Also, "loose" opfamily |
17926 | * entries will have dependencies on their parent opfamily, which we |
17927 | * should drop since they'd likewise become useless self-dependencies. |
17928 | * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.) |
17929 | * |
17930 | * Skip this for pre-8.3 source servers: pg_opfamily doesn't exist there, |
17931 | * and the (known) cases where it would matter to have these dependencies |
17932 | * can't arise anyway. |
17933 | */ |
17934 | if (fout->remoteVersion >= 80300) |
17935 | { |
17936 | appendPQExpBufferStr(query, "UNION ALL\n" |
17937 | "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype " |
17938 | "FROM pg_depend d, pg_amop o " |
17939 | "WHERE deptype NOT IN ('p', 'e', 'i') AND " |
17940 | "classid = 'pg_amop'::regclass AND objid = o.oid " |
17941 | "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n" ); |
17942 | |
17943 | /* Likewise for pg_amproc entries */ |
17944 | appendPQExpBufferStr(query, "UNION ALL\n" |
17945 | "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype " |
17946 | "FROM pg_depend d, pg_amproc p " |
17947 | "WHERE deptype NOT IN ('p', 'e', 'i') AND " |
17948 | "classid = 'pg_amproc'::regclass AND objid = p.oid " |
17949 | "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n" ); |
17950 | } |
17951 | |
17952 | /* Sort the output for efficiency below */ |
17953 | appendPQExpBufferStr(query, "ORDER BY 1,2" ); |
17954 | |
17955 | res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); |
17956 | |
17957 | ntups = PQntuples(res); |
17958 | |
17959 | i_classid = PQfnumber(res, "classid" ); |
17960 | i_objid = PQfnumber(res, "objid" ); |
17961 | i_refclassid = PQfnumber(res, "refclassid" ); |
17962 | i_refobjid = PQfnumber(res, "refobjid" ); |
17963 | i_deptype = PQfnumber(res, "deptype" ); |
17964 | |
17965 | /* |
17966 | * Since we ordered the SELECT by referencing ID, we can expect that |
17967 | * multiple entries for the same object will appear together; this saves |
17968 | * on searches. |
17969 | */ |
17970 | dobj = NULL; |
17971 | |
17972 | for (i = 0; i < ntups; i++) |
17973 | { |
17974 | CatalogId objId; |
17975 | CatalogId refobjId; |
17976 | char deptype; |
17977 | |
17978 | objId.tableoid = atooid(PQgetvalue(res, i, i_classid)); |
17979 | objId.oid = atooid(PQgetvalue(res, i, i_objid)); |
17980 | refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid)); |
17981 | refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid)); |
17982 | deptype = *(PQgetvalue(res, i, i_deptype)); |
17983 | |
17984 | if (dobj == NULL || |
17985 | dobj->catId.tableoid != objId.tableoid || |
17986 | dobj->catId.oid != objId.oid) |
17987 | dobj = findObjectByCatalogId(objId); |
17988 | |
17989 | /* |
17990 | * Failure to find objects mentioned in pg_depend is not unexpected, |
17991 | * since for example we don't collect info about TOAST tables. |
17992 | */ |
17993 | if (dobj == NULL) |
17994 | { |
17995 | #ifdef NOT_USED |
17996 | pg_log_warning("no referencing object %u %u" , |
17997 | objId.tableoid, objId.oid); |
17998 | #endif |
17999 | continue; |
18000 | } |
18001 | |
18002 | refdobj = findObjectByCatalogId(refobjId); |
18003 | |
18004 | if (refdobj == NULL) |
18005 | { |
18006 | #ifdef NOT_USED |
18007 | pg_log_warning("no referenced object %u %u" , |
18008 | refobjId.tableoid, refobjId.oid); |
18009 | #endif |
18010 | continue; |
18011 | } |
18012 | |
18013 | /* |
18014 | * Ordinarily, table rowtypes have implicit dependencies on their |
18015 | * tables. However, for a composite type the implicit dependency goes |
18016 | * the other way in pg_depend; which is the right thing for DROP but |
18017 | * it doesn't produce the dependency ordering we need. So in that one |
18018 | * case, we reverse the direction of the dependency. |
18019 | */ |
18020 | if (deptype == 'i' && |
18021 | dobj->objType == DO_TABLE && |
18022 | refdobj->objType == DO_TYPE) |
18023 | addObjectDependency(refdobj, dobj->dumpId); |
18024 | else |
18025 | /* normal case */ |
18026 | addObjectDependency(dobj, refdobj->dumpId); |
18027 | } |
18028 | |
18029 | PQclear(res); |
18030 | |
18031 | destroyPQExpBuffer(query); |
18032 | } |
18033 | |
18034 | |
18035 | /* |
18036 | * createBoundaryObjects - create dummy DumpableObjects to represent |
18037 | * dump section boundaries. |
18038 | */ |
18039 | static DumpableObject * |
18040 | createBoundaryObjects(void) |
18041 | { |
18042 | DumpableObject *dobjs; |
18043 | |
18044 | dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject)); |
18045 | |
18046 | dobjs[0].objType = DO_PRE_DATA_BOUNDARY; |
18047 | dobjs[0].catId = nilCatalogId; |
18048 | AssignDumpId(dobjs + 0); |
18049 | dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY" ); |
18050 | |
18051 | dobjs[1].objType = DO_POST_DATA_BOUNDARY; |
18052 | dobjs[1].catId = nilCatalogId; |
18053 | AssignDumpId(dobjs + 1); |
18054 | dobjs[1].name = pg_strdup("POST-DATA BOUNDARY" ); |
18055 | |
18056 | return dobjs; |
18057 | } |
18058 | |
18059 | /* |
18060 | * addBoundaryDependencies - add dependencies as needed to enforce the dump |
18061 | * section boundaries. |
18062 | */ |
18063 | static void |
18064 | addBoundaryDependencies(DumpableObject **dobjs, int numObjs, |
18065 | DumpableObject *boundaryObjs) |
18066 | { |
18067 | DumpableObject *preDataBound = boundaryObjs + 0; |
18068 | DumpableObject *postDataBound = boundaryObjs + 1; |
18069 | int i; |
18070 | |
18071 | for (i = 0; i < numObjs; i++) |
18072 | { |
18073 | DumpableObject *dobj = dobjs[i]; |
18074 | |
18075 | /* |
18076 | * The classification of object types here must match the SECTION_xxx |
18077 | * values assigned during subsequent ArchiveEntry calls! |
18078 | */ |
18079 | switch (dobj->objType) |
18080 | { |
18081 | case DO_NAMESPACE: |
18082 | case DO_EXTENSION: |
18083 | case DO_TYPE: |
18084 | case DO_SHELL_TYPE: |
18085 | case DO_FUNC: |
18086 | case DO_AGG: |
18087 | case DO_OPERATOR: |
18088 | case DO_ACCESS_METHOD: |
18089 | case DO_OPCLASS: |
18090 | case DO_OPFAMILY: |
18091 | case DO_COLLATION: |
18092 | case DO_CONVERSION: |
18093 | case DO_TABLE: |
18094 | case DO_ATTRDEF: |
18095 | case DO_PROCLANG: |
18096 | case DO_CAST: |
18097 | case DO_DUMMY_TYPE: |
18098 | case DO_TSPARSER: |
18099 | case DO_TSDICT: |
18100 | case DO_TSTEMPLATE: |
18101 | case DO_TSCONFIG: |
18102 | case DO_FDW: |
18103 | case DO_FOREIGN_SERVER: |
18104 | case DO_TRANSFORM: |
18105 | case DO_BLOB: |
18106 | /* Pre-data objects: must come before the pre-data boundary */ |
18107 | addObjectDependency(preDataBound, dobj->dumpId); |
18108 | break; |
18109 | case DO_TABLE_DATA: |
18110 | case DO_SEQUENCE_SET: |
18111 | case DO_BLOB_DATA: |
18112 | /* Data objects: must come between the boundaries */ |
18113 | addObjectDependency(dobj, preDataBound->dumpId); |
18114 | addObjectDependency(postDataBound, dobj->dumpId); |
18115 | break; |
18116 | case DO_INDEX: |
18117 | case DO_INDEX_ATTACH: |
18118 | case DO_STATSEXT: |
18119 | case DO_REFRESH_MATVIEW: |
18120 | case DO_TRIGGER: |
18121 | case DO_EVENT_TRIGGER: |
18122 | case DO_DEFAULT_ACL: |
18123 | case DO_POLICY: |
18124 | case DO_PUBLICATION: |
18125 | case DO_PUBLICATION_REL: |
18126 | case DO_SUBSCRIPTION: |
18127 | /* Post-data objects: must come after the post-data boundary */ |
18128 | addObjectDependency(dobj, postDataBound->dumpId); |
18129 | break; |
18130 | case DO_RULE: |
18131 | /* Rules are post-data, but only if dumped separately */ |
18132 | if (((RuleInfo *) dobj)->separate) |
18133 | addObjectDependency(dobj, postDataBound->dumpId); |
18134 | break; |
18135 | case DO_CONSTRAINT: |
18136 | case DO_FK_CONSTRAINT: |
18137 | /* Constraints are post-data, but only if dumped separately */ |
18138 | if (((ConstraintInfo *) dobj)->separate) |
18139 | addObjectDependency(dobj, postDataBound->dumpId); |
18140 | break; |
18141 | case DO_PRE_DATA_BOUNDARY: |
18142 | /* nothing to do */ |
18143 | break; |
18144 | case DO_POST_DATA_BOUNDARY: |
18145 | /* must come after the pre-data boundary */ |
18146 | addObjectDependency(dobj, preDataBound->dumpId); |
18147 | break; |
18148 | } |
18149 | } |
18150 | } |
18151 | |
18152 | |
18153 | /* |
18154 | * BuildArchiveDependencies - create dependency data for archive TOC entries |
18155 | * |
18156 | * The raw dependency data obtained by getDependencies() is not terribly |
18157 | * useful in an archive dump, because in many cases there are dependency |
18158 | * chains linking through objects that don't appear explicitly in the dump. |
18159 | * For example, a view will depend on its _RETURN rule while the _RETURN rule |
18160 | * will depend on other objects --- but the rule will not appear as a separate |
18161 | * object in the dump. We need to adjust the view's dependencies to include |
18162 | * whatever the rule depends on that is included in the dump. |
18163 | * |
18164 | * Just to make things more complicated, there are also "special" dependencies |
18165 | * such as the dependency of a TABLE DATA item on its TABLE, which we must |
18166 | * not rearrange because pg_restore knows that TABLE DATA only depends on |
18167 | * its table. In these cases we must leave the dependencies strictly as-is |
18168 | * even if they refer to not-to-be-dumped objects. |
18169 | * |
18170 | * To handle this, the convention is that "special" dependencies are created |
18171 | * during ArchiveEntry calls, and an archive TOC item that has any such |
18172 | * entries will not be touched here. Otherwise, we recursively search the |
18173 | * DumpableObject data structures to build the correct dependencies for each |
18174 | * archive TOC item. |
18175 | */ |
18176 | static void |
18177 | BuildArchiveDependencies(Archive *fout) |
18178 | { |
18179 | ArchiveHandle *AH = (ArchiveHandle *) fout; |
18180 | TocEntry *te; |
18181 | |
18182 | /* Scan all TOC entries in the archive */ |
18183 | for (te = AH->toc->next; te != AH->toc; te = te->next) |
18184 | { |
18185 | DumpableObject *dobj; |
18186 | DumpId *dependencies; |
18187 | int nDeps; |
18188 | int allocDeps; |
18189 | |
18190 | /* No need to process entries that will not be dumped */ |
18191 | if (te->reqs == 0) |
18192 | continue; |
18193 | /* Ignore entries that already have "special" dependencies */ |
18194 | if (te->nDeps > 0) |
18195 | continue; |
18196 | /* Otherwise, look up the item's original DumpableObject, if any */ |
18197 | dobj = findObjectByDumpId(te->dumpId); |
18198 | if (dobj == NULL) |
18199 | continue; |
18200 | /* No work if it has no dependencies */ |
18201 | if (dobj->nDeps <= 0) |
18202 | continue; |
18203 | /* Set up work array */ |
18204 | allocDeps = 64; |
18205 | dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId)); |
18206 | nDeps = 0; |
18207 | /* Recursively find all dumpable dependencies */ |
18208 | findDumpableDependencies(AH, dobj, |
18209 | &dependencies, &nDeps, &allocDeps); |
18210 | /* And save 'em ... */ |
18211 | if (nDeps > 0) |
18212 | { |
18213 | dependencies = (DumpId *) pg_realloc(dependencies, |
18214 | nDeps * sizeof(DumpId)); |
18215 | te->dependencies = dependencies; |
18216 | te->nDeps = nDeps; |
18217 | } |
18218 | else |
18219 | free(dependencies); |
18220 | } |
18221 | } |
18222 | |
18223 | /* Recursive search subroutine for BuildArchiveDependencies */ |
18224 | static void |
18225 | findDumpableDependencies(ArchiveHandle *AH, DumpableObject *dobj, |
18226 | DumpId **dependencies, int *nDeps, int *allocDeps) |
18227 | { |
18228 | int i; |
18229 | |
18230 | /* |
18231 | * Ignore section boundary objects: if we search through them, we'll |
18232 | * report lots of bogus dependencies. |
18233 | */ |
18234 | if (dobj->objType == DO_PRE_DATA_BOUNDARY || |
18235 | dobj->objType == DO_POST_DATA_BOUNDARY) |
18236 | return; |
18237 | |
18238 | for (i = 0; i < dobj->nDeps; i++) |
18239 | { |
18240 | DumpId depid = dobj->dependencies[i]; |
18241 | |
18242 | if (TocIDRequired(AH, depid) != 0) |
18243 | { |
18244 | /* Object will be dumped, so just reference it as a dependency */ |
18245 | if (*nDeps >= *allocDeps) |
18246 | { |
18247 | *allocDeps *= 2; |
18248 | *dependencies = (DumpId *) pg_realloc(*dependencies, |
18249 | *allocDeps * sizeof(DumpId)); |
18250 | } |
18251 | (*dependencies)[*nDeps] = depid; |
18252 | (*nDeps)++; |
18253 | } |
18254 | else |
18255 | { |
18256 | /* |
18257 | * Object will not be dumped, so recursively consider its deps. We |
18258 | * rely on the assumption that sortDumpableObjects already broke |
18259 | * any dependency loops, else we might recurse infinitely. |
18260 | */ |
18261 | DumpableObject *otherdobj = findObjectByDumpId(depid); |
18262 | |
18263 | if (otherdobj) |
18264 | findDumpableDependencies(AH, otherdobj, |
18265 | dependencies, nDeps, allocDeps); |
18266 | } |
18267 | } |
18268 | } |
18269 | |
18270 | |
18271 | /* |
18272 | * getFormattedTypeName - retrieve a nicely-formatted type name for the |
18273 | * given type OID. |
18274 | * |
18275 | * This does not guarantee to schema-qualify the output, so it should not |
18276 | * be used to create the target object name for CREATE or ALTER commands. |
18277 | * |
18278 | * TODO: there might be some value in caching the results. |
18279 | */ |
18280 | static char * |
18281 | getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts) |
18282 | { |
18283 | char *result; |
18284 | PQExpBuffer query; |
18285 | PGresult *res; |
18286 | |
18287 | if (oid == 0) |
18288 | { |
18289 | if ((opts & zeroAsOpaque) != 0) |
18290 | return pg_strdup(g_opaque_type); |
18291 | else if ((opts & zeroAsAny) != 0) |
18292 | return pg_strdup("'any'" ); |
18293 | else if ((opts & zeroAsStar) != 0) |
18294 | return pg_strdup("*" ); |
18295 | else if ((opts & zeroAsNone) != 0) |
18296 | return pg_strdup("NONE" ); |
18297 | } |
18298 | |
18299 | query = createPQExpBuffer(); |
18300 | appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)" , |
18301 | oid); |
18302 | |
18303 | res = ExecuteSqlQueryForSingleRow(fout, query->data); |
18304 | |
18305 | /* result of format_type is already quoted */ |
18306 | result = pg_strdup(PQgetvalue(res, 0, 0)); |
18307 | |
18308 | PQclear(res); |
18309 | destroyPQExpBuffer(query); |
18310 | |
18311 | return result; |
18312 | } |
18313 | |
18314 | /* |
18315 | * Return a column list clause for the given relation. |
18316 | * |
18317 | * Special case: if there are no undropped columns in the relation, return |
18318 | * "", not an invalid "()" column list. |
18319 | */ |
18320 | static const char * |
18321 | fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer) |
18322 | { |
18323 | int numatts = ti->numatts; |
18324 | char **attnames = ti->attnames; |
18325 | bool *attisdropped = ti->attisdropped; |
18326 | char *attgenerated = ti->attgenerated; |
18327 | bool needComma; |
18328 | int i; |
18329 | |
18330 | appendPQExpBufferChar(buffer, '('); |
18331 | needComma = false; |
18332 | for (i = 0; i < numatts; i++) |
18333 | { |
18334 | if (attisdropped[i]) |
18335 | continue; |
18336 | if (attgenerated[i]) |
18337 | continue; |
18338 | if (needComma) |
18339 | appendPQExpBufferStr(buffer, ", " ); |
18340 | appendPQExpBufferStr(buffer, fmtId(attnames[i])); |
18341 | needComma = true; |
18342 | } |
18343 | |
18344 | if (!needComma) |
18345 | return "" ; /* no undropped columns */ |
18346 | |
18347 | appendPQExpBufferChar(buffer, ')'); |
18348 | return buffer->data; |
18349 | } |
18350 | |
18351 | /* |
18352 | * Check if a reloptions array is nonempty. |
18353 | */ |
18354 | static bool |
18355 | nonemptyReloptions(const char *reloptions) |
18356 | { |
18357 | /* Don't want to print it if it's just "{}" */ |
18358 | return (reloptions != NULL && strlen(reloptions) > 2); |
18359 | } |
18360 | |
18361 | /* |
18362 | * Format a reloptions array and append it to the given buffer. |
18363 | * |
18364 | * "prefix" is prepended to the option names; typically it's "" or "toast.". |
18365 | */ |
18366 | static void |
18367 | appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, |
18368 | const char *prefix, Archive *fout) |
18369 | { |
18370 | bool res; |
18371 | |
18372 | res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding, |
18373 | fout->std_strings); |
18374 | if (!res) |
18375 | pg_log_warning("could not parse reloptions array" ); |
18376 | } |
18377 | |