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-2017, 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 #ifdef HAVE_TERMIOS_H
37 #include <termios.h>
38 #endif
39
40 #include "getopt_long.h"
41
42 #include "access/attnum.h"
43 #include "access/sysattr.h"
44 #include "access/transam.h"
45 #include "catalog/pg_am.h"
46 #include "catalog/pg_attribute.h"
47 #include "catalog/pg_cast.h"
48 #include "catalog/pg_class.h"
49 #include "catalog/pg_default_acl.h"
50 #include "catalog/pg_largeobject.h"
51 #include "catalog/pg_largeobject_metadata.h"
52 #include "catalog/pg_proc.h"
53 #include "catalog/pg_trigger.h"
54 #include "catalog/pg_type.h"
55 #include "libpq/libpq-fs.h"
56
57 #include "dumputils.h"
58 #include "parallel.h"
59 #include "pg_backup_db.h"
60 #include "pg_backup_utils.h"
61 #include "pg_dump.h"
62 #include "fe_utils/connect.h"
63 #include "fe_utils/string_utils.h"
64
65
66 typedef struct
67 {
68 const char *descr; /* comment for an object */
69 Oid classoid; /* object class (catalog OID) */
70 Oid objoid; /* object OID */
71 int objsubid; /* subobject (table column #) */
72 } CommentItem;
73
74 typedef struct
75 {
76 const char *provider; /* label provider of this security label */
77 const char *label; /* security label for an object */
78 Oid classoid; /* object class (catalog OID) */
79 Oid objoid; /* object OID */
80 int objsubid; /* subobject (table column #) */
81 } SecLabelItem;
82
83 typedef enum OidOptions
84 {
85 zeroAsOpaque = 1,
86 zeroAsAny = 2,
87 zeroAsStar = 4,
88 zeroAsNone = 8
89 } OidOptions;
90
91 /* global decls */
92 bool g_verbose; /* User wants verbose narration of our
93 * activities. */
94 static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
95
96 /* subquery used to convert user ID (eg, datdba) to user name */
97 static const char *username_subquery;
98
99 /*
100 * For 8.0 and earlier servers, pulled from pg_database, for 8.1+ we use
101 * FirstNormalObjectId - 1.
102 */
103 static Oid g_last_builtin_oid; /* value of the last builtin oid */
104
105 /* The specified names/patterns should to match at least one entity */
106 static int strict_names = 0;
107
108 /*
109 * Object inclusion/exclusion lists
110 *
111 * The string lists record the patterns given by command-line switches,
112 * which we then convert to lists of OIDs of matching objects.
113 */
114 static SimpleStringList schema_include_patterns = {NULL, NULL};
115 static SimpleOidList schema_include_oids = {NULL, NULL};
116 static SimpleStringList schema_exclude_patterns = {NULL, NULL};
117 static SimpleOidList schema_exclude_oids = {NULL, NULL};
118
119 static SimpleStringList table_include_patterns = {NULL, NULL};
120 static SimpleOidList table_include_oids = {NULL, NULL};
121 static SimpleStringList table_exclude_patterns = {NULL, NULL};
122 static SimpleOidList table_exclude_oids = {NULL, NULL};
123 static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
124 static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
125
126
127 char g_opaque_type[10]; /* name for the opaque type */
128
129 /* placeholders for the delimiters for comments */
130 char g_comment_start[10];
131 char g_comment_end[10];
132
133 static const CatalogId nilCatalogId = {0, 0};
134
135 /*
136 * Macro for producing quoted, schema-qualified name of a dumpable object.
137 * Note implicit dependence on "fout"; we should get rid of that argument.
138 */
139 #define fmtQualifiedDumpable(obj) \
140 fmtQualifiedId(fout->remoteVersion, \
141 (obj)->dobj.namespace->dobj.name, \
142 (obj)->dobj.name)
143
144 static void help(const char *progname);
145 static void setup_connection(Archive *AH,
146 const char *dumpencoding, const char *dumpsnapshot,
147 char *use_role);
148 static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
149 static void expand_schema_name_patterns(Archive *fout,
150 SimpleStringList *patterns,
151 SimpleOidList *oids,
152 bool strict_names);
153 static void expand_table_name_patterns(Archive *fout,
154 SimpleStringList *patterns,
155 SimpleOidList *oids,
156 bool strict_names);
157 static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid);
158 static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
159 static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo);
160 static void guessConstraintInheritance(TableInfo *tblinfo, int numTables);
161 static void dumpComment(Archive *fout, const char *type, const char *name,
162 const char *namespace, const char *owner,
163 CatalogId catalogId, int subid, DumpId dumpId);
164 static int findComments(Archive *fout, Oid classoid, Oid objoid,
165 CommentItem **items);
166 static int collectComments(Archive *fout, CommentItem **items);
167 static void dumpSecLabel(Archive *fout, const char *type, const char *name,
168 const char *namespace, const char *owner,
169 CatalogId catalogId, int subid, DumpId dumpId);
170 static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
171 SecLabelItem **items);
172 static int collectSecLabels(Archive *fout, SecLabelItem **items);
173 static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
174 static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
175 static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
176 static void dumpType(Archive *fout, TypeInfo *tyinfo);
177 static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
178 static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
179 static void dumpRangeType(Archive *fout, TypeInfo *tyinfo);
180 static void dumpUndefinedType(Archive *fout, TypeInfo *tyinfo);
181 static void dumpDomain(Archive *fout, TypeInfo *tyinfo);
182 static void dumpCompositeType(Archive *fout, TypeInfo *tyinfo);
183 static void dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo);
184 static void dumpShellType(Archive *fout, ShellTypeInfo *stinfo);
185 static void dumpProcLang(Archive *fout, ProcLangInfo *plang);
186 static void dumpFunc(Archive *fout, FuncInfo *finfo);
187 static void dumpCast(Archive *fout, CastInfo *cast);
188 static void dumpTransform(Archive *fout, TransformInfo *transform);
189 static void dumpOpr(Archive *fout, OprInfo *oprinfo);
190 static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
191 static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
192 static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
193 static void dumpCollation(Archive *fout, CollInfo *collinfo);
194 static void dumpConversion(Archive *fout, ConvInfo *convinfo);
195 static void dumpRule(Archive *fout, RuleInfo *rinfo);
196 static void dumpAgg(Archive *fout, AggInfo *agginfo);
197 static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
198 static void dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo);
199 static void dumpTable(Archive *fout, TableInfo *tbinfo);
200 static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
201 static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
202 static void dumpSequence(Archive *fout, TableInfo *tbinfo);
203 static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo);
204 static void dumpIndex(Archive *fout, IndxInfo *indxinfo);
205 static void dumpStatisticsExt(Archive *fout, StatsExtInfo *statsextinfo);
206 static void dumpConstraint(Archive *fout, ConstraintInfo *coninfo);
207 static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo);
208 static void dumpTSParser(Archive *fout, TSParserInfo *prsinfo);
209 static void dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo);
210 static void dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo);
211 static void dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo);
212 static void dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo);
213 static void dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo);
214 static void dumpUserMappings(Archive *fout,
215 const char *servername, const char *namespace,
216 const char *owner, CatalogId catalogId, DumpId dumpId);
217 static void dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo);
218
219 static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
220 const char *type, const char *name, const char *subname,
221 const char *nspname, const char *owner,
222 const char *acls, const char *racls,
223 const char *initacls, const char *initracls);
224
225 static void getDependencies(Archive *fout);
226 static void BuildArchiveDependencies(Archive *fout);
227 static void findDumpableDependencies(ArchiveHandle *AH, DumpableObject *dobj,
228 DumpId **dependencies, int *nDeps, int *allocDeps);
229
230 static DumpableObject *createBoundaryObjects(void);
231 static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
232 DumpableObject *boundaryObjs);
233
234 static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
235 static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, bool oids, char relkind);
236 static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids);
237 static void buildMatViewRefreshDependencies(Archive *fout);
238 static void getTableDataFKConstraints(void);
239 static char *format_function_arguments(FuncInfo *finfo, char *funcargs,
240 bool is_agg);
241 static char *format_function_arguments_old(Archive *fout,
242 FuncInfo *finfo, int nallargs,
243 char **allargtypes,
244 char **argmodes,
245 char **argnames);
246 static char *format_function_signature(Archive *fout,
247 FuncInfo *finfo, bool honor_quotes);
248 static char *convertRegProcReference(Archive *fout,
249 const char *proc);
250 static char *getFormattedOperatorName(Archive *fout, const char *oproid);
251 static char *convertTSFunction(Archive *fout, Oid funcOid);
252 static Oid findLastBuiltinOid_V71(Archive *fout, const char *);
253 static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
254 static void getBlobs(Archive *fout);
255 static void dumpBlob(Archive *fout, BlobInfo *binfo);
256 static int dumpBlobs(Archive *fout, void *arg);
257 static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
258 static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
259 static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
260 static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
261 static void dumpDatabase(Archive *AH);
262 static void dumpEncoding(Archive *AH);
263 static void dumpStdStrings(Archive *AH);
264 static void dumpSearchPath(Archive *AH);
265 static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
266 PQExpBuffer upgrade_buffer, Oid pg_type_oid);
267 static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
268 PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
269 static void binary_upgrade_set_pg_class_oids(Archive *fout,
270 PQExpBuffer upgrade_buffer,
271 Oid pg_class_oid, bool is_index);
272 static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
273 DumpableObject *dobj,
274 const char *objtype,
275 const char *objname,
276 const char *objnamespace);
277 static const char *getAttrName(int attrnum, TableInfo *tblInfo);
278 static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
279 static bool nonemptyReloptions(const char *reloptions);
280 static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
281 const char *prefix, Archive *fout);
282 static char *get_synchronized_snapshot(Archive *fout);
283 static void setupDumpWorker(Archive *AHX);
284
285
286 int
main(int argc,char ** argv)287 main(int argc, char **argv)
288 {
289 int c;
290 const char *filename = NULL;
291 const char *format = "p";
292 TableInfo *tblinfo;
293 int numTables;
294 DumpableObject **dobjs;
295 int numObjs;
296 DumpableObject *boundaryObjs;
297 int i;
298 int optindex;
299 RestoreOptions *ropt;
300 Archive *fout; /* the script file */
301 const char *dumpencoding = NULL;
302 const char *dumpsnapshot = NULL;
303 char *use_role = NULL;
304 int numWorkers = 1;
305 int compressLevel = -1;
306 int plainText = 0;
307 ArchiveFormat archiveFormat = archUnknown;
308 ArchiveMode archiveMode;
309
310 static DumpOptions dopt;
311
312 static struct option long_options[] = {
313 {"data-only", no_argument, NULL, 'a'},
314 {"blobs", no_argument, NULL, 'b'},
315 {"no-blobs", no_argument, NULL, 'B'},
316 {"clean", no_argument, NULL, 'c'},
317 {"create", no_argument, NULL, 'C'},
318 {"dbname", required_argument, NULL, 'd'},
319 {"file", required_argument, NULL, 'f'},
320 {"format", required_argument, NULL, 'F'},
321 {"host", required_argument, NULL, 'h'},
322 {"jobs", 1, NULL, 'j'},
323 {"no-reconnect", no_argument, NULL, 'R'},
324 {"oids", no_argument, NULL, 'o'},
325 {"no-owner", no_argument, NULL, 'O'},
326 {"port", required_argument, NULL, 'p'},
327 {"schema", required_argument, NULL, 'n'},
328 {"exclude-schema", required_argument, NULL, 'N'},
329 {"schema-only", no_argument, NULL, 's'},
330 {"superuser", required_argument, NULL, 'S'},
331 {"table", required_argument, NULL, 't'},
332 {"exclude-table", required_argument, NULL, 'T'},
333 {"no-password", no_argument, NULL, 'w'},
334 {"password", no_argument, NULL, 'W'},
335 {"username", required_argument, NULL, 'U'},
336 {"verbose", no_argument, NULL, 'v'},
337 {"no-privileges", no_argument, NULL, 'x'},
338 {"no-acl", no_argument, NULL, 'x'},
339 {"compress", required_argument, NULL, 'Z'},
340 {"encoding", required_argument, NULL, 'E'},
341 {"help", no_argument, NULL, '?'},
342 {"version", no_argument, NULL, 'V'},
343
344 /*
345 * the following options don't have an equivalent short option letter
346 */
347 {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
348 {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
349 {"column-inserts", no_argument, &dopt.column_inserts, 1},
350 {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
351 {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
352 {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
353 {"exclude-table-data", required_argument, NULL, 4},
354 {"if-exists", no_argument, &dopt.if_exists, 1},
355 {"inserts", no_argument, &dopt.dump_inserts, 1},
356 {"lock-wait-timeout", required_argument, NULL, 2},
357 {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
358 {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
359 {"role", required_argument, NULL, 3},
360 {"section", required_argument, NULL, 5},
361 {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
362 {"snapshot", required_argument, NULL, 6},
363 {"strict-names", no_argument, &strict_names, 1},
364 {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
365 {"no-publications", no_argument, &dopt.no_publications, 1},
366 {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
367 {"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
368 {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
369 {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
370 {"no-sync", no_argument, NULL, 7},
371
372 {NULL, 0, NULL, 0}
373 };
374
375 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
376
377 /*
378 * Initialize what we need for parallel execution, especially for thread
379 * support on Windows.
380 */
381 init_parallel_dump_utils();
382
383 g_verbose = false;
384
385 strcpy(g_comment_start, "-- ");
386 g_comment_end[0] = '\0';
387 strcpy(g_opaque_type, "opaque");
388
389 progname = get_progname(argv[0]);
390
391 if (argc > 1)
392 {
393 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
394 {
395 help(progname);
396 exit_nicely(0);
397 }
398 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
399 {
400 puts("pg_dump (PostgreSQL) " PG_VERSION);
401 exit_nicely(0);
402 }
403 }
404
405 InitDumpOptions(&dopt);
406
407 while ((c = getopt_long(argc, argv, "abBcCd:E:f:F:h:j:n:N:oOp:RsS:t:T:U:vwWxZ:",
408 long_options, &optindex)) != -1)
409 {
410 switch (c)
411 {
412 case 'a': /* Dump data only */
413 dopt.dataOnly = true;
414 break;
415
416 case 'b': /* Dump blobs */
417 dopt.outputBlobs = true;
418 break;
419
420 case 'B': /* Don't dump blobs */
421 dopt.dontOutputBlobs = true;
422 break;
423
424 case 'c': /* clean (i.e., drop) schema prior to create */
425 dopt.outputClean = 1;
426 break;
427
428 case 'C': /* Create DB */
429 dopt.outputCreateDB = 1;
430 break;
431
432 case 'd': /* database name */
433 dopt.cparams.dbname = pg_strdup(optarg);
434 break;
435
436 case 'E': /* Dump encoding */
437 dumpencoding = pg_strdup(optarg);
438 break;
439
440 case 'f':
441 filename = pg_strdup(optarg);
442 break;
443
444 case 'F':
445 format = pg_strdup(optarg);
446 break;
447
448 case 'h': /* server host */
449 dopt.cparams.pghost = pg_strdup(optarg);
450 break;
451
452 case 'j': /* number of dump jobs */
453 numWorkers = atoi(optarg);
454 break;
455
456 case 'n': /* include schema(s) */
457 simple_string_list_append(&schema_include_patterns, optarg);
458 dopt.include_everything = false;
459 break;
460
461 case 'N': /* exclude schema(s) */
462 simple_string_list_append(&schema_exclude_patterns, optarg);
463 break;
464
465 case 'o': /* Dump oids */
466 dopt.oids = true;
467 break;
468
469 case 'O': /* Don't reconnect to match owner */
470 dopt.outputNoOwner = 1;
471 break;
472
473 case 'p': /* server port */
474 dopt.cparams.pgport = pg_strdup(optarg);
475 break;
476
477 case 'R':
478 /* no-op, still accepted for backwards compatibility */
479 break;
480
481 case 's': /* dump schema only */
482 dopt.schemaOnly = true;
483 break;
484
485 case 'S': /* Username for superuser in plain text output */
486 dopt.outputSuperuser = pg_strdup(optarg);
487 break;
488
489 case 't': /* include table(s) */
490 simple_string_list_append(&table_include_patterns, optarg);
491 dopt.include_everything = false;
492 break;
493
494 case 'T': /* exclude table(s) */
495 simple_string_list_append(&table_exclude_patterns, optarg);
496 break;
497
498 case 'U':
499 dopt.cparams.username = pg_strdup(optarg);
500 break;
501
502 case 'v': /* verbose */
503 g_verbose = true;
504 break;
505
506 case 'w':
507 dopt.cparams.promptPassword = TRI_NO;
508 break;
509
510 case 'W':
511 dopt.cparams.promptPassword = TRI_YES;
512 break;
513
514 case 'x': /* skip ACL dump */
515 dopt.aclsSkip = true;
516 break;
517
518 case 'Z': /* Compression Level */
519 compressLevel = atoi(optarg);
520 if (compressLevel < 0 || compressLevel > 9)
521 {
522 write_msg(NULL, "compression level must be in range 0..9\n");
523 exit_nicely(1);
524 }
525 break;
526
527 case 0:
528 /* This covers the long options. */
529 break;
530
531 case 2: /* lock-wait-timeout */
532 dopt.lockWaitTimeout = pg_strdup(optarg);
533 break;
534
535 case 3: /* SET ROLE */
536 use_role = pg_strdup(optarg);
537 break;
538
539 case 4: /* exclude table(s) data */
540 simple_string_list_append(&tabledata_exclude_patterns, optarg);
541 break;
542
543 case 5: /* section */
544 set_dump_section(optarg, &dopt.dumpSections);
545 break;
546
547 case 6: /* snapshot */
548 dumpsnapshot = pg_strdup(optarg);
549 break;
550
551 case 7: /* no-sync */
552 dosync = false;
553 break;
554
555 default:
556 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
557 exit_nicely(1);
558 }
559 }
560
561 /*
562 * Non-option argument specifies database name as long as it wasn't
563 * already specified with -d / --dbname
564 */
565 if (optind < argc && dopt.cparams.dbname == NULL)
566 dopt.cparams.dbname = argv[optind++];
567
568 /* Complain if any arguments remain */
569 if (optind < argc)
570 {
571 fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
572 progname, argv[optind]);
573 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
574 progname);
575 exit_nicely(1);
576 }
577
578 /* --column-inserts implies --inserts */
579 if (dopt.column_inserts)
580 dopt.dump_inserts = 1;
581
582 /*
583 * Binary upgrade mode implies dumping sequence data even in schema-only
584 * mode. This is not exposed as a separate option, but kept separate
585 * internally for clarity.
586 */
587 if (dopt.binary_upgrade)
588 dopt.sequence_data = 1;
589
590 if (dopt.dataOnly && dopt.schemaOnly)
591 {
592 write_msg(NULL, "options -s/--schema-only and -a/--data-only cannot be used together\n");
593 exit_nicely(1);
594 }
595
596 if (dopt.dataOnly && dopt.outputClean)
597 {
598 write_msg(NULL, "options -c/--clean and -a/--data-only cannot be used together\n");
599 exit_nicely(1);
600 }
601
602 if (dopt.dump_inserts && dopt.oids)
603 {
604 write_msg(NULL, "options --inserts/--column-inserts and -o/--oids cannot be used together\n");
605 write_msg(NULL, "(The INSERT command cannot set OIDs.)\n");
606 exit_nicely(1);
607 }
608
609 if (dopt.if_exists && !dopt.outputClean)
610 exit_horribly(NULL, "option --if-exists requires option -c/--clean\n");
611
612 /* Identify archive format to emit */
613 archiveFormat = parseArchiveFormat(format, &archiveMode);
614
615 /* archiveFormat specific setup */
616 if (archiveFormat == archNull)
617 plainText = 1;
618
619 /* Custom and directory formats are compressed by default, others not */
620 if (compressLevel == -1)
621 {
622 #ifdef HAVE_LIBZ
623 if (archiveFormat == archCustom || archiveFormat == archDirectory)
624 compressLevel = Z_DEFAULT_COMPRESSION;
625 else
626 #endif
627 compressLevel = 0;
628 }
629
630 #ifndef HAVE_LIBZ
631 if (compressLevel != 0)
632 write_msg(NULL, "WARNING: requested compression not available in this "
633 "installation -- archive will be uncompressed\n");
634 compressLevel = 0;
635 #endif
636
637 /*
638 * On Windows we can only have at most MAXIMUM_WAIT_OBJECTS (= 64 usually)
639 * parallel jobs because that's the maximum limit for the
640 * WaitForMultipleObjects() call.
641 */
642 if (numWorkers <= 0
643 #ifdef WIN32
644 || numWorkers > MAXIMUM_WAIT_OBJECTS
645 #endif
646 )
647 exit_horribly(NULL, "invalid number of parallel jobs\n");
648
649 /* Parallel backup only in the directory archive format so far */
650 if (archiveFormat != archDirectory && numWorkers > 1)
651 exit_horribly(NULL, "parallel backup only supported by the directory format\n");
652
653 /* Open the output file */
654 fout = CreateArchive(filename, archiveFormat, compressLevel, dosync,
655 archiveMode, setupDumpWorker);
656
657 /* Make dump options accessible right away */
658 SetArchiveOptions(fout, &dopt, NULL);
659
660 /* Register the cleanup hook */
661 on_exit_close_archive(fout);
662
663 /* Let the archiver know how noisy to be */
664 fout->verbose = g_verbose;
665
666 /*
667 * We allow the server to be back to 8.0, and up to any minor release of
668 * our own major version. (See also version check in pg_dumpall.c.)
669 */
670 fout->minRemoteVersion = 80000;
671 fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
672
673 fout->numWorkers = numWorkers;
674
675 /*
676 * Open the database using the Archiver, so it knows about it. Errors mean
677 * death.
678 */
679 ConnectDatabase(fout, &dopt.cparams, false);
680 setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
681
682 /*
683 * Disable security label support if server version < v9.1.x (prevents
684 * access to nonexistent pg_seclabel catalog)
685 */
686 if (fout->remoteVersion < 90100)
687 dopt.no_security_labels = 1;
688
689 /*
690 * On hot standbys, never try to dump unlogged table data, since it will
691 * just throw an error.
692 */
693 if (fout->isStandby)
694 dopt.no_unlogged_table_data = true;
695
696 /* Select the appropriate subquery to convert user IDs to names */
697 if (fout->remoteVersion >= 80100)
698 username_subquery = "SELECT rolname FROM pg_catalog.pg_roles WHERE oid =";
699 else
700 username_subquery = "SELECT usename FROM pg_catalog.pg_user WHERE usesysid =";
701
702 /* check the version for the synchronized snapshots feature */
703 if (numWorkers > 1 && fout->remoteVersion < 90200
704 && !dopt.no_synchronized_snapshots)
705 exit_horribly(NULL,
706 "Synchronized snapshots are not supported by this server version.\n"
707 "Run with --no-synchronized-snapshots instead if you do not need\n"
708 "synchronized snapshots.\n");
709
710 /* check the version when a snapshot is explicitly specified by user */
711 if (dumpsnapshot && fout->remoteVersion < 90200)
712 exit_horribly(NULL,
713 "Exported snapshots are not supported by this server version.\n");
714
715 /*
716 * Find the last built-in OID, if needed (prior to 8.1)
717 *
718 * With 8.1 and above, we can just use FirstNormalObjectId - 1.
719 */
720 if (fout->remoteVersion < 80100)
721 g_last_builtin_oid = findLastBuiltinOid_V71(fout,
722 PQdb(GetConnection(fout)));
723 else
724 g_last_builtin_oid = FirstNormalObjectId - 1;
725
726 if (g_verbose)
727 write_msg(NULL, "last built-in OID is %u\n", g_last_builtin_oid);
728
729 /* Expand schema selection patterns into OID lists */
730 if (schema_include_patterns.head != NULL)
731 {
732 expand_schema_name_patterns(fout, &schema_include_patterns,
733 &schema_include_oids,
734 strict_names);
735 if (schema_include_oids.head == NULL)
736 exit_horribly(NULL, "no matching schemas were found\n");
737 }
738 expand_schema_name_patterns(fout, &schema_exclude_patterns,
739 &schema_exclude_oids,
740 false);
741 /* non-matching exclusion patterns aren't an error */
742
743 /* Expand table selection patterns into OID lists */
744 if (table_include_patterns.head != NULL)
745 {
746 expand_table_name_patterns(fout, &table_include_patterns,
747 &table_include_oids,
748 strict_names);
749 if (table_include_oids.head == NULL)
750 exit_horribly(NULL, "no matching tables were found\n");
751 }
752 expand_table_name_patterns(fout, &table_exclude_patterns,
753 &table_exclude_oids,
754 false);
755
756 expand_table_name_patterns(fout, &tabledata_exclude_patterns,
757 &tabledata_exclude_oids,
758 false);
759
760 /* non-matching exclusion patterns aren't an error */
761
762 /*
763 * Dumping blobs is the default for dumps where an inclusion switch is not
764 * used (an "include everything" dump). -B can be used to exclude blobs
765 * from those dumps. -b can be used to include blobs even when an
766 * inclusion switch is used.
767 *
768 * -s means "schema only" and blobs are data, not schema, so we never
769 * include blobs when -s is used.
770 */
771 if (dopt.include_everything && !dopt.schemaOnly && !dopt.dontOutputBlobs)
772 dopt.outputBlobs = true;
773
774 /*
775 * Now scan the database and create DumpableObject structs for all the
776 * objects we intend to dump.
777 */
778 tblinfo = getSchemaData(fout, &numTables);
779
780 if (fout->remoteVersion < 80400)
781 guessConstraintInheritance(tblinfo, numTables);
782
783 if (!dopt.schemaOnly)
784 {
785 getTableData(&dopt, tblinfo, numTables, dopt.oids, 0);
786 buildMatViewRefreshDependencies(fout);
787 if (dopt.dataOnly)
788 getTableDataFKConstraints();
789 }
790
791 if (dopt.schemaOnly && dopt.sequence_data)
792 getTableData(&dopt, tblinfo, numTables, dopt.oids, RELKIND_SEQUENCE);
793
794 /*
795 * In binary-upgrade mode, we do not have to worry about the actual blob
796 * data or the associated metadata that resides in the pg_largeobject and
797 * pg_largeobject_metadata tables, respectivly.
798 *
799 * However, we do need to collect blob information as there may be
800 * comments or other information on blobs that we do need to dump out.
801 */
802 if (dopt.outputBlobs || dopt.binary_upgrade)
803 getBlobs(fout);
804
805 /*
806 * Collect dependency data to assist in ordering the objects.
807 */
808 getDependencies(fout);
809
810 /* Lastly, create dummy objects to represent the section boundaries */
811 boundaryObjs = createBoundaryObjects();
812
813 /* Get pointers to all the known DumpableObjects */
814 getDumpableObjects(&dobjs, &numObjs);
815
816 /*
817 * Add dummy dependencies to enforce the dump section ordering.
818 */
819 addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
820
821 /*
822 * Sort the objects into a safe dump order (no forward references).
823 *
824 * We rely on dependency information to help us determine a safe order, so
825 * the initial sort is mostly for cosmetic purposes: we sort by name to
826 * ensure that logically identical schemas will dump identically.
827 */
828 sortDumpableObjectsByTypeName(dobjs, numObjs);
829
830 /* If we do a parallel dump, we want the largest tables to go first */
831 if (archiveFormat == archDirectory && numWorkers > 1)
832 sortDataAndIndexObjectsBySize(dobjs, numObjs);
833
834 sortDumpableObjects(dobjs, numObjs,
835 boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
836
837 /*
838 * Create archive TOC entries for all the objects to be dumped, in a safe
839 * order.
840 */
841
842 /* First the special ENCODING, STDSTRINGS, and SEARCHPATH entries. */
843 dumpEncoding(fout);
844 dumpStdStrings(fout);
845 dumpSearchPath(fout);
846
847 /* The database item is always next, unless we don't want it at all */
848 if (dopt.include_everything && !dopt.dataOnly)
849 dumpDatabase(fout);
850
851 /* Now the rearrangeable objects. */
852 for (i = 0; i < numObjs; i++)
853 dumpDumpableObject(fout, dobjs[i]);
854
855 /*
856 * Set up options info to ensure we dump what we want.
857 */
858 ropt = NewRestoreOptions();
859 ropt->filename = filename;
860
861 /* if you change this list, see dumpOptionsFromRestoreOptions */
862 ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
863 ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
864 ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
865 ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
866 ropt->cparams.promptPassword = dopt.cparams.promptPassword;
867 ropt->dropSchema = dopt.outputClean;
868 ropt->dataOnly = dopt.dataOnly;
869 ropt->schemaOnly = dopt.schemaOnly;
870 ropt->if_exists = dopt.if_exists;
871 ropt->column_inserts = dopt.column_inserts;
872 ropt->dumpSections = dopt.dumpSections;
873 ropt->aclsSkip = dopt.aclsSkip;
874 ropt->superuser = dopt.outputSuperuser;
875 ropt->createDB = dopt.outputCreateDB;
876 ropt->noOwner = dopt.outputNoOwner;
877 ropt->noTablespace = dopt.outputNoTablespaces;
878 ropt->disable_triggers = dopt.disable_triggers;
879 ropt->use_setsessauth = dopt.use_setsessauth;
880 ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
881 ropt->dump_inserts = dopt.dump_inserts;
882 ropt->no_publications = dopt.no_publications;
883 ropt->no_security_labels = dopt.no_security_labels;
884 ropt->no_subscriptions = dopt.no_subscriptions;
885 ropt->lockWaitTimeout = dopt.lockWaitTimeout;
886 ropt->include_everything = dopt.include_everything;
887 ropt->enable_row_security = dopt.enable_row_security;
888 ropt->sequence_data = dopt.sequence_data;
889 ropt->binary_upgrade = dopt.binary_upgrade;
890
891 if (compressLevel == -1)
892 ropt->compression = 0;
893 else
894 ropt->compression = compressLevel;
895
896 ropt->suppressDumpWarnings = true; /* We've already shown them */
897
898 SetArchiveOptions(fout, &dopt, ropt);
899
900 /* Mark which entries should be output */
901 ProcessArchiveRestoreOptions(fout);
902
903 /*
904 * The archive's TOC entries are now marked as to which ones will actually
905 * be output, so we can set up their dependency lists properly. This isn't
906 * necessary for plain-text output, though.
907 */
908 if (!plainText)
909 BuildArchiveDependencies(fout);
910
911 /*
912 * And finally we can do the actual output.
913 *
914 * Note: for non-plain-text output formats, the output file is written
915 * inside CloseArchive(). This is, um, bizarre; but not worth changing
916 * right now.
917 */
918 if (plainText)
919 RestoreArchive(fout);
920
921 CloseArchive(fout);
922
923 exit_nicely(0);
924 }
925
926
927 static void
help(const char * progname)928 help(const char *progname)
929 {
930 printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
931 printf(_("Usage:\n"));
932 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
933
934 printf(_("\nGeneral options:\n"));
935 printf(_(" -f, --file=FILENAME output file or directory name\n"));
936 printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
937 " plain text (default))\n"));
938 printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
939 printf(_(" -v, --verbose verbose mode\n"));
940 printf(_(" -V, --version output version information, then exit\n"));
941 printf(_(" -Z, --compress=0-9 compression level for compressed formats\n"));
942 printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
943 printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
944 printf(_(" -?, --help show this help, then exit\n"));
945
946 printf(_("\nOptions controlling the output content:\n"));
947 printf(_(" -a, --data-only dump only the data, not the schema\n"));
948 printf(_(" -b, --blobs include large objects in dump\n"));
949 printf(_(" -B, --no-blobs exclude large objects in dump\n"));
950 printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
951 printf(_(" -C, --create include commands to create database in dump\n"));
952 printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
953 printf(_(" -n, --schema=SCHEMA dump the named schema(s) only\n"));
954 printf(_(" -N, --exclude-schema=SCHEMA do NOT dump the named schema(s)\n"));
955 printf(_(" -o, --oids include OIDs in dump\n"));
956 printf(_(" -O, --no-owner skip restoration of object ownership in\n"
957 " plain-text format\n"));
958 printf(_(" -s, --schema-only dump only the schema, no data\n"));
959 printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
960 printf(_(" -t, --table=TABLE dump the named table(s) only\n"));
961 printf(_(" -T, --exclude-table=TABLE do NOT dump the named table(s)\n"));
962 printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
963 printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
964 printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
965 printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
966 printf(_(" --disable-triggers disable triggers during data-only restore\n"));
967 printf(_(" --enable-row-security enable row security (dump only content user has\n"
968 " access to)\n"));
969 printf(_(" --exclude-table-data=TABLE do NOT dump data for the named table(s)\n"));
970 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
971 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
972 printf(_(" --no-publications do not dump publications\n"));
973 printf(_(" --no-security-labels do not dump security label assignments\n"));
974 printf(_(" --no-subscriptions do not dump subscriptions\n"));
975 printf(_(" --no-synchronized-snapshots do not use synchronized snapshots in parallel jobs\n"));
976 printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
977 printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
978 printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
979 printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
980 printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
981 printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
982 printf(_(" --strict-names require table and/or schema include patterns to\n"
983 " match at least one entity each\n"));
984 printf(_(" --use-set-session-authorization\n"
985 " use SET SESSION AUTHORIZATION commands instead of\n"
986 " ALTER OWNER commands to set ownership\n"));
987
988 printf(_("\nConnection options:\n"));
989 printf(_(" -d, --dbname=DBNAME database to dump\n"));
990 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
991 printf(_(" -p, --port=PORT database server port number\n"));
992 printf(_(" -U, --username=NAME connect as specified database user\n"));
993 printf(_(" -w, --no-password never prompt for password\n"));
994 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
995 printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
996
997 printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
998 "variable value is used.\n\n"));
999 printf(_("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
1000 }
1001
1002 static void
setup_connection(Archive * AH,const char * dumpencoding,const char * dumpsnapshot,char * use_role)1003 setup_connection(Archive *AH, const char *dumpencoding,
1004 const char *dumpsnapshot, char *use_role)
1005 {
1006 DumpOptions *dopt = AH->dopt;
1007 PGconn *conn = GetConnection(AH);
1008 const char *std_strings;
1009
1010 PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1011
1012 /*
1013 * Set the client encoding if requested.
1014 */
1015 if (dumpencoding)
1016 {
1017 if (PQsetClientEncoding(conn, dumpencoding) < 0)
1018 exit_horribly(NULL, "invalid client encoding \"%s\" specified\n",
1019 dumpencoding);
1020 }
1021
1022 /*
1023 * Get the active encoding and the standard_conforming_strings setting, so
1024 * we know how to escape strings.
1025 */
1026 AH->encoding = PQclientEncoding(conn);
1027
1028 std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1029 AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1030
1031 /*
1032 * Set the role if requested. In a parallel dump worker, we'll be passed
1033 * use_role == NULL, but AH->use_role is already set (if user specified it
1034 * originally) and we should use that.
1035 */
1036 if (!use_role && AH->use_role)
1037 use_role = AH->use_role;
1038
1039 /* Set the role if requested */
1040 if (use_role && AH->remoteVersion >= 80100)
1041 {
1042 PQExpBuffer query = createPQExpBuffer();
1043
1044 appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1045 ExecuteSqlStatement(AH, query->data);
1046 destroyPQExpBuffer(query);
1047
1048 /* save it for possible later use by parallel workers */
1049 if (!AH->use_role)
1050 AH->use_role = pg_strdup(use_role);
1051 }
1052
1053 /* Set the datestyle to ISO to ensure the dump's portability */
1054 ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1055
1056 /* Likewise, avoid using sql_standard intervalstyle */
1057 if (AH->remoteVersion >= 80400)
1058 ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1059
1060 /*
1061 * Set extra_float_digits so that we can dump float data exactly (given
1062 * correctly implemented float I/O code, anyway)
1063 */
1064 if (AH->remoteVersion >= 90000)
1065 ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1066 else
1067 ExecuteSqlStatement(AH, "SET extra_float_digits TO 2");
1068
1069 /*
1070 * If synchronized scanning is supported, disable it, to prevent
1071 * unpredictable changes in row ordering across a dump and reload.
1072 */
1073 if (AH->remoteVersion >= 80300)
1074 ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1075
1076 /*
1077 * Disable timeouts if supported.
1078 */
1079 ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1080 if (AH->remoteVersion >= 90300)
1081 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1082 if (AH->remoteVersion >= 90600)
1083 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1084
1085 /*
1086 * Quote all identifiers, if requested.
1087 */
1088 if (quote_all_identifiers && AH->remoteVersion >= 90100)
1089 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1090
1091 /*
1092 * Adjust row-security mode, if supported.
1093 */
1094 if (AH->remoteVersion >= 90500)
1095 {
1096 if (dopt->enable_row_security)
1097 ExecuteSqlStatement(AH, "SET row_security = on");
1098 else
1099 ExecuteSqlStatement(AH, "SET row_security = off");
1100 }
1101
1102 /*
1103 * Start transaction-snapshot mode transaction to dump consistent data.
1104 */
1105 ExecuteSqlStatement(AH, "BEGIN");
1106 if (AH->remoteVersion >= 90100)
1107 {
1108 /*
1109 * To support the combination of serializable_deferrable with the jobs
1110 * option we use REPEATABLE READ for the worker connections that are
1111 * passed a snapshot. As long as the snapshot is acquired in a
1112 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1113 * REPEATABLE READ transaction provides the appropriate integrity
1114 * guarantees. This is a kluge, but safe for back-patching.
1115 */
1116 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1117 ExecuteSqlStatement(AH,
1118 "SET TRANSACTION ISOLATION LEVEL "
1119 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1120 else
1121 ExecuteSqlStatement(AH,
1122 "SET TRANSACTION ISOLATION LEVEL "
1123 "REPEATABLE READ, READ ONLY");
1124 }
1125 else
1126 {
1127 ExecuteSqlStatement(AH,
1128 "SET TRANSACTION ISOLATION LEVEL "
1129 "SERIALIZABLE, READ ONLY");
1130 }
1131
1132 /*
1133 * If user specified a snapshot to use, select that. In a parallel dump
1134 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1135 * is already set (if the server can handle it) and we should use that.
1136 */
1137 if (dumpsnapshot)
1138 AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1139
1140 if (AH->sync_snapshot_id)
1141 {
1142 PQExpBuffer query = createPQExpBuffer();
1143
1144 appendPQExpBuffer(query, "SET TRANSACTION SNAPSHOT ");
1145 appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1146 ExecuteSqlStatement(AH, query->data);
1147 destroyPQExpBuffer(query);
1148 }
1149 else if (AH->numWorkers > 1 &&
1150 AH->remoteVersion >= 90200 &&
1151 !dopt->no_synchronized_snapshots)
1152 {
1153 if (AH->isStandby && AH->remoteVersion < 100000)
1154 exit_horribly(NULL,
1155 "Synchronized snapshots on standby servers are not supported by this server version.\n"
1156 "Run with --no-synchronized-snapshots instead if you do not need\n"
1157 "synchronized snapshots.\n");
1158
1159
1160 AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1161 }
1162 }
1163
1164 /* Set up connection for a parallel worker process */
1165 static void
setupDumpWorker(Archive * AH)1166 setupDumpWorker(Archive *AH)
1167 {
1168 /*
1169 * We want to re-select all the same values the master connection is
1170 * using. We'll have inherited directly-usable values in
1171 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1172 * inherited encoding value back to a string to pass to setup_connection.
1173 */
1174 setup_connection(AH,
1175 pg_encoding_to_char(AH->encoding),
1176 NULL,
1177 NULL);
1178 }
1179
1180 static char *
get_synchronized_snapshot(Archive * fout)1181 get_synchronized_snapshot(Archive *fout)
1182 {
1183 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1184 char *result;
1185 PGresult *res;
1186
1187 res = ExecuteSqlQueryForSingleRow(fout, query);
1188 result = pg_strdup(PQgetvalue(res, 0, 0));
1189 PQclear(res);
1190
1191 return result;
1192 }
1193
1194 static ArchiveFormat
parseArchiveFormat(const char * format,ArchiveMode * mode)1195 parseArchiveFormat(const char *format, ArchiveMode *mode)
1196 {
1197 ArchiveFormat archiveFormat;
1198
1199 *mode = archModeWrite;
1200
1201 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1202 {
1203 /* This is used by pg_dumpall, and is not documented */
1204 archiveFormat = archNull;
1205 *mode = archModeAppend;
1206 }
1207 else if (pg_strcasecmp(format, "c") == 0)
1208 archiveFormat = archCustom;
1209 else if (pg_strcasecmp(format, "custom") == 0)
1210 archiveFormat = archCustom;
1211 else if (pg_strcasecmp(format, "d") == 0)
1212 archiveFormat = archDirectory;
1213 else if (pg_strcasecmp(format, "directory") == 0)
1214 archiveFormat = archDirectory;
1215 else if (pg_strcasecmp(format, "p") == 0)
1216 archiveFormat = archNull;
1217 else if (pg_strcasecmp(format, "plain") == 0)
1218 archiveFormat = archNull;
1219 else if (pg_strcasecmp(format, "t") == 0)
1220 archiveFormat = archTar;
1221 else if (pg_strcasecmp(format, "tar") == 0)
1222 archiveFormat = archTar;
1223 else
1224 exit_horribly(NULL, "invalid output format \"%s\" specified\n", format);
1225 return archiveFormat;
1226 }
1227
1228 /*
1229 * Find the OIDs of all schemas matching the given list of patterns,
1230 * and append them to the given OID list.
1231 */
1232 static void
expand_schema_name_patterns(Archive * fout,SimpleStringList * patterns,SimpleOidList * oids,bool strict_names)1233 expand_schema_name_patterns(Archive *fout,
1234 SimpleStringList *patterns,
1235 SimpleOidList *oids,
1236 bool strict_names)
1237 {
1238 PQExpBuffer query;
1239 PGresult *res;
1240 SimpleStringListCell *cell;
1241 int i;
1242
1243 if (patterns->head == NULL)
1244 return; /* nothing to do */
1245
1246 query = createPQExpBuffer();
1247
1248 /*
1249 * The loop below runs multiple SELECTs might sometimes result in
1250 * duplicate entries in the OID list, but we don't care.
1251 */
1252
1253 for (cell = patterns->head; cell; cell = cell->next)
1254 {
1255 appendPQExpBuffer(query,
1256 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1257 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1258 false, NULL, "n.nspname", NULL, NULL);
1259
1260 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1261 if (strict_names && PQntuples(res) == 0)
1262 exit_horribly(NULL, "no matching schemas were found for pattern \"%s\"\n", cell->val);
1263
1264 for (i = 0; i < PQntuples(res); i++)
1265 {
1266 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1267 }
1268
1269 PQclear(res);
1270 resetPQExpBuffer(query);
1271 }
1272
1273 destroyPQExpBuffer(query);
1274 }
1275
1276 /*
1277 * Find the OIDs of all tables matching the given list of patterns,
1278 * and append them to the given OID list.
1279 */
1280 static void
expand_table_name_patterns(Archive * fout,SimpleStringList * patterns,SimpleOidList * oids,bool strict_names)1281 expand_table_name_patterns(Archive *fout,
1282 SimpleStringList *patterns, SimpleOidList *oids,
1283 bool strict_names)
1284 {
1285 PQExpBuffer query;
1286 PGresult *res;
1287 SimpleStringListCell *cell;
1288 int i;
1289
1290 if (patterns->head == NULL)
1291 return; /* nothing to do */
1292
1293 query = createPQExpBuffer();
1294
1295 /*
1296 * this might sometimes result in duplicate entries in the OID list, but
1297 * we don't care.
1298 */
1299
1300 for (cell = patterns->head; cell; cell = cell->next)
1301 {
1302 /*
1303 * Query must remain ABSOLUTELY devoid of unqualified names. This
1304 * would be unnecessary given a pg_table_is_visible() variant taking a
1305 * search_path argument.
1306 */
1307 appendPQExpBuffer(query,
1308 "SELECT c.oid"
1309 "\nFROM pg_catalog.pg_class c"
1310 "\n LEFT JOIN pg_catalog.pg_namespace n"
1311 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1312 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1313 "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1314 RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1315 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1316 RELKIND_PARTITIONED_TABLE);
1317 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1318 false, "n.nspname", "c.relname", NULL,
1319 "pg_catalog.pg_table_is_visible(c.oid)");
1320
1321 ExecuteSqlStatement(fout, "RESET search_path");
1322 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1323 PQclear(ExecuteSqlQueryForSingleRow(fout,
1324 ALWAYS_SECURE_SEARCH_PATH_SQL));
1325 if (strict_names && PQntuples(res) == 0)
1326 exit_horribly(NULL, "no matching tables were found for pattern \"%s\"\n", cell->val);
1327
1328 for (i = 0; i < PQntuples(res); i++)
1329 {
1330 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1331 }
1332
1333 PQclear(res);
1334 resetPQExpBuffer(query);
1335 }
1336
1337 destroyPQExpBuffer(query);
1338 }
1339
1340 /*
1341 * checkExtensionMembership
1342 * Determine whether object is an extension member, and if so,
1343 * record an appropriate dependency and set the object's dump flag.
1344 *
1345 * It's important to call this for each object that could be an extension
1346 * member. Generally, we integrate this with determining the object's
1347 * to-be-dumped-ness, since extension membership overrides other rules for that.
1348 *
1349 * Returns true if object is an extension member, else false.
1350 */
1351 static bool
checkExtensionMembership(DumpableObject * dobj,Archive * fout)1352 checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1353 {
1354 ExtensionInfo *ext = findOwningExtension(dobj->catId);
1355
1356 if (ext == NULL)
1357 return false;
1358
1359 dobj->ext_member = true;
1360
1361 /* Record dependency so that getDependencies needn't deal with that */
1362 addObjectDependency(dobj, ext->dobj.dumpId);
1363
1364 /*
1365 * In 9.6 and above, mark the member object to have any non-initial ACL,
1366 * policies, and security labels dumped.
1367 *
1368 * Note that any initial ACLs (see pg_init_privs) will be removed when we
1369 * extract the information about the object. We don't provide support for
1370 * initial policies and security labels and it seems unlikely for those to
1371 * ever exist, but we may have to revisit this later.
1372 *
1373 * Prior to 9.6, we do not include any extension member components.
1374 *
1375 * In binary upgrades, we still dump all components of the members
1376 * individually, since the idea is to exactly reproduce the database
1377 * contents rather than replace the extension contents with something
1378 * different.
1379 */
1380 if (fout->dopt->binary_upgrade)
1381 dobj->dump = ext->dobj.dump;
1382 else
1383 {
1384 if (fout->remoteVersion < 90600)
1385 dobj->dump = DUMP_COMPONENT_NONE;
1386 else
1387 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL |
1388 DUMP_COMPONENT_SECLABEL |
1389 DUMP_COMPONENT_POLICY);
1390 }
1391
1392 return true;
1393 }
1394
1395 /*
1396 * selectDumpableNamespace: policy-setting subroutine
1397 * Mark a namespace as to be dumped or not
1398 */
1399 static void
selectDumpableNamespace(NamespaceInfo * nsinfo,Archive * fout)1400 selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1401 {
1402 /*
1403 * If specific tables are being dumped, do not dump any complete
1404 * namespaces. If specific namespaces are being dumped, dump just those
1405 * namespaces. Otherwise, dump all non-system namespaces.
1406 */
1407 if (table_include_oids.head != NULL)
1408 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1409 else if (schema_include_oids.head != NULL)
1410 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1411 simple_oid_list_member(&schema_include_oids,
1412 nsinfo->dobj.catId.oid) ?
1413 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1414 else if (fout->remoteVersion >= 90600 &&
1415 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1416 {
1417 /*
1418 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1419 * they are interesting (and not the original ACLs which were set at
1420 * initdb time, see pg_init_privs).
1421 */
1422 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1423 }
1424 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1425 strcmp(nsinfo->dobj.name, "information_schema") == 0)
1426 {
1427 /* Other system schemas don't get dumped */
1428 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1429 }
1430 else
1431 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1432
1433 /*
1434 * In any case, a namespace can be excluded by an exclusion switch
1435 */
1436 if (nsinfo->dobj.dump_contains &&
1437 simple_oid_list_member(&schema_exclude_oids,
1438 nsinfo->dobj.catId.oid))
1439 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1440
1441 /*
1442 * If the schema belongs to an extension, allow extension membership to
1443 * override the dump decision for the schema itself. However, this does
1444 * not change dump_contains, so this won't change what we do with objects
1445 * within the schema. (If they belong to the extension, they'll get
1446 * suppressed by it, otherwise not.)
1447 */
1448 (void) checkExtensionMembership(&nsinfo->dobj, fout);
1449 }
1450
1451 /*
1452 * selectDumpableTable: policy-setting subroutine
1453 * Mark a table as to be dumped or not
1454 */
1455 static void
selectDumpableTable(TableInfo * tbinfo,Archive * fout)1456 selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1457 {
1458 if (checkExtensionMembership(&tbinfo->dobj, fout))
1459 return; /* extension membership overrides all else */
1460
1461 /*
1462 * If specific tables are being dumped, dump just those tables; else, dump
1463 * according to the parent namespace's dump flag.
1464 */
1465 if (table_include_oids.head != NULL)
1466 tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
1467 tbinfo->dobj.catId.oid) ?
1468 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1469 else
1470 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
1471
1472 /*
1473 * In any case, a table can be excluded by an exclusion switch
1474 */
1475 if (tbinfo->dobj.dump &&
1476 simple_oid_list_member(&table_exclude_oids,
1477 tbinfo->dobj.catId.oid))
1478 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
1479 }
1480
1481 /*
1482 * selectDumpableType: policy-setting subroutine
1483 * Mark a type as to be dumped or not
1484 *
1485 * If it's a table's rowtype or an autogenerated array type, we also apply a
1486 * special type code to facilitate sorting into the desired order. (We don't
1487 * want to consider those to be ordinary types because that would bring tables
1488 * up into the datatype part of the dump order.) We still set the object's
1489 * dump flag; that's not going to cause the dummy type to be dumped, but we
1490 * need it so that casts involving such types will be dumped correctly -- see
1491 * dumpCast. This means the flag should be set the same as for the underlying
1492 * object (the table or base type).
1493 */
1494 static void
selectDumpableType(TypeInfo * tyinfo,Archive * fout)1495 selectDumpableType(TypeInfo *tyinfo, Archive *fout)
1496 {
1497 /* skip complex types, except for standalone composite types */
1498 if (OidIsValid(tyinfo->typrelid) &&
1499 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
1500 {
1501 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
1502
1503 tyinfo->dobj.objType = DO_DUMMY_TYPE;
1504 if (tytable != NULL)
1505 tyinfo->dobj.dump = tytable->dobj.dump;
1506 else
1507 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
1508 return;
1509 }
1510
1511 /* skip auto-generated array types */
1512 if (tyinfo->isArray)
1513 {
1514 tyinfo->dobj.objType = DO_DUMMY_TYPE;
1515
1516 /*
1517 * Fall through to set the dump flag; we assume that the subsequent
1518 * rules will do the same thing as they would for the array's base
1519 * type. (We cannot reliably look up the base type here, since
1520 * getTypes may not have processed it yet.)
1521 */
1522 }
1523
1524 if (checkExtensionMembership(&tyinfo->dobj, fout))
1525 return; /* extension membership overrides all else */
1526
1527 /* Dump based on if the contents of the namespace are being dumped */
1528 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
1529 }
1530
1531 /*
1532 * selectDumpableDefaultACL: policy-setting subroutine
1533 * Mark a default ACL as to be dumped or not
1534 *
1535 * For per-schema default ACLs, dump if the schema is to be dumped.
1536 * Otherwise dump if we are dumping "everything". Note that dataOnly
1537 * and aclsSkip are checked separately.
1538 */
1539 static void
selectDumpableDefaultACL(DefaultACLInfo * dinfo,DumpOptions * dopt)1540 selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
1541 {
1542 /* Default ACLs can't be extension members */
1543
1544 if (dinfo->dobj.namespace)
1545 /* default ACLs are considered part of the namespace */
1546 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
1547 else
1548 dinfo->dobj.dump = dopt->include_everything ?
1549 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1550 }
1551
1552 /*
1553 * selectDumpableCast: policy-setting subroutine
1554 * Mark a cast as to be dumped or not
1555 *
1556 * Casts do not belong to any particular namespace (since they haven't got
1557 * names), nor do they have identifiable owners. To distinguish user-defined
1558 * casts from built-in ones, we must resort to checking whether the cast's
1559 * OID is in the range reserved for initdb.
1560 */
1561 static void
selectDumpableCast(CastInfo * cast,Archive * fout)1562 selectDumpableCast(CastInfo *cast, Archive *fout)
1563 {
1564 if (checkExtensionMembership(&cast->dobj, fout))
1565 return; /* extension membership overrides all else */
1566
1567 /*
1568 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
1569 * support ACLs currently.
1570 */
1571 if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1572 cast->dobj.dump = DUMP_COMPONENT_NONE;
1573 else
1574 cast->dobj.dump = fout->dopt->include_everything ?
1575 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1576 }
1577
1578 /*
1579 * selectDumpableProcLang: policy-setting subroutine
1580 * Mark a procedural language as to be dumped or not
1581 *
1582 * Procedural languages do not belong to any particular namespace. To
1583 * identify built-in languages, we must resort to checking whether the
1584 * language's OID is in the range reserved for initdb.
1585 */
1586 static void
selectDumpableProcLang(ProcLangInfo * plang,Archive * fout)1587 selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
1588 {
1589 if (checkExtensionMembership(&plang->dobj, fout))
1590 return; /* extension membership overrides all else */
1591
1592 /*
1593 * Only include procedural languages when we are dumping everything.
1594 *
1595 * For from-initdb procedural languages, only include ACLs, as we do for
1596 * the pg_catalog namespace. We need this because procedural languages do
1597 * not live in any namespace.
1598 */
1599 if (!fout->dopt->include_everything)
1600 plang->dobj.dump = DUMP_COMPONENT_NONE;
1601 else
1602 {
1603 if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1604 plang->dobj.dump = fout->remoteVersion < 90600 ?
1605 DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
1606 else
1607 plang->dobj.dump = DUMP_COMPONENT_ALL;
1608 }
1609 }
1610
1611 /*
1612 * selectDumpableAccessMethod: policy-setting subroutine
1613 * Mark an access method as to be dumped or not
1614 *
1615 * Access methods do not belong to any particular namespace. To identify
1616 * built-in access methods, we must resort to checking whether the
1617 * method's OID is in the range reserved for initdb.
1618 */
1619 static void
selectDumpableAccessMethod(AccessMethodInfo * method,Archive * fout)1620 selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
1621 {
1622 if (checkExtensionMembership(&method->dobj, fout))
1623 return; /* extension membership overrides all else */
1624
1625 /*
1626 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
1627 * they do not support ACLs currently.
1628 */
1629 if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1630 method->dobj.dump = DUMP_COMPONENT_NONE;
1631 else
1632 method->dobj.dump = fout->dopt->include_everything ?
1633 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1634 }
1635
1636 /*
1637 * selectDumpableExtension: policy-setting subroutine
1638 * Mark an extension as to be dumped or not
1639 *
1640 * Normally, we dump all extensions, or none of them if include_everything
1641 * is false (i.e., a --schema or --table switch was given). However, in
1642 * binary-upgrade mode it's necessary to skip built-in extensions, since we
1643 * assume those will already be installed in the target database. We identify
1644 * such extensions by their having OIDs in the range reserved for initdb.
1645 */
1646 static void
selectDumpableExtension(ExtensionInfo * extinfo,DumpOptions * dopt)1647 selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
1648 {
1649 /*
1650 * Use DUMP_COMPONENT_ACL for from-initdb extensions, to allow users to
1651 * change permissions on those objects, if they wish to, and have those
1652 * changes preserved.
1653 */
1654 if (dopt->binary_upgrade && extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1655 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
1656 else
1657 extinfo->dobj.dump = extinfo->dobj.dump_contains =
1658 dopt->include_everything ? DUMP_COMPONENT_ALL :
1659 DUMP_COMPONENT_NONE;
1660 }
1661
1662 /*
1663 * selectDumpablePublicationTable: policy-setting subroutine
1664 * Mark a publication table as to be dumped or not
1665 *
1666 * Publication tables have schemas, but those are ignored in decision making,
1667 * because publications are only dumped when we are dumping everything.
1668 */
1669 static void
selectDumpablePublicationTable(DumpableObject * dobj,Archive * fout)1670 selectDumpablePublicationTable(DumpableObject *dobj, Archive *fout)
1671 {
1672 if (checkExtensionMembership(dobj, fout))
1673 return; /* extension membership overrides all else */
1674
1675 dobj->dump = fout->dopt->include_everything ?
1676 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1677 }
1678
1679 /*
1680 * selectDumpableObject: policy-setting subroutine
1681 * Mark a generic dumpable object as to be dumped or not
1682 *
1683 * Use this only for object types without a special-case routine above.
1684 */
1685 static void
selectDumpableObject(DumpableObject * dobj,Archive * fout)1686 selectDumpableObject(DumpableObject *dobj, Archive *fout)
1687 {
1688 if (checkExtensionMembership(dobj, fout))
1689 return; /* extension membership overrides all else */
1690
1691 /*
1692 * Default policy is to dump if parent namespace is dumpable, or for
1693 * non-namespace-associated items, dump if we're dumping "everything".
1694 */
1695 if (dobj->namespace)
1696 dobj->dump = dobj->namespace->dobj.dump_contains;
1697 else
1698 dobj->dump = fout->dopt->include_everything ?
1699 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1700 }
1701
1702 /*
1703 * Dump a table's contents for loading using the COPY command
1704 * - this routine is called by the Archiver when it wants the table
1705 * to be dumped.
1706 */
1707
1708 static int
dumpTableData_copy(Archive * fout,void * dcontext)1709 dumpTableData_copy(Archive *fout, void *dcontext)
1710 {
1711 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
1712 TableInfo *tbinfo = tdinfo->tdtable;
1713 const char *classname = tbinfo->dobj.name;
1714 const bool hasoids = tbinfo->hasoids;
1715 const bool oids = tdinfo->oids;
1716 PQExpBuffer q = createPQExpBuffer();
1717
1718 /*
1719 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
1720 * which uses it already.
1721 */
1722 PQExpBuffer clistBuf = createPQExpBuffer();
1723 PGconn *conn = GetConnection(fout);
1724 PGresult *res;
1725 int ret;
1726 char *copybuf;
1727 const char *column_list;
1728
1729 if (g_verbose)
1730 write_msg(NULL, "dumping contents of table \"%s.%s\"\n",
1731 tbinfo->dobj.namespace->dobj.name, classname);
1732
1733 /*
1734 * Specify the column list explicitly so that we have no possibility of
1735 * retrieving data in the wrong column order. (The default column
1736 * ordering of COPY will not be what we want in certain corner cases
1737 * involving ADD COLUMN and inheritance.)
1738 */
1739 column_list = fmtCopyColumnList(tbinfo, clistBuf);
1740
1741 if (oids && hasoids)
1742 {
1743 appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;",
1744 fmtQualifiedDumpable(tbinfo),
1745 column_list);
1746 }
1747 else if (tdinfo->filtercond)
1748 {
1749 /* Note: this syntax is only supported in 8.2 and up */
1750 appendPQExpBufferStr(q, "COPY (SELECT ");
1751 /* klugery to get rid of parens in column list */
1752 if (strlen(column_list) > 2)
1753 {
1754 appendPQExpBufferStr(q, column_list + 1);
1755 q->data[q->len - 1] = ' ';
1756 }
1757 else
1758 appendPQExpBufferStr(q, "* ");
1759 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
1760 fmtQualifiedDumpable(tbinfo),
1761 tdinfo->filtercond);
1762 }
1763 else
1764 {
1765 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
1766 fmtQualifiedDumpable(tbinfo),
1767 column_list);
1768 }
1769 res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
1770 PQclear(res);
1771 destroyPQExpBuffer(clistBuf);
1772
1773 for (;;)
1774 {
1775 ret = PQgetCopyData(conn, ©buf, 0);
1776
1777 if (ret < 0)
1778 break; /* done or error */
1779
1780 if (copybuf)
1781 {
1782 WriteData(fout, copybuf, ret);
1783 PQfreemem(copybuf);
1784 }
1785
1786 /* ----------
1787 * THROTTLE:
1788 *
1789 * There was considerable discussion in late July, 2000 regarding
1790 * slowing down pg_dump when backing up large tables. Users with both
1791 * slow & fast (multi-processor) machines experienced performance
1792 * degradation when doing a backup.
1793 *
1794 * Initial attempts based on sleeping for a number of ms for each ms
1795 * of work were deemed too complex, then a simple 'sleep in each loop'
1796 * implementation was suggested. The latter failed because the loop
1797 * was too tight. Finally, the following was implemented:
1798 *
1799 * If throttle is non-zero, then
1800 * See how long since the last sleep.
1801 * Work out how long to sleep (based on ratio).
1802 * If sleep is more than 100ms, then
1803 * sleep
1804 * reset timer
1805 * EndIf
1806 * EndIf
1807 *
1808 * where the throttle value was the number of ms to sleep per ms of
1809 * work. The calculation was done in each loop.
1810 *
1811 * Most of the hard work is done in the backend, and this solution
1812 * still did not work particularly well: on slow machines, the ratio
1813 * was 50:1, and on medium paced machines, 1:1, and on fast
1814 * multi-processor machines, it had little or no effect, for reasons
1815 * that were unclear.
1816 *
1817 * Further discussion ensued, and the proposal was dropped.
1818 *
1819 * For those people who want this feature, it can be implemented using
1820 * gettimeofday in each loop, calculating the time since last sleep,
1821 * multiplying that by the sleep ratio, then if the result is more
1822 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
1823 * function to sleep for a subsecond period ie.
1824 *
1825 * select(0, NULL, NULL, NULL, &tvi);
1826 *
1827 * This will return after the interval specified in the structure tvi.
1828 * Finally, call gettimeofday again to save the 'last sleep time'.
1829 * ----------
1830 */
1831 }
1832 archprintf(fout, "\\.\n\n\n");
1833
1834 if (ret == -2)
1835 {
1836 /* copy data transfer failed */
1837 write_msg(NULL, "Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.\n", classname);
1838 write_msg(NULL, "Error message from server: %s", PQerrorMessage(conn));
1839 write_msg(NULL, "The command was: %s\n", q->data);
1840 exit_nicely(1);
1841 }
1842
1843 /* Check command status and return to normal libpq state */
1844 res = PQgetResult(conn);
1845 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1846 {
1847 write_msg(NULL, "Dumping the contents of table \"%s\" failed: PQgetResult() failed.\n", classname);
1848 write_msg(NULL, "Error message from server: %s", PQerrorMessage(conn));
1849 write_msg(NULL, "The command was: %s\n", q->data);
1850 exit_nicely(1);
1851 }
1852 PQclear(res);
1853
1854 /* Do this to ensure we've pumped libpq back to idle state */
1855 if (PQgetResult(conn) != NULL)
1856 write_msg(NULL, "WARNING: unexpected extra results during COPY of table \"%s\"\n",
1857 classname);
1858
1859 destroyPQExpBuffer(q);
1860 return 1;
1861 }
1862
1863 /*
1864 * Dump table data using INSERT commands.
1865 *
1866 * Caution: when we restore from an archive file direct to database, the
1867 * INSERT commands emitted by this function have to be parsed by
1868 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
1869 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
1870 */
1871 static int
dumpTableData_insert(Archive * fout,void * dcontext)1872 dumpTableData_insert(Archive *fout, void *dcontext)
1873 {
1874 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
1875 TableInfo *tbinfo = tdinfo->tdtable;
1876 DumpOptions *dopt = fout->dopt;
1877 PQExpBuffer q = createPQExpBuffer();
1878 PQExpBuffer insertStmt = NULL;
1879 PGresult *res;
1880 int tuple;
1881 int nfields;
1882 int field;
1883
1884 appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
1885 "SELECT * FROM ONLY %s",
1886 fmtQualifiedDumpable(tbinfo));
1887 if (tdinfo->filtercond)
1888 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
1889
1890 ExecuteSqlStatement(fout, q->data);
1891
1892 while (1)
1893 {
1894 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
1895 PGRES_TUPLES_OK);
1896 nfields = PQnfields(res);
1897 for (tuple = 0; tuple < PQntuples(res); tuple++)
1898 {
1899 /*
1900 * First time through, we build as much of the INSERT statement as
1901 * possible in "insertStmt", which we can then just print for each
1902 * line. If the table happens to have zero columns then this will
1903 * be a complete statement, otherwise it will end in "VALUES(" and
1904 * be ready to have the row's column values appended.
1905 */
1906 if (insertStmt == NULL)
1907 {
1908 insertStmt = createPQExpBuffer();
1909 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
1910 fmtQualifiedDumpable(tbinfo));
1911
1912 /* corner case for zero-column table */
1913 if (nfields == 0)
1914 {
1915 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
1916 }
1917 else
1918 {
1919 /* append the list of column names if required */
1920 if (dopt->column_inserts)
1921 {
1922 appendPQExpBufferChar(insertStmt, '(');
1923 for (field = 0; field < nfields; field++)
1924 {
1925 if (field > 0)
1926 appendPQExpBufferStr(insertStmt, ", ");
1927 appendPQExpBufferStr(insertStmt,
1928 fmtId(PQfname(res, field)));
1929 }
1930 appendPQExpBufferStr(insertStmt, ") ");
1931 }
1932
1933 if (tbinfo->needs_override)
1934 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
1935
1936 appendPQExpBufferStr(insertStmt, "VALUES (");
1937 }
1938 }
1939
1940 archputs(insertStmt->data, fout);
1941
1942 /* if it is zero-column table then we're done */
1943 if (nfields == 0)
1944 continue;
1945
1946 for (field = 0; field < nfields; field++)
1947 {
1948 if (field > 0)
1949 archputs(", ", fout);
1950 if (PQgetisnull(res, tuple, field))
1951 {
1952 archputs("NULL", fout);
1953 continue;
1954 }
1955
1956 /* XXX This code is partially duplicated in ruleutils.c */
1957 switch (PQftype(res, field))
1958 {
1959 case INT2OID:
1960 case INT4OID:
1961 case INT8OID:
1962 case OIDOID:
1963 case FLOAT4OID:
1964 case FLOAT8OID:
1965 case NUMERICOID:
1966 {
1967 /*
1968 * These types are printed without quotes unless
1969 * they contain values that aren't accepted by the
1970 * scanner unquoted (e.g., 'NaN'). Note that
1971 * strtod() and friends might accept NaN, so we
1972 * can't use that to test.
1973 *
1974 * In reality we only need to defend against
1975 * infinity and NaN, so we need not get too crazy
1976 * about pattern matching here.
1977 */
1978 const char *s = PQgetvalue(res, tuple, field);
1979
1980 if (strspn(s, "0123456789 +-eE.") == strlen(s))
1981 archputs(s, fout);
1982 else
1983 archprintf(fout, "'%s'", s);
1984 }
1985 break;
1986
1987 case BITOID:
1988 case VARBITOID:
1989 archprintf(fout, "B'%s'",
1990 PQgetvalue(res, tuple, field));
1991 break;
1992
1993 case BOOLOID:
1994 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
1995 archputs("true", fout);
1996 else
1997 archputs("false", fout);
1998 break;
1999
2000 default:
2001 /* All other types are printed as string literals. */
2002 resetPQExpBuffer(q);
2003 appendStringLiteralAH(q,
2004 PQgetvalue(res, tuple, field),
2005 fout);
2006 archputs(q->data, fout);
2007 break;
2008 }
2009 }
2010 archputs(");\n", fout);
2011 }
2012
2013 if (PQntuples(res) <= 0)
2014 {
2015 PQclear(res);
2016 break;
2017 }
2018 PQclear(res);
2019 }
2020
2021 archputs("\n\n", fout);
2022
2023 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2024
2025 destroyPQExpBuffer(q);
2026 if (insertStmt != NULL)
2027 destroyPQExpBuffer(insertStmt);
2028
2029 return 1;
2030 }
2031
2032
2033 /*
2034 * dumpTableData -
2035 * dump the contents of a single table
2036 *
2037 * Actually, this just makes an ArchiveEntry for the table contents.
2038 */
2039 static void
dumpTableData(Archive * fout,TableDataInfo * tdinfo)2040 dumpTableData(Archive *fout, TableDataInfo *tdinfo)
2041 {
2042 DumpOptions *dopt = fout->dopt;
2043 TableInfo *tbinfo = tdinfo->tdtable;
2044 PQExpBuffer copyBuf = createPQExpBuffer();
2045 PQExpBuffer clistBuf = createPQExpBuffer();
2046 DataDumperPtr dumpFn;
2047 char *copyStmt;
2048
2049 /* We had better have loaded per-column details about this table */
2050 Assert(tbinfo->interesting);
2051
2052 if (!dopt->dump_inserts)
2053 {
2054 /* Dump/restore using COPY */
2055 dumpFn = dumpTableData_copy;
2056 /* must use 2 steps here 'cause fmtId is nonreentrant */
2057 appendPQExpBuffer(copyBuf, "COPY %s ",
2058 fmtQualifiedDumpable(tbinfo));
2059 appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n",
2060 fmtCopyColumnList(tbinfo, clistBuf),
2061 (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : "");
2062 copyStmt = copyBuf->data;
2063 }
2064 else
2065 {
2066 /* Restore using INSERT */
2067 dumpFn = dumpTableData_insert;
2068 copyStmt = NULL;
2069 }
2070
2071 /*
2072 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2073 * dependency on its table as "special" and pass it to ArchiveEntry now.
2074 * See comments for BuildArchiveDependencies.
2075 */
2076 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2077 ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2078 tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name,
2079 NULL, tbinfo->rolname,
2080 false, "TABLE DATA", SECTION_DATA,
2081 "", "", copyStmt,
2082 &(tbinfo->dobj.dumpId), 1,
2083 dumpFn, tdinfo);
2084
2085 destroyPQExpBuffer(copyBuf);
2086 destroyPQExpBuffer(clistBuf);
2087 }
2088
2089 /*
2090 * refreshMatViewData -
2091 * load or refresh the contents of a single materialized view
2092 *
2093 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2094 * statement.
2095 */
2096 static void
refreshMatViewData(Archive * fout,TableDataInfo * tdinfo)2097 refreshMatViewData(Archive *fout, TableDataInfo *tdinfo)
2098 {
2099 TableInfo *tbinfo = tdinfo->tdtable;
2100 PQExpBuffer q;
2101
2102 /* If the materialized view is not flagged as populated, skip this. */
2103 if (!tbinfo->relispopulated)
2104 return;
2105
2106 q = createPQExpBuffer();
2107
2108 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2109 fmtQualifiedDumpable(tbinfo));
2110
2111 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2112 ArchiveEntry(fout,
2113 tdinfo->dobj.catId, /* catalog ID */
2114 tdinfo->dobj.dumpId, /* dump ID */
2115 tbinfo->dobj.name, /* Name */
2116 tbinfo->dobj.namespace->dobj.name, /* Namespace */
2117 NULL, /* Tablespace */
2118 tbinfo->rolname, /* Owner */
2119 false, /* with oids */
2120 "MATERIALIZED VIEW DATA", /* Desc */
2121 SECTION_POST_DATA, /* Section */
2122 q->data, /* Create */
2123 "", /* Del */
2124 NULL, /* Copy */
2125 tdinfo->dobj.dependencies, /* Deps */
2126 tdinfo->dobj.nDeps, /* # Deps */
2127 NULL, /* Dumper */
2128 NULL); /* Dumper Arg */
2129
2130 destroyPQExpBuffer(q);
2131 }
2132
2133 /*
2134 * getTableData -
2135 * set up dumpable objects representing the contents of tables
2136 */
2137 static void
getTableData(DumpOptions * dopt,TableInfo * tblinfo,int numTables,bool oids,char relkind)2138 getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, bool oids, char relkind)
2139 {
2140 int i;
2141
2142 for (i = 0; i < numTables; i++)
2143 {
2144 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2145 (!relkind || tblinfo[i].relkind == relkind))
2146 makeTableDataInfo(dopt, &(tblinfo[i]), oids);
2147 }
2148 }
2149
2150 /*
2151 * Make a dumpable object for the data of this specific table
2152 *
2153 * Note: we make a TableDataInfo if and only if we are going to dump the
2154 * table data; the "dump" flag in such objects isn't used.
2155 */
2156 static void
makeTableDataInfo(DumpOptions * dopt,TableInfo * tbinfo,bool oids)2157 makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids)
2158 {
2159 TableDataInfo *tdinfo;
2160
2161 /*
2162 * Nothing to do if we already decided to dump the table. This will
2163 * happen for "config" tables.
2164 */
2165 if (tbinfo->dataObj != NULL)
2166 return;
2167
2168 /* Skip VIEWs (no data to dump) */
2169 if (tbinfo->relkind == RELKIND_VIEW)
2170 return;
2171 /* Skip FOREIGN TABLEs (no data to dump) */
2172 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2173 return;
2174 /* Skip partitioned tables (data in partitions) */
2175 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2176 return;
2177
2178 /* Don't dump data in unlogged tables, if so requested */
2179 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2180 dopt->no_unlogged_table_data)
2181 return;
2182
2183 /* Check that the data is not explicitly excluded */
2184 if (simple_oid_list_member(&tabledata_exclude_oids,
2185 tbinfo->dobj.catId.oid))
2186 return;
2187
2188 /* OK, let's dump it */
2189 tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2190
2191 if (tbinfo->relkind == RELKIND_MATVIEW)
2192 tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2193 else if (tbinfo->relkind == RELKIND_SEQUENCE)
2194 tdinfo->dobj.objType = DO_SEQUENCE_SET;
2195 else
2196 tdinfo->dobj.objType = DO_TABLE_DATA;
2197
2198 /*
2199 * Note: use tableoid 0 so that this object won't be mistaken for
2200 * something that pg_depend entries apply to.
2201 */
2202 tdinfo->dobj.catId.tableoid = 0;
2203 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2204 AssignDumpId(&tdinfo->dobj);
2205 tdinfo->dobj.name = tbinfo->dobj.name;
2206 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2207 tdinfo->tdtable = tbinfo;
2208 tdinfo->oids = oids;
2209 tdinfo->filtercond = NULL; /* might get set later */
2210 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2211
2212 tbinfo->dataObj = tdinfo;
2213
2214 /* Make sure that we'll collect per-column info for this table. */
2215 tbinfo->interesting = true;
2216 }
2217
2218 /*
2219 * The refresh for a materialized view must be dependent on the refresh for
2220 * any materialized view that this one is dependent on.
2221 *
2222 * This must be called after all the objects are created, but before they are
2223 * sorted.
2224 */
2225 static void
buildMatViewRefreshDependencies(Archive * fout)2226 buildMatViewRefreshDependencies(Archive *fout)
2227 {
2228 PQExpBuffer query;
2229 PGresult *res;
2230 int ntups,
2231 i;
2232 int i_classid,
2233 i_objid,
2234 i_refobjid;
2235
2236 /* No Mat Views before 9.3. */
2237 if (fout->remoteVersion < 90300)
2238 return;
2239
2240 query = createPQExpBuffer();
2241
2242 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
2243 "( "
2244 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
2245 "FROM pg_depend d1 "
2246 "JOIN pg_class c1 ON c1.oid = d1.objid "
2247 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
2248 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
2249 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
2250 "AND d2.objid = r1.oid "
2251 "AND d2.refobjid <> d1.objid "
2252 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
2253 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2254 CppAsString2(RELKIND_VIEW) ") "
2255 "WHERE d1.classid = 'pg_class'::regclass "
2256 "UNION "
2257 "SELECT w.objid, d3.refobjid, c3.relkind "
2258 "FROM w "
2259 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
2260 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
2261 "AND d3.objid = r3.oid "
2262 "AND d3.refobjid <> w.refobjid "
2263 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
2264 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2265 CppAsString2(RELKIND_VIEW) ") "
2266 ") "
2267 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
2268 "FROM w "
2269 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
2270
2271 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
2272
2273 ntups = PQntuples(res);
2274
2275 i_classid = PQfnumber(res, "classid");
2276 i_objid = PQfnumber(res, "objid");
2277 i_refobjid = PQfnumber(res, "refobjid");
2278
2279 for (i = 0; i < ntups; i++)
2280 {
2281 CatalogId objId;
2282 CatalogId refobjId;
2283 DumpableObject *dobj;
2284 DumpableObject *refdobj;
2285 TableInfo *tbinfo;
2286 TableInfo *reftbinfo;
2287
2288 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
2289 objId.oid = atooid(PQgetvalue(res, i, i_objid));
2290 refobjId.tableoid = objId.tableoid;
2291 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
2292
2293 dobj = findObjectByCatalogId(objId);
2294 if (dobj == NULL)
2295 continue;
2296
2297 Assert(dobj->objType == DO_TABLE);
2298 tbinfo = (TableInfo *) dobj;
2299 Assert(tbinfo->relkind == RELKIND_MATVIEW);
2300 dobj = (DumpableObject *) tbinfo->dataObj;
2301 if (dobj == NULL)
2302 continue;
2303 Assert(dobj->objType == DO_REFRESH_MATVIEW);
2304
2305 refdobj = findObjectByCatalogId(refobjId);
2306 if (refdobj == NULL)
2307 continue;
2308
2309 Assert(refdobj->objType == DO_TABLE);
2310 reftbinfo = (TableInfo *) refdobj;
2311 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
2312 refdobj = (DumpableObject *) reftbinfo->dataObj;
2313 if (refdobj == NULL)
2314 continue;
2315 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
2316
2317 addObjectDependency(dobj, refdobj->dumpId);
2318
2319 if (!reftbinfo->relispopulated)
2320 tbinfo->relispopulated = false;
2321 }
2322
2323 PQclear(res);
2324
2325 destroyPQExpBuffer(query);
2326 }
2327
2328 /*
2329 * getTableDataFKConstraints -
2330 * add dump-order dependencies reflecting foreign key constraints
2331 *
2332 * This code is executed only in a data-only dump --- in schema+data dumps
2333 * we handle foreign key issues by not creating the FK constraints until
2334 * after the data is loaded. In a data-only dump, however, we want to
2335 * order the table data objects in such a way that a table's referenced
2336 * tables are restored first. (In the presence of circular references or
2337 * self-references this may be impossible; we'll detect and complain about
2338 * that during the dependency sorting step.)
2339 */
2340 static void
getTableDataFKConstraints(void)2341 getTableDataFKConstraints(void)
2342 {
2343 DumpableObject **dobjs;
2344 int numObjs;
2345 int i;
2346
2347 /* Search through all the dumpable objects for FK constraints */
2348 getDumpableObjects(&dobjs, &numObjs);
2349 for (i = 0; i < numObjs; i++)
2350 {
2351 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
2352 {
2353 ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
2354 TableInfo *ftable;
2355
2356 /* Not interesting unless both tables are to be dumped */
2357 if (cinfo->contable == NULL ||
2358 cinfo->contable->dataObj == NULL)
2359 continue;
2360 ftable = findTableByOid(cinfo->confrelid);
2361 if (ftable == NULL ||
2362 ftable->dataObj == NULL)
2363 continue;
2364
2365 /*
2366 * Okay, make referencing table's TABLE_DATA object depend on the
2367 * referenced table's TABLE_DATA object.
2368 */
2369 addObjectDependency(&cinfo->contable->dataObj->dobj,
2370 ftable->dataObj->dobj.dumpId);
2371 }
2372 }
2373 free(dobjs);
2374 }
2375
2376
2377 /*
2378 * guessConstraintInheritance:
2379 * In pre-8.4 databases, we can't tell for certain which constraints
2380 * are inherited. We assume a CHECK constraint is inherited if its name
2381 * matches the name of any constraint in the parent. Originally this code
2382 * tried to compare the expression texts, but that can fail for various
2383 * reasons --- for example, if the parent and child tables are in different
2384 * schemas, reverse-listing of function calls may produce different text
2385 * (schema-qualified or not) depending on search path.
2386 *
2387 * In 8.4 and up we can rely on the conislocal field to decide which
2388 * constraints must be dumped; much safer.
2389 *
2390 * This function assumes all conislocal flags were initialized to TRUE.
2391 * It clears the flag on anything that seems to be inherited.
2392 */
2393 static void
guessConstraintInheritance(TableInfo * tblinfo,int numTables)2394 guessConstraintInheritance(TableInfo *tblinfo, int numTables)
2395 {
2396 int i,
2397 j,
2398 k;
2399
2400 for (i = 0; i < numTables; i++)
2401 {
2402 TableInfo *tbinfo = &(tblinfo[i]);
2403 int numParents;
2404 TableInfo **parents;
2405 TableInfo *parent;
2406
2407 /* Sequences and views never have parents */
2408 if (tbinfo->relkind == RELKIND_SEQUENCE ||
2409 tbinfo->relkind == RELKIND_VIEW)
2410 continue;
2411
2412 /* Don't bother computing anything for non-target tables, either */
2413 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
2414 continue;
2415
2416 numParents = tbinfo->numParents;
2417 parents = tbinfo->parents;
2418
2419 if (numParents == 0)
2420 continue; /* nothing to see here, move along */
2421
2422 /* scan for inherited CHECK constraints */
2423 for (j = 0; j < tbinfo->ncheck; j++)
2424 {
2425 ConstraintInfo *constr;
2426
2427 constr = &(tbinfo->checkexprs[j]);
2428
2429 for (k = 0; k < numParents; k++)
2430 {
2431 int l;
2432
2433 parent = parents[k];
2434 for (l = 0; l < parent->ncheck; l++)
2435 {
2436 ConstraintInfo *pconstr = &(parent->checkexprs[l]);
2437
2438 if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
2439 {
2440 constr->conislocal = false;
2441 break;
2442 }
2443 }
2444 if (!constr->conislocal)
2445 break;
2446 }
2447 }
2448 }
2449 }
2450
2451
2452 /*
2453 * dumpDatabase:
2454 * dump the database definition
2455 */
2456 static void
dumpDatabase(Archive * fout)2457 dumpDatabase(Archive *fout)
2458 {
2459 DumpOptions *dopt = fout->dopt;
2460 PQExpBuffer dbQry = createPQExpBuffer();
2461 PQExpBuffer delQry = createPQExpBuffer();
2462 PQExpBuffer creaQry = createPQExpBuffer();
2463 PQExpBuffer labelq = createPQExpBuffer();
2464 PGconn *conn = GetConnection(fout);
2465 PGresult *res;
2466 int i_tableoid,
2467 i_oid,
2468 i_dba,
2469 i_encoding,
2470 i_collate,
2471 i_ctype,
2472 i_frozenxid,
2473 i_minmxid,
2474 i_tablespace;
2475 CatalogId dbCatId;
2476 DumpId dbDumpId;
2477 const char *datname,
2478 *dba,
2479 *encoding,
2480 *collate,
2481 *ctype,
2482 *tablespace;
2483 uint32 frozenxid,
2484 minmxid;
2485 char *qdatname;
2486
2487 datname = PQdb(conn);
2488 qdatname = pg_strdup(fmtId(datname));
2489
2490 if (g_verbose)
2491 write_msg(NULL, "saving database definition\n");
2492
2493 /* Get the database owner and parameters from pg_database */
2494 if (fout->remoteVersion >= 90300)
2495 {
2496 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
2497 "(%s datdba) AS dba, "
2498 "pg_encoding_to_char(encoding) AS encoding, "
2499 "datcollate, datctype, datfrozenxid, datminmxid, "
2500 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
2501 "shobj_description(oid, 'pg_database') AS description "
2502
2503 "FROM pg_database "
2504 "WHERE datname = ",
2505 username_subquery);
2506 appendStringLiteralAH(dbQry, datname, fout);
2507 }
2508 else if (fout->remoteVersion >= 80400)
2509 {
2510 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
2511 "(%s datdba) AS dba, "
2512 "pg_encoding_to_char(encoding) AS encoding, "
2513 "datcollate, datctype, datfrozenxid, 0 AS datminmxid, "
2514 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
2515 "shobj_description(oid, 'pg_database') AS description "
2516
2517 "FROM pg_database "
2518 "WHERE datname = ",
2519 username_subquery);
2520 appendStringLiteralAH(dbQry, datname, fout);
2521 }
2522 else if (fout->remoteVersion >= 80200)
2523 {
2524 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
2525 "(%s datdba) AS dba, "
2526 "pg_encoding_to_char(encoding) AS encoding, "
2527 "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
2528 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
2529 "shobj_description(oid, 'pg_database') AS description "
2530
2531 "FROM pg_database "
2532 "WHERE datname = ",
2533 username_subquery);
2534 appendStringLiteralAH(dbQry, datname, fout);
2535 }
2536 else
2537 {
2538 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
2539 "(%s datdba) AS dba, "
2540 "pg_encoding_to_char(encoding) AS encoding, "
2541 "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
2542 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace "
2543 "FROM pg_database "
2544 "WHERE datname = ",
2545 username_subquery);
2546 appendStringLiteralAH(dbQry, datname, fout);
2547 }
2548
2549 res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
2550
2551 i_tableoid = PQfnumber(res, "tableoid");
2552 i_oid = PQfnumber(res, "oid");
2553 i_dba = PQfnumber(res, "dba");
2554 i_encoding = PQfnumber(res, "encoding");
2555 i_collate = PQfnumber(res, "datcollate");
2556 i_ctype = PQfnumber(res, "datctype");
2557 i_frozenxid = PQfnumber(res, "datfrozenxid");
2558 i_minmxid = PQfnumber(res, "datminmxid");
2559 i_tablespace = PQfnumber(res, "tablespace");
2560
2561 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
2562 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
2563 dba = PQgetvalue(res, 0, i_dba);
2564 encoding = PQgetvalue(res, 0, i_encoding);
2565 collate = PQgetvalue(res, 0, i_collate);
2566 ctype = PQgetvalue(res, 0, i_ctype);
2567 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
2568 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
2569 tablespace = PQgetvalue(res, 0, i_tablespace);
2570
2571 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
2572 qdatname);
2573 if (strlen(encoding) > 0)
2574 {
2575 appendPQExpBufferStr(creaQry, " ENCODING = ");
2576 appendStringLiteralAH(creaQry, encoding, fout);
2577 }
2578 if (strlen(collate) > 0)
2579 {
2580 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
2581 appendStringLiteralAH(creaQry, collate, fout);
2582 }
2583 if (strlen(ctype) > 0)
2584 {
2585 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
2586 appendStringLiteralAH(creaQry, ctype, fout);
2587 }
2588 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
2589 !dopt->outputNoTablespaces)
2590 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
2591 fmtId(tablespace));
2592 appendPQExpBufferStr(creaQry, ";\n");
2593
2594 if (dopt->binary_upgrade)
2595 {
2596 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
2597 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
2598 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
2599 "WHERE datname = ",
2600 frozenxid, minmxid);
2601 appendStringLiteralAH(creaQry, datname, fout);
2602 appendPQExpBufferStr(creaQry, ";\n");
2603 }
2604
2605 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
2606 qdatname);
2607
2608 dbDumpId = createDumpId();
2609
2610 ArchiveEntry(fout,
2611 dbCatId, /* catalog ID */
2612 dbDumpId, /* dump ID */
2613 datname, /* Name */
2614 NULL, /* Namespace */
2615 NULL, /* Tablespace */
2616 dba, /* Owner */
2617 false, /* with oids */
2618 "DATABASE", /* Desc */
2619 SECTION_PRE_DATA, /* Section */
2620 creaQry->data, /* Create */
2621 delQry->data, /* Del */
2622 NULL, /* Copy */
2623 NULL, /* Deps */
2624 0, /* # Deps */
2625 NULL, /* Dumper */
2626 NULL); /* Dumper Arg */
2627
2628 /*
2629 * pg_largeobject and pg_largeobject_metadata come from the old system
2630 * intact, so set their relfrozenxids and relminmxids.
2631 */
2632 if (dopt->binary_upgrade)
2633 {
2634 PGresult *lo_res;
2635 PQExpBuffer loFrozenQry = createPQExpBuffer();
2636 PQExpBuffer loOutQry = createPQExpBuffer();
2637 int i_relfrozenxid,
2638 i_relminmxid;
2639
2640 /*
2641 * pg_largeobject
2642 */
2643 if (fout->remoteVersion >= 90300)
2644 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid\n"
2645 "FROM pg_catalog.pg_class\n"
2646 "WHERE oid = %u;\n",
2647 LargeObjectRelationId);
2648 else
2649 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid\n"
2650 "FROM pg_catalog.pg_class\n"
2651 "WHERE oid = %u;\n",
2652 LargeObjectRelationId);
2653
2654 lo_res = ExecuteSqlQueryForSingleRow(fout, loFrozenQry->data);
2655
2656 i_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
2657 i_relminmxid = PQfnumber(lo_res, "relminmxid");
2658
2659 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
2660 appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n"
2661 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
2662 "WHERE oid = %u;\n",
2663 atoi(PQgetvalue(lo_res, 0, i_relfrozenxid)),
2664 atoi(PQgetvalue(lo_res, 0, i_relminmxid)),
2665 LargeObjectRelationId);
2666 ArchiveEntry(fout, nilCatalogId, createDumpId(),
2667 "pg_largeobject", NULL, NULL, "",
2668 false, "pg_largeobject", SECTION_PRE_DATA,
2669 loOutQry->data, "", NULL,
2670 NULL, 0,
2671 NULL, NULL);
2672
2673 PQclear(lo_res);
2674
2675 /*
2676 * pg_largeobject_metadata
2677 */
2678 if (fout->remoteVersion >= 90000)
2679 {
2680 resetPQExpBuffer(loFrozenQry);
2681 resetPQExpBuffer(loOutQry);
2682
2683 if (fout->remoteVersion >= 90300)
2684 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid\n"
2685 "FROM pg_catalog.pg_class\n"
2686 "WHERE oid = %u;\n",
2687 LargeObjectMetadataRelationId);
2688 else
2689 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid\n"
2690 "FROM pg_catalog.pg_class\n"
2691 "WHERE oid = %u;\n",
2692 LargeObjectMetadataRelationId);
2693
2694 lo_res = ExecuteSqlQueryForSingleRow(fout, loFrozenQry->data);
2695
2696 i_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
2697 i_relminmxid = PQfnumber(lo_res, "relminmxid");
2698
2699 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
2700 appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n"
2701 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
2702 "WHERE oid = %u;\n",
2703 atoi(PQgetvalue(lo_res, 0, i_relfrozenxid)),
2704 atoi(PQgetvalue(lo_res, 0, i_relminmxid)),
2705 LargeObjectMetadataRelationId);
2706 ArchiveEntry(fout, nilCatalogId, createDumpId(),
2707 "pg_largeobject_metadata", NULL, NULL, "",
2708 false, "pg_largeobject_metadata", SECTION_PRE_DATA,
2709 loOutQry->data, "", NULL,
2710 NULL, 0,
2711 NULL, NULL);
2712
2713 PQclear(lo_res);
2714 }
2715
2716 destroyPQExpBuffer(loFrozenQry);
2717 destroyPQExpBuffer(loOutQry);
2718 }
2719
2720 /* Compute correct tag for archive entry */
2721 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
2722
2723 /* Dump DB comment if any */
2724 if (fout->remoteVersion >= 80200)
2725 {
2726 /*
2727 * 8.2 and up keep comments on shared objects in a shared table, so we
2728 * cannot use the dumpComment() code used for other database objects.
2729 * Be careful that the ArchiveEntry parameters match that function.
2730 */
2731 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
2732
2733 if (comment && *comment)
2734 {
2735 resetPQExpBuffer(dbQry);
2736
2737 /*
2738 * Generates warning when loaded into a differently-named
2739 * database.
2740 */
2741 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
2742 appendStringLiteralAH(dbQry, comment, fout);
2743 appendPQExpBufferStr(dbQry, ";\n");
2744
2745 ArchiveEntry(fout, nilCatalogId, createDumpId(),
2746 labelq->data, NULL, NULL, dba,
2747 false, "COMMENT", SECTION_NONE,
2748 dbQry->data, "", NULL,
2749 &(dbDumpId), 1,
2750 NULL, NULL);
2751 }
2752 }
2753 else
2754 {
2755 dumpComment(fout, "DATABASE", qdatname, NULL, dba,
2756 dbCatId, 0, dbDumpId);
2757 }
2758
2759 /* Dump shared security label. */
2760 if (!dopt->no_security_labels && fout->remoteVersion >= 90200)
2761 {
2762 PGresult *shres;
2763 PQExpBuffer seclabelQry;
2764
2765 seclabelQry = createPQExpBuffer();
2766
2767 buildShSecLabelQuery(conn, "pg_database", dbCatId.oid, seclabelQry);
2768 shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
2769 resetPQExpBuffer(seclabelQry);
2770 emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
2771 if (seclabelQry->len > 0)
2772 ArchiveEntry(fout, nilCatalogId, createDumpId(),
2773 labelq->data, NULL, NULL, dba,
2774 false, "SECURITY LABEL", SECTION_NONE,
2775 seclabelQry->data, "", NULL,
2776 &(dbDumpId), 1,
2777 NULL, NULL);
2778 destroyPQExpBuffer(seclabelQry);
2779 PQclear(shres);
2780 }
2781
2782 PQclear(res);
2783
2784 free(qdatname);
2785 destroyPQExpBuffer(dbQry);
2786 destroyPQExpBuffer(delQry);
2787 destroyPQExpBuffer(creaQry);
2788 destroyPQExpBuffer(labelq);
2789 }
2790
2791 /*
2792 * dumpEncoding: put the correct encoding into the archive
2793 */
2794 static void
dumpEncoding(Archive * AH)2795 dumpEncoding(Archive *AH)
2796 {
2797 const char *encname = pg_encoding_to_char(AH->encoding);
2798 PQExpBuffer qry = createPQExpBuffer();
2799
2800 if (g_verbose)
2801 write_msg(NULL, "saving encoding = %s\n", encname);
2802
2803 appendPQExpBufferStr(qry, "SET client_encoding = ");
2804 appendStringLiteralAH(qry, encname, AH);
2805 appendPQExpBufferStr(qry, ";\n");
2806
2807 ArchiveEntry(AH, nilCatalogId, createDumpId(),
2808 "ENCODING", NULL, NULL, "",
2809 false, "ENCODING", SECTION_PRE_DATA,
2810 qry->data, "", NULL,
2811 NULL, 0,
2812 NULL, NULL);
2813
2814 destroyPQExpBuffer(qry);
2815 }
2816
2817
2818 /*
2819 * dumpStdStrings: put the correct escape string behavior into the archive
2820 */
2821 static void
dumpStdStrings(Archive * AH)2822 dumpStdStrings(Archive *AH)
2823 {
2824 const char *stdstrings = AH->std_strings ? "on" : "off";
2825 PQExpBuffer qry = createPQExpBuffer();
2826
2827 if (g_verbose)
2828 write_msg(NULL, "saving standard_conforming_strings = %s\n",
2829 stdstrings);
2830
2831 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
2832 stdstrings);
2833
2834 ArchiveEntry(AH, nilCatalogId, createDumpId(),
2835 "STDSTRINGS", NULL, NULL, "",
2836 false, "STDSTRINGS", SECTION_PRE_DATA,
2837 qry->data, "", NULL,
2838 NULL, 0,
2839 NULL, NULL);
2840
2841 destroyPQExpBuffer(qry);
2842 }
2843
2844 /*
2845 * dumpSearchPath: record the active search_path in the archive
2846 */
2847 static void
dumpSearchPath(Archive * AH)2848 dumpSearchPath(Archive *AH)
2849 {
2850 PQExpBuffer qry = createPQExpBuffer();
2851 PQExpBuffer path = createPQExpBuffer();
2852 PGresult *res;
2853 char **schemanames = NULL;
2854 int nschemanames = 0;
2855 int i;
2856
2857 /*
2858 * We use the result of current_schemas(), not the search_path GUC,
2859 * because that might contain wildcards such as "$user", which won't
2860 * necessarily have the same value during restore. Also, this way avoids
2861 * listing schemas that may appear in search_path but not actually exist,
2862 * which seems like a prudent exclusion.
2863 */
2864 res = ExecuteSqlQueryForSingleRow(AH,
2865 "SELECT pg_catalog.current_schemas(false)");
2866
2867 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
2868 exit_horribly(NULL, "could not parse result of current_schemas()\n");
2869
2870 /*
2871 * We use set_config(), not a simple "SET search_path" command, because
2872 * the latter has less-clean behavior if the search path is empty. While
2873 * that's likely to get fixed at some point, it seems like a good idea to
2874 * be as backwards-compatible as possible in what we put into archives.
2875 */
2876 for (i = 0; i < nschemanames; i++)
2877 {
2878 if (i > 0)
2879 appendPQExpBufferStr(path, ", ");
2880 appendPQExpBufferStr(path, fmtId(schemanames[i]));
2881 }
2882
2883 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
2884 appendStringLiteralAH(qry, path->data, AH);
2885 appendPQExpBufferStr(qry, ", false);\n");
2886
2887 if (g_verbose)
2888 write_msg(NULL, "saving search_path = %s\n", path->data);
2889
2890 ArchiveEntry(AH, nilCatalogId, createDumpId(),
2891 "SEARCHPATH", NULL, NULL, "",
2892 false, "SEARCHPATH", SECTION_PRE_DATA,
2893 qry->data, "", NULL,
2894 NULL, 0,
2895 NULL, NULL);
2896
2897 /* Also save it in AH->searchpath, in case we're doing plain text dump */
2898 AH->searchpath = pg_strdup(qry->data);
2899
2900 if (schemanames)
2901 free(schemanames);
2902 PQclear(res);
2903 destroyPQExpBuffer(qry);
2904 destroyPQExpBuffer(path);
2905 }
2906
2907
2908 /*
2909 * getBlobs:
2910 * Collect schema-level data about large objects
2911 */
2912 static void
getBlobs(Archive * fout)2913 getBlobs(Archive *fout)
2914 {
2915 DumpOptions *dopt = fout->dopt;
2916 PQExpBuffer blobQry = createPQExpBuffer();
2917 BlobInfo *binfo;
2918 DumpableObject *bdata;
2919 PGresult *res;
2920 int ntups;
2921 int i;
2922 int i_oid;
2923 int i_lomowner;
2924 int i_lomacl;
2925 int i_rlomacl;
2926 int i_initlomacl;
2927 int i_initrlomacl;
2928
2929 /* Verbose message */
2930 if (g_verbose)
2931 write_msg(NULL, "reading large objects\n");
2932
2933 /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */
2934 if (fout->remoteVersion >= 90600)
2935 {
2936 PQExpBuffer acl_subquery = createPQExpBuffer();
2937 PQExpBuffer racl_subquery = createPQExpBuffer();
2938 PQExpBuffer init_acl_subquery = createPQExpBuffer();
2939 PQExpBuffer init_racl_subquery = createPQExpBuffer();
2940
2941 buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
2942 init_racl_subquery, "l.lomacl", "l.lomowner", "'L'",
2943 dopt->binary_upgrade);
2944
2945 appendPQExpBuffer(blobQry,
2946 "SELECT l.oid, (%s l.lomowner) AS rolname, "
2947 "%s AS lomacl, "
2948 "%s AS rlomacl, "
2949 "%s AS initlomacl, "
2950 "%s AS initrlomacl "
2951 "FROM pg_largeobject_metadata l "
2952 "LEFT JOIN pg_init_privs pip ON "
2953 "(l.oid = pip.objoid "
2954 "AND pip.classoid = 'pg_largeobject'::regclass "
2955 "AND pip.objsubid = 0) ",
2956 username_subquery,
2957 acl_subquery->data,
2958 racl_subquery->data,
2959 init_acl_subquery->data,
2960 init_racl_subquery->data);
2961
2962 destroyPQExpBuffer(acl_subquery);
2963 destroyPQExpBuffer(racl_subquery);
2964 destroyPQExpBuffer(init_acl_subquery);
2965 destroyPQExpBuffer(init_racl_subquery);
2966 }
2967 else if (fout->remoteVersion >= 90000)
2968 appendPQExpBuffer(blobQry,
2969 "SELECT oid, (%s lomowner) AS rolname, lomacl, "
2970 "NULL AS rlomacl, NULL AS initlomacl, "
2971 "NULL AS initrlomacl "
2972 " FROM pg_largeobject_metadata",
2973 username_subquery);
2974 else
2975 appendPQExpBufferStr(blobQry,
2976 "SELECT DISTINCT loid AS oid, "
2977 "NULL::name AS rolname, NULL::oid AS lomacl, "
2978 "NULL::oid AS rlomacl, NULL::oid AS initlomacl, "
2979 "NULL::oid AS initrlomacl "
2980 " FROM pg_largeobject");
2981
2982 res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK);
2983
2984 i_oid = PQfnumber(res, "oid");
2985 i_lomowner = PQfnumber(res, "rolname");
2986 i_lomacl = PQfnumber(res, "lomacl");
2987 i_rlomacl = PQfnumber(res, "rlomacl");
2988 i_initlomacl = PQfnumber(res, "initlomacl");
2989 i_initrlomacl = PQfnumber(res, "initrlomacl");
2990
2991 ntups = PQntuples(res);
2992
2993 /*
2994 * Each large object has its own BLOB archive entry.
2995 */
2996 binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo));
2997
2998 for (i = 0; i < ntups; i++)
2999 {
3000 binfo[i].dobj.objType = DO_BLOB;
3001 binfo[i].dobj.catId.tableoid = LargeObjectRelationId;
3002 binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
3003 AssignDumpId(&binfo[i].dobj);
3004
3005 binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid));
3006 binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner));
3007 binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl));
3008 binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl));
3009 binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl));
3010 binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl));
3011
3012 if (PQgetisnull(res, i, i_lomacl) &&
3013 PQgetisnull(res, i, i_rlomacl) &&
3014 PQgetisnull(res, i, i_initlomacl) &&
3015 PQgetisnull(res, i, i_initrlomacl))
3016 binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
3017
3018 /*
3019 * In binary-upgrade mode for blobs, we do *not* dump out the data or
3020 * the ACLs, should any exist. The data and ACL (if any) will be
3021 * copied by pg_upgrade, which simply copies the pg_largeobject and
3022 * pg_largeobject_metadata tables.
3023 *
3024 * We *do* dump out the definition of the blob because we need that to
3025 * make the restoration of the comments, and anything else, work since
3026 * pg_upgrade copies the files behind pg_largeobject and
3027 * pg_largeobject_metadata after the dump is restored.
3028 */
3029 if (dopt->binary_upgrade)
3030 binfo[i].dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL);
3031 }
3032
3033 /*
3034 * If we have any large objects, a "BLOBS" archive entry is needed. This
3035 * is just a placeholder for sorting; it carries no data now.
3036 */
3037 if (ntups > 0)
3038 {
3039 bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3040 bdata->objType = DO_BLOB_DATA;
3041 bdata->catId = nilCatalogId;
3042 AssignDumpId(bdata);
3043 bdata->name = pg_strdup("BLOBS");
3044 }
3045
3046 PQclear(res);
3047 destroyPQExpBuffer(blobQry);
3048 }
3049
3050 /*
3051 * dumpBlob
3052 *
3053 * dump the definition (metadata) of the given large object
3054 */
3055 static void
dumpBlob(Archive * fout,BlobInfo * binfo)3056 dumpBlob(Archive *fout, BlobInfo *binfo)
3057 {
3058 PQExpBuffer cquery = createPQExpBuffer();
3059 PQExpBuffer dquery = createPQExpBuffer();
3060
3061 appendPQExpBuffer(cquery,
3062 "SELECT pg_catalog.lo_create('%s');\n",
3063 binfo->dobj.name);
3064
3065 appendPQExpBuffer(dquery,
3066 "SELECT pg_catalog.lo_unlink('%s');\n",
3067 binfo->dobj.name);
3068
3069 if (binfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3070 ArchiveEntry(fout, binfo->dobj.catId, binfo->dobj.dumpId,
3071 binfo->dobj.name,
3072 NULL, NULL,
3073 binfo->rolname, false,
3074 "BLOB", SECTION_PRE_DATA,
3075 cquery->data, dquery->data, NULL,
3076 NULL, 0,
3077 NULL, NULL);
3078
3079 /* Dump comment if any */
3080 if (binfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3081 dumpComment(fout, "LARGE OBJECT", binfo->dobj.name,
3082 NULL, binfo->rolname,
3083 binfo->dobj.catId, 0, binfo->dobj.dumpId);
3084
3085 /* Dump security label if any */
3086 if (binfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3087 dumpSecLabel(fout, "LARGE OBJECT", binfo->dobj.name,
3088 NULL, binfo->rolname,
3089 binfo->dobj.catId, 0, binfo->dobj.dumpId);
3090
3091 /* Dump ACL if any */
3092 if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL))
3093 dumpACL(fout, binfo->dobj.dumpId, InvalidDumpId, "LARGE OBJECT",
3094 binfo->dobj.name, NULL,
3095 NULL, binfo->rolname, binfo->blobacl, binfo->rblobacl,
3096 binfo->initblobacl, binfo->initrblobacl);
3097
3098 destroyPQExpBuffer(cquery);
3099 destroyPQExpBuffer(dquery);
3100 }
3101
3102 /*
3103 * dumpBlobs:
3104 * dump the data contents of all large objects
3105 */
3106 static int
dumpBlobs(Archive * fout,void * arg)3107 dumpBlobs(Archive *fout, void *arg)
3108 {
3109 const char *blobQry;
3110 const char *blobFetchQry;
3111 PGconn *conn = GetConnection(fout);
3112 PGresult *res;
3113 char buf[LOBBUFSIZE];
3114 int ntups;
3115 int i;
3116 int cnt;
3117
3118 if (g_verbose)
3119 write_msg(NULL, "saving large objects\n");
3120
3121 /*
3122 * Currently, we re-fetch all BLOB OIDs using a cursor. Consider scanning
3123 * the already-in-memory dumpable objects instead...
3124 */
3125 if (fout->remoteVersion >= 90000)
3126 blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_largeobject_metadata";
3127 else
3128 blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject";
3129
3130 ExecuteSqlStatement(fout, blobQry);
3131
3132 /* Command to fetch from cursor */
3133 blobFetchQry = "FETCH 1000 IN bloboid";
3134
3135 do
3136 {
3137 /* Do a fetch */
3138 res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK);
3139
3140 /* Process the tuples, if any */
3141 ntups = PQntuples(res);
3142 for (i = 0; i < ntups; i++)
3143 {
3144 Oid blobOid;
3145 int loFd;
3146
3147 blobOid = atooid(PQgetvalue(res, i, 0));
3148 /* Open the BLOB */
3149 loFd = lo_open(conn, blobOid, INV_READ);
3150 if (loFd == -1)
3151 exit_horribly(NULL, "could not open large object %u: %s",
3152 blobOid, PQerrorMessage(conn));
3153
3154 StartBlob(fout, blobOid);
3155
3156 /* Now read it in chunks, sending data to archive */
3157 do
3158 {
3159 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
3160 if (cnt < 0)
3161 exit_horribly(NULL, "error reading large object %u: %s",
3162 blobOid, PQerrorMessage(conn));
3163
3164 WriteData(fout, buf, cnt);
3165 } while (cnt > 0);
3166
3167 lo_close(conn, loFd);
3168
3169 EndBlob(fout, blobOid);
3170 }
3171
3172 PQclear(res);
3173 } while (ntups > 0);
3174
3175 return 1;
3176 }
3177
3178 /*
3179 * getPolicies
3180 * get information about all RLS policies on dumpable tables.
3181 */
3182 void
getPolicies(Archive * fout,TableInfo tblinfo[],int numTables)3183 getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
3184 {
3185 PQExpBuffer query;
3186 PGresult *res;
3187 PolicyInfo *polinfo;
3188 int i_oid;
3189 int i_tableoid;
3190 int i_polrelid;
3191 int i_polname;
3192 int i_polcmd;
3193 int i_polpermissive;
3194 int i_polroles;
3195 int i_polqual;
3196 int i_polwithcheck;
3197 int i,
3198 j,
3199 ntups;
3200
3201 if (fout->remoteVersion < 90500)
3202 return;
3203
3204 query = createPQExpBuffer();
3205
3206 /*
3207 * First, check which tables have RLS enabled. We represent RLS being
3208 * enabled on a table by creating a PolicyInfo object with null polname.
3209 */
3210 for (i = 0; i < numTables; i++)
3211 {
3212 TableInfo *tbinfo = &tblinfo[i];
3213
3214 /* Ignore row security on tables not to be dumped */
3215 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
3216 continue;
3217
3218 if (tbinfo->rowsec)
3219 {
3220 /*
3221 * Note: use tableoid 0 so that this object won't be mistaken for
3222 * something that pg_depend entries apply to.
3223 */
3224 polinfo = pg_malloc(sizeof(PolicyInfo));
3225 polinfo->dobj.objType = DO_POLICY;
3226 polinfo->dobj.catId.tableoid = 0;
3227 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3228 AssignDumpId(&polinfo->dobj);
3229 polinfo->dobj.namespace = tbinfo->dobj.namespace;
3230 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
3231 polinfo->poltable = tbinfo;
3232 polinfo->polname = NULL;
3233 polinfo->polcmd = '\0';
3234 polinfo->polpermissive = 0;
3235 polinfo->polroles = NULL;
3236 polinfo->polqual = NULL;
3237 polinfo->polwithcheck = NULL;
3238 }
3239 }
3240
3241 /*
3242 * Now, read all RLS policies, and create PolicyInfo objects for all those
3243 * that are of interest.
3244 */
3245 if (g_verbose)
3246 write_msg(NULL, "reading row-level security policies\n");
3247
3248 printfPQExpBuffer(query,
3249 "SELECT oid, tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
3250 if (fout->remoteVersion >= 100000)
3251 appendPQExpBuffer(query, "pol.polpermissive, ");
3252 else
3253 appendPQExpBuffer(query, "'t' as polpermissive, ");
3254 appendPQExpBuffer(query,
3255 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
3256 " 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, "
3257 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
3258 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
3259 "FROM pg_catalog.pg_policy pol");
3260
3261 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3262
3263 ntups = PQntuples(res);
3264 if (ntups > 0)
3265 {
3266 i_oid = PQfnumber(res, "oid");
3267 i_tableoid = PQfnumber(res, "tableoid");
3268 i_polrelid = PQfnumber(res, "polrelid");
3269 i_polname = PQfnumber(res, "polname");
3270 i_polcmd = PQfnumber(res, "polcmd");
3271 i_polpermissive = PQfnumber(res, "polpermissive");
3272 i_polroles = PQfnumber(res, "polroles");
3273 i_polqual = PQfnumber(res, "polqual");
3274 i_polwithcheck = PQfnumber(res, "polwithcheck");
3275
3276 polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
3277
3278 for (j = 0; j < ntups; j++)
3279 {
3280 Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
3281 TableInfo *tbinfo = findTableByOid(polrelid);
3282
3283 /*
3284 * Ignore row security on tables not to be dumped. (This will
3285 * result in some harmless wasted slots in polinfo[].)
3286 */
3287 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
3288 continue;
3289
3290 polinfo[j].dobj.objType = DO_POLICY;
3291 polinfo[j].dobj.catId.tableoid =
3292 atooid(PQgetvalue(res, j, i_tableoid));
3293 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
3294 AssignDumpId(&polinfo[j].dobj);
3295 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
3296 polinfo[j].poltable = tbinfo;
3297 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
3298 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
3299
3300 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
3301 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
3302
3303 if (PQgetisnull(res, j, i_polroles))
3304 polinfo[j].polroles = NULL;
3305 else
3306 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
3307
3308 if (PQgetisnull(res, j, i_polqual))
3309 polinfo[j].polqual = NULL;
3310 else
3311 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
3312
3313 if (PQgetisnull(res, j, i_polwithcheck))
3314 polinfo[j].polwithcheck = NULL;
3315 else
3316 polinfo[j].polwithcheck
3317 = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
3318 }
3319 }
3320
3321 PQclear(res);
3322
3323 destroyPQExpBuffer(query);
3324 }
3325
3326 /*
3327 * dumpPolicy
3328 * dump the definition of the given policy
3329 */
3330 static void
dumpPolicy(Archive * fout,PolicyInfo * polinfo)3331 dumpPolicy(Archive *fout, PolicyInfo *polinfo)
3332 {
3333 DumpOptions *dopt = fout->dopt;
3334 TableInfo *tbinfo = polinfo->poltable;
3335 PQExpBuffer query;
3336 PQExpBuffer delqry;
3337 PQExpBuffer polprefix;
3338 char *qtabname;
3339 const char *cmd;
3340 char *tag;
3341
3342 if (dopt->dataOnly)
3343 return;
3344
3345 /*
3346 * If polname is NULL, then this record is just indicating that ROW LEVEL
3347 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
3348 * ROW LEVEL SECURITY.
3349 */
3350 if (polinfo->polname == NULL)
3351 {
3352 query = createPQExpBuffer();
3353
3354 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
3355 fmtQualifiedDumpable(tbinfo));
3356
3357 /*
3358 * We must emit the ROW SECURITY object's dependency on its table
3359 * explicitly, because it will not match anything in pg_depend (unlike
3360 * the case for other PolicyInfo objects).
3361 */
3362 if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY)
3363 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
3364 polinfo->dobj.name,
3365 polinfo->dobj.namespace->dobj.name,
3366 NULL,
3367 tbinfo->rolname, false,
3368 "ROW SECURITY", SECTION_POST_DATA,
3369 query->data, "", NULL,
3370 &(tbinfo->dobj.dumpId), 1,
3371 NULL, NULL);
3372
3373 destroyPQExpBuffer(query);
3374 return;
3375 }
3376
3377 if (polinfo->polcmd == '*')
3378 cmd = "";
3379 else if (polinfo->polcmd == 'r')
3380 cmd = " FOR SELECT";
3381 else if (polinfo->polcmd == 'a')
3382 cmd = " FOR INSERT";
3383 else if (polinfo->polcmd == 'w')
3384 cmd = " FOR UPDATE";
3385 else if (polinfo->polcmd == 'd')
3386 cmd = " FOR DELETE";
3387 else
3388 {
3389 write_msg(NULL, "unexpected policy command type: %c\n",
3390 polinfo->polcmd);
3391 exit_nicely(1);
3392 }
3393
3394 query = createPQExpBuffer();
3395 delqry = createPQExpBuffer();
3396 polprefix = createPQExpBuffer();
3397
3398 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
3399
3400 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
3401
3402 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
3403 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
3404
3405 if (polinfo->polroles != NULL)
3406 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
3407
3408 if (polinfo->polqual != NULL)
3409 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
3410
3411 if (polinfo->polwithcheck != NULL)
3412 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
3413
3414 appendPQExpBuffer(query, ";\n");
3415
3416 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
3417 appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
3418
3419 appendPQExpBuffer(polprefix, "POLICY %s ON",
3420 fmtId(polinfo->polname));
3421
3422 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
3423
3424 if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY)
3425 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
3426 tag,
3427 polinfo->dobj.namespace->dobj.name,
3428 NULL,
3429 tbinfo->rolname, false,
3430 "POLICY", SECTION_POST_DATA,
3431 query->data, delqry->data, NULL,
3432 NULL, 0,
3433 NULL, NULL);
3434
3435 if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3436 dumpComment(fout, polprefix->data, qtabname,
3437 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
3438 polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
3439
3440 free(tag);
3441 destroyPQExpBuffer(query);
3442 destroyPQExpBuffer(delqry);
3443 destroyPQExpBuffer(polprefix);
3444 free(qtabname);
3445 }
3446
3447 /*
3448 * getPublications
3449 * get information about publications
3450 */
3451 PublicationInfo *
getPublications(Archive * fout,int * numPublications)3452 getPublications(Archive *fout, int *numPublications)
3453 {
3454 DumpOptions *dopt = fout->dopt;
3455 PQExpBuffer query;
3456 PGresult *res;
3457 PublicationInfo *pubinfo;
3458 int i_tableoid;
3459 int i_oid;
3460 int i_pubname;
3461 int i_rolname;
3462 int i_puballtables;
3463 int i_pubinsert;
3464 int i_pubupdate;
3465 int i_pubdelete;
3466 int i,
3467 ntups;
3468
3469 if (dopt->no_publications || fout->remoteVersion < 100000)
3470 {
3471 *numPublications = 0;
3472 return NULL;
3473 }
3474
3475 query = createPQExpBuffer();
3476
3477 resetPQExpBuffer(query);
3478
3479 /* Get the publications. */
3480 appendPQExpBuffer(query,
3481 "SELECT p.tableoid, p.oid, p.pubname, "
3482 "(%s p.pubowner) AS rolname, "
3483 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete "
3484 "FROM pg_publication p",
3485 username_subquery);
3486
3487 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3488
3489 ntups = PQntuples(res);
3490
3491 i_tableoid = PQfnumber(res, "tableoid");
3492 i_oid = PQfnumber(res, "oid");
3493 i_pubname = PQfnumber(res, "pubname");
3494 i_rolname = PQfnumber(res, "rolname");
3495 i_puballtables = PQfnumber(res, "puballtables");
3496 i_pubinsert = PQfnumber(res, "pubinsert");
3497 i_pubupdate = PQfnumber(res, "pubupdate");
3498 i_pubdelete = PQfnumber(res, "pubdelete");
3499
3500 pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
3501
3502 for (i = 0; i < ntups; i++)
3503 {
3504 pubinfo[i].dobj.objType = DO_PUBLICATION;
3505 pubinfo[i].dobj.catId.tableoid =
3506 atooid(PQgetvalue(res, i, i_tableoid));
3507 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
3508 AssignDumpId(&pubinfo[i].dobj);
3509 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
3510 pubinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
3511 pubinfo[i].puballtables =
3512 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
3513 pubinfo[i].pubinsert =
3514 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
3515 pubinfo[i].pubupdate =
3516 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
3517 pubinfo[i].pubdelete =
3518 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
3519
3520 if (strlen(pubinfo[i].rolname) == 0)
3521 write_msg(NULL, "WARNING: owner of publication \"%s\" appears to be invalid\n",
3522 pubinfo[i].dobj.name);
3523
3524 /* Decide whether we want to dump it */
3525 selectDumpableObject(&(pubinfo[i].dobj), fout);
3526 }
3527 PQclear(res);
3528
3529 destroyPQExpBuffer(query);
3530
3531 *numPublications = ntups;
3532 return pubinfo;
3533 }
3534
3535 /*
3536 * dumpPublication
3537 * dump the definition of the given publication
3538 */
3539 static void
dumpPublication(Archive * fout,PublicationInfo * pubinfo)3540 dumpPublication(Archive *fout, PublicationInfo *pubinfo)
3541 {
3542 PQExpBuffer delq;
3543 PQExpBuffer query;
3544 char *qpubname;
3545 bool first = true;
3546
3547 if (!(pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
3548 return;
3549
3550 delq = createPQExpBuffer();
3551 query = createPQExpBuffer();
3552
3553 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
3554
3555 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
3556 qpubname);
3557
3558 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
3559 qpubname);
3560
3561 if (pubinfo->puballtables)
3562 appendPQExpBufferStr(query, " FOR ALL TABLES");
3563
3564 appendPQExpBufferStr(query, " WITH (publish = '");
3565 if (pubinfo->pubinsert)
3566 {
3567 appendPQExpBufferStr(query, "insert");
3568 first = false;
3569 }
3570
3571 if (pubinfo->pubupdate)
3572 {
3573 if (!first)
3574 appendPQExpBufferStr(query, ", ");
3575
3576 appendPQExpBufferStr(query, "update");
3577 first = false;
3578 }
3579
3580 if (pubinfo->pubdelete)
3581 {
3582 if (!first)
3583 appendPQExpBufferStr(query, ", ");
3584
3585 appendPQExpBufferStr(query, "delete");
3586 first = false;
3587 }
3588
3589 appendPQExpBufferStr(query, "');\n");
3590
3591 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
3592 pubinfo->dobj.name,
3593 NULL,
3594 NULL,
3595 pubinfo->rolname, false,
3596 "PUBLICATION", SECTION_POST_DATA,
3597 query->data, delq->data, NULL,
3598 NULL, 0,
3599 NULL, NULL);
3600
3601 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3602 dumpComment(fout, "PUBLICATION", qpubname,
3603 NULL, pubinfo->rolname,
3604 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
3605
3606 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3607 dumpSecLabel(fout, "PUBLICATION", qpubname,
3608 NULL, pubinfo->rolname,
3609 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
3610
3611 destroyPQExpBuffer(delq);
3612 destroyPQExpBuffer(query);
3613 free(qpubname);
3614 }
3615
3616 /*
3617 * getPublicationTables
3618 * get information about publication membership for dumpable tables.
3619 */
3620 void
getPublicationTables(Archive * fout,TableInfo tblinfo[],int numTables)3621 getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
3622 {
3623 PQExpBuffer query;
3624 PGresult *res;
3625 PublicationRelInfo *pubrinfo;
3626 DumpOptions *dopt = fout->dopt;
3627 int i_tableoid;
3628 int i_oid;
3629 int i_prpubid;
3630 int i_prrelid;
3631 int i,
3632 j,
3633 ntups;
3634
3635 if (dopt->no_publications || fout->remoteVersion < 100000)
3636 return;
3637
3638 query = createPQExpBuffer();
3639
3640 /* Collect all publication membership info. */
3641 appendPQExpBufferStr(query,
3642 "SELECT tableoid, oid, prpubid, prrelid "
3643 "FROM pg_catalog.pg_publication_rel");
3644 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3645
3646 ntups = PQntuples(res);
3647
3648 i_tableoid = PQfnumber(res, "tableoid");
3649 i_oid = PQfnumber(res, "oid");
3650 i_prpubid = PQfnumber(res, "prpubid");
3651 i_prrelid = PQfnumber(res, "prrelid");
3652
3653 /* this allocation may be more than we need */
3654 pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
3655 j = 0;
3656
3657 for (i = 0; i < ntups; i++)
3658 {
3659 Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
3660 Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
3661 PublicationInfo *pubinfo;
3662 TableInfo *tbinfo;
3663
3664 /*
3665 * Ignore any entries for which we aren't interested in either the
3666 * publication or the rel.
3667 */
3668 pubinfo = findPublicationByOid(prpubid);
3669 if (pubinfo == NULL)
3670 continue;
3671 tbinfo = findTableByOid(prrelid);
3672 if (tbinfo == NULL)
3673 continue;
3674
3675 /*
3676 * Ignore publication membership of tables whose definitions are not
3677 * to be dumped.
3678 */
3679 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
3680 continue;
3681
3682 /* OK, make a DumpableObject for this relationship */
3683 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
3684 pubrinfo[j].dobj.catId.tableoid =
3685 atooid(PQgetvalue(res, i, i_tableoid));
3686 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
3687 AssignDumpId(&pubrinfo[j].dobj);
3688 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
3689 pubrinfo[j].dobj.name = tbinfo->dobj.name;
3690 pubrinfo[j].publication = pubinfo;
3691 pubrinfo[j].pubtable = tbinfo;
3692
3693 /* Decide whether we want to dump it */
3694 selectDumpablePublicationTable(&(pubrinfo[j].dobj), fout);
3695
3696 j++;
3697 }
3698
3699 PQclear(res);
3700 destroyPQExpBuffer(query);
3701 }
3702
3703 /*
3704 * dumpPublicationTable
3705 * dump the definition of the given publication table mapping
3706 */
3707 static void
dumpPublicationTable(Archive * fout,PublicationRelInfo * pubrinfo)3708 dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo)
3709 {
3710 PublicationInfo *pubinfo = pubrinfo->publication;
3711 TableInfo *tbinfo = pubrinfo->pubtable;
3712 PQExpBuffer query;
3713 char *tag;
3714
3715 if (!(pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
3716 return;
3717
3718 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
3719
3720 query = createPQExpBuffer();
3721
3722 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
3723 fmtId(pubinfo->dobj.name));
3724 appendPQExpBuffer(query, " %s;\n",
3725 fmtQualifiedDumpable(tbinfo));
3726
3727 /*
3728 * There is no point in creating a drop query as the drop is done by table
3729 * drop. (If you think to change this, see also _printTocEntry().)
3730 * Although this object doesn't really have ownership as such, set the
3731 * owner field anyway to ensure that the command is run by the correct
3732 * role at restore time.
3733 */
3734 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
3735 tag,
3736 tbinfo->dobj.namespace->dobj.name,
3737 NULL,
3738 pubinfo->rolname, false,
3739 "PUBLICATION TABLE", SECTION_POST_DATA,
3740 query->data, "", NULL,
3741 NULL, 0,
3742 NULL, NULL);
3743
3744 free(tag);
3745 destroyPQExpBuffer(query);
3746 }
3747
3748 /*
3749 * Is the currently connected user a superuser?
3750 */
3751 static bool
is_superuser(Archive * fout)3752 is_superuser(Archive *fout)
3753 {
3754 ArchiveHandle *AH = (ArchiveHandle *) fout;
3755 const char *val;
3756
3757 val = PQparameterStatus(AH->connection, "is_superuser");
3758
3759 if (val && strcmp(val, "on") == 0)
3760 return true;
3761
3762 return false;
3763 }
3764
3765 /*
3766 * getSubscriptions
3767 * get information about subscriptions
3768 */
3769 void
getSubscriptions(Archive * fout)3770 getSubscriptions(Archive *fout)
3771 {
3772 DumpOptions *dopt = fout->dopt;
3773 PQExpBuffer query;
3774 PGresult *res;
3775 SubscriptionInfo *subinfo;
3776 int i_tableoid;
3777 int i_oid;
3778 int i_subname;
3779 int i_rolname;
3780 int i_subconninfo;
3781 int i_subslotname;
3782 int i_subsynccommit;
3783 int i_subpublications;
3784 int i,
3785 ntups;
3786
3787 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
3788 return;
3789
3790 if (!is_superuser(fout))
3791 {
3792 int n;
3793
3794 res = ExecuteSqlQuery(fout,
3795 "SELECT count(*) FROM pg_subscription "
3796 "WHERE subdbid = (SELECT oid FROM pg_database"
3797 " WHERE datname = current_database())",
3798 PGRES_TUPLES_OK);
3799 n = atoi(PQgetvalue(res, 0, 0));
3800 if (n > 0)
3801 write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
3802 PQclear(res);
3803 return;
3804 }
3805
3806 query = createPQExpBuffer();
3807
3808 resetPQExpBuffer(query);
3809
3810 /* Get the subscriptions in current database. */
3811 appendPQExpBuffer(query,
3812 "SELECT s.tableoid, s.oid, s.subname,"
3813 "(%s s.subowner) AS rolname, "
3814 " s.subconninfo, s.subslotname, s.subsynccommit, "
3815 " s.subpublications "
3816 "FROM pg_subscription s "
3817 "WHERE s.subdbid = (SELECT oid FROM pg_database"
3818 " WHERE datname = current_database())",
3819 username_subquery);
3820 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3821
3822 ntups = PQntuples(res);
3823
3824 i_tableoid = PQfnumber(res, "tableoid");
3825 i_oid = PQfnumber(res, "oid");
3826 i_subname = PQfnumber(res, "subname");
3827 i_rolname = PQfnumber(res, "rolname");
3828 i_subconninfo = PQfnumber(res, "subconninfo");
3829 i_subslotname = PQfnumber(res, "subslotname");
3830 i_subsynccommit = PQfnumber(res, "subsynccommit");
3831 i_subpublications = PQfnumber(res, "subpublications");
3832
3833 subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
3834
3835 for (i = 0; i < ntups; i++)
3836 {
3837 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
3838 subinfo[i].dobj.catId.tableoid =
3839 atooid(PQgetvalue(res, i, i_tableoid));
3840 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
3841 AssignDumpId(&subinfo[i].dobj);
3842 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
3843 subinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
3844 subinfo[i].subconninfo = pg_strdup(PQgetvalue(res, i, i_subconninfo));
3845 if (PQgetisnull(res, i, i_subslotname))
3846 subinfo[i].subslotname = NULL;
3847 else
3848 subinfo[i].subslotname = pg_strdup(PQgetvalue(res, i, i_subslotname));
3849 subinfo[i].subsynccommit =
3850 pg_strdup(PQgetvalue(res, i, i_subsynccommit));
3851 subinfo[i].subpublications =
3852 pg_strdup(PQgetvalue(res, i, i_subpublications));
3853
3854 if (strlen(subinfo[i].rolname) == 0)
3855 write_msg(NULL, "WARNING: owner of subscription \"%s\" appears to be invalid\n",
3856 subinfo[i].dobj.name);
3857
3858 /* Decide whether we want to dump it */
3859 selectDumpableObject(&(subinfo[i].dobj), fout);
3860 }
3861 PQclear(res);
3862
3863 destroyPQExpBuffer(query);
3864 }
3865
3866 /*
3867 * dumpSubscription
3868 * dump the definition of the given subscription
3869 */
3870 static void
dumpSubscription(Archive * fout,SubscriptionInfo * subinfo)3871 dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
3872 {
3873 PQExpBuffer delq;
3874 PQExpBuffer query;
3875 PQExpBuffer publications;
3876 char *qsubname;
3877 char **pubnames = NULL;
3878 int npubnames = 0;
3879 int i;
3880
3881 if (!(subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
3882 return;
3883
3884 delq = createPQExpBuffer();
3885 query = createPQExpBuffer();
3886
3887 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
3888
3889 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
3890 qsubname);
3891
3892 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
3893 qsubname);
3894 appendStringLiteralAH(query, subinfo->subconninfo, fout);
3895
3896 /* Build list of quoted publications and append them to query. */
3897 if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
3898 {
3899 write_msg(NULL,
3900 "WARNING: could not parse subpublications array\n");
3901 if (pubnames)
3902 free(pubnames);
3903 pubnames = NULL;
3904 npubnames = 0;
3905 }
3906
3907 publications = createPQExpBuffer();
3908 for (i = 0; i < npubnames; i++)
3909 {
3910 if (i > 0)
3911 appendPQExpBufferStr(publications, ", ");
3912
3913 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
3914 }
3915
3916 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
3917 if (subinfo->subslotname)
3918 appendStringLiteralAH(query, subinfo->subslotname, fout);
3919 else
3920 appendPQExpBufferStr(query, "NONE");
3921
3922 if (strcmp(subinfo->subsynccommit, "off") != 0)
3923 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
3924
3925 appendPQExpBufferStr(query, ");\n");
3926
3927 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
3928 subinfo->dobj.name,
3929 NULL,
3930 NULL,
3931 subinfo->rolname, false,
3932 "SUBSCRIPTION", SECTION_POST_DATA,
3933 query->data, delq->data, NULL,
3934 NULL, 0,
3935 NULL, NULL);
3936
3937 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3938 dumpComment(fout, "SUBSCRIPTION", qsubname,
3939 NULL, subinfo->rolname,
3940 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
3941
3942 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3943 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
3944 NULL, subinfo->rolname,
3945 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
3946
3947 destroyPQExpBuffer(publications);
3948 if (pubnames)
3949 free(pubnames);
3950
3951 destroyPQExpBuffer(delq);
3952 destroyPQExpBuffer(query);
3953 free(qsubname);
3954 }
3955
3956 /*
3957 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
3958 * the object needs.
3959 */
3960 static void
append_depends_on_extension(Archive * fout,PQExpBuffer create,DumpableObject * dobj,const char * catalog,const char * keyword,const char * objname)3961 append_depends_on_extension(Archive *fout,
3962 PQExpBuffer create,
3963 DumpableObject *dobj,
3964 const char *catalog,
3965 const char *keyword,
3966 const char *objname)
3967 {
3968 if (dobj->depends_on_ext)
3969 {
3970 char *nm;
3971 PGresult *res;
3972 PQExpBuffer query;
3973 int ntups;
3974 int i_extname;
3975 int i;
3976
3977 /* dodge fmtId() non-reentrancy */
3978 nm = pg_strdup(objname);
3979
3980 query = createPQExpBuffer();
3981 appendPQExpBuffer(query,
3982 "SELECT e.extname "
3983 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
3984 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
3985 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
3986 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
3987 catalog,
3988 dobj->catId.oid);
3989 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3990 ntups = PQntuples(res);
3991 i_extname = PQfnumber(res, "extname");
3992 for (i = 0; i < ntups; i++)
3993 {
3994 appendPQExpBuffer(create, "ALTER %s %s DEPENDS ON EXTENSION %s;\n",
3995 keyword, nm,
3996 fmtId(PQgetvalue(res, i, i_extname)));
3997 }
3998
3999 PQclear(res);
4000 destroyPQExpBuffer(query);
4001 pg_free(nm);
4002 }
4003 }
4004
4005
4006 static void
binary_upgrade_set_type_oids_by_type_oid(Archive * fout,PQExpBuffer upgrade_buffer,Oid pg_type_oid)4007 binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
4008 PQExpBuffer upgrade_buffer,
4009 Oid pg_type_oid)
4010 {
4011 PQExpBuffer upgrade_query = createPQExpBuffer();
4012 PGresult *upgrade_res;
4013 Oid pg_type_array_oid;
4014
4015 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
4016 appendPQExpBuffer(upgrade_buffer,
4017 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
4018 pg_type_oid);
4019
4020 /* we only support old >= 8.3 for binary upgrades */
4021 appendPQExpBuffer(upgrade_query,
4022 "SELECT typarray "
4023 "FROM pg_catalog.pg_type "
4024 "WHERE pg_type.oid = '%u'::pg_catalog.oid;",
4025 pg_type_oid);
4026
4027 upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4028
4029 pg_type_array_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "typarray")));
4030
4031 if (OidIsValid(pg_type_array_oid))
4032 {
4033 appendPQExpBufferStr(upgrade_buffer,
4034 "\n-- For binary upgrade, must preserve pg_type array oid\n");
4035 appendPQExpBuffer(upgrade_buffer,
4036 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
4037 pg_type_array_oid);
4038 }
4039
4040 PQclear(upgrade_res);
4041 destroyPQExpBuffer(upgrade_query);
4042 }
4043
4044 static bool
binary_upgrade_set_type_oids_by_rel_oid(Archive * fout,PQExpBuffer upgrade_buffer,Oid pg_rel_oid)4045 binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
4046 PQExpBuffer upgrade_buffer,
4047 Oid pg_rel_oid)
4048 {
4049 PQExpBuffer upgrade_query = createPQExpBuffer();
4050 PGresult *upgrade_res;
4051 Oid pg_type_oid;
4052 bool toast_set = false;
4053
4054 /* we only support old >= 8.3 for binary upgrades */
4055 appendPQExpBuffer(upgrade_query,
4056 "SELECT c.reltype AS crel, t.reltype AS trel "
4057 "FROM pg_catalog.pg_class c "
4058 "LEFT JOIN pg_catalog.pg_class t ON "
4059 " (c.reltoastrelid = t.oid) "
4060 "WHERE c.oid = '%u'::pg_catalog.oid;",
4061 pg_rel_oid);
4062
4063 upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4064
4065 pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
4066
4067 binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
4068 pg_type_oid);
4069
4070 if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
4071 {
4072 /* Toast tables do not have pg_type array rows */
4073 Oid pg_type_toast_oid = atooid(PQgetvalue(upgrade_res, 0,
4074 PQfnumber(upgrade_res, "trel")));
4075
4076 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type toast oid\n");
4077 appendPQExpBuffer(upgrade_buffer,
4078 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_type_oid('%u'::pg_catalog.oid);\n\n",
4079 pg_type_toast_oid);
4080
4081 toast_set = true;
4082 }
4083
4084 PQclear(upgrade_res);
4085 destroyPQExpBuffer(upgrade_query);
4086
4087 return toast_set;
4088 }
4089
4090 static void
binary_upgrade_set_pg_class_oids(Archive * fout,PQExpBuffer upgrade_buffer,Oid pg_class_oid,bool is_index)4091 binary_upgrade_set_pg_class_oids(Archive *fout,
4092 PQExpBuffer upgrade_buffer, Oid pg_class_oid,
4093 bool is_index)
4094 {
4095 PQExpBuffer upgrade_query = createPQExpBuffer();
4096 PGresult *upgrade_res;
4097 Oid pg_class_reltoastrelid;
4098 Oid pg_index_indexrelid;
4099
4100 appendPQExpBuffer(upgrade_query,
4101 "SELECT c.reltoastrelid, i.indexrelid "
4102 "FROM pg_catalog.pg_class c LEFT JOIN "
4103 "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
4104 "WHERE c.oid = '%u'::pg_catalog.oid;",
4105 pg_class_oid);
4106
4107 upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4108
4109 pg_class_reltoastrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastrelid")));
4110 pg_index_indexrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "indexrelid")));
4111
4112 appendPQExpBufferStr(upgrade_buffer,
4113 "\n-- For binary upgrade, must preserve pg_class oids\n");
4114
4115 if (!is_index)
4116 {
4117 appendPQExpBuffer(upgrade_buffer,
4118 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
4119 pg_class_oid);
4120 /* only tables have toast tables, not indexes */
4121 if (OidIsValid(pg_class_reltoastrelid))
4122 {
4123 /*
4124 * One complexity is that the table definition might not require
4125 * the creation of a TOAST table, and the TOAST table might have
4126 * been created long after table creation, when the table was
4127 * loaded with wide data. By setting the TOAST oid we force
4128 * creation of the TOAST heap and TOAST index by the backend so we
4129 * can cleanly copy the files during binary upgrade.
4130 */
4131
4132 appendPQExpBuffer(upgrade_buffer,
4133 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
4134 pg_class_reltoastrelid);
4135
4136 /* every toast table has an index */
4137 appendPQExpBuffer(upgrade_buffer,
4138 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
4139 pg_index_indexrelid);
4140 }
4141 }
4142 else
4143 appendPQExpBuffer(upgrade_buffer,
4144 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
4145 pg_class_oid);
4146
4147 appendPQExpBufferChar(upgrade_buffer, '\n');
4148
4149 PQclear(upgrade_res);
4150 destroyPQExpBuffer(upgrade_query);
4151 }
4152
4153 /*
4154 * If the DumpableObject is a member of an extension, add a suitable
4155 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
4156 *
4157 * For somewhat historical reasons, objname should already be quoted,
4158 * but not objnamespace (if any).
4159 */
4160 static void
binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,DumpableObject * dobj,const char * objtype,const char * objname,const char * objnamespace)4161 binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
4162 DumpableObject *dobj,
4163 const char *objtype,
4164 const char *objname,
4165 const char *objnamespace)
4166 {
4167 DumpableObject *extobj = NULL;
4168 int i;
4169
4170 if (!dobj->ext_member)
4171 return;
4172
4173 /*
4174 * Find the parent extension. We could avoid this search if we wanted to
4175 * add a link field to DumpableObject, but the space costs of that would
4176 * be considerable. We assume that member objects could only have a
4177 * direct dependency on their own extension, not any others.
4178 */
4179 for (i = 0; i < dobj->nDeps; i++)
4180 {
4181 extobj = findObjectByDumpId(dobj->dependencies[i]);
4182 if (extobj && extobj->objType == DO_EXTENSION)
4183 break;
4184 extobj = NULL;
4185 }
4186 if (extobj == NULL)
4187 exit_horribly(NULL, "could not find parent extension for %s %s\n",
4188 objtype, objname);
4189
4190 appendPQExpBufferStr(upgrade_buffer,
4191 "\n-- For binary upgrade, handle extension membership the hard way\n");
4192 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
4193 fmtId(extobj->name),
4194 objtype);
4195 if (objnamespace && *objnamespace)
4196 appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
4197 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
4198 }
4199
4200 /*
4201 * getNamespaces:
4202 * read all namespaces in the system catalogs and return them in the
4203 * NamespaceInfo* structure
4204 *
4205 * numNamespaces is set to the number of namespaces read in
4206 */
4207 NamespaceInfo *
getNamespaces(Archive * fout,int * numNamespaces)4208 getNamespaces(Archive *fout, int *numNamespaces)
4209 {
4210 DumpOptions *dopt = fout->dopt;
4211 PGresult *res;
4212 int ntups;
4213 int i;
4214 PQExpBuffer query;
4215 NamespaceInfo *nsinfo;
4216 int i_tableoid;
4217 int i_oid;
4218 int i_nspname;
4219 int i_rolname;
4220 int i_nspacl;
4221 int i_rnspacl;
4222 int i_initnspacl;
4223 int i_initrnspacl;
4224
4225 query = createPQExpBuffer();
4226
4227 /*
4228 * we fetch all namespaces including system ones, so that every object we
4229 * read in can be linked to a containing namespace.
4230 */
4231 if (fout->remoteVersion >= 90600)
4232 {
4233 PQExpBuffer acl_subquery = createPQExpBuffer();
4234 PQExpBuffer racl_subquery = createPQExpBuffer();
4235 PQExpBuffer init_acl_subquery = createPQExpBuffer();
4236 PQExpBuffer init_racl_subquery = createPQExpBuffer();
4237
4238 buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
4239 init_racl_subquery, "n.nspacl", "n.nspowner", "'n'",
4240 dopt->binary_upgrade);
4241
4242 appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, "
4243 "(%s nspowner) AS rolname, "
4244 "%s as nspacl, "
4245 "%s as rnspacl, "
4246 "%s as initnspacl, "
4247 "%s as initrnspacl "
4248 "FROM pg_namespace n "
4249 "LEFT JOIN pg_init_privs pip "
4250 "ON (n.oid = pip.objoid "
4251 "AND pip.classoid = 'pg_namespace'::regclass "
4252 "AND pip.objsubid = 0",
4253 username_subquery,
4254 acl_subquery->data,
4255 racl_subquery->data,
4256 init_acl_subquery->data,
4257 init_racl_subquery->data);
4258
4259 /*
4260 * When we are doing a 'clean' run, we will be dropping and recreating
4261 * the 'public' schema (the only object which has that kind of
4262 * treatment in the backend and which has an entry in pg_init_privs)
4263 * and therefore we should not consider any initial privileges in
4264 * pg_init_privs in that case.
4265 *
4266 * See pg_backup_archiver.c:_printTocEntry() for the details on why
4267 * the public schema is special in this regard.
4268 *
4269 * Note that if the public schema is dropped and re-created, this is
4270 * essentially a no-op because the new public schema won't have an
4271 * entry in pg_init_privs anyway, as the entry will be removed when
4272 * the public schema is dropped.
4273 *
4274 * Further, we have to handle the case where the public schema does
4275 * not exist at all.
4276 */
4277 if (dopt->outputClean)
4278 appendPQExpBuffer(query, " AND pip.objoid <> "
4279 "coalesce((select oid from pg_namespace "
4280 "where nspname = 'public'),0)");
4281
4282 appendPQExpBuffer(query, ") ");
4283
4284 destroyPQExpBuffer(acl_subquery);
4285 destroyPQExpBuffer(racl_subquery);
4286 destroyPQExpBuffer(init_acl_subquery);
4287 destroyPQExpBuffer(init_racl_subquery);
4288 }
4289 else
4290 appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
4291 "(%s nspowner) AS rolname, "
4292 "nspacl, NULL as rnspacl, "
4293 "NULL AS initnspacl, NULL as initrnspacl "
4294 "FROM pg_namespace",
4295 username_subquery);
4296
4297 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4298
4299 ntups = PQntuples(res);
4300
4301 nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
4302
4303 i_tableoid = PQfnumber(res, "tableoid");
4304 i_oid = PQfnumber(res, "oid");
4305 i_nspname = PQfnumber(res, "nspname");
4306 i_rolname = PQfnumber(res, "rolname");
4307 i_nspacl = PQfnumber(res, "nspacl");
4308 i_rnspacl = PQfnumber(res, "rnspacl");
4309 i_initnspacl = PQfnumber(res, "initnspacl");
4310 i_initrnspacl = PQfnumber(res, "initrnspacl");
4311
4312 for (i = 0; i < ntups; i++)
4313 {
4314 nsinfo[i].dobj.objType = DO_NAMESPACE;
4315 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4316 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4317 AssignDumpId(&nsinfo[i].dobj);
4318 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
4319 nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
4320 nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl));
4321 nsinfo[i].rnspacl = pg_strdup(PQgetvalue(res, i, i_rnspacl));
4322 nsinfo[i].initnspacl = pg_strdup(PQgetvalue(res, i, i_initnspacl));
4323 nsinfo[i].initrnspacl = pg_strdup(PQgetvalue(res, i, i_initrnspacl));
4324
4325 /* Decide whether to dump this namespace */
4326 selectDumpableNamespace(&nsinfo[i], fout);
4327
4328 /*
4329 * Do not try to dump ACL if the ACL is empty or the default.
4330 *
4331 * This is useful because, for some schemas/objects, the only
4332 * component we are going to try and dump is the ACL and if we can
4333 * remove that then 'dump' goes to zero/false and we don't consider
4334 * this object for dumping at all later on.
4335 */
4336 if (PQgetisnull(res, i, i_nspacl) && PQgetisnull(res, i, i_rnspacl) &&
4337 PQgetisnull(res, i, i_initnspacl) &&
4338 PQgetisnull(res, i, i_initrnspacl))
4339 nsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
4340
4341 if (strlen(nsinfo[i].rolname) == 0)
4342 write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
4343 nsinfo[i].dobj.name);
4344 }
4345
4346 PQclear(res);
4347 destroyPQExpBuffer(query);
4348
4349 *numNamespaces = ntups;
4350
4351 return nsinfo;
4352 }
4353
4354 /*
4355 * findNamespace:
4356 * given a namespace OID, look up the info read by getNamespaces
4357 */
4358 static NamespaceInfo *
findNamespace(Archive * fout,Oid nsoid)4359 findNamespace(Archive *fout, Oid nsoid)
4360 {
4361 NamespaceInfo *nsinfo;
4362
4363 nsinfo = findNamespaceByOid(nsoid);
4364 if (nsinfo == NULL)
4365 exit_horribly(NULL, "schema with OID %u does not exist\n", nsoid);
4366 return nsinfo;
4367 }
4368
4369 /*
4370 * getExtensions:
4371 * read all extensions in the system catalogs and return them in the
4372 * ExtensionInfo* structure
4373 *
4374 * numExtensions is set to the number of extensions read in
4375 */
4376 ExtensionInfo *
getExtensions(Archive * fout,int * numExtensions)4377 getExtensions(Archive *fout, int *numExtensions)
4378 {
4379 DumpOptions *dopt = fout->dopt;
4380 PGresult *res;
4381 int ntups;
4382 int i;
4383 PQExpBuffer query;
4384 ExtensionInfo *extinfo;
4385 int i_tableoid;
4386 int i_oid;
4387 int i_extname;
4388 int i_nspname;
4389 int i_extrelocatable;
4390 int i_extversion;
4391 int i_extconfig;
4392 int i_extcondition;
4393
4394 /*
4395 * Before 9.1, there are no extensions.
4396 */
4397 if (fout->remoteVersion < 90100)
4398 {
4399 *numExtensions = 0;
4400 return NULL;
4401 }
4402
4403 query = createPQExpBuffer();
4404
4405 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
4406 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
4407 "FROM pg_extension x "
4408 "JOIN pg_namespace n ON n.oid = x.extnamespace");
4409
4410 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4411
4412 ntups = PQntuples(res);
4413
4414 extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
4415
4416 i_tableoid = PQfnumber(res, "tableoid");
4417 i_oid = PQfnumber(res, "oid");
4418 i_extname = PQfnumber(res, "extname");
4419 i_nspname = PQfnumber(res, "nspname");
4420 i_extrelocatable = PQfnumber(res, "extrelocatable");
4421 i_extversion = PQfnumber(res, "extversion");
4422 i_extconfig = PQfnumber(res, "extconfig");
4423 i_extcondition = PQfnumber(res, "extcondition");
4424
4425 for (i = 0; i < ntups; i++)
4426 {
4427 extinfo[i].dobj.objType = DO_EXTENSION;
4428 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4429 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4430 AssignDumpId(&extinfo[i].dobj);
4431 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
4432 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
4433 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
4434 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
4435 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
4436 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
4437
4438 /* Decide whether we want to dump it */
4439 selectDumpableExtension(&(extinfo[i]), dopt);
4440 }
4441
4442 PQclear(res);
4443 destroyPQExpBuffer(query);
4444
4445 *numExtensions = ntups;
4446
4447 return extinfo;
4448 }
4449
4450 /*
4451 * getTypes:
4452 * read all types in the system catalogs and return them in the
4453 * TypeInfo* structure
4454 *
4455 * numTypes is set to the number of types read in
4456 *
4457 * NB: this must run after getFuncs() because we assume we can do
4458 * findFuncByOid().
4459 */
4460 TypeInfo *
getTypes(Archive * fout,int * numTypes)4461 getTypes(Archive *fout, int *numTypes)
4462 {
4463 DumpOptions *dopt = fout->dopt;
4464 PGresult *res;
4465 int ntups;
4466 int i;
4467 PQExpBuffer query = createPQExpBuffer();
4468 TypeInfo *tyinfo;
4469 ShellTypeInfo *stinfo;
4470 int i_tableoid;
4471 int i_oid;
4472 int i_typname;
4473 int i_typnamespace;
4474 int i_typacl;
4475 int i_rtypacl;
4476 int i_inittypacl;
4477 int i_initrtypacl;
4478 int i_rolname;
4479 int i_typelem;
4480 int i_typrelid;
4481 int i_typrelkind;
4482 int i_typtype;
4483 int i_typisdefined;
4484 int i_isarray;
4485
4486 /*
4487 * we include even the built-in types because those may be used as array
4488 * elements by user-defined types
4489 *
4490 * we filter out the built-in types when we dump out the types
4491 *
4492 * same approach for undefined (shell) types and array types
4493 *
4494 * Note: as of 8.3 we can reliably detect whether a type is an
4495 * auto-generated array type by checking the element type's typarray.
4496 * (Before that the test is capable of generating false positives.) We
4497 * still check for name beginning with '_', though, so as to avoid the
4498 * cost of the subselect probe for all standard types. This would have to
4499 * be revisited if the backend ever allows renaming of array types.
4500 */
4501
4502 if (fout->remoteVersion >= 90600)
4503 {
4504 PQExpBuffer acl_subquery = createPQExpBuffer();
4505 PQExpBuffer racl_subquery = createPQExpBuffer();
4506 PQExpBuffer initacl_subquery = createPQExpBuffer();
4507 PQExpBuffer initracl_subquery = createPQExpBuffer();
4508
4509 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
4510 initracl_subquery, "t.typacl", "t.typowner", "'T'",
4511 dopt->binary_upgrade);
4512
4513 appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
4514 "t.typnamespace, "
4515 "%s AS typacl, "
4516 "%s AS rtypacl, "
4517 "%s AS inittypacl, "
4518 "%s AS initrtypacl, "
4519 "(%s t.typowner) AS rolname, "
4520 "t.typelem, t.typrelid, "
4521 "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
4522 "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
4523 "t.typtype, t.typisdefined, "
4524 "t.typname[0] = '_' AND t.typelem != 0 AND "
4525 "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
4526 "FROM pg_type t "
4527 "LEFT JOIN pg_init_privs pip ON "
4528 "(t.oid = pip.objoid "
4529 "AND pip.classoid = 'pg_type'::regclass "
4530 "AND pip.objsubid = 0) ",
4531 acl_subquery->data,
4532 racl_subquery->data,
4533 initacl_subquery->data,
4534 initracl_subquery->data,
4535 username_subquery);
4536
4537 destroyPQExpBuffer(acl_subquery);
4538 destroyPQExpBuffer(racl_subquery);
4539 destroyPQExpBuffer(initacl_subquery);
4540 destroyPQExpBuffer(initracl_subquery);
4541 }
4542 else if (fout->remoteVersion >= 90200)
4543 {
4544 appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
4545 "typnamespace, typacl, NULL as rtypacl, "
4546 "NULL AS inittypacl, NULL AS initrtypacl, "
4547 "(%s typowner) AS rolname, "
4548 "typelem, typrelid, "
4549 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
4550 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
4551 "typtype, typisdefined, "
4552 "typname[0] = '_' AND typelem != 0 AND "
4553 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
4554 "FROM pg_type",
4555 username_subquery);
4556 }
4557 else if (fout->remoteVersion >= 80300)
4558 {
4559 appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
4560 "typnamespace, NULL AS typacl, NULL as rtypacl, "
4561 "NULL AS inittypacl, NULL AS initrtypacl, "
4562 "(%s typowner) AS rolname, "
4563 "typelem, typrelid, "
4564 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
4565 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
4566 "typtype, typisdefined, "
4567 "typname[0] = '_' AND typelem != 0 AND "
4568 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
4569 "FROM pg_type",
4570 username_subquery);
4571 }
4572 else
4573 {
4574 appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
4575 "typnamespace, NULL AS typacl, NULL as rtypacl, "
4576 "NULL AS inittypacl, NULL AS initrtypacl, "
4577 "(%s typowner) AS rolname, "
4578 "typelem, typrelid, "
4579 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
4580 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
4581 "typtype, typisdefined, "
4582 "typname[0] = '_' AND typelem != 0 AS isarray "
4583 "FROM pg_type",
4584 username_subquery);
4585 }
4586
4587 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4588
4589 ntups = PQntuples(res);
4590
4591 tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
4592
4593 i_tableoid = PQfnumber(res, "tableoid");
4594 i_oid = PQfnumber(res, "oid");
4595 i_typname = PQfnumber(res, "typname");
4596 i_typnamespace = PQfnumber(res, "typnamespace");
4597 i_typacl = PQfnumber(res, "typacl");
4598 i_rtypacl = PQfnumber(res, "rtypacl");
4599 i_inittypacl = PQfnumber(res, "inittypacl");
4600 i_initrtypacl = PQfnumber(res, "initrtypacl");
4601 i_rolname = PQfnumber(res, "rolname");
4602 i_typelem = PQfnumber(res, "typelem");
4603 i_typrelid = PQfnumber(res, "typrelid");
4604 i_typrelkind = PQfnumber(res, "typrelkind");
4605 i_typtype = PQfnumber(res, "typtype");
4606 i_typisdefined = PQfnumber(res, "typisdefined");
4607 i_isarray = PQfnumber(res, "isarray");
4608
4609 for (i = 0; i < ntups; i++)
4610 {
4611 tyinfo[i].dobj.objType = DO_TYPE;
4612 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4613 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4614 AssignDumpId(&tyinfo[i].dobj);
4615 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
4616 tyinfo[i].dobj.namespace =
4617 findNamespace(fout,
4618 atooid(PQgetvalue(res, i, i_typnamespace)));
4619 tyinfo[i].ftypname = NULL; /* may get filled later */
4620 tyinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
4621 tyinfo[i].typacl = pg_strdup(PQgetvalue(res, i, i_typacl));
4622 tyinfo[i].rtypacl = pg_strdup(PQgetvalue(res, i, i_rtypacl));
4623 tyinfo[i].inittypacl = pg_strdup(PQgetvalue(res, i, i_inittypacl));
4624 tyinfo[i].initrtypacl = pg_strdup(PQgetvalue(res, i, i_initrtypacl));
4625 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
4626 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
4627 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
4628 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
4629 tyinfo[i].shellType = NULL;
4630
4631 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
4632 tyinfo[i].isDefined = true;
4633 else
4634 tyinfo[i].isDefined = false;
4635
4636 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
4637 tyinfo[i].isArray = true;
4638 else
4639 tyinfo[i].isArray = false;
4640
4641 /* Decide whether we want to dump it */
4642 selectDumpableType(&tyinfo[i], fout);
4643
4644 /* Do not try to dump ACL if no ACL exists. */
4645 if (PQgetisnull(res, i, i_typacl) && PQgetisnull(res, i, i_rtypacl) &&
4646 PQgetisnull(res, i, i_inittypacl) &&
4647 PQgetisnull(res, i, i_initrtypacl))
4648 tyinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
4649
4650 /*
4651 * If it's a domain, fetch info about its constraints, if any
4652 */
4653 tyinfo[i].nDomChecks = 0;
4654 tyinfo[i].domChecks = NULL;
4655 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
4656 tyinfo[i].typtype == TYPTYPE_DOMAIN)
4657 getDomainConstraints(fout, &(tyinfo[i]));
4658
4659 /*
4660 * If it's a base type, make a DumpableObject representing a shell
4661 * definition of the type. We will need to dump that ahead of the I/O
4662 * functions for the type. Similarly, range types need a shell
4663 * definition in case they have a canonicalize function.
4664 *
4665 * Note: the shell type doesn't have a catId. You might think it
4666 * should copy the base type's catId, but then it might capture the
4667 * pg_depend entries for the type, which we don't want.
4668 */
4669 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
4670 (tyinfo[i].typtype == TYPTYPE_BASE ||
4671 tyinfo[i].typtype == TYPTYPE_RANGE))
4672 {
4673 stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
4674 stinfo->dobj.objType = DO_SHELL_TYPE;
4675 stinfo->dobj.catId = nilCatalogId;
4676 AssignDumpId(&stinfo->dobj);
4677 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
4678 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
4679 stinfo->baseType = &(tyinfo[i]);
4680 tyinfo[i].shellType = stinfo;
4681
4682 /*
4683 * Initially mark the shell type as not to be dumped. We'll only
4684 * dump it if the I/O or canonicalize functions need to be dumped;
4685 * this is taken care of while sorting dependencies.
4686 */
4687 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
4688 }
4689
4690 if (strlen(tyinfo[i].rolname) == 0)
4691 write_msg(NULL, "WARNING: owner of data type \"%s\" appears to be invalid\n",
4692 tyinfo[i].dobj.name);
4693 }
4694
4695 *numTypes = ntups;
4696
4697 PQclear(res);
4698
4699 destroyPQExpBuffer(query);
4700
4701 return tyinfo;
4702 }
4703
4704 /*
4705 * getOperators:
4706 * read all operators in the system catalogs and return them in the
4707 * OprInfo* structure
4708 *
4709 * numOprs is set to the number of operators read in
4710 */
4711 OprInfo *
getOperators(Archive * fout,int * numOprs)4712 getOperators(Archive *fout, int *numOprs)
4713 {
4714 PGresult *res;
4715 int ntups;
4716 int i;
4717 PQExpBuffer query = createPQExpBuffer();
4718 OprInfo *oprinfo;
4719 int i_tableoid;
4720 int i_oid;
4721 int i_oprname;
4722 int i_oprnamespace;
4723 int i_rolname;
4724 int i_oprkind;
4725 int i_oprcode;
4726
4727 /*
4728 * find all operators, including builtin operators; we filter out
4729 * system-defined operators at dump-out time.
4730 */
4731
4732 appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
4733 "oprnamespace, "
4734 "(%s oprowner) AS rolname, "
4735 "oprkind, "
4736 "oprcode::oid AS oprcode "
4737 "FROM pg_operator",
4738 username_subquery);
4739
4740 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4741
4742 ntups = PQntuples(res);
4743 *numOprs = ntups;
4744
4745 oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
4746
4747 i_tableoid = PQfnumber(res, "tableoid");
4748 i_oid = PQfnumber(res, "oid");
4749 i_oprname = PQfnumber(res, "oprname");
4750 i_oprnamespace = PQfnumber(res, "oprnamespace");
4751 i_rolname = PQfnumber(res, "rolname");
4752 i_oprkind = PQfnumber(res, "oprkind");
4753 i_oprcode = PQfnumber(res, "oprcode");
4754
4755 for (i = 0; i < ntups; i++)
4756 {
4757 oprinfo[i].dobj.objType = DO_OPERATOR;
4758 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4759 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4760 AssignDumpId(&oprinfo[i].dobj);
4761 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
4762 oprinfo[i].dobj.namespace =
4763 findNamespace(fout,
4764 atooid(PQgetvalue(res, i, i_oprnamespace)));
4765 oprinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
4766 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
4767 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
4768
4769 /* Decide whether we want to dump it */
4770 selectDumpableObject(&(oprinfo[i].dobj), fout);
4771
4772 /* Operators do not currently have ACLs. */
4773 oprinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
4774
4775 if (strlen(oprinfo[i].rolname) == 0)
4776 write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
4777 oprinfo[i].dobj.name);
4778 }
4779
4780 PQclear(res);
4781
4782 destroyPQExpBuffer(query);
4783
4784 return oprinfo;
4785 }
4786
4787 /*
4788 * getCollations:
4789 * read all collations in the system catalogs and return them in the
4790 * CollInfo* structure
4791 *
4792 * numCollations is set to the number of collations read in
4793 */
4794 CollInfo *
getCollations(Archive * fout,int * numCollations)4795 getCollations(Archive *fout, int *numCollations)
4796 {
4797 PGresult *res;
4798 int ntups;
4799 int i;
4800 PQExpBuffer query;
4801 CollInfo *collinfo;
4802 int i_tableoid;
4803 int i_oid;
4804 int i_collname;
4805 int i_collnamespace;
4806 int i_rolname;
4807
4808 /* Collations didn't exist pre-9.1 */
4809 if (fout->remoteVersion < 90100)
4810 {
4811 *numCollations = 0;
4812 return NULL;
4813 }
4814
4815 query = createPQExpBuffer();
4816
4817 /*
4818 * find all collations, including builtin collations; we filter out
4819 * system-defined collations at dump-out time.
4820 */
4821
4822 appendPQExpBuffer(query, "SELECT tableoid, oid, collname, "
4823 "collnamespace, "
4824 "(%s collowner) AS rolname "
4825 "FROM pg_collation",
4826 username_subquery);
4827
4828 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4829
4830 ntups = PQntuples(res);
4831 *numCollations = ntups;
4832
4833 collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
4834
4835 i_tableoid = PQfnumber(res, "tableoid");
4836 i_oid = PQfnumber(res, "oid");
4837 i_collname = PQfnumber(res, "collname");
4838 i_collnamespace = PQfnumber(res, "collnamespace");
4839 i_rolname = PQfnumber(res, "rolname");
4840
4841 for (i = 0; i < ntups; i++)
4842 {
4843 collinfo[i].dobj.objType = DO_COLLATION;
4844 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4845 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4846 AssignDumpId(&collinfo[i].dobj);
4847 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
4848 collinfo[i].dobj.namespace =
4849 findNamespace(fout,
4850 atooid(PQgetvalue(res, i, i_collnamespace)));
4851 collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
4852
4853 /* Decide whether we want to dump it */
4854 selectDumpableObject(&(collinfo[i].dobj), fout);
4855
4856 /* Collations do not currently have ACLs. */
4857 collinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
4858 }
4859
4860 PQclear(res);
4861
4862 destroyPQExpBuffer(query);
4863
4864 return collinfo;
4865 }
4866
4867 /*
4868 * getConversions:
4869 * read all conversions in the system catalogs and return them in the
4870 * ConvInfo* structure
4871 *
4872 * numConversions is set to the number of conversions read in
4873 */
4874 ConvInfo *
getConversions(Archive * fout,int * numConversions)4875 getConversions(Archive *fout, int *numConversions)
4876 {
4877 PGresult *res;
4878 int ntups;
4879 int i;
4880 PQExpBuffer query;
4881 ConvInfo *convinfo;
4882 int i_tableoid;
4883 int i_oid;
4884 int i_conname;
4885 int i_connamespace;
4886 int i_rolname;
4887
4888 query = createPQExpBuffer();
4889
4890 /*
4891 * find all conversions, including builtin conversions; we filter out
4892 * system-defined conversions at dump-out time.
4893 */
4894
4895 appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
4896 "connamespace, "
4897 "(%s conowner) AS rolname "
4898 "FROM pg_conversion",
4899 username_subquery);
4900
4901 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4902
4903 ntups = PQntuples(res);
4904 *numConversions = ntups;
4905
4906 convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
4907
4908 i_tableoid = PQfnumber(res, "tableoid");
4909 i_oid = PQfnumber(res, "oid");
4910 i_conname = PQfnumber(res, "conname");
4911 i_connamespace = PQfnumber(res, "connamespace");
4912 i_rolname = PQfnumber(res, "rolname");
4913
4914 for (i = 0; i < ntups; i++)
4915 {
4916 convinfo[i].dobj.objType = DO_CONVERSION;
4917 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4918 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4919 AssignDumpId(&convinfo[i].dobj);
4920 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
4921 convinfo[i].dobj.namespace =
4922 findNamespace(fout,
4923 atooid(PQgetvalue(res, i, i_connamespace)));
4924 convinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
4925
4926 /* Decide whether we want to dump it */
4927 selectDumpableObject(&(convinfo[i].dobj), fout);
4928
4929 /* Conversions do not currently have ACLs. */
4930 convinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
4931 }
4932
4933 PQclear(res);
4934
4935 destroyPQExpBuffer(query);
4936
4937 return convinfo;
4938 }
4939
4940 /*
4941 * getAccessMethods:
4942 * read all user-defined access methods in the system catalogs and return
4943 * them in the AccessMethodInfo* structure
4944 *
4945 * numAccessMethods is set to the number of access methods read in
4946 */
4947 AccessMethodInfo *
getAccessMethods(Archive * fout,int * numAccessMethods)4948 getAccessMethods(Archive *fout, int *numAccessMethods)
4949 {
4950 PGresult *res;
4951 int ntups;
4952 int i;
4953 PQExpBuffer query;
4954 AccessMethodInfo *aminfo;
4955 int i_tableoid;
4956 int i_oid;
4957 int i_amname;
4958 int i_amhandler;
4959 int i_amtype;
4960
4961 /* Before 9.6, there are no user-defined access methods */
4962 if (fout->remoteVersion < 90600)
4963 {
4964 *numAccessMethods = 0;
4965 return NULL;
4966 }
4967
4968 query = createPQExpBuffer();
4969
4970 /* Select all access methods from pg_am table */
4971 appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, "
4972 "amhandler::pg_catalog.regproc AS amhandler "
4973 "FROM pg_am");
4974
4975 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4976
4977 ntups = PQntuples(res);
4978 *numAccessMethods = ntups;
4979
4980 aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
4981
4982 i_tableoid = PQfnumber(res, "tableoid");
4983 i_oid = PQfnumber(res, "oid");
4984 i_amname = PQfnumber(res, "amname");
4985 i_amhandler = PQfnumber(res, "amhandler");
4986 i_amtype = PQfnumber(res, "amtype");
4987
4988 for (i = 0; i < ntups; i++)
4989 {
4990 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
4991 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4992 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4993 AssignDumpId(&aminfo[i].dobj);
4994 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
4995 aminfo[i].dobj.namespace = NULL;
4996 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
4997 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
4998
4999 /* Decide whether we want to dump it */
5000 selectDumpableAccessMethod(&(aminfo[i]), fout);
5001
5002 /* Access methods do not currently have ACLs. */
5003 aminfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5004 }
5005
5006 PQclear(res);
5007
5008 destroyPQExpBuffer(query);
5009
5010 return aminfo;
5011 }
5012
5013
5014 /*
5015 * getOpclasses:
5016 * read all opclasses in the system catalogs and return them in the
5017 * OpclassInfo* structure
5018 *
5019 * numOpclasses is set to the number of opclasses read in
5020 */
5021 OpclassInfo *
getOpclasses(Archive * fout,int * numOpclasses)5022 getOpclasses(Archive *fout, int *numOpclasses)
5023 {
5024 PGresult *res;
5025 int ntups;
5026 int i;
5027 PQExpBuffer query = createPQExpBuffer();
5028 OpclassInfo *opcinfo;
5029 int i_tableoid;
5030 int i_oid;
5031 int i_opcname;
5032 int i_opcnamespace;
5033 int i_rolname;
5034
5035 /*
5036 * find all opclasses, including builtin opclasses; we filter out
5037 * system-defined opclasses at dump-out time.
5038 */
5039
5040 appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
5041 "opcnamespace, "
5042 "(%s opcowner) AS rolname "
5043 "FROM pg_opclass",
5044 username_subquery);
5045
5046 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5047
5048 ntups = PQntuples(res);
5049 *numOpclasses = ntups;
5050
5051 opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
5052
5053 i_tableoid = PQfnumber(res, "tableoid");
5054 i_oid = PQfnumber(res, "oid");
5055 i_opcname = PQfnumber(res, "opcname");
5056 i_opcnamespace = PQfnumber(res, "opcnamespace");
5057 i_rolname = PQfnumber(res, "rolname");
5058
5059 for (i = 0; i < ntups; i++)
5060 {
5061 opcinfo[i].dobj.objType = DO_OPCLASS;
5062 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5063 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5064 AssignDumpId(&opcinfo[i].dobj);
5065 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
5066 opcinfo[i].dobj.namespace =
5067 findNamespace(fout,
5068 atooid(PQgetvalue(res, i, i_opcnamespace)));
5069 opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5070
5071 /* Decide whether we want to dump it */
5072 selectDumpableObject(&(opcinfo[i].dobj), fout);
5073
5074 /* Op Classes do not currently have ACLs. */
5075 opcinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5076
5077 if (strlen(opcinfo[i].rolname) == 0)
5078 write_msg(NULL, "WARNING: owner of operator class \"%s\" appears to be invalid\n",
5079 opcinfo[i].dobj.name);
5080 }
5081
5082 PQclear(res);
5083
5084 destroyPQExpBuffer(query);
5085
5086 return opcinfo;
5087 }
5088
5089 /*
5090 * getOpfamilies:
5091 * read all opfamilies in the system catalogs and return them in the
5092 * OpfamilyInfo* structure
5093 *
5094 * numOpfamilies is set to the number of opfamilies read in
5095 */
5096 OpfamilyInfo *
getOpfamilies(Archive * fout,int * numOpfamilies)5097 getOpfamilies(Archive *fout, int *numOpfamilies)
5098 {
5099 PGresult *res;
5100 int ntups;
5101 int i;
5102 PQExpBuffer query;
5103 OpfamilyInfo *opfinfo;
5104 int i_tableoid;
5105 int i_oid;
5106 int i_opfname;
5107 int i_opfnamespace;
5108 int i_rolname;
5109
5110 /* Before 8.3, there is no separate concept of opfamilies */
5111 if (fout->remoteVersion < 80300)
5112 {
5113 *numOpfamilies = 0;
5114 return NULL;
5115 }
5116
5117 query = createPQExpBuffer();
5118
5119 /*
5120 * find all opfamilies, including builtin opfamilies; we filter out
5121 * system-defined opfamilies at dump-out time.
5122 */
5123
5124 appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
5125 "opfnamespace, "
5126 "(%s opfowner) AS rolname "
5127 "FROM pg_opfamily",
5128 username_subquery);
5129
5130 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5131
5132 ntups = PQntuples(res);
5133 *numOpfamilies = ntups;
5134
5135 opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
5136
5137 i_tableoid = PQfnumber(res, "tableoid");
5138 i_oid = PQfnumber(res, "oid");
5139 i_opfname = PQfnumber(res, "opfname");
5140 i_opfnamespace = PQfnumber(res, "opfnamespace");
5141 i_rolname = PQfnumber(res, "rolname");
5142
5143 for (i = 0; i < ntups; i++)
5144 {
5145 opfinfo[i].dobj.objType = DO_OPFAMILY;
5146 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5147 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5148 AssignDumpId(&opfinfo[i].dobj);
5149 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
5150 opfinfo[i].dobj.namespace =
5151 findNamespace(fout,
5152 atooid(PQgetvalue(res, i, i_opfnamespace)));
5153 opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5154
5155 /* Decide whether we want to dump it */
5156 selectDumpableObject(&(opfinfo[i].dobj), fout);
5157
5158 /* Extensions do not currently have ACLs. */
5159 opfinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5160
5161 if (strlen(opfinfo[i].rolname) == 0)
5162 write_msg(NULL, "WARNING: owner of operator family \"%s\" appears to be invalid\n",
5163 opfinfo[i].dobj.name);
5164 }
5165
5166 PQclear(res);
5167
5168 destroyPQExpBuffer(query);
5169
5170 return opfinfo;
5171 }
5172
5173 /*
5174 * getAggregates:
5175 * read all the user-defined aggregates in the system catalogs and
5176 * return them in the AggInfo* structure
5177 *
5178 * numAggs is set to the number of aggregates read in
5179 */
5180 AggInfo *
getAggregates(Archive * fout,int * numAggs)5181 getAggregates(Archive *fout, int *numAggs)
5182 {
5183 DumpOptions *dopt = fout->dopt;
5184 PGresult *res;
5185 int ntups;
5186 int i;
5187 PQExpBuffer query = createPQExpBuffer();
5188 AggInfo *agginfo;
5189 int i_tableoid;
5190 int i_oid;
5191 int i_aggname;
5192 int i_aggnamespace;
5193 int i_pronargs;
5194 int i_proargtypes;
5195 int i_rolname;
5196 int i_aggacl;
5197 int i_raggacl;
5198 int i_initaggacl;
5199 int i_initraggacl;
5200
5201 /*
5202 * Find all interesting aggregates. See comment in getFuncs() for the
5203 * rationale behind the filtering logic.
5204 */
5205 if (fout->remoteVersion >= 90600)
5206 {
5207 PQExpBuffer acl_subquery = createPQExpBuffer();
5208 PQExpBuffer racl_subquery = createPQExpBuffer();
5209 PQExpBuffer initacl_subquery = createPQExpBuffer();
5210 PQExpBuffer initracl_subquery = createPQExpBuffer();
5211
5212 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
5213 initracl_subquery, "p.proacl", "p.proowner", "'f'",
5214 dopt->binary_upgrade);
5215
5216 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
5217 "p.proname AS aggname, "
5218 "p.pronamespace AS aggnamespace, "
5219 "p.pronargs, p.proargtypes, "
5220 "(%s p.proowner) AS rolname, "
5221 "%s AS aggacl, "
5222 "%s AS raggacl, "
5223 "%s AS initaggacl, "
5224 "%s AS initraggacl "
5225 "FROM pg_proc p "
5226 "LEFT JOIN pg_init_privs pip ON "
5227 "(p.oid = pip.objoid "
5228 "AND pip.classoid = 'pg_proc'::regclass "
5229 "AND pip.objsubid = 0) "
5230 "WHERE p.proisagg AND ("
5231 "p.pronamespace != "
5232 "(SELECT oid FROM pg_namespace "
5233 "WHERE nspname = 'pg_catalog') OR "
5234 "p.proacl IS DISTINCT FROM pip.initprivs",
5235 username_subquery,
5236 acl_subquery->data,
5237 racl_subquery->data,
5238 initacl_subquery->data,
5239 initracl_subquery->data);
5240 if (dopt->binary_upgrade)
5241 appendPQExpBufferStr(query,
5242 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
5243 "classid = 'pg_proc'::regclass AND "
5244 "objid = p.oid AND "
5245 "refclassid = 'pg_extension'::regclass AND "
5246 "deptype = 'e')");
5247 appendPQExpBufferChar(query, ')');
5248
5249 destroyPQExpBuffer(acl_subquery);
5250 destroyPQExpBuffer(racl_subquery);
5251 destroyPQExpBuffer(initacl_subquery);
5252 destroyPQExpBuffer(initracl_subquery);
5253 }
5254 else if (fout->remoteVersion >= 80200)
5255 {
5256 appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
5257 "pronamespace AS aggnamespace, "
5258 "pronargs, proargtypes, "
5259 "(%s proowner) AS rolname, "
5260 "proacl AS aggacl, "
5261 "NULL AS raggacl, "
5262 "NULL AS initaggacl, NULL AS initraggacl "
5263 "FROM pg_proc p "
5264 "WHERE proisagg AND ("
5265 "pronamespace != "
5266 "(SELECT oid FROM pg_namespace "
5267 "WHERE nspname = 'pg_catalog')",
5268 username_subquery);
5269 if (dopt->binary_upgrade && fout->remoteVersion >= 90100)
5270 appendPQExpBufferStr(query,
5271 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
5272 "classid = 'pg_proc'::regclass AND "
5273 "objid = p.oid AND "
5274 "refclassid = 'pg_extension'::regclass AND "
5275 "deptype = 'e')");
5276 appendPQExpBufferChar(query, ')');
5277 }
5278 else
5279 {
5280 appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
5281 "pronamespace AS aggnamespace, "
5282 "CASE WHEN proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype THEN 0 ELSE 1 END AS pronargs, "
5283 "proargtypes, "
5284 "(%s proowner) AS rolname, "
5285 "proacl AS aggacl, "
5286 "NULL AS raggacl, "
5287 "NULL AS initaggacl, NULL AS initraggacl "
5288 "FROM pg_proc "
5289 "WHERE proisagg "
5290 "AND pronamespace != "
5291 "(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog')",
5292 username_subquery);
5293 }
5294
5295 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5296
5297 ntups = PQntuples(res);
5298 *numAggs = ntups;
5299
5300 agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
5301
5302 i_tableoid = PQfnumber(res, "tableoid");
5303 i_oid = PQfnumber(res, "oid");
5304 i_aggname = PQfnumber(res, "aggname");
5305 i_aggnamespace = PQfnumber(res, "aggnamespace");
5306 i_pronargs = PQfnumber(res, "pronargs");
5307 i_proargtypes = PQfnumber(res, "proargtypes");
5308 i_rolname = PQfnumber(res, "rolname");
5309 i_aggacl = PQfnumber(res, "aggacl");
5310 i_raggacl = PQfnumber(res, "raggacl");
5311 i_initaggacl = PQfnumber(res, "initaggacl");
5312 i_initraggacl = PQfnumber(res, "initraggacl");
5313
5314 for (i = 0; i < ntups; i++)
5315 {
5316 agginfo[i].aggfn.dobj.objType = DO_AGG;
5317 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5318 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5319 AssignDumpId(&agginfo[i].aggfn.dobj);
5320 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
5321 agginfo[i].aggfn.dobj.namespace =
5322 findNamespace(fout,
5323 atooid(PQgetvalue(res, i, i_aggnamespace)));
5324 agginfo[i].aggfn.rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5325 if (strlen(agginfo[i].aggfn.rolname) == 0)
5326 write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
5327 agginfo[i].aggfn.dobj.name);
5328 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
5329 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
5330 agginfo[i].aggfn.proacl = pg_strdup(PQgetvalue(res, i, i_aggacl));
5331 agginfo[i].aggfn.rproacl = pg_strdup(PQgetvalue(res, i, i_raggacl));
5332 agginfo[i].aggfn.initproacl = pg_strdup(PQgetvalue(res, i, i_initaggacl));
5333 agginfo[i].aggfn.initrproacl = pg_strdup(PQgetvalue(res, i, i_initraggacl));
5334 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
5335 if (agginfo[i].aggfn.nargs == 0)
5336 agginfo[i].aggfn.argtypes = NULL;
5337 else
5338 {
5339 agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
5340 parseOidArray(PQgetvalue(res, i, i_proargtypes),
5341 agginfo[i].aggfn.argtypes,
5342 agginfo[i].aggfn.nargs);
5343 }
5344
5345 /* Decide whether we want to dump it */
5346 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
5347
5348 /* Do not try to dump ACL if no ACL exists. */
5349 if (PQgetisnull(res, i, i_aggacl) && PQgetisnull(res, i, i_raggacl) &&
5350 PQgetisnull(res, i, i_initaggacl) &&
5351 PQgetisnull(res, i, i_initraggacl))
5352 agginfo[i].aggfn.dobj.dump &= ~DUMP_COMPONENT_ACL;
5353 }
5354
5355 PQclear(res);
5356
5357 destroyPQExpBuffer(query);
5358
5359 return agginfo;
5360 }
5361
5362 /*
5363 * getFuncs:
5364 * read all the user-defined functions in the system catalogs and
5365 * return them in the FuncInfo* structure
5366 *
5367 * numFuncs is set to the number of functions read in
5368 */
5369 FuncInfo *
getFuncs(Archive * fout,int * numFuncs)5370 getFuncs(Archive *fout, int *numFuncs)
5371 {
5372 DumpOptions *dopt = fout->dopt;
5373 PGresult *res;
5374 int ntups;
5375 int i;
5376 PQExpBuffer query = createPQExpBuffer();
5377 FuncInfo *finfo;
5378 int i_tableoid;
5379 int i_oid;
5380 int i_proname;
5381 int i_pronamespace;
5382 int i_rolname;
5383 int i_prolang;
5384 int i_pronargs;
5385 int i_proargtypes;
5386 int i_prorettype;
5387 int i_proacl;
5388 int i_rproacl;
5389 int i_initproacl;
5390 int i_initrproacl;
5391
5392 /*
5393 * Find all interesting functions. This is a bit complicated:
5394 *
5395 * 1. Always exclude aggregates; those are handled elsewhere.
5396 *
5397 * 2. Always exclude functions that are internally dependent on something
5398 * else, since presumably those will be created as a result of creating
5399 * the something else. This currently acts only to suppress constructor
5400 * functions for range types (so we only need it in 9.2 and up). Note
5401 * this is OK only because the constructors don't have any dependencies
5402 * the range type doesn't have; otherwise we might not get creation
5403 * ordering correct.
5404 *
5405 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
5406 * they're members of extensions and we are in binary-upgrade mode then
5407 * include them, since we want to dump extension members individually in
5408 * that mode. Also, if they are used by casts or transforms then we need
5409 * to gather the information about them, though they won't be dumped if
5410 * they are built-in. Also, in 9.6 and up, include functions in
5411 * pg_catalog if they have an ACL different from what's shown in
5412 * pg_init_privs.
5413 */
5414 if (fout->remoteVersion >= 90600)
5415 {
5416 PQExpBuffer acl_subquery = createPQExpBuffer();
5417 PQExpBuffer racl_subquery = createPQExpBuffer();
5418 PQExpBuffer initacl_subquery = createPQExpBuffer();
5419 PQExpBuffer initracl_subquery = createPQExpBuffer();
5420
5421 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
5422 initracl_subquery, "p.proacl", "p.proowner", "'f'",
5423 dopt->binary_upgrade);
5424
5425 appendPQExpBuffer(query,
5426 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
5427 "p.pronargs, p.proargtypes, p.prorettype, "
5428 "%s AS proacl, "
5429 "%s AS rproacl, "
5430 "%s AS initproacl, "
5431 "%s AS initrproacl, "
5432 "p.pronamespace, "
5433 "(%s p.proowner) AS rolname "
5434 "FROM pg_proc p "
5435 "LEFT JOIN pg_init_privs pip ON "
5436 "(p.oid = pip.objoid "
5437 "AND pip.classoid = 'pg_proc'::regclass "
5438 "AND pip.objsubid = 0) "
5439 "WHERE NOT proisagg"
5440 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
5441 "WHERE classid = 'pg_proc'::regclass AND "
5442 "objid = p.oid AND deptype = 'i')"
5443 "\n AND ("
5444 "\n pronamespace != "
5445 "(SELECT oid FROM pg_namespace "
5446 "WHERE nspname = 'pg_catalog')"
5447 "\n OR EXISTS (SELECT 1 FROM pg_cast"
5448 "\n WHERE pg_cast.oid > %u "
5449 "\n AND p.oid = pg_cast.castfunc)"
5450 "\n OR EXISTS (SELECT 1 FROM pg_transform"
5451 "\n WHERE pg_transform.oid > %u AND "
5452 "\n (p.oid = pg_transform.trffromsql"
5453 "\n OR p.oid = pg_transform.trftosql))",
5454 acl_subquery->data,
5455 racl_subquery->data,
5456 initacl_subquery->data,
5457 initracl_subquery->data,
5458 username_subquery,
5459 g_last_builtin_oid,
5460 g_last_builtin_oid);
5461 if (dopt->binary_upgrade)
5462 appendPQExpBufferStr(query,
5463 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
5464 "classid = 'pg_proc'::regclass AND "
5465 "objid = p.oid AND "
5466 "refclassid = 'pg_extension'::regclass AND "
5467 "deptype = 'e')");
5468 appendPQExpBufferStr(query,
5469 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
5470 appendPQExpBufferChar(query, ')');
5471
5472 destroyPQExpBuffer(acl_subquery);
5473 destroyPQExpBuffer(racl_subquery);
5474 destroyPQExpBuffer(initacl_subquery);
5475 destroyPQExpBuffer(initracl_subquery);
5476 }
5477 else
5478 {
5479 appendPQExpBuffer(query,
5480 "SELECT tableoid, oid, proname, prolang, "
5481 "pronargs, proargtypes, prorettype, proacl, "
5482 "NULL as rproacl, "
5483 "NULL as initproacl, NULL AS initrproacl, "
5484 "pronamespace, "
5485 "(%s proowner) AS rolname "
5486 "FROM pg_proc p "
5487 "WHERE NOT proisagg",
5488 username_subquery);
5489 if (fout->remoteVersion >= 90200)
5490 appendPQExpBufferStr(query,
5491 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
5492 "WHERE classid = 'pg_proc'::regclass AND "
5493 "objid = p.oid AND deptype = 'i')");
5494 appendPQExpBuffer(query,
5495 "\n AND ("
5496 "\n pronamespace != "
5497 "(SELECT oid FROM pg_namespace "
5498 "WHERE nspname = 'pg_catalog')"
5499 "\n OR EXISTS (SELECT 1 FROM pg_cast"
5500 "\n WHERE pg_cast.oid > '%u'::oid"
5501 "\n AND p.oid = pg_cast.castfunc)",
5502 g_last_builtin_oid);
5503
5504 if (fout->remoteVersion >= 90500)
5505 appendPQExpBuffer(query,
5506 "\n OR EXISTS (SELECT 1 FROM pg_transform"
5507 "\n WHERE pg_transform.oid > '%u'::oid"
5508 "\n AND (p.oid = pg_transform.trffromsql"
5509 "\n OR p.oid = pg_transform.trftosql))",
5510 g_last_builtin_oid);
5511
5512 if (dopt->binary_upgrade && fout->remoteVersion >= 90100)
5513 appendPQExpBufferStr(query,
5514 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
5515 "classid = 'pg_proc'::regclass AND "
5516 "objid = p.oid AND "
5517 "refclassid = 'pg_extension'::regclass AND "
5518 "deptype = 'e')");
5519 appendPQExpBufferChar(query, ')');
5520 }
5521
5522 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5523
5524 ntups = PQntuples(res);
5525
5526 *numFuncs = ntups;
5527
5528 finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
5529
5530 i_tableoid = PQfnumber(res, "tableoid");
5531 i_oid = PQfnumber(res, "oid");
5532 i_proname = PQfnumber(res, "proname");
5533 i_pronamespace = PQfnumber(res, "pronamespace");
5534 i_rolname = PQfnumber(res, "rolname");
5535 i_prolang = PQfnumber(res, "prolang");
5536 i_pronargs = PQfnumber(res, "pronargs");
5537 i_proargtypes = PQfnumber(res, "proargtypes");
5538 i_prorettype = PQfnumber(res, "prorettype");
5539 i_proacl = PQfnumber(res, "proacl");
5540 i_rproacl = PQfnumber(res, "rproacl");
5541 i_initproacl = PQfnumber(res, "initproacl");
5542 i_initrproacl = PQfnumber(res, "initrproacl");
5543
5544 for (i = 0; i < ntups; i++)
5545 {
5546 finfo[i].dobj.objType = DO_FUNC;
5547 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5548 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5549 AssignDumpId(&finfo[i].dobj);
5550 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
5551 finfo[i].dobj.namespace =
5552 findNamespace(fout,
5553 atooid(PQgetvalue(res, i, i_pronamespace)));
5554 finfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5555 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
5556 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
5557 finfo[i].proacl = pg_strdup(PQgetvalue(res, i, i_proacl));
5558 finfo[i].rproacl = pg_strdup(PQgetvalue(res, i, i_rproacl));
5559 finfo[i].initproacl = pg_strdup(PQgetvalue(res, i, i_initproacl));
5560 finfo[i].initrproacl = pg_strdup(PQgetvalue(res, i, i_initrproacl));
5561 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
5562 if (finfo[i].nargs == 0)
5563 finfo[i].argtypes = NULL;
5564 else
5565 {
5566 finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
5567 parseOidArray(PQgetvalue(res, i, i_proargtypes),
5568 finfo[i].argtypes, finfo[i].nargs);
5569 }
5570
5571 /* Decide whether we want to dump it */
5572 selectDumpableObject(&(finfo[i].dobj), fout);
5573
5574 /* Do not try to dump ACL if no ACL exists. */
5575 if (PQgetisnull(res, i, i_proacl) && PQgetisnull(res, i, i_rproacl) &&
5576 PQgetisnull(res, i, i_initproacl) &&
5577 PQgetisnull(res, i, i_initrproacl))
5578 finfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5579
5580 if (strlen(finfo[i].rolname) == 0)
5581 write_msg(NULL,
5582 "WARNING: owner of function \"%s\" appears to be invalid\n",
5583 finfo[i].dobj.name);
5584 }
5585
5586 PQclear(res);
5587
5588 destroyPQExpBuffer(query);
5589
5590 return finfo;
5591 }
5592
5593 /*
5594 * getTables
5595 * read all the tables (no indexes)
5596 * in the system catalogs return them in the TableInfo* structure
5597 *
5598 * numTables is set to the number of tables read in
5599 */
5600 TableInfo *
getTables(Archive * fout,int * numTables)5601 getTables(Archive *fout, int *numTables)
5602 {
5603 DumpOptions *dopt = fout->dopt;
5604 PGresult *res;
5605 int ntups;
5606 int i;
5607 PQExpBuffer query = createPQExpBuffer();
5608 TableInfo *tblinfo;
5609 int i_reltableoid;
5610 int i_reloid;
5611 int i_relname;
5612 int i_relnamespace;
5613 int i_relkind;
5614 int i_relacl;
5615 int i_rrelacl;
5616 int i_initrelacl;
5617 int i_initrrelacl;
5618 int i_rolname;
5619 int i_relchecks;
5620 int i_relhastriggers;
5621 int i_relhasindex;
5622 int i_relhasrules;
5623 int i_relrowsec;
5624 int i_relforcerowsec;
5625 int i_relhasoids;
5626 int i_relfrozenxid;
5627 int i_relminmxid;
5628 int i_toastoid;
5629 int i_toastfrozenxid;
5630 int i_toastminmxid;
5631 int i_relpersistence;
5632 int i_relispopulated;
5633 int i_relreplident;
5634 int i_owning_tab;
5635 int i_owning_col;
5636 int i_reltablespace;
5637 int i_reloptions;
5638 int i_checkoption;
5639 int i_toastreloptions;
5640 int i_reloftype;
5641 int i_relpages;
5642 int i_is_identity_sequence;
5643 int i_changed_acl;
5644 int i_partkeydef;
5645 int i_ispartition;
5646 int i_partbound;
5647
5648 /*
5649 * Find all the tables and table-like objects.
5650 *
5651 * We include system catalogs, so that we can work if a user table is
5652 * defined to inherit from a system catalog (pretty weird, but...)
5653 *
5654 * We ignore relations that are not ordinary tables, sequences, views,
5655 * materialized views, composite types, or foreign tables.
5656 *
5657 * Composite-type table entries won't be dumped as such, but we have to
5658 * make a DumpableObject for them so that we can track dependencies of the
5659 * composite type (pg_depend entries for columns of the composite type
5660 * link to the pg_class entry not the pg_type entry).
5661 *
5662 * Note: in this phase we should collect only a minimal amount of
5663 * information about each table, basically just enough to decide if it is
5664 * interesting. We must fetch all tables in this phase because otherwise
5665 * we cannot correctly identify inherited columns, owned sequences, etc.
5666 */
5667
5668 if (fout->remoteVersion >= 90600)
5669 {
5670 char *partkeydef = "NULL";
5671 char *ispartition = "false";
5672 char *partbound = "NULL";
5673
5674 PQExpBuffer acl_subquery = createPQExpBuffer();
5675 PQExpBuffer racl_subquery = createPQExpBuffer();
5676 PQExpBuffer initacl_subquery = createPQExpBuffer();
5677 PQExpBuffer initracl_subquery = createPQExpBuffer();
5678
5679 PQExpBuffer attacl_subquery = createPQExpBuffer();
5680 PQExpBuffer attracl_subquery = createPQExpBuffer();
5681 PQExpBuffer attinitacl_subquery = createPQExpBuffer();
5682 PQExpBuffer attinitracl_subquery = createPQExpBuffer();
5683
5684 /*
5685 * Collect the information about any partitioned tables, which were
5686 * added in PG10.
5687 */
5688
5689 if (fout->remoteVersion >= 100000)
5690 {
5691 partkeydef = "pg_get_partkeydef(c.oid)";
5692 ispartition = "c.relispartition";
5693 partbound = "pg_get_expr(c.relpartbound, c.oid)";
5694 }
5695
5696 /*
5697 * Left join to pick up dependency info linking sequences to their
5698 * owning column, if any (note this dependency is AUTO as of 8.2)
5699 *
5700 * Left join to detect if any privileges are still as-set-at-init, in
5701 * which case we won't dump out ACL commands for those.
5702 */
5703
5704 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
5705 initracl_subquery, "c.relacl", "c.relowner",
5706 "CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
5707 " THEN 's' ELSE 'r' END::\"char\"",
5708 dopt->binary_upgrade);
5709
5710 buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery,
5711 attinitracl_subquery, "at.attacl", "c.relowner", "'c'",
5712 dopt->binary_upgrade);
5713
5714 appendPQExpBuffer(query,
5715 "SELECT c.tableoid, c.oid, c.relname, "
5716 "%s AS relacl, %s as rrelacl, "
5717 "%s AS initrelacl, %s as initrrelacl, "
5718 "c.relkind, c.relnamespace, "
5719 "(%s c.relowner) AS rolname, "
5720 "c.relchecks, c.relhastriggers, "
5721 "c.relhasindex, c.relhasrules, c.relhasoids, "
5722 "c.relrowsecurity, c.relforcerowsecurity, "
5723 "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
5724 "tc.relfrozenxid AS tfrozenxid, "
5725 "tc.relminmxid AS tminmxid, "
5726 "c.relpersistence, c.relispopulated, "
5727 "c.relreplident, c.relpages, "
5728 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
5729 "d.refobjid AS owning_tab, "
5730 "d.refobjsubid AS owning_col, "
5731 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
5732 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
5733 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
5734 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
5735 "tc.reloptions AS toast_reloptions, "
5736 "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, "
5737 "EXISTS (SELECT 1 FROM pg_attribute at LEFT JOIN pg_init_privs pip ON "
5738 "(c.oid = pip.objoid "
5739 "AND pip.classoid = 'pg_class'::regclass "
5740 "AND pip.objsubid = at.attnum)"
5741 "WHERE at.attrelid = c.oid AND ("
5742 "%s IS NOT NULL "
5743 "OR %s IS NOT NULL "
5744 "OR %s IS NOT NULL "
5745 "OR %s IS NOT NULL"
5746 "))"
5747 "AS changed_acl, "
5748 "%s AS partkeydef, "
5749 "%s AS ispartition, "
5750 "%s AS partbound "
5751 "FROM pg_class c "
5752 "LEFT JOIN pg_depend d ON "
5753 "(c.relkind = '%c' AND "
5754 "d.classid = c.tableoid AND d.objid = c.oid AND "
5755 "d.objsubid = 0 AND "
5756 "d.refclassid = c.tableoid AND d.deptype IN ('a', 'i')) "
5757 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
5758 "LEFT JOIN pg_init_privs pip ON "
5759 "(c.oid = pip.objoid "
5760 "AND pip.classoid = 'pg_class'::regclass "
5761 "AND pip.objsubid = 0) "
5762 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') "
5763 "ORDER BY c.oid",
5764 acl_subquery->data,
5765 racl_subquery->data,
5766 initacl_subquery->data,
5767 initracl_subquery->data,
5768 username_subquery,
5769 RELKIND_SEQUENCE,
5770 attacl_subquery->data,
5771 attracl_subquery->data,
5772 attinitacl_subquery->data,
5773 attinitracl_subquery->data,
5774 partkeydef,
5775 ispartition,
5776 partbound,
5777 RELKIND_SEQUENCE,
5778 RELKIND_RELATION, RELKIND_SEQUENCE,
5779 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
5780 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
5781 RELKIND_PARTITIONED_TABLE);
5782
5783 destroyPQExpBuffer(acl_subquery);
5784 destroyPQExpBuffer(racl_subquery);
5785 destroyPQExpBuffer(initacl_subquery);
5786 destroyPQExpBuffer(initracl_subquery);
5787
5788 destroyPQExpBuffer(attacl_subquery);
5789 destroyPQExpBuffer(attracl_subquery);
5790 destroyPQExpBuffer(attinitacl_subquery);
5791 destroyPQExpBuffer(attinitracl_subquery);
5792 }
5793 else if (fout->remoteVersion >= 90500)
5794 {
5795 /*
5796 * Left join to pick up dependency info linking sequences to their
5797 * owning column, if any (note this dependency is AUTO as of 8.2)
5798 */
5799 appendPQExpBuffer(query,
5800 "SELECT c.tableoid, c.oid, c.relname, "
5801 "c.relacl, NULL as rrelacl, "
5802 "NULL AS initrelacl, NULL AS initrrelacl, "
5803 "c.relkind, "
5804 "c.relnamespace, "
5805 "(%s c.relowner) AS rolname, "
5806 "c.relchecks, c.relhastriggers, "
5807 "c.relhasindex, c.relhasrules, c.relhasoids, "
5808 "c.relrowsecurity, c.relforcerowsecurity, "
5809 "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
5810 "tc.relfrozenxid AS tfrozenxid, "
5811 "tc.relminmxid AS tminmxid, "
5812 "c.relpersistence, c.relispopulated, "
5813 "c.relreplident, c.relpages, "
5814 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
5815 "d.refobjid AS owning_tab, "
5816 "d.refobjsubid AS owning_col, "
5817 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
5818 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
5819 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
5820 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
5821 "tc.reloptions AS toast_reloptions, "
5822 "NULL AS changed_acl, "
5823 "NULL AS partkeydef, "
5824 "false AS ispartition, "
5825 "NULL AS partbound "
5826 "FROM pg_class c "
5827 "LEFT JOIN pg_depend d ON "
5828 "(c.relkind = '%c' AND "
5829 "d.classid = c.tableoid AND d.objid = c.oid AND "
5830 "d.objsubid = 0 AND "
5831 "d.refclassid = c.tableoid AND d.deptype = 'a') "
5832 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
5833 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
5834 "ORDER BY c.oid",
5835 username_subquery,
5836 RELKIND_SEQUENCE,
5837 RELKIND_RELATION, RELKIND_SEQUENCE,
5838 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
5839 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
5840 }
5841 else if (fout->remoteVersion >= 90400)
5842 {
5843 /*
5844 * Left join to pick up dependency info linking sequences to their
5845 * owning column, if any (note this dependency is AUTO as of 8.2)
5846 */
5847 appendPQExpBuffer(query,
5848 "SELECT c.tableoid, c.oid, c.relname, "
5849 "c.relacl, NULL as rrelacl, "
5850 "NULL AS initrelacl, NULL AS initrrelacl, "
5851 "c.relkind, "
5852 "c.relnamespace, "
5853 "(%s c.relowner) AS rolname, "
5854 "c.relchecks, c.relhastriggers, "
5855 "c.relhasindex, c.relhasrules, c.relhasoids, "
5856 "'f'::bool AS relrowsecurity, "
5857 "'f'::bool AS relforcerowsecurity, "
5858 "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
5859 "tc.relfrozenxid AS tfrozenxid, "
5860 "tc.relminmxid AS tminmxid, "
5861 "c.relpersistence, c.relispopulated, "
5862 "c.relreplident, c.relpages, "
5863 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
5864 "d.refobjid AS owning_tab, "
5865 "d.refobjsubid AS owning_col, "
5866 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
5867 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
5868 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
5869 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
5870 "tc.reloptions AS toast_reloptions, "
5871 "NULL AS changed_acl, "
5872 "NULL AS partkeydef, "
5873 "false AS ispartition, "
5874 "NULL AS partbound "
5875 "FROM pg_class c "
5876 "LEFT JOIN pg_depend d ON "
5877 "(c.relkind = '%c' AND "
5878 "d.classid = c.tableoid AND d.objid = c.oid AND "
5879 "d.objsubid = 0 AND "
5880 "d.refclassid = c.tableoid AND d.deptype = 'a') "
5881 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
5882 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
5883 "ORDER BY c.oid",
5884 username_subquery,
5885 RELKIND_SEQUENCE,
5886 RELKIND_RELATION, RELKIND_SEQUENCE,
5887 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
5888 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
5889 }
5890 else if (fout->remoteVersion >= 90300)
5891 {
5892 /*
5893 * Left join to pick up dependency info linking sequences to their
5894 * owning column, if any (note this dependency is AUTO as of 8.2)
5895 */
5896 appendPQExpBuffer(query,
5897 "SELECT c.tableoid, c.oid, c.relname, "
5898 "c.relacl, NULL as rrelacl, "
5899 "NULL AS initrelacl, NULL AS initrrelacl, "
5900 "c.relkind, "
5901 "c.relnamespace, "
5902 "(%s c.relowner) AS rolname, "
5903 "c.relchecks, c.relhastriggers, "
5904 "c.relhasindex, c.relhasrules, c.relhasoids, "
5905 "'f'::bool AS relrowsecurity, "
5906 "'f'::bool AS relforcerowsecurity, "
5907 "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
5908 "tc.relfrozenxid AS tfrozenxid, "
5909 "tc.relminmxid AS tminmxid, "
5910 "c.relpersistence, c.relispopulated, "
5911 "'d' AS relreplident, c.relpages, "
5912 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
5913 "d.refobjid AS owning_tab, "
5914 "d.refobjsubid AS owning_col, "
5915 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
5916 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
5917 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
5918 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
5919 "tc.reloptions AS toast_reloptions, "
5920 "NULL AS changed_acl, "
5921 "NULL AS partkeydef, "
5922 "false AS ispartition, "
5923 "NULL AS partbound "
5924 "FROM pg_class c "
5925 "LEFT JOIN pg_depend d ON "
5926 "(c.relkind = '%c' AND "
5927 "d.classid = c.tableoid AND d.objid = c.oid AND "
5928 "d.objsubid = 0 AND "
5929 "d.refclassid = c.tableoid AND d.deptype = 'a') "
5930 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
5931 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
5932 "ORDER BY c.oid",
5933 username_subquery,
5934 RELKIND_SEQUENCE,
5935 RELKIND_RELATION, RELKIND_SEQUENCE,
5936 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
5937 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
5938 }
5939 else if (fout->remoteVersion >= 90100)
5940 {
5941 /*
5942 * Left join to pick up dependency info linking sequences to their
5943 * owning column, if any (note this dependency is AUTO as of 8.2)
5944 */
5945 appendPQExpBuffer(query,
5946 "SELECT c.tableoid, c.oid, c.relname, "
5947 "c.relacl, NULL as rrelacl, "
5948 "NULL AS initrelacl, NULL AS initrrelacl, "
5949 "c.relkind, "
5950 "c.relnamespace, "
5951 "(%s c.relowner) AS rolname, "
5952 "c.relchecks, c.relhastriggers, "
5953 "c.relhasindex, c.relhasrules, c.relhasoids, "
5954 "'f'::bool AS relrowsecurity, "
5955 "'f'::bool AS relforcerowsecurity, "
5956 "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
5957 "tc.relfrozenxid AS tfrozenxid, "
5958 "0 AS tminmxid, "
5959 "c.relpersistence, 't' as relispopulated, "
5960 "'d' AS relreplident, c.relpages, "
5961 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
5962 "d.refobjid AS owning_tab, "
5963 "d.refobjsubid AS owning_col, "
5964 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
5965 "c.reloptions AS reloptions, "
5966 "tc.reloptions AS toast_reloptions, "
5967 "NULL AS changed_acl, "
5968 "NULL AS partkeydef, "
5969 "false AS ispartition, "
5970 "NULL AS partbound "
5971 "FROM pg_class c "
5972 "LEFT JOIN pg_depend d ON "
5973 "(c.relkind = '%c' AND "
5974 "d.classid = c.tableoid AND d.objid = c.oid AND "
5975 "d.objsubid = 0 AND "
5976 "d.refclassid = c.tableoid AND d.deptype = 'a') "
5977 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
5978 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
5979 "ORDER BY c.oid",
5980 username_subquery,
5981 RELKIND_SEQUENCE,
5982 RELKIND_RELATION, RELKIND_SEQUENCE,
5983 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
5984 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
5985 }
5986 else if (fout->remoteVersion >= 90000)
5987 {
5988 /*
5989 * Left join to pick up dependency info linking sequences to their
5990 * owning column, if any (note this dependency is AUTO as of 8.2)
5991 */
5992 appendPQExpBuffer(query,
5993 "SELECT c.tableoid, c.oid, c.relname, "
5994 "c.relacl, NULL as rrelacl, "
5995 "NULL AS initrelacl, NULL AS initrrelacl, "
5996 "c.relkind, "
5997 "c.relnamespace, "
5998 "(%s c.relowner) AS rolname, "
5999 "c.relchecks, c.relhastriggers, "
6000 "c.relhasindex, c.relhasrules, c.relhasoids, "
6001 "'f'::bool AS relrowsecurity, "
6002 "'f'::bool AS relforcerowsecurity, "
6003 "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
6004 "tc.relfrozenxid AS tfrozenxid, "
6005 "0 AS tminmxid, "
6006 "'p' AS relpersistence, 't' as relispopulated, "
6007 "'d' AS relreplident, c.relpages, "
6008 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
6009 "d.refobjid AS owning_tab, "
6010 "d.refobjsubid AS owning_col, "
6011 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6012 "c.reloptions AS reloptions, "
6013 "tc.reloptions AS toast_reloptions, "
6014 "NULL AS changed_acl, "
6015 "NULL AS partkeydef, "
6016 "false AS ispartition, "
6017 "NULL AS partbound "
6018 "FROM pg_class c "
6019 "LEFT JOIN pg_depend d ON "
6020 "(c.relkind = '%c' AND "
6021 "d.classid = c.tableoid AND d.objid = c.oid AND "
6022 "d.objsubid = 0 AND "
6023 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6024 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6025 "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
6026 "ORDER BY c.oid",
6027 username_subquery,
6028 RELKIND_SEQUENCE,
6029 RELKIND_RELATION, RELKIND_SEQUENCE,
6030 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
6031 }
6032 else if (fout->remoteVersion >= 80400)
6033 {
6034 /*
6035 * Left join to pick up dependency info linking sequences to their
6036 * owning column, if any (note this dependency is AUTO as of 8.2)
6037 */
6038 appendPQExpBuffer(query,
6039 "SELECT c.tableoid, c.oid, c.relname, "
6040 "c.relacl, NULL as rrelacl, "
6041 "NULL AS initrelacl, NULL AS initrrelacl, "
6042 "c.relkind, "
6043 "c.relnamespace, "
6044 "(%s c.relowner) AS rolname, "
6045 "c.relchecks, c.relhastriggers, "
6046 "c.relhasindex, c.relhasrules, c.relhasoids, "
6047 "'f'::bool AS relrowsecurity, "
6048 "'f'::bool AS relforcerowsecurity, "
6049 "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
6050 "tc.relfrozenxid AS tfrozenxid, "
6051 "0 AS tminmxid, "
6052 "'p' AS relpersistence, 't' as relispopulated, "
6053 "'d' AS relreplident, c.relpages, "
6054 "NULL AS reloftype, "
6055 "d.refobjid AS owning_tab, "
6056 "d.refobjsubid AS owning_col, "
6057 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6058 "c.reloptions AS reloptions, "
6059 "tc.reloptions AS toast_reloptions, "
6060 "NULL AS changed_acl, "
6061 "NULL AS partkeydef, "
6062 "false AS ispartition, "
6063 "NULL AS partbound "
6064 "FROM pg_class c "
6065 "LEFT JOIN pg_depend d ON "
6066 "(c.relkind = '%c' AND "
6067 "d.classid = c.tableoid AND d.objid = c.oid AND "
6068 "d.objsubid = 0 AND "
6069 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6070 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6071 "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
6072 "ORDER BY c.oid",
6073 username_subquery,
6074 RELKIND_SEQUENCE,
6075 RELKIND_RELATION, RELKIND_SEQUENCE,
6076 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
6077 }
6078 else if (fout->remoteVersion >= 80200)
6079 {
6080 /*
6081 * Left join to pick up dependency info linking sequences to their
6082 * owning column, if any (note this dependency is AUTO as of 8.2)
6083 */
6084 appendPQExpBuffer(query,
6085 "SELECT c.tableoid, c.oid, c.relname, "
6086 "c.relacl, NULL as rrelacl, "
6087 "NULL AS initrelacl, NULL AS initrrelacl, "
6088 "c.relkind, "
6089 "c.relnamespace, "
6090 "(%s c.relowner) AS rolname, "
6091 "c.relchecks, (c.reltriggers <> 0) AS relhastriggers, "
6092 "c.relhasindex, c.relhasrules, c.relhasoids, "
6093 "'f'::bool AS relrowsecurity, "
6094 "'f'::bool AS relforcerowsecurity, "
6095 "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
6096 "tc.relfrozenxid AS tfrozenxid, "
6097 "0 AS tminmxid, "
6098 "'p' AS relpersistence, 't' as relispopulated, "
6099 "'d' AS relreplident, c.relpages, "
6100 "NULL AS reloftype, "
6101 "d.refobjid AS owning_tab, "
6102 "d.refobjsubid AS owning_col, "
6103 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6104 "c.reloptions AS reloptions, "
6105 "NULL AS toast_reloptions, "
6106 "NULL AS changed_acl, "
6107 "NULL AS partkeydef, "
6108 "false AS ispartition, "
6109 "NULL AS partbound "
6110 "FROM pg_class c "
6111 "LEFT JOIN pg_depend d ON "
6112 "(c.relkind = '%c' AND "
6113 "d.classid = c.tableoid AND d.objid = c.oid AND "
6114 "d.objsubid = 0 AND "
6115 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6116 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6117 "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
6118 "ORDER BY c.oid",
6119 username_subquery,
6120 RELKIND_SEQUENCE,
6121 RELKIND_RELATION, RELKIND_SEQUENCE,
6122 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
6123 }
6124 else
6125 {
6126 /*
6127 * Left join to pick up dependency info linking sequences to their
6128 * owning column, if any
6129 */
6130 appendPQExpBuffer(query,
6131 "SELECT c.tableoid, c.oid, relname, "
6132 "relacl, NULL as rrelacl, "
6133 "NULL AS initrelacl, NULL AS initrrelacl, "
6134 "relkind, relnamespace, "
6135 "(%s relowner) AS rolname, "
6136 "relchecks, (reltriggers <> 0) AS relhastriggers, "
6137 "relhasindex, relhasrules, relhasoids, "
6138 "'f'::bool AS relrowsecurity, "
6139 "'f'::bool AS relforcerowsecurity, "
6140 "0 AS relfrozenxid, 0 AS relminmxid,"
6141 "0 AS toid, "
6142 "0 AS tfrozenxid, 0 AS tminmxid,"
6143 "'p' AS relpersistence, 't' as relispopulated, "
6144 "'d' AS relreplident, relpages, "
6145 "NULL AS reloftype, "
6146 "d.refobjid AS owning_tab, "
6147 "d.refobjsubid AS owning_col, "
6148 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6149 "NULL AS reloptions, "
6150 "NULL AS toast_reloptions, "
6151 "NULL AS changed_acl, "
6152 "NULL AS partkeydef, "
6153 "false AS ispartition, "
6154 "NULL AS partbound "
6155 "FROM pg_class c "
6156 "LEFT JOIN pg_depend d ON "
6157 "(c.relkind = '%c' AND "
6158 "d.classid = c.tableoid AND d.objid = c.oid AND "
6159 "d.objsubid = 0 AND "
6160 "d.refclassid = c.tableoid AND d.deptype = 'i') "
6161 "WHERE relkind in ('%c', '%c', '%c', '%c') "
6162 "ORDER BY c.oid",
6163 username_subquery,
6164 RELKIND_SEQUENCE,
6165 RELKIND_RELATION, RELKIND_SEQUENCE,
6166 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
6167 }
6168
6169 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6170
6171 ntups = PQntuples(res);
6172
6173 *numTables = ntups;
6174
6175 /*
6176 * Extract data from result and lock dumpable tables. We do the locking
6177 * before anything else, to minimize the window wherein a table could
6178 * disappear under us.
6179 *
6180 * Note that we have to save info about all tables here, even when dumping
6181 * only one, because we don't yet know which tables might be inheritance
6182 * ancestors of the target table.
6183 */
6184 tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
6185
6186 i_reltableoid = PQfnumber(res, "tableoid");
6187 i_reloid = PQfnumber(res, "oid");
6188 i_relname = PQfnumber(res, "relname");
6189 i_relnamespace = PQfnumber(res, "relnamespace");
6190 i_relacl = PQfnumber(res, "relacl");
6191 i_rrelacl = PQfnumber(res, "rrelacl");
6192 i_initrelacl = PQfnumber(res, "initrelacl");
6193 i_initrrelacl = PQfnumber(res, "initrrelacl");
6194 i_relkind = PQfnumber(res, "relkind");
6195 i_rolname = PQfnumber(res, "rolname");
6196 i_relchecks = PQfnumber(res, "relchecks");
6197 i_relhastriggers = PQfnumber(res, "relhastriggers");
6198 i_relhasindex = PQfnumber(res, "relhasindex");
6199 i_relhasrules = PQfnumber(res, "relhasrules");
6200 i_relrowsec = PQfnumber(res, "relrowsecurity");
6201 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
6202 i_relhasoids = PQfnumber(res, "relhasoids");
6203 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
6204 i_relminmxid = PQfnumber(res, "relminmxid");
6205 i_toastoid = PQfnumber(res, "toid");
6206 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
6207 i_toastminmxid = PQfnumber(res, "tminmxid");
6208 i_relpersistence = PQfnumber(res, "relpersistence");
6209 i_relispopulated = PQfnumber(res, "relispopulated");
6210 i_relreplident = PQfnumber(res, "relreplident");
6211 i_relpages = PQfnumber(res, "relpages");
6212 i_owning_tab = PQfnumber(res, "owning_tab");
6213 i_owning_col = PQfnumber(res, "owning_col");
6214 i_reltablespace = PQfnumber(res, "reltablespace");
6215 i_reloptions = PQfnumber(res, "reloptions");
6216 i_checkoption = PQfnumber(res, "checkoption");
6217 i_toastreloptions = PQfnumber(res, "toast_reloptions");
6218 i_reloftype = PQfnumber(res, "reloftype");
6219 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
6220 i_changed_acl = PQfnumber(res, "changed_acl");
6221 i_partkeydef = PQfnumber(res, "partkeydef");
6222 i_ispartition = PQfnumber(res, "ispartition");
6223 i_partbound = PQfnumber(res, "partbound");
6224
6225 if (dopt->lockWaitTimeout)
6226 {
6227 /*
6228 * Arrange to fail instead of waiting forever for a table lock.
6229 *
6230 * NB: this coding assumes that the only queries issued within the
6231 * following loop are LOCK TABLEs; else the timeout may be undesirably
6232 * applied to other things too.
6233 */
6234 resetPQExpBuffer(query);
6235 appendPQExpBufferStr(query, "SET statement_timeout = ");
6236 appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
6237 ExecuteSqlStatement(fout, query->data);
6238 }
6239
6240 for (i = 0; i < ntups; i++)
6241 {
6242 tblinfo[i].dobj.objType = DO_TABLE;
6243 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
6244 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
6245 AssignDumpId(&tblinfo[i].dobj);
6246 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
6247 tblinfo[i].dobj.namespace =
6248 findNamespace(fout,
6249 atooid(PQgetvalue(res, i, i_relnamespace)));
6250 tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
6251 tblinfo[i].relacl = pg_strdup(PQgetvalue(res, i, i_relacl));
6252 tblinfo[i].rrelacl = pg_strdup(PQgetvalue(res, i, i_rrelacl));
6253 tblinfo[i].initrelacl = pg_strdup(PQgetvalue(res, i, i_initrelacl));
6254 tblinfo[i].initrrelacl = pg_strdup(PQgetvalue(res, i, i_initrrelacl));
6255 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
6256 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
6257 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
6258 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
6259 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
6260 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
6261 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
6262 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
6263 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
6264 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
6265 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
6266 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
6267 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
6268 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
6269 tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
6270 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
6271 if (PQgetisnull(res, i, i_reloftype))
6272 tblinfo[i].reloftype = NULL;
6273 else
6274 tblinfo[i].reloftype = pg_strdup(PQgetvalue(res, i, i_reloftype));
6275 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
6276 if (PQgetisnull(res, i, i_owning_tab))
6277 {
6278 tblinfo[i].owning_tab = InvalidOid;
6279 tblinfo[i].owning_col = 0;
6280 }
6281 else
6282 {
6283 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
6284 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
6285 }
6286 tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
6287 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
6288 if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
6289 tblinfo[i].checkoption = NULL;
6290 else
6291 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
6292 tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
6293
6294 /* other fields were zeroed above */
6295
6296 /*
6297 * Decide whether we want to dump this table.
6298 */
6299 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
6300 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
6301 else
6302 selectDumpableTable(&tblinfo[i], fout);
6303
6304 /*
6305 * If the table-level and all column-level ACLs for this table are
6306 * unchanged, then we don't need to worry about including the ACLs for
6307 * this table. If any column-level ACLs have been changed, the
6308 * 'changed_acl' column from the query will indicate that.
6309 *
6310 * This can result in a significant performance improvement in cases
6311 * where we are only looking to dump out the ACL (eg: pg_catalog).
6312 */
6313 if (PQgetisnull(res, i, i_relacl) && PQgetisnull(res, i, i_rrelacl) &&
6314 PQgetisnull(res, i, i_initrelacl) &&
6315 PQgetisnull(res, i, i_initrrelacl) &&
6316 strcmp(PQgetvalue(res, i, i_changed_acl), "f") == 0)
6317 tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
6318
6319 tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false;
6320 tblinfo[i].dummy_view = false; /* might get set during sort */
6321 tblinfo[i].postponed_def = false; /* might get set during sort */
6322
6323 tblinfo[i].is_identity_sequence = (i_is_identity_sequence >= 0 &&
6324 strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
6325
6326 /* Partition key string or NULL */
6327 tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef));
6328 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
6329 tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound));
6330
6331 /*
6332 * Read-lock target tables to make sure they aren't DROPPED or altered
6333 * in schema before we get around to dumping them.
6334 *
6335 * Note that we don't explicitly lock parents of the target tables; we
6336 * assume our lock on the child is enough to prevent schema
6337 * alterations to parent tables.
6338 *
6339 * NOTE: it'd be kinda nice to lock other relations too, not only
6340 * plain or partitioned tables, but the backend doesn't presently
6341 * allow that.
6342 *
6343 * We only need to lock the table for certain components; see
6344 * pg_dump.h
6345 */
6346 if (tblinfo[i].dobj.dump &&
6347 (tblinfo[i].relkind == RELKIND_RELATION ||
6348 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE) &&
6349 (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK))
6350 {
6351 resetPQExpBuffer(query);
6352 appendPQExpBuffer(query,
6353 "LOCK TABLE %s IN ACCESS SHARE MODE",
6354 fmtQualifiedDumpable(&tblinfo[i]));
6355 ExecuteSqlStatement(fout, query->data);
6356 }
6357
6358 /* Emit notice if join for owner failed */
6359 if (strlen(tblinfo[i].rolname) == 0)
6360 write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n",
6361 tblinfo[i].dobj.name);
6362 }
6363
6364 if (dopt->lockWaitTimeout)
6365 {
6366 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
6367 }
6368
6369 PQclear(res);
6370
6371 destroyPQExpBuffer(query);
6372
6373 return tblinfo;
6374 }
6375
6376 /*
6377 * getOwnedSeqs
6378 * identify owned sequences and mark them as dumpable if owning table is
6379 *
6380 * We used to do this in getTables(), but it's better to do it after the
6381 * index used by findTableByOid() has been set up.
6382 */
6383 void
getOwnedSeqs(Archive * fout,TableInfo tblinfo[],int numTables)6384 getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
6385 {
6386 int i;
6387
6388 /*
6389 * Force sequences that are "owned" by table columns to be dumped whenever
6390 * their owning table is being dumped.
6391 */
6392 for (i = 0; i < numTables; i++)
6393 {
6394 TableInfo *seqinfo = &tblinfo[i];
6395 TableInfo *owning_tab;
6396
6397 if (!OidIsValid(seqinfo->owning_tab))
6398 continue; /* not an owned sequence */
6399
6400 owning_tab = findTableByOid(seqinfo->owning_tab);
6401 if (owning_tab == NULL)
6402 exit_horribly(NULL, "failed sanity check, parent table with OID %u of sequence with OID %u not found\n",
6403 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
6404
6405 /*
6406 * Only dump identity sequences if we're going to dump the table that
6407 * it belongs to.
6408 */
6409 if (owning_tab->dobj.dump == DUMP_COMPONENT_NONE &&
6410 seqinfo->is_identity_sequence)
6411 {
6412 seqinfo->dobj.dump = DUMP_COMPONENT_NONE;
6413 continue;
6414 }
6415
6416 /*
6417 * Otherwise we need to dump the components that are being dumped for
6418 * the table and any components which the sequence is explicitly
6419 * marked with.
6420 *
6421 * We can't simply use the set of components which are being dumped
6422 * for the table as the table might be in an extension (and only the
6423 * non-extension components, eg: ACLs if changed, security labels, and
6424 * policies, are being dumped) while the sequence is not (and
6425 * therefore the definition and other components should also be
6426 * dumped).
6427 *
6428 * If the sequence is part of the extension then it should be properly
6429 * marked by checkExtensionMembership() and this will be a no-op as
6430 * the table will be equivalently marked.
6431 */
6432 seqinfo->dobj.dump = seqinfo->dobj.dump | owning_tab->dobj.dump;
6433
6434 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
6435 seqinfo->interesting = true;
6436 }
6437 }
6438
6439 /*
6440 * getInherits
6441 * read all the inheritance information
6442 * from the system catalogs return them in the InhInfo* structure
6443 *
6444 * numInherits is set to the number of pairs read in
6445 */
6446 InhInfo *
getInherits(Archive * fout,int * numInherits)6447 getInherits(Archive *fout, int *numInherits)
6448 {
6449 PGresult *res;
6450 int ntups;
6451 int i;
6452 PQExpBuffer query = createPQExpBuffer();
6453 InhInfo *inhinfo;
6454
6455 int i_inhrelid;
6456 int i_inhparent;
6457
6458 /*
6459 * Find all the inheritance information, excluding implicit inheritance
6460 * via partitioning. We handle that case using getPartitions(), because
6461 * we want more information about partitions than just the parent-child
6462 * relationship.
6463 */
6464 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
6465
6466 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6467
6468 ntups = PQntuples(res);
6469
6470 *numInherits = ntups;
6471
6472 inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
6473
6474 i_inhrelid = PQfnumber(res, "inhrelid");
6475 i_inhparent = PQfnumber(res, "inhparent");
6476
6477 for (i = 0; i < ntups; i++)
6478 {
6479 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
6480 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
6481 }
6482
6483 PQclear(res);
6484
6485 destroyPQExpBuffer(query);
6486
6487 return inhinfo;
6488 }
6489
6490 /*
6491 * getIndexes
6492 * get information about every index on a dumpable table
6493 *
6494 * Note: index data is not returned directly to the caller, but it
6495 * does get entered into the DumpableObject tables.
6496 */
6497 void
getIndexes(Archive * fout,TableInfo tblinfo[],int numTables)6498 getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
6499 {
6500 int i,
6501 j;
6502 PQExpBuffer query = createPQExpBuffer();
6503 PGresult *res;
6504 IndxInfo *indxinfo;
6505 ConstraintInfo *constrinfo;
6506 int i_tableoid,
6507 i_oid,
6508 i_indexname,
6509 i_indexdef,
6510 i_indnkeys,
6511 i_indkey,
6512 i_indisclustered,
6513 i_indisreplident,
6514 i_contype,
6515 i_conname,
6516 i_condeferrable,
6517 i_condeferred,
6518 i_contableoid,
6519 i_conoid,
6520 i_condef,
6521 i_tablespace,
6522 i_indreloptions,
6523 i_relpages;
6524 int ntups;
6525
6526 for (i = 0; i < numTables; i++)
6527 {
6528 TableInfo *tbinfo = &tblinfo[i];
6529
6530 /* Only plain tables and materialized views have indexes. */
6531 if (tbinfo->relkind != RELKIND_RELATION &&
6532 tbinfo->relkind != RELKIND_MATVIEW)
6533 continue;
6534 if (!tbinfo->hasindex)
6535 continue;
6536
6537 /* Ignore indexes of tables whose definitions are not to be dumped */
6538 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
6539 continue;
6540
6541 if (g_verbose)
6542 write_msg(NULL, "reading indexes for table \"%s.%s\"\n",
6543 tbinfo->dobj.namespace->dobj.name,
6544 tbinfo->dobj.name);
6545
6546 /*
6547 * The point of the messy-looking outer join is to find a constraint
6548 * that is related by an internal dependency link to the index. If we
6549 * find one, create a CONSTRAINT entry linked to the INDEX entry. We
6550 * assume an index won't have more than one internal dependency.
6551 *
6552 * As of 9.0 we don't need to look at pg_depend but can check for a
6553 * match to pg_constraint.conindid. The check on conrelid is
6554 * redundant but useful because that column is indexed while conindid
6555 * is not.
6556 */
6557 resetPQExpBuffer(query);
6558 if (fout->remoteVersion >= 90400)
6559 {
6560 /*
6561 * the test on indisready is necessary in 9.2, and harmless in
6562 * earlier/later versions
6563 */
6564 appendPQExpBuffer(query,
6565 "SELECT t.tableoid, t.oid, "
6566 "t.relname AS indexname, "
6567 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
6568 "t.relnatts AS indnkeys, "
6569 "i.indkey, i.indisclustered, "
6570 "i.indisreplident, t.relpages, "
6571 "c.contype, c.conname, "
6572 "c.condeferrable, c.condeferred, "
6573 "c.tableoid AS contableoid, "
6574 "c.oid AS conoid, "
6575 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
6576 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
6577 "t.reloptions AS indreloptions "
6578 "FROM pg_catalog.pg_index i "
6579 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
6580 "LEFT JOIN pg_catalog.pg_constraint c "
6581 "ON (i.indrelid = c.conrelid AND "
6582 "i.indexrelid = c.conindid AND "
6583 "c.contype IN ('p','u','x')) "
6584 "WHERE i.indrelid = '%u'::pg_catalog.oid "
6585 "AND i.indisvalid AND i.indisready "
6586 "ORDER BY indexname",
6587 tbinfo->dobj.catId.oid);
6588 }
6589 else if (fout->remoteVersion >= 90000)
6590 {
6591 /*
6592 * the test on indisready is necessary in 9.2, and harmless in
6593 * earlier/later versions
6594 */
6595 appendPQExpBuffer(query,
6596 "SELECT t.tableoid, t.oid, "
6597 "t.relname AS indexname, "
6598 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
6599 "t.relnatts AS indnkeys, "
6600 "i.indkey, i.indisclustered, "
6601 "false AS indisreplident, t.relpages, "
6602 "c.contype, c.conname, "
6603 "c.condeferrable, c.condeferred, "
6604 "c.tableoid AS contableoid, "
6605 "c.oid AS conoid, "
6606 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
6607 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
6608 "t.reloptions AS indreloptions "
6609 "FROM pg_catalog.pg_index i "
6610 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
6611 "LEFT JOIN pg_catalog.pg_constraint c "
6612 "ON (i.indrelid = c.conrelid AND "
6613 "i.indexrelid = c.conindid AND "
6614 "c.contype IN ('p','u','x')) "
6615 "WHERE i.indrelid = '%u'::pg_catalog.oid "
6616 "AND i.indisvalid AND i.indisready "
6617 "ORDER BY indexname",
6618 tbinfo->dobj.catId.oid);
6619 }
6620 else if (fout->remoteVersion >= 80200)
6621 {
6622 appendPQExpBuffer(query,
6623 "SELECT t.tableoid, t.oid, "
6624 "t.relname AS indexname, "
6625 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
6626 "t.relnatts AS indnkeys, "
6627 "i.indkey, i.indisclustered, "
6628 "false AS indisreplident, t.relpages, "
6629 "c.contype, c.conname, "
6630 "c.condeferrable, c.condeferred, "
6631 "c.tableoid AS contableoid, "
6632 "c.oid AS conoid, "
6633 "null AS condef, "
6634 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
6635 "t.reloptions AS indreloptions "
6636 "FROM pg_catalog.pg_index i "
6637 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
6638 "LEFT JOIN pg_catalog.pg_depend d "
6639 "ON (d.classid = t.tableoid "
6640 "AND d.objid = t.oid "
6641 "AND d.deptype = 'i') "
6642 "LEFT JOIN pg_catalog.pg_constraint c "
6643 "ON (d.refclassid = c.tableoid "
6644 "AND d.refobjid = c.oid) "
6645 "WHERE i.indrelid = '%u'::pg_catalog.oid "
6646 "AND i.indisvalid "
6647 "ORDER BY indexname",
6648 tbinfo->dobj.catId.oid);
6649 }
6650 else
6651 {
6652 appendPQExpBuffer(query,
6653 "SELECT t.tableoid, t.oid, "
6654 "t.relname AS indexname, "
6655 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
6656 "t.relnatts AS indnkeys, "
6657 "i.indkey, i.indisclustered, "
6658 "false AS indisreplident, t.relpages, "
6659 "c.contype, c.conname, "
6660 "c.condeferrable, c.condeferred, "
6661 "c.tableoid AS contableoid, "
6662 "c.oid AS conoid, "
6663 "null AS condef, "
6664 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
6665 "null AS indreloptions "
6666 "FROM pg_catalog.pg_index i "
6667 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
6668 "LEFT JOIN pg_catalog.pg_depend d "
6669 "ON (d.classid = t.tableoid "
6670 "AND d.objid = t.oid "
6671 "AND d.deptype = 'i') "
6672 "LEFT JOIN pg_catalog.pg_constraint c "
6673 "ON (d.refclassid = c.tableoid "
6674 "AND d.refobjid = c.oid) "
6675 "WHERE i.indrelid = '%u'::pg_catalog.oid "
6676 "ORDER BY indexname",
6677 tbinfo->dobj.catId.oid);
6678 }
6679
6680 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6681
6682 ntups = PQntuples(res);
6683
6684 i_tableoid = PQfnumber(res, "tableoid");
6685 i_oid = PQfnumber(res, "oid");
6686 i_indexname = PQfnumber(res, "indexname");
6687 i_indexdef = PQfnumber(res, "indexdef");
6688 i_indnkeys = PQfnumber(res, "indnkeys");
6689 i_indkey = PQfnumber(res, "indkey");
6690 i_indisclustered = PQfnumber(res, "indisclustered");
6691 i_indisreplident = PQfnumber(res, "indisreplident");
6692 i_relpages = PQfnumber(res, "relpages");
6693 i_contype = PQfnumber(res, "contype");
6694 i_conname = PQfnumber(res, "conname");
6695 i_condeferrable = PQfnumber(res, "condeferrable");
6696 i_condeferred = PQfnumber(res, "condeferred");
6697 i_contableoid = PQfnumber(res, "contableoid");
6698 i_conoid = PQfnumber(res, "conoid");
6699 i_condef = PQfnumber(res, "condef");
6700 i_tablespace = PQfnumber(res, "tablespace");
6701 i_indreloptions = PQfnumber(res, "indreloptions");
6702
6703 indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
6704 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
6705
6706 for (j = 0; j < ntups; j++)
6707 {
6708 char contype;
6709
6710 indxinfo[j].dobj.objType = DO_INDEX;
6711 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
6712 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
6713 AssignDumpId(&indxinfo[j].dobj);
6714 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
6715 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
6716 indxinfo[j].indextable = tbinfo;
6717 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
6718 indxinfo[j].indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
6719 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
6720 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
6721 indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnkeys * sizeof(Oid));
6722 parseOidArray(PQgetvalue(res, j, i_indkey),
6723 indxinfo[j].indkeys, indxinfo[j].indnkeys);
6724 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
6725 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
6726 indxinfo[j].relpages = atoi(PQgetvalue(res, j, i_relpages));
6727 contype = *(PQgetvalue(res, j, i_contype));
6728
6729 if (contype == 'p' || contype == 'u' || contype == 'x')
6730 {
6731 /*
6732 * If we found a constraint matching the index, create an
6733 * entry for it.
6734 */
6735 constrinfo[j].dobj.objType = DO_CONSTRAINT;
6736 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
6737 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
6738 AssignDumpId(&constrinfo[j].dobj);
6739 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
6740 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
6741 constrinfo[j].contable = tbinfo;
6742 constrinfo[j].condomain = NULL;
6743 constrinfo[j].contype = contype;
6744 if (contype == 'x')
6745 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
6746 else
6747 constrinfo[j].condef = NULL;
6748 constrinfo[j].confrelid = InvalidOid;
6749 constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
6750 constrinfo[j].condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
6751 constrinfo[j].condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
6752 constrinfo[j].conislocal = true;
6753 constrinfo[j].separate = true;
6754
6755 indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId;
6756 }
6757 else
6758 {
6759 /* Plain secondary index */
6760 indxinfo[j].indexconstraint = 0;
6761 }
6762 }
6763
6764 PQclear(res);
6765 }
6766
6767 destroyPQExpBuffer(query);
6768 }
6769
6770 /*
6771 * getExtendedStatistics
6772 * get information about extended-statistics objects.
6773 *
6774 * Note: extended statistics data is not returned directly to the caller, but
6775 * it does get entered into the DumpableObject tables.
6776 */
6777 void
getExtendedStatistics(Archive * fout)6778 getExtendedStatistics(Archive *fout)
6779 {
6780 PQExpBuffer query;
6781 PGresult *res;
6782 StatsExtInfo *statsextinfo;
6783 int ntups;
6784 int i_tableoid;
6785 int i_oid;
6786 int i_stxname;
6787 int i_stxnamespace;
6788 int i_rolname;
6789 int i;
6790
6791 /* Extended statistics were new in v10 */
6792 if (fout->remoteVersion < 100000)
6793 return;
6794
6795 query = createPQExpBuffer();
6796
6797 appendPQExpBuffer(query, "SELECT tableoid, oid, stxname, "
6798 "stxnamespace, (%s stxowner) AS rolname "
6799 "FROM pg_catalog.pg_statistic_ext",
6800 username_subquery);
6801
6802 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6803
6804 ntups = PQntuples(res);
6805
6806 i_tableoid = PQfnumber(res, "tableoid");
6807 i_oid = PQfnumber(res, "oid");
6808 i_stxname = PQfnumber(res, "stxname");
6809 i_stxnamespace = PQfnumber(res, "stxnamespace");
6810 i_rolname = PQfnumber(res, "rolname");
6811
6812 statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
6813
6814 for (i = 0; i < ntups; i++)
6815 {
6816 statsextinfo[i].dobj.objType = DO_STATSEXT;
6817 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6818 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6819 AssignDumpId(&statsextinfo[i].dobj);
6820 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
6821 statsextinfo[i].dobj.namespace =
6822 findNamespace(fout,
6823 atooid(PQgetvalue(res, i, i_stxnamespace)));
6824 statsextinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
6825
6826 /* Decide whether we want to dump it */
6827 selectDumpableObject(&(statsextinfo[i].dobj), fout);
6828
6829 /* Stats objects do not currently have ACLs. */
6830 statsextinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
6831 }
6832
6833 PQclear(res);
6834 destroyPQExpBuffer(query);
6835 }
6836
6837 /*
6838 * getConstraints
6839 *
6840 * Get info about constraints on dumpable tables.
6841 *
6842 * Currently handles foreign keys only.
6843 * Unique and primary key constraints are handled with indexes,
6844 * while check constraints are processed in getTableAttrs().
6845 */
6846 void
getConstraints(Archive * fout,TableInfo tblinfo[],int numTables)6847 getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
6848 {
6849 int i,
6850 j;
6851 ConstraintInfo *constrinfo;
6852 PQExpBuffer query;
6853 PGresult *res;
6854 int i_contableoid,
6855 i_conoid,
6856 i_conname,
6857 i_confrelid,
6858 i_condef;
6859 int ntups;
6860
6861 query = createPQExpBuffer();
6862
6863 for (i = 0; i < numTables; i++)
6864 {
6865 TableInfo *tbinfo = &tblinfo[i];
6866
6867 if (!tbinfo->hastriggers ||
6868 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
6869 continue;
6870
6871 if (g_verbose)
6872 write_msg(NULL, "reading foreign key constraints for table \"%s.%s\"\n",
6873 tbinfo->dobj.namespace->dobj.name,
6874 tbinfo->dobj.name);
6875
6876 resetPQExpBuffer(query);
6877 appendPQExpBuffer(query,
6878 "SELECT tableoid, oid, conname, confrelid, "
6879 "pg_catalog.pg_get_constraintdef(oid) AS condef "
6880 "FROM pg_catalog.pg_constraint "
6881 "WHERE conrelid = '%u'::pg_catalog.oid "
6882 "AND contype = 'f'",
6883 tbinfo->dobj.catId.oid);
6884 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6885
6886 ntups = PQntuples(res);
6887
6888 i_contableoid = PQfnumber(res, "tableoid");
6889 i_conoid = PQfnumber(res, "oid");
6890 i_conname = PQfnumber(res, "conname");
6891 i_confrelid = PQfnumber(res, "confrelid");
6892 i_condef = PQfnumber(res, "condef");
6893
6894 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
6895
6896 for (j = 0; j < ntups; j++)
6897 {
6898 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
6899 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
6900 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
6901 AssignDumpId(&constrinfo[j].dobj);
6902 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
6903 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
6904 constrinfo[j].contable = tbinfo;
6905 constrinfo[j].condomain = NULL;
6906 constrinfo[j].contype = 'f';
6907 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
6908 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
6909 constrinfo[j].conindex = 0;
6910 constrinfo[j].condeferrable = false;
6911 constrinfo[j].condeferred = false;
6912 constrinfo[j].conislocal = true;
6913 constrinfo[j].separate = true;
6914 }
6915
6916 PQclear(res);
6917 }
6918
6919 destroyPQExpBuffer(query);
6920 }
6921
6922 /*
6923 * getDomainConstraints
6924 *
6925 * Get info about constraints on a domain.
6926 */
6927 static void
getDomainConstraints(Archive * fout,TypeInfo * tyinfo)6928 getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
6929 {
6930 int i;
6931 ConstraintInfo *constrinfo;
6932 PQExpBuffer query;
6933 PGresult *res;
6934 int i_tableoid,
6935 i_oid,
6936 i_conname,
6937 i_consrc;
6938 int ntups;
6939
6940 query = createPQExpBuffer();
6941
6942 if (fout->remoteVersion >= 90100)
6943 appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
6944 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
6945 "convalidated "
6946 "FROM pg_catalog.pg_constraint "
6947 "WHERE contypid = '%u'::pg_catalog.oid "
6948 "ORDER BY conname",
6949 tyinfo->dobj.catId.oid);
6950
6951 else
6952 appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
6953 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
6954 "true as convalidated "
6955 "FROM pg_catalog.pg_constraint "
6956 "WHERE contypid = '%u'::pg_catalog.oid "
6957 "ORDER BY conname",
6958 tyinfo->dobj.catId.oid);
6959
6960 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6961
6962 ntups = PQntuples(res);
6963
6964 i_tableoid = PQfnumber(res, "tableoid");
6965 i_oid = PQfnumber(res, "oid");
6966 i_conname = PQfnumber(res, "conname");
6967 i_consrc = PQfnumber(res, "consrc");
6968
6969 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
6970
6971 tyinfo->nDomChecks = ntups;
6972 tyinfo->domChecks = constrinfo;
6973
6974 for (i = 0; i < ntups; i++)
6975 {
6976 bool validated = PQgetvalue(res, i, 4)[0] == 't';
6977
6978 constrinfo[i].dobj.objType = DO_CONSTRAINT;
6979 constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6980 constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6981 AssignDumpId(&constrinfo[i].dobj);
6982 constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6983 constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
6984 constrinfo[i].contable = NULL;
6985 constrinfo[i].condomain = tyinfo;
6986 constrinfo[i].contype = 'c';
6987 constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
6988 constrinfo[i].confrelid = InvalidOid;
6989 constrinfo[i].conindex = 0;
6990 constrinfo[i].condeferrable = false;
6991 constrinfo[i].condeferred = false;
6992 constrinfo[i].conislocal = true;
6993
6994 constrinfo[i].separate = !validated;
6995
6996 /*
6997 * Make the domain depend on the constraint, ensuring it won't be
6998 * output till any constraint dependencies are OK. If the constraint
6999 * has not been validated, it's going to be dumped after the domain
7000 * anyway, so this doesn't matter.
7001 */
7002 if (validated)
7003 addObjectDependency(&tyinfo->dobj,
7004 constrinfo[i].dobj.dumpId);
7005 }
7006
7007 PQclear(res);
7008
7009 destroyPQExpBuffer(query);
7010 }
7011
7012 /*
7013 * getRules
7014 * get basic information about every rule in the system
7015 *
7016 * numRules is set to the number of rules read in
7017 */
7018 RuleInfo *
getRules(Archive * fout,int * numRules)7019 getRules(Archive *fout, int *numRules)
7020 {
7021 PGresult *res;
7022 int ntups;
7023 int i;
7024 PQExpBuffer query = createPQExpBuffer();
7025 RuleInfo *ruleinfo;
7026 int i_tableoid;
7027 int i_oid;
7028 int i_rulename;
7029 int i_ruletable;
7030 int i_ev_type;
7031 int i_is_instead;
7032 int i_ev_enabled;
7033
7034 if (fout->remoteVersion >= 80300)
7035 {
7036 appendPQExpBufferStr(query, "SELECT "
7037 "tableoid, oid, rulename, "
7038 "ev_class AS ruletable, ev_type, is_instead, "
7039 "ev_enabled "
7040 "FROM pg_rewrite "
7041 "ORDER BY oid");
7042 }
7043 else
7044 {
7045 appendPQExpBufferStr(query, "SELECT "
7046 "tableoid, oid, rulename, "
7047 "ev_class AS ruletable, ev_type, is_instead, "
7048 "'O'::char AS ev_enabled "
7049 "FROM pg_rewrite "
7050 "ORDER BY oid");
7051 }
7052
7053 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7054
7055 ntups = PQntuples(res);
7056
7057 *numRules = ntups;
7058
7059 ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
7060
7061 i_tableoid = PQfnumber(res, "tableoid");
7062 i_oid = PQfnumber(res, "oid");
7063 i_rulename = PQfnumber(res, "rulename");
7064 i_ruletable = PQfnumber(res, "ruletable");
7065 i_ev_type = PQfnumber(res, "ev_type");
7066 i_is_instead = PQfnumber(res, "is_instead");
7067 i_ev_enabled = PQfnumber(res, "ev_enabled");
7068
7069 for (i = 0; i < ntups; i++)
7070 {
7071 Oid ruletableoid;
7072
7073 ruleinfo[i].dobj.objType = DO_RULE;
7074 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7075 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7076 AssignDumpId(&ruleinfo[i].dobj);
7077 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
7078 ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
7079 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
7080 if (ruleinfo[i].ruletable == NULL)
7081 exit_horribly(NULL, "failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found\n",
7082 ruletableoid, ruleinfo[i].dobj.catId.oid);
7083 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
7084 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
7085 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
7086 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
7087 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
7088 if (ruleinfo[i].ruletable)
7089 {
7090 /*
7091 * If the table is a view or materialized view, force its ON
7092 * SELECT rule to be sorted before the view itself --- this
7093 * ensures that any dependencies for the rule affect the table's
7094 * positioning. Other rules are forced to appear after their
7095 * table.
7096 */
7097 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
7098 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
7099 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
7100 {
7101 addObjectDependency(&ruleinfo[i].ruletable->dobj,
7102 ruleinfo[i].dobj.dumpId);
7103 /* We'll merge the rule into CREATE VIEW, if possible */
7104 ruleinfo[i].separate = false;
7105 }
7106 else
7107 {
7108 addObjectDependency(&ruleinfo[i].dobj,
7109 ruleinfo[i].ruletable->dobj.dumpId);
7110 ruleinfo[i].separate = true;
7111 }
7112 }
7113 else
7114 ruleinfo[i].separate = true;
7115 }
7116
7117 PQclear(res);
7118
7119 destroyPQExpBuffer(query);
7120
7121 return ruleinfo;
7122 }
7123
7124 /*
7125 * getTriggers
7126 * get information about every trigger on a dumpable table
7127 *
7128 * Note: trigger data is not returned directly to the caller, but it
7129 * does get entered into the DumpableObject tables.
7130 */
7131 void
getTriggers(Archive * fout,TableInfo tblinfo[],int numTables)7132 getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
7133 {
7134 int i,
7135 j;
7136 PQExpBuffer query = createPQExpBuffer();
7137 PGresult *res;
7138 TriggerInfo *tginfo;
7139 int i_tableoid,
7140 i_oid,
7141 i_tgname,
7142 i_tgfname,
7143 i_tgtype,
7144 i_tgnargs,
7145 i_tgargs,
7146 i_tgisconstraint,
7147 i_tgconstrname,
7148 i_tgconstrrelid,
7149 i_tgconstrrelname,
7150 i_tgenabled,
7151 i_tgdeferrable,
7152 i_tginitdeferred,
7153 i_tgdef;
7154 int ntups;
7155
7156 for (i = 0; i < numTables; i++)
7157 {
7158 TableInfo *tbinfo = &tblinfo[i];
7159
7160 if (!tbinfo->hastriggers ||
7161 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7162 continue;
7163
7164 if (g_verbose)
7165 write_msg(NULL, "reading triggers for table \"%s.%s\"\n",
7166 tbinfo->dobj.namespace->dobj.name,
7167 tbinfo->dobj.name);
7168
7169 resetPQExpBuffer(query);
7170 if (fout->remoteVersion >= 90000)
7171 {
7172 /*
7173 * NB: think not to use pretty=true in pg_get_triggerdef. It
7174 * could result in non-forward-compatible dumps of WHEN clauses
7175 * due to under-parenthesization.
7176 */
7177 appendPQExpBuffer(query,
7178 "SELECT tgname, "
7179 "tgfoid::pg_catalog.regproc AS tgfname, "
7180 "pg_catalog.pg_get_triggerdef(oid, false) AS tgdef, "
7181 "tgenabled, tableoid, oid "
7182 "FROM pg_catalog.pg_trigger t "
7183 "WHERE tgrelid = '%u'::pg_catalog.oid "
7184 "AND NOT tgisinternal",
7185 tbinfo->dobj.catId.oid);
7186 }
7187 else if (fout->remoteVersion >= 80300)
7188 {
7189 /*
7190 * We ignore triggers that are tied to a foreign-key constraint
7191 */
7192 appendPQExpBuffer(query,
7193 "SELECT tgname, "
7194 "tgfoid::pg_catalog.regproc AS tgfname, "
7195 "tgtype, tgnargs, tgargs, tgenabled, "
7196 "tgisconstraint, tgconstrname, tgdeferrable, "
7197 "tgconstrrelid, tginitdeferred, tableoid, oid, "
7198 "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
7199 "FROM pg_catalog.pg_trigger t "
7200 "WHERE tgrelid = '%u'::pg_catalog.oid "
7201 "AND tgconstraint = 0",
7202 tbinfo->dobj.catId.oid);
7203 }
7204 else
7205 {
7206 /*
7207 * We ignore triggers that are tied to a foreign-key constraint,
7208 * but in these versions we have to grovel through pg_constraint
7209 * to find out
7210 */
7211 appendPQExpBuffer(query,
7212 "SELECT tgname, "
7213 "tgfoid::pg_catalog.regproc AS tgfname, "
7214 "tgtype, tgnargs, tgargs, tgenabled, "
7215 "tgisconstraint, tgconstrname, tgdeferrable, "
7216 "tgconstrrelid, tginitdeferred, tableoid, oid, "
7217 "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
7218 "FROM pg_catalog.pg_trigger t "
7219 "WHERE tgrelid = '%u'::pg_catalog.oid "
7220 "AND (NOT tgisconstraint "
7221 " OR NOT EXISTS"
7222 " (SELECT 1 FROM pg_catalog.pg_depend d "
7223 " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
7224 " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
7225 tbinfo->dobj.catId.oid);
7226 }
7227
7228 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7229
7230 ntups = PQntuples(res);
7231
7232 i_tableoid = PQfnumber(res, "tableoid");
7233 i_oid = PQfnumber(res, "oid");
7234 i_tgname = PQfnumber(res, "tgname");
7235 i_tgfname = PQfnumber(res, "tgfname");
7236 i_tgtype = PQfnumber(res, "tgtype");
7237 i_tgnargs = PQfnumber(res, "tgnargs");
7238 i_tgargs = PQfnumber(res, "tgargs");
7239 i_tgisconstraint = PQfnumber(res, "tgisconstraint");
7240 i_tgconstrname = PQfnumber(res, "tgconstrname");
7241 i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
7242 i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
7243 i_tgenabled = PQfnumber(res, "tgenabled");
7244 i_tgdeferrable = PQfnumber(res, "tgdeferrable");
7245 i_tginitdeferred = PQfnumber(res, "tginitdeferred");
7246 i_tgdef = PQfnumber(res, "tgdef");
7247
7248 tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
7249
7250 tbinfo->numTriggers = ntups;
7251 tbinfo->triggers = tginfo;
7252
7253 for (j = 0; j < ntups; j++)
7254 {
7255 tginfo[j].dobj.objType = DO_TRIGGER;
7256 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7257 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7258 AssignDumpId(&tginfo[j].dobj);
7259 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
7260 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
7261 tginfo[j].tgtable = tbinfo;
7262 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
7263 if (i_tgdef >= 0)
7264 {
7265 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
7266
7267 /* remaining fields are not valid if we have tgdef */
7268 tginfo[j].tgfname = NULL;
7269 tginfo[j].tgtype = 0;
7270 tginfo[j].tgnargs = 0;
7271 tginfo[j].tgargs = NULL;
7272 tginfo[j].tgisconstraint = false;
7273 tginfo[j].tgdeferrable = false;
7274 tginfo[j].tginitdeferred = false;
7275 tginfo[j].tgconstrname = NULL;
7276 tginfo[j].tgconstrrelid = InvalidOid;
7277 tginfo[j].tgconstrrelname = NULL;
7278 }
7279 else
7280 {
7281 tginfo[j].tgdef = NULL;
7282
7283 tginfo[j].tgfname = pg_strdup(PQgetvalue(res, j, i_tgfname));
7284 tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
7285 tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
7286 tginfo[j].tgargs = pg_strdup(PQgetvalue(res, j, i_tgargs));
7287 tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
7288 tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
7289 tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
7290
7291 if (tginfo[j].tgisconstraint)
7292 {
7293 tginfo[j].tgconstrname = pg_strdup(PQgetvalue(res, j, i_tgconstrname));
7294 tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
7295 if (OidIsValid(tginfo[j].tgconstrrelid))
7296 {
7297 if (PQgetisnull(res, j, i_tgconstrrelname))
7298 exit_horribly(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n",
7299 tginfo[j].dobj.name,
7300 tbinfo->dobj.name,
7301 tginfo[j].tgconstrrelid);
7302 tginfo[j].tgconstrrelname = pg_strdup(PQgetvalue(res, j, i_tgconstrrelname));
7303 }
7304 else
7305 tginfo[j].tgconstrrelname = NULL;
7306 }
7307 else
7308 {
7309 tginfo[j].tgconstrname = NULL;
7310 tginfo[j].tgconstrrelid = InvalidOid;
7311 tginfo[j].tgconstrrelname = NULL;
7312 }
7313 }
7314 }
7315
7316 PQclear(res);
7317 }
7318
7319 destroyPQExpBuffer(query);
7320 }
7321
7322 /*
7323 * getEventTriggers
7324 * get information about event triggers
7325 */
7326 EventTriggerInfo *
getEventTriggers(Archive * fout,int * numEventTriggers)7327 getEventTriggers(Archive *fout, int *numEventTriggers)
7328 {
7329 int i;
7330 PQExpBuffer query;
7331 PGresult *res;
7332 EventTriggerInfo *evtinfo;
7333 int i_tableoid,
7334 i_oid,
7335 i_evtname,
7336 i_evtevent,
7337 i_evtowner,
7338 i_evttags,
7339 i_evtfname,
7340 i_evtenabled;
7341 int ntups;
7342
7343 /* Before 9.3, there are no event triggers */
7344 if (fout->remoteVersion < 90300)
7345 {
7346 *numEventTriggers = 0;
7347 return NULL;
7348 }
7349
7350 query = createPQExpBuffer();
7351
7352 appendPQExpBuffer(query,
7353 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
7354 "evtevent, (%s evtowner) AS evtowner, "
7355 "array_to_string(array("
7356 "select quote_literal(x) "
7357 " from unnest(evttags) as t(x)), ', ') as evttags, "
7358 "e.evtfoid::regproc as evtfname "
7359 "FROM pg_event_trigger e "
7360 "ORDER BY e.oid",
7361 username_subquery);
7362
7363 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7364
7365 ntups = PQntuples(res);
7366
7367 *numEventTriggers = ntups;
7368
7369 evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
7370
7371 i_tableoid = PQfnumber(res, "tableoid");
7372 i_oid = PQfnumber(res, "oid");
7373 i_evtname = PQfnumber(res, "evtname");
7374 i_evtevent = PQfnumber(res, "evtevent");
7375 i_evtowner = PQfnumber(res, "evtowner");
7376 i_evttags = PQfnumber(res, "evttags");
7377 i_evtfname = PQfnumber(res, "evtfname");
7378 i_evtenabled = PQfnumber(res, "evtenabled");
7379
7380 for (i = 0; i < ntups; i++)
7381 {
7382 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
7383 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7384 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7385 AssignDumpId(&evtinfo[i].dobj);
7386 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
7387 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
7388 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
7389 evtinfo[i].evtowner = pg_strdup(PQgetvalue(res, i, i_evtowner));
7390 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
7391 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
7392 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
7393
7394 /* Decide whether we want to dump it */
7395 selectDumpableObject(&(evtinfo[i].dobj), fout);
7396
7397 /* Event Triggers do not currently have ACLs. */
7398 evtinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
7399 }
7400
7401 PQclear(res);
7402
7403 destroyPQExpBuffer(query);
7404
7405 return evtinfo;
7406 }
7407
7408 /*
7409 * getProcLangs
7410 * get basic information about every procedural language in the system
7411 *
7412 * numProcLangs is set to the number of langs read in
7413 *
7414 * NB: this must run after getFuncs() because we assume we can do
7415 * findFuncByOid().
7416 */
7417 ProcLangInfo *
getProcLangs(Archive * fout,int * numProcLangs)7418 getProcLangs(Archive *fout, int *numProcLangs)
7419 {
7420 DumpOptions *dopt = fout->dopt;
7421 PGresult *res;
7422 int ntups;
7423 int i;
7424 PQExpBuffer query = createPQExpBuffer();
7425 ProcLangInfo *planginfo;
7426 int i_tableoid;
7427 int i_oid;
7428 int i_lanname;
7429 int i_lanpltrusted;
7430 int i_lanplcallfoid;
7431 int i_laninline;
7432 int i_lanvalidator;
7433 int i_lanacl;
7434 int i_rlanacl;
7435 int i_initlanacl;
7436 int i_initrlanacl;
7437 int i_lanowner;
7438
7439 if (fout->remoteVersion >= 90600)
7440 {
7441 PQExpBuffer acl_subquery = createPQExpBuffer();
7442 PQExpBuffer racl_subquery = createPQExpBuffer();
7443 PQExpBuffer initacl_subquery = createPQExpBuffer();
7444 PQExpBuffer initracl_subquery = createPQExpBuffer();
7445
7446 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
7447 initracl_subquery, "l.lanacl", "l.lanowner", "'l'",
7448 dopt->binary_upgrade);
7449
7450 /* pg_language has a laninline column */
7451 appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, "
7452 "l.lanname, l.lanpltrusted, l.lanplcallfoid, "
7453 "l.laninline, l.lanvalidator, "
7454 "%s AS lanacl, "
7455 "%s AS rlanacl, "
7456 "%s AS initlanacl, "
7457 "%s AS initrlanacl, "
7458 "(%s l.lanowner) AS lanowner "
7459 "FROM pg_language l "
7460 "LEFT JOIN pg_init_privs pip ON "
7461 "(l.oid = pip.objoid "
7462 "AND pip.classoid = 'pg_language'::regclass "
7463 "AND pip.objsubid = 0) "
7464 "WHERE l.lanispl "
7465 "ORDER BY l.oid",
7466 acl_subquery->data,
7467 racl_subquery->data,
7468 initacl_subquery->data,
7469 initracl_subquery->data,
7470 username_subquery);
7471
7472 destroyPQExpBuffer(acl_subquery);
7473 destroyPQExpBuffer(racl_subquery);
7474 destroyPQExpBuffer(initacl_subquery);
7475 destroyPQExpBuffer(initracl_subquery);
7476 }
7477 else if (fout->remoteVersion >= 90000)
7478 {
7479 /* pg_language has a laninline column */
7480 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7481 "lanname, lanpltrusted, lanplcallfoid, "
7482 "laninline, lanvalidator, lanacl, NULL AS rlanacl, "
7483 "NULL AS initlanacl, NULL AS initrlanacl, "
7484 "(%s lanowner) AS lanowner "
7485 "FROM pg_language "
7486 "WHERE lanispl "
7487 "ORDER BY oid",
7488 username_subquery);
7489 }
7490 else if (fout->remoteVersion >= 80300)
7491 {
7492 /* pg_language has a lanowner column */
7493 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7494 "lanname, lanpltrusted, lanplcallfoid, "
7495 "0 AS laninline, lanvalidator, lanacl, "
7496 "NULL AS rlanacl, "
7497 "NULL AS initlanacl, NULL AS initrlanacl, "
7498 "(%s lanowner) AS lanowner "
7499 "FROM pg_language "
7500 "WHERE lanispl "
7501 "ORDER BY oid",
7502 username_subquery);
7503 }
7504 else if (fout->remoteVersion >= 80100)
7505 {
7506 /* Languages are owned by the bootstrap superuser, OID 10 */
7507 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7508 "lanname, lanpltrusted, lanplcallfoid, "
7509 "0 AS laninline, lanvalidator, lanacl, "
7510 "NULL AS rlanacl, "
7511 "NULL AS initlanacl, NULL AS initrlanacl, "
7512 "(%s '10') AS lanowner "
7513 "FROM pg_language "
7514 "WHERE lanispl "
7515 "ORDER BY oid",
7516 username_subquery);
7517 }
7518 else
7519 {
7520 /* Languages are owned by the bootstrap superuser, sysid 1 */
7521 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7522 "lanname, lanpltrusted, lanplcallfoid, "
7523 "0 AS laninline, lanvalidator, lanacl, "
7524 "NULL AS rlanacl, "
7525 "NULL AS initlanacl, NULL AS initrlanacl, "
7526 "(%s '1') AS lanowner "
7527 "FROM pg_language "
7528 "WHERE lanispl "
7529 "ORDER BY oid",
7530 username_subquery);
7531 }
7532
7533 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7534
7535 ntups = PQntuples(res);
7536
7537 *numProcLangs = ntups;
7538
7539 planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
7540
7541 i_tableoid = PQfnumber(res, "tableoid");
7542 i_oid = PQfnumber(res, "oid");
7543 i_lanname = PQfnumber(res, "lanname");
7544 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
7545 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
7546 i_laninline = PQfnumber(res, "laninline");
7547 i_lanvalidator = PQfnumber(res, "lanvalidator");
7548 i_lanacl = PQfnumber(res, "lanacl");
7549 i_rlanacl = PQfnumber(res, "rlanacl");
7550 i_initlanacl = PQfnumber(res, "initlanacl");
7551 i_initrlanacl = PQfnumber(res, "initrlanacl");
7552 i_lanowner = PQfnumber(res, "lanowner");
7553
7554 for (i = 0; i < ntups; i++)
7555 {
7556 planginfo[i].dobj.objType = DO_PROCLANG;
7557 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7558 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7559 AssignDumpId(&planginfo[i].dobj);
7560
7561 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
7562 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
7563 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
7564 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
7565 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
7566 planginfo[i].lanacl = pg_strdup(PQgetvalue(res, i, i_lanacl));
7567 planginfo[i].rlanacl = pg_strdup(PQgetvalue(res, i, i_rlanacl));
7568 planginfo[i].initlanacl = pg_strdup(PQgetvalue(res, i, i_initlanacl));
7569 planginfo[i].initrlanacl = pg_strdup(PQgetvalue(res, i, i_initrlanacl));
7570 planginfo[i].lanowner = pg_strdup(PQgetvalue(res, i, i_lanowner));
7571
7572 /* Decide whether we want to dump it */
7573 selectDumpableProcLang(&(planginfo[i]), fout);
7574
7575 /* Do not try to dump ACL if no ACL exists. */
7576 if (PQgetisnull(res, i, i_lanacl) && PQgetisnull(res, i, i_rlanacl) &&
7577 PQgetisnull(res, i, i_initlanacl) &&
7578 PQgetisnull(res, i, i_initrlanacl))
7579 planginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
7580 }
7581
7582 PQclear(res);
7583
7584 destroyPQExpBuffer(query);
7585
7586 return planginfo;
7587 }
7588
7589 /*
7590 * getCasts
7591 * get basic information about every cast in the system
7592 *
7593 * numCasts is set to the number of casts read in
7594 */
7595 CastInfo *
getCasts(Archive * fout,int * numCasts)7596 getCasts(Archive *fout, int *numCasts)
7597 {
7598 PGresult *res;
7599 int ntups;
7600 int i;
7601 PQExpBuffer query = createPQExpBuffer();
7602 CastInfo *castinfo;
7603 int i_tableoid;
7604 int i_oid;
7605 int i_castsource;
7606 int i_casttarget;
7607 int i_castfunc;
7608 int i_castcontext;
7609 int i_castmethod;
7610
7611 if (fout->remoteVersion >= 80400)
7612 {
7613 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
7614 "castsource, casttarget, castfunc, castcontext, "
7615 "castmethod "
7616 "FROM pg_cast ORDER BY 3,4");
7617 }
7618 else
7619 {
7620 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
7621 "castsource, casttarget, castfunc, castcontext, "
7622 "CASE WHEN castfunc = 0 THEN 'b' ELSE 'f' END AS castmethod "
7623 "FROM pg_cast ORDER BY 3,4");
7624 }
7625
7626 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7627
7628 ntups = PQntuples(res);
7629
7630 *numCasts = ntups;
7631
7632 castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
7633
7634 i_tableoid = PQfnumber(res, "tableoid");
7635 i_oid = PQfnumber(res, "oid");
7636 i_castsource = PQfnumber(res, "castsource");
7637 i_casttarget = PQfnumber(res, "casttarget");
7638 i_castfunc = PQfnumber(res, "castfunc");
7639 i_castcontext = PQfnumber(res, "castcontext");
7640 i_castmethod = PQfnumber(res, "castmethod");
7641
7642 for (i = 0; i < ntups; i++)
7643 {
7644 PQExpBufferData namebuf;
7645 TypeInfo *sTypeInfo;
7646 TypeInfo *tTypeInfo;
7647
7648 castinfo[i].dobj.objType = DO_CAST;
7649 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7650 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7651 AssignDumpId(&castinfo[i].dobj);
7652 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
7653 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
7654 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
7655 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
7656 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
7657
7658 /*
7659 * Try to name cast as concatenation of typnames. This is only used
7660 * for purposes of sorting. If we fail to find either type, the name
7661 * will be an empty string.
7662 */
7663 initPQExpBuffer(&namebuf);
7664 sTypeInfo = findTypeByOid(castinfo[i].castsource);
7665 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
7666 if (sTypeInfo && tTypeInfo)
7667 appendPQExpBuffer(&namebuf, "%s %s",
7668 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
7669 castinfo[i].dobj.name = namebuf.data;
7670
7671 /* Decide whether we want to dump it */
7672 selectDumpableCast(&(castinfo[i]), fout);
7673
7674 /* Casts do not currently have ACLs. */
7675 castinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
7676 }
7677
7678 PQclear(res);
7679
7680 destroyPQExpBuffer(query);
7681
7682 return castinfo;
7683 }
7684
7685 static char *
get_language_name(Archive * fout,Oid langid)7686 get_language_name(Archive *fout, Oid langid)
7687 {
7688 PQExpBuffer query;
7689 PGresult *res;
7690 char *lanname;
7691
7692 query = createPQExpBuffer();
7693 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
7694 res = ExecuteSqlQueryForSingleRow(fout, query->data);
7695 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
7696 destroyPQExpBuffer(query);
7697 PQclear(res);
7698
7699 return lanname;
7700 }
7701
7702 /*
7703 * getTransforms
7704 * get basic information about every transform in the system
7705 *
7706 * numTransforms is set to the number of transforms read in
7707 */
7708 TransformInfo *
getTransforms(Archive * fout,int * numTransforms)7709 getTransforms(Archive *fout, int *numTransforms)
7710 {
7711 PGresult *res;
7712 int ntups;
7713 int i;
7714 PQExpBuffer query;
7715 TransformInfo *transforminfo;
7716 int i_tableoid;
7717 int i_oid;
7718 int i_trftype;
7719 int i_trflang;
7720 int i_trffromsql;
7721 int i_trftosql;
7722
7723 /* Transforms didn't exist pre-9.5 */
7724 if (fout->remoteVersion < 90500)
7725 {
7726 *numTransforms = 0;
7727 return NULL;
7728 }
7729
7730 query = createPQExpBuffer();
7731
7732 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7733 "trftype, trflang, trffromsql::oid, trftosql::oid "
7734 "FROM pg_transform "
7735 "ORDER BY 3,4");
7736
7737 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7738
7739 ntups = PQntuples(res);
7740
7741 *numTransforms = ntups;
7742
7743 transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
7744
7745 i_tableoid = PQfnumber(res, "tableoid");
7746 i_oid = PQfnumber(res, "oid");
7747 i_trftype = PQfnumber(res, "trftype");
7748 i_trflang = PQfnumber(res, "trflang");
7749 i_trffromsql = PQfnumber(res, "trffromsql");
7750 i_trftosql = PQfnumber(res, "trftosql");
7751
7752 for (i = 0; i < ntups; i++)
7753 {
7754 PQExpBufferData namebuf;
7755 TypeInfo *typeInfo;
7756 char *lanname;
7757
7758 transforminfo[i].dobj.objType = DO_TRANSFORM;
7759 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7760 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7761 AssignDumpId(&transforminfo[i].dobj);
7762 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
7763 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
7764 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
7765 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
7766
7767 /*
7768 * Try to name transform as concatenation of type and language name.
7769 * This is only used for purposes of sorting. If we fail to find
7770 * either, the name will be an empty string.
7771 */
7772 initPQExpBuffer(&namebuf);
7773 typeInfo = findTypeByOid(transforminfo[i].trftype);
7774 lanname = get_language_name(fout, transforminfo[i].trflang);
7775 if (typeInfo && lanname)
7776 appendPQExpBuffer(&namebuf, "%s %s",
7777 typeInfo->dobj.name, lanname);
7778 transforminfo[i].dobj.name = namebuf.data;
7779 free(lanname);
7780
7781 /* Decide whether we want to dump it */
7782 selectDumpableObject(&(transforminfo[i].dobj), fout);
7783 }
7784
7785 PQclear(res);
7786
7787 destroyPQExpBuffer(query);
7788
7789 return transforminfo;
7790 }
7791
7792 /*
7793 * getTableAttrs -
7794 * for each interesting table, read info about its attributes
7795 * (names, types, default values, CHECK constraints, etc)
7796 *
7797 * This is implemented in a very inefficient way right now, looping
7798 * through the tblinfo and doing a join per table to find the attrs and their
7799 * types. However, because we want type names and so forth to be named
7800 * relative to the schema of each table, we couldn't do it in just one
7801 * query. (Maybe one query per schema?)
7802 *
7803 * modifies tblinfo
7804 */
7805 void
getTableAttrs(Archive * fout,TableInfo * tblinfo,int numTables)7806 getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
7807 {
7808 DumpOptions *dopt = fout->dopt;
7809 int i,
7810 j;
7811 PQExpBuffer q = createPQExpBuffer();
7812 int i_attnum;
7813 int i_attname;
7814 int i_atttypname;
7815 int i_atttypmod;
7816 int i_attstattarget;
7817 int i_attstorage;
7818 int i_typstorage;
7819 int i_attnotnull;
7820 int i_atthasdef;
7821 int i_attidentity;
7822 int i_attisdropped;
7823 int i_attlen;
7824 int i_attalign;
7825 int i_attislocal;
7826 int i_attoptions;
7827 int i_attcollation;
7828 int i_attfdwoptions;
7829 PGresult *res;
7830 int ntups;
7831 bool hasdefaults;
7832
7833 for (i = 0; i < numTables; i++)
7834 {
7835 TableInfo *tbinfo = &tblinfo[i];
7836
7837 /* Don't bother to collect info for sequences */
7838 if (tbinfo->relkind == RELKIND_SEQUENCE)
7839 continue;
7840
7841 /* Don't bother with uninteresting tables, either */
7842 if (!tbinfo->interesting)
7843 continue;
7844
7845 /* find all the user attributes and their types */
7846
7847 /*
7848 * we must read the attribute names in attribute number order! because
7849 * we will use the attnum to index into the attnames array later.
7850 */
7851 if (g_verbose)
7852 write_msg(NULL, "finding the columns and types of table \"%s.%s\"\n",
7853 tbinfo->dobj.namespace->dobj.name,
7854 tbinfo->dobj.name);
7855
7856 resetPQExpBuffer(q);
7857
7858 if (fout->remoteVersion >= 100000)
7859 {
7860 /*
7861 * attidentity is new in version 10.
7862 */
7863 appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
7864 "a.attstattarget, a.attstorage, t.typstorage, "
7865 "a.attnotnull, a.atthasdef, a.attisdropped, "
7866 "a.attlen, a.attalign, a.attislocal, "
7867 "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
7868 "array_to_string(a.attoptions, ', ') AS attoptions, "
7869 "CASE WHEN a.attcollation <> t.typcollation "
7870 "THEN a.attcollation ELSE 0 END AS attcollation, "
7871 "a.attidentity, "
7872 "pg_catalog.array_to_string(ARRAY("
7873 "SELECT pg_catalog.quote_ident(option_name) || "
7874 "' ' || pg_catalog.quote_literal(option_value) "
7875 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
7876 "ORDER BY option_name"
7877 "), E',\n ') AS attfdwoptions "
7878 "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
7879 "ON a.atttypid = t.oid "
7880 "WHERE a.attrelid = '%u'::pg_catalog.oid "
7881 "AND a.attnum > 0::pg_catalog.int2 "
7882 "ORDER BY a.attnum",
7883 tbinfo->dobj.catId.oid);
7884 }
7885 else if (fout->remoteVersion >= 90200)
7886 {
7887 /*
7888 * attfdwoptions is new in 9.2.
7889 */
7890 appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
7891 "a.attstattarget, a.attstorage, t.typstorage, "
7892 "a.attnotnull, a.atthasdef, a.attisdropped, "
7893 "a.attlen, a.attalign, a.attislocal, "
7894 "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
7895 "array_to_string(a.attoptions, ', ') AS attoptions, "
7896 "CASE WHEN a.attcollation <> t.typcollation "
7897 "THEN a.attcollation ELSE 0 END AS attcollation, "
7898 "pg_catalog.array_to_string(ARRAY("
7899 "SELECT pg_catalog.quote_ident(option_name) || "
7900 "' ' || pg_catalog.quote_literal(option_value) "
7901 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
7902 "ORDER BY option_name"
7903 "), E',\n ') AS attfdwoptions "
7904 "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
7905 "ON a.atttypid = t.oid "
7906 "WHERE a.attrelid = '%u'::pg_catalog.oid "
7907 "AND a.attnum > 0::pg_catalog.int2 "
7908 "ORDER BY a.attnum",
7909 tbinfo->dobj.catId.oid);
7910 }
7911 else if (fout->remoteVersion >= 90100)
7912 {
7913 /*
7914 * attcollation is new in 9.1. Since we only want to dump COLLATE
7915 * clauses for attributes whose collation is different from their
7916 * type's default, we use a CASE here to suppress uninteresting
7917 * attcollations cheaply.
7918 */
7919 appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
7920 "a.attstattarget, a.attstorage, t.typstorage, "
7921 "a.attnotnull, a.atthasdef, a.attisdropped, "
7922 "a.attlen, a.attalign, a.attislocal, "
7923 "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
7924 "array_to_string(a.attoptions, ', ') AS attoptions, "
7925 "CASE WHEN a.attcollation <> t.typcollation "
7926 "THEN a.attcollation ELSE 0 END AS attcollation, "
7927 "NULL AS attfdwoptions "
7928 "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
7929 "ON a.atttypid = t.oid "
7930 "WHERE a.attrelid = '%u'::pg_catalog.oid "
7931 "AND a.attnum > 0::pg_catalog.int2 "
7932 "ORDER BY a.attnum",
7933 tbinfo->dobj.catId.oid);
7934 }
7935 else if (fout->remoteVersion >= 90000)
7936 {
7937 /* attoptions is new in 9.0 */
7938 appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
7939 "a.attstattarget, a.attstorage, t.typstorage, "
7940 "a.attnotnull, a.atthasdef, a.attisdropped, "
7941 "a.attlen, a.attalign, a.attislocal, "
7942 "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
7943 "array_to_string(a.attoptions, ', ') AS attoptions, "
7944 "0 AS attcollation, "
7945 "NULL AS attfdwoptions "
7946 "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
7947 "ON a.atttypid = t.oid "
7948 "WHERE a.attrelid = '%u'::pg_catalog.oid "
7949 "AND a.attnum > 0::pg_catalog.int2 "
7950 "ORDER BY a.attnum",
7951 tbinfo->dobj.catId.oid);
7952 }
7953 else
7954 {
7955 /* need left join here to not fail on dropped columns ... */
7956 appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
7957 "a.attstattarget, a.attstorage, t.typstorage, "
7958 "a.attnotnull, a.atthasdef, a.attisdropped, "
7959 "a.attlen, a.attalign, a.attislocal, "
7960 "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
7961 "'' AS attoptions, 0 AS attcollation, "
7962 "NULL AS attfdwoptions "
7963 "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
7964 "ON a.atttypid = t.oid "
7965 "WHERE a.attrelid = '%u'::pg_catalog.oid "
7966 "AND a.attnum > 0::pg_catalog.int2 "
7967 "ORDER BY a.attnum",
7968 tbinfo->dobj.catId.oid);
7969 }
7970
7971 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
7972
7973 ntups = PQntuples(res);
7974
7975 i_attnum = PQfnumber(res, "attnum");
7976 i_attname = PQfnumber(res, "attname");
7977 i_atttypname = PQfnumber(res, "atttypname");
7978 i_atttypmod = PQfnumber(res, "atttypmod");
7979 i_attstattarget = PQfnumber(res, "attstattarget");
7980 i_attstorage = PQfnumber(res, "attstorage");
7981 i_typstorage = PQfnumber(res, "typstorage");
7982 i_attnotnull = PQfnumber(res, "attnotnull");
7983 i_atthasdef = PQfnumber(res, "atthasdef");
7984 i_attidentity = PQfnumber(res, "attidentity");
7985 i_attisdropped = PQfnumber(res, "attisdropped");
7986 i_attlen = PQfnumber(res, "attlen");
7987 i_attalign = PQfnumber(res, "attalign");
7988 i_attislocal = PQfnumber(res, "attislocal");
7989 i_attoptions = PQfnumber(res, "attoptions");
7990 i_attcollation = PQfnumber(res, "attcollation");
7991 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
7992
7993 tbinfo->numatts = ntups;
7994 tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
7995 tbinfo->atttypnames = (char **) pg_malloc(ntups * sizeof(char *));
7996 tbinfo->atttypmod = (int *) pg_malloc(ntups * sizeof(int));
7997 tbinfo->attstattarget = (int *) pg_malloc(ntups * sizeof(int));
7998 tbinfo->attstorage = (char *) pg_malloc(ntups * sizeof(char));
7999 tbinfo->typstorage = (char *) pg_malloc(ntups * sizeof(char));
8000 tbinfo->attidentity = (char *) pg_malloc(ntups * sizeof(char));
8001 tbinfo->attisdropped = (bool *) pg_malloc(ntups * sizeof(bool));
8002 tbinfo->attlen = (int *) pg_malloc(ntups * sizeof(int));
8003 tbinfo->attalign = (char *) pg_malloc(ntups * sizeof(char));
8004 tbinfo->attislocal = (bool *) pg_malloc(ntups * sizeof(bool));
8005 tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
8006 tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
8007 tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
8008 tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
8009 tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
8010 tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
8011 hasdefaults = false;
8012
8013 for (j = 0; j < ntups; j++)
8014 {
8015 if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
8016 exit_horribly(NULL,
8017 "invalid column numbering in table \"%s\"\n",
8018 tbinfo->dobj.name);
8019 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, j, i_attname));
8020 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, j, i_atttypname));
8021 tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
8022 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
8023 tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
8024 tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
8025 tbinfo->attidentity[j] = (i_attidentity >= 0 ? *(PQgetvalue(res, j, i_attidentity)) : '\0');
8026 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
8027 tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
8028 tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen));
8029 tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign));
8030 tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
8031 tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
8032 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
8033 tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
8034 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
8035 tbinfo->attrdefs[j] = NULL; /* fix below */
8036 if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
8037 hasdefaults = true;
8038 /* these flags will be set in flagInhAttrs() */
8039 tbinfo->inhNotNull[j] = false;
8040 }
8041
8042 PQclear(res);
8043
8044 /*
8045 * Get info about column defaults
8046 */
8047 if (hasdefaults)
8048 {
8049 AttrDefInfo *attrdefs;
8050 int numDefaults;
8051
8052 if (g_verbose)
8053 write_msg(NULL, "finding default expressions of table \"%s.%s\"\n",
8054 tbinfo->dobj.namespace->dobj.name,
8055 tbinfo->dobj.name);
8056
8057 printfPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
8058 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
8059 "FROM pg_catalog.pg_attrdef "
8060 "WHERE adrelid = '%u'::pg_catalog.oid",
8061 tbinfo->dobj.catId.oid);
8062
8063 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8064
8065 numDefaults = PQntuples(res);
8066 attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
8067
8068 for (j = 0; j < numDefaults; j++)
8069 {
8070 int adnum;
8071
8072 adnum = atoi(PQgetvalue(res, j, 2));
8073
8074 if (adnum <= 0 || adnum > ntups)
8075 exit_horribly(NULL,
8076 "invalid adnum value %d for table \"%s\"\n",
8077 adnum, tbinfo->dobj.name);
8078
8079 /*
8080 * dropped columns shouldn't have defaults, but just in case,
8081 * ignore 'em
8082 */
8083 if (tbinfo->attisdropped[adnum - 1])
8084 continue;
8085
8086 attrdefs[j].dobj.objType = DO_ATTRDEF;
8087 attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
8088 attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
8089 AssignDumpId(&attrdefs[j].dobj);
8090 attrdefs[j].adtable = tbinfo;
8091 attrdefs[j].adnum = adnum;
8092 attrdefs[j].adef_expr = pg_strdup(PQgetvalue(res, j, 3));
8093
8094 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
8095 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
8096
8097 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
8098
8099 /*
8100 * Defaults on a VIEW must always be dumped as separate ALTER
8101 * TABLE commands. Defaults on regular tables are dumped as
8102 * part of the CREATE TABLE if possible, which it won't be if
8103 * the column is not going to be emitted explicitly.
8104 */
8105 if (tbinfo->relkind == RELKIND_VIEW)
8106 {
8107 attrdefs[j].separate = true;
8108 }
8109 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
8110 {
8111 /* column will be suppressed, print default separately */
8112 attrdefs[j].separate = true;
8113 }
8114 else
8115 {
8116 attrdefs[j].separate = false;
8117
8118 /*
8119 * Mark the default as needing to appear before the table,
8120 * so that any dependencies it has must be emitted before
8121 * the CREATE TABLE. If this is not possible, we'll
8122 * change to "separate" mode while sorting dependencies.
8123 */
8124 addObjectDependency(&tbinfo->dobj,
8125 attrdefs[j].dobj.dumpId);
8126 }
8127
8128 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
8129 }
8130 PQclear(res);
8131 }
8132
8133 /*
8134 * Get info about table CHECK constraints
8135 */
8136 if (tbinfo->ncheck > 0)
8137 {
8138 ConstraintInfo *constrs;
8139 int numConstrs;
8140
8141 if (g_verbose)
8142 write_msg(NULL, "finding check constraints for table \"%s.%s\"\n",
8143 tbinfo->dobj.namespace->dobj.name,
8144 tbinfo->dobj.name);
8145
8146 resetPQExpBuffer(q);
8147 if (fout->remoteVersion >= 90200)
8148 {
8149 /*
8150 * convalidated is new in 9.2 (actually, it is there in 9.1,
8151 * but it wasn't ever false for check constraints until 9.2).
8152 */
8153 appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
8154 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8155 "conislocal, convalidated "
8156 "FROM pg_catalog.pg_constraint "
8157 "WHERE conrelid = '%u'::pg_catalog.oid "
8158 " AND contype = 'c' "
8159 "ORDER BY conname",
8160 tbinfo->dobj.catId.oid);
8161 }
8162 else if (fout->remoteVersion >= 80400)
8163 {
8164 /* conislocal is new in 8.4 */
8165 appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
8166 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8167 "conislocal, true AS convalidated "
8168 "FROM pg_catalog.pg_constraint "
8169 "WHERE conrelid = '%u'::pg_catalog.oid "
8170 " AND contype = 'c' "
8171 "ORDER BY conname",
8172 tbinfo->dobj.catId.oid);
8173 }
8174 else
8175 {
8176 appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
8177 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8178 "true AS conislocal, true AS convalidated "
8179 "FROM pg_catalog.pg_constraint "
8180 "WHERE conrelid = '%u'::pg_catalog.oid "
8181 " AND contype = 'c' "
8182 "ORDER BY conname",
8183 tbinfo->dobj.catId.oid);
8184 }
8185
8186 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8187
8188 numConstrs = PQntuples(res);
8189 if (numConstrs != tbinfo->ncheck)
8190 {
8191 write_msg(NULL, ngettext("expected %d check constraint on table \"%s\" but found %d\n",
8192 "expected %d check constraints on table \"%s\" but found %d\n",
8193 tbinfo->ncheck),
8194 tbinfo->ncheck, tbinfo->dobj.name, numConstrs);
8195 write_msg(NULL, "(The system catalogs might be corrupted.)\n");
8196 exit_nicely(1);
8197 }
8198
8199 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
8200 tbinfo->checkexprs = constrs;
8201
8202 for (j = 0; j < numConstrs; j++)
8203 {
8204 bool validated = PQgetvalue(res, j, 5)[0] == 't';
8205
8206 constrs[j].dobj.objType = DO_CONSTRAINT;
8207 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
8208 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
8209 AssignDumpId(&constrs[j].dobj);
8210 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, 2));
8211 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
8212 constrs[j].contable = tbinfo;
8213 constrs[j].condomain = NULL;
8214 constrs[j].contype = 'c';
8215 constrs[j].condef = pg_strdup(PQgetvalue(res, j, 3));
8216 constrs[j].confrelid = InvalidOid;
8217 constrs[j].conindex = 0;
8218 constrs[j].condeferrable = false;
8219 constrs[j].condeferred = false;
8220 constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
8221
8222 /*
8223 * An unvalidated constraint needs to be dumped separately, so
8224 * that potentially-violating existing data is loaded before
8225 * the constraint.
8226 */
8227 constrs[j].separate = !validated;
8228
8229 constrs[j].dobj.dump = tbinfo->dobj.dump;
8230
8231 /*
8232 * Mark the constraint as needing to appear before the table
8233 * --- this is so that any other dependencies of the
8234 * constraint will be emitted before we try to create the
8235 * table. If the constraint is to be dumped separately, it
8236 * will be dumped after data is loaded anyway, so don't do it.
8237 * (There's an automatic dependency in the opposite direction
8238 * anyway, so don't need to add one manually here.)
8239 */
8240 if (!constrs[j].separate)
8241 addObjectDependency(&tbinfo->dobj,
8242 constrs[j].dobj.dumpId);
8243
8244 /*
8245 * If the constraint is inherited, this will be detected later
8246 * (in pre-8.4 databases). We also detect later if the
8247 * constraint must be split out from the table definition.
8248 */
8249 }
8250 PQclear(res);
8251 }
8252 }
8253
8254 destroyPQExpBuffer(q);
8255 }
8256
8257 /*
8258 * Test whether a column should be printed as part of table's CREATE TABLE.
8259 * Column number is zero-based.
8260 *
8261 * Normally this is always true, but it's false for dropped columns, as well
8262 * as those that were inherited without any local definition. (If we print
8263 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
8264 * For partitions, it's always true, because we want the partitions to be
8265 * created independently and ATTACH PARTITION used afterwards.
8266 *
8267 * In binary_upgrade mode, we must print all columns and fix the attislocal/
8268 * attisdropped state later, so as to keep control of the physical column
8269 * order.
8270 *
8271 * This function exists because there are scattered nonobvious places that
8272 * must be kept in sync with this decision.
8273 */
8274 bool
shouldPrintColumn(DumpOptions * dopt,TableInfo * tbinfo,int colno)8275 shouldPrintColumn(DumpOptions *dopt, TableInfo *tbinfo, int colno)
8276 {
8277 if (dopt->binary_upgrade)
8278 return true;
8279 if (tbinfo->attisdropped[colno])
8280 return false;
8281 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
8282 }
8283
8284
8285 /*
8286 * getTSParsers:
8287 * read all text search parsers in the system catalogs and return them
8288 * in the TSParserInfo* structure
8289 *
8290 * numTSParsers is set to the number of parsers read in
8291 */
8292 TSParserInfo *
getTSParsers(Archive * fout,int * numTSParsers)8293 getTSParsers(Archive *fout, int *numTSParsers)
8294 {
8295 PGresult *res;
8296 int ntups;
8297 int i;
8298 PQExpBuffer query;
8299 TSParserInfo *prsinfo;
8300 int i_tableoid;
8301 int i_oid;
8302 int i_prsname;
8303 int i_prsnamespace;
8304 int i_prsstart;
8305 int i_prstoken;
8306 int i_prsend;
8307 int i_prsheadline;
8308 int i_prslextype;
8309
8310 /* Before 8.3, there is no built-in text search support */
8311 if (fout->remoteVersion < 80300)
8312 {
8313 *numTSParsers = 0;
8314 return NULL;
8315 }
8316
8317 query = createPQExpBuffer();
8318
8319 /*
8320 * find all text search objects, including builtin ones; we filter out
8321 * system-defined objects at dump-out time.
8322 */
8323
8324 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
8325 "prsstart::oid, prstoken::oid, "
8326 "prsend::oid, prsheadline::oid, prslextype::oid "
8327 "FROM pg_ts_parser");
8328
8329 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8330
8331 ntups = PQntuples(res);
8332 *numTSParsers = ntups;
8333
8334 prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
8335
8336 i_tableoid = PQfnumber(res, "tableoid");
8337 i_oid = PQfnumber(res, "oid");
8338 i_prsname = PQfnumber(res, "prsname");
8339 i_prsnamespace = PQfnumber(res, "prsnamespace");
8340 i_prsstart = PQfnumber(res, "prsstart");
8341 i_prstoken = PQfnumber(res, "prstoken");
8342 i_prsend = PQfnumber(res, "prsend");
8343 i_prsheadline = PQfnumber(res, "prsheadline");
8344 i_prslextype = PQfnumber(res, "prslextype");
8345
8346 for (i = 0; i < ntups; i++)
8347 {
8348 prsinfo[i].dobj.objType = DO_TSPARSER;
8349 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8350 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8351 AssignDumpId(&prsinfo[i].dobj);
8352 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
8353 prsinfo[i].dobj.namespace =
8354 findNamespace(fout,
8355 atooid(PQgetvalue(res, i, i_prsnamespace)));
8356 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
8357 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
8358 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
8359 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
8360 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
8361
8362 /* Decide whether we want to dump it */
8363 selectDumpableObject(&(prsinfo[i].dobj), fout);
8364
8365 /* Text Search Parsers do not currently have ACLs. */
8366 prsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8367 }
8368
8369 PQclear(res);
8370
8371 destroyPQExpBuffer(query);
8372
8373 return prsinfo;
8374 }
8375
8376 /*
8377 * getTSDictionaries:
8378 * read all text search dictionaries in the system catalogs and return them
8379 * in the TSDictInfo* structure
8380 *
8381 * numTSDicts is set to the number of dictionaries read in
8382 */
8383 TSDictInfo *
getTSDictionaries(Archive * fout,int * numTSDicts)8384 getTSDictionaries(Archive *fout, int *numTSDicts)
8385 {
8386 PGresult *res;
8387 int ntups;
8388 int i;
8389 PQExpBuffer query;
8390 TSDictInfo *dictinfo;
8391 int i_tableoid;
8392 int i_oid;
8393 int i_dictname;
8394 int i_dictnamespace;
8395 int i_rolname;
8396 int i_dicttemplate;
8397 int i_dictinitoption;
8398
8399 /* Before 8.3, there is no built-in text search support */
8400 if (fout->remoteVersion < 80300)
8401 {
8402 *numTSDicts = 0;
8403 return NULL;
8404 }
8405
8406 query = createPQExpBuffer();
8407
8408 appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
8409 "dictnamespace, (%s dictowner) AS rolname, "
8410 "dicttemplate, dictinitoption "
8411 "FROM pg_ts_dict",
8412 username_subquery);
8413
8414 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8415
8416 ntups = PQntuples(res);
8417 *numTSDicts = ntups;
8418
8419 dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
8420
8421 i_tableoid = PQfnumber(res, "tableoid");
8422 i_oid = PQfnumber(res, "oid");
8423 i_dictname = PQfnumber(res, "dictname");
8424 i_dictnamespace = PQfnumber(res, "dictnamespace");
8425 i_rolname = PQfnumber(res, "rolname");
8426 i_dictinitoption = PQfnumber(res, "dictinitoption");
8427 i_dicttemplate = PQfnumber(res, "dicttemplate");
8428
8429 for (i = 0; i < ntups; i++)
8430 {
8431 dictinfo[i].dobj.objType = DO_TSDICT;
8432 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8433 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8434 AssignDumpId(&dictinfo[i].dobj);
8435 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
8436 dictinfo[i].dobj.namespace =
8437 findNamespace(fout,
8438 atooid(PQgetvalue(res, i, i_dictnamespace)));
8439 dictinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
8440 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
8441 if (PQgetisnull(res, i, i_dictinitoption))
8442 dictinfo[i].dictinitoption = NULL;
8443 else
8444 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
8445
8446 /* Decide whether we want to dump it */
8447 selectDumpableObject(&(dictinfo[i].dobj), fout);
8448
8449 /* Text Search Dictionaries do not currently have ACLs. */
8450 dictinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8451 }
8452
8453 PQclear(res);
8454
8455 destroyPQExpBuffer(query);
8456
8457 return dictinfo;
8458 }
8459
8460 /*
8461 * getTSTemplates:
8462 * read all text search templates in the system catalogs and return them
8463 * in the TSTemplateInfo* structure
8464 *
8465 * numTSTemplates is set to the number of templates read in
8466 */
8467 TSTemplateInfo *
getTSTemplates(Archive * fout,int * numTSTemplates)8468 getTSTemplates(Archive *fout, int *numTSTemplates)
8469 {
8470 PGresult *res;
8471 int ntups;
8472 int i;
8473 PQExpBuffer query;
8474 TSTemplateInfo *tmplinfo;
8475 int i_tableoid;
8476 int i_oid;
8477 int i_tmplname;
8478 int i_tmplnamespace;
8479 int i_tmplinit;
8480 int i_tmpllexize;
8481
8482 /* Before 8.3, there is no built-in text search support */
8483 if (fout->remoteVersion < 80300)
8484 {
8485 *numTSTemplates = 0;
8486 return NULL;
8487 }
8488
8489 query = createPQExpBuffer();
8490
8491 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
8492 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
8493 "FROM pg_ts_template");
8494
8495 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8496
8497 ntups = PQntuples(res);
8498 *numTSTemplates = ntups;
8499
8500 tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
8501
8502 i_tableoid = PQfnumber(res, "tableoid");
8503 i_oid = PQfnumber(res, "oid");
8504 i_tmplname = PQfnumber(res, "tmplname");
8505 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
8506 i_tmplinit = PQfnumber(res, "tmplinit");
8507 i_tmpllexize = PQfnumber(res, "tmpllexize");
8508
8509 for (i = 0; i < ntups; i++)
8510 {
8511 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
8512 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8513 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8514 AssignDumpId(&tmplinfo[i].dobj);
8515 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
8516 tmplinfo[i].dobj.namespace =
8517 findNamespace(fout,
8518 atooid(PQgetvalue(res, i, i_tmplnamespace)));
8519 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
8520 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
8521
8522 /* Decide whether we want to dump it */
8523 selectDumpableObject(&(tmplinfo[i].dobj), fout);
8524
8525 /* Text Search Templates do not currently have ACLs. */
8526 tmplinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8527 }
8528
8529 PQclear(res);
8530
8531 destroyPQExpBuffer(query);
8532
8533 return tmplinfo;
8534 }
8535
8536 /*
8537 * getTSConfigurations:
8538 * read all text search configurations in the system catalogs and return
8539 * them in the TSConfigInfo* structure
8540 *
8541 * numTSConfigs is set to the number of configurations read in
8542 */
8543 TSConfigInfo *
getTSConfigurations(Archive * fout,int * numTSConfigs)8544 getTSConfigurations(Archive *fout, int *numTSConfigs)
8545 {
8546 PGresult *res;
8547 int ntups;
8548 int i;
8549 PQExpBuffer query;
8550 TSConfigInfo *cfginfo;
8551 int i_tableoid;
8552 int i_oid;
8553 int i_cfgname;
8554 int i_cfgnamespace;
8555 int i_rolname;
8556 int i_cfgparser;
8557
8558 /* Before 8.3, there is no built-in text search support */
8559 if (fout->remoteVersion < 80300)
8560 {
8561 *numTSConfigs = 0;
8562 return NULL;
8563 }
8564
8565 query = createPQExpBuffer();
8566
8567 appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
8568 "cfgnamespace, (%s cfgowner) AS rolname, cfgparser "
8569 "FROM pg_ts_config",
8570 username_subquery);
8571
8572 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8573
8574 ntups = PQntuples(res);
8575 *numTSConfigs = ntups;
8576
8577 cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
8578
8579 i_tableoid = PQfnumber(res, "tableoid");
8580 i_oid = PQfnumber(res, "oid");
8581 i_cfgname = PQfnumber(res, "cfgname");
8582 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
8583 i_rolname = PQfnumber(res, "rolname");
8584 i_cfgparser = PQfnumber(res, "cfgparser");
8585
8586 for (i = 0; i < ntups; i++)
8587 {
8588 cfginfo[i].dobj.objType = DO_TSCONFIG;
8589 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8590 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8591 AssignDumpId(&cfginfo[i].dobj);
8592 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
8593 cfginfo[i].dobj.namespace =
8594 findNamespace(fout,
8595 atooid(PQgetvalue(res, i, i_cfgnamespace)));
8596 cfginfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
8597 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
8598
8599 /* Decide whether we want to dump it */
8600 selectDumpableObject(&(cfginfo[i].dobj), fout);
8601
8602 /* Text Search Configurations do not currently have ACLs. */
8603 cfginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8604 }
8605
8606 PQclear(res);
8607
8608 destroyPQExpBuffer(query);
8609
8610 return cfginfo;
8611 }
8612
8613 /*
8614 * getForeignDataWrappers:
8615 * read all foreign-data wrappers in the system catalogs and return
8616 * them in the FdwInfo* structure
8617 *
8618 * numForeignDataWrappers is set to the number of fdws read in
8619 */
8620 FdwInfo *
getForeignDataWrappers(Archive * fout,int * numForeignDataWrappers)8621 getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
8622 {
8623 DumpOptions *dopt = fout->dopt;
8624 PGresult *res;
8625 int ntups;
8626 int i;
8627 PQExpBuffer query;
8628 FdwInfo *fdwinfo;
8629 int i_tableoid;
8630 int i_oid;
8631 int i_fdwname;
8632 int i_rolname;
8633 int i_fdwhandler;
8634 int i_fdwvalidator;
8635 int i_fdwacl;
8636 int i_rfdwacl;
8637 int i_initfdwacl;
8638 int i_initrfdwacl;
8639 int i_fdwoptions;
8640
8641 /* Before 8.4, there are no foreign-data wrappers */
8642 if (fout->remoteVersion < 80400)
8643 {
8644 *numForeignDataWrappers = 0;
8645 return NULL;
8646 }
8647
8648 query = createPQExpBuffer();
8649
8650 if (fout->remoteVersion >= 90600)
8651 {
8652 PQExpBuffer acl_subquery = createPQExpBuffer();
8653 PQExpBuffer racl_subquery = createPQExpBuffer();
8654 PQExpBuffer initacl_subquery = createPQExpBuffer();
8655 PQExpBuffer initracl_subquery = createPQExpBuffer();
8656
8657 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
8658 initracl_subquery, "f.fdwacl", "f.fdwowner", "'F'",
8659 dopt->binary_upgrade);
8660
8661 appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.fdwname, "
8662 "(%s f.fdwowner) AS rolname, "
8663 "f.fdwhandler::pg_catalog.regproc, "
8664 "f.fdwvalidator::pg_catalog.regproc, "
8665 "%s AS fdwacl, "
8666 "%s AS rfdwacl, "
8667 "%s AS initfdwacl, "
8668 "%s AS initrfdwacl, "
8669 "array_to_string(ARRAY("
8670 "SELECT quote_ident(option_name) || ' ' || "
8671 "quote_literal(option_value) "
8672 "FROM pg_options_to_table(f.fdwoptions) "
8673 "ORDER BY option_name"
8674 "), E',\n ') AS fdwoptions "
8675 "FROM pg_foreign_data_wrapper f "
8676 "LEFT JOIN pg_init_privs pip ON "
8677 "(f.oid = pip.objoid "
8678 "AND pip.classoid = 'pg_foreign_data_wrapper'::regclass "
8679 "AND pip.objsubid = 0) ",
8680 username_subquery,
8681 acl_subquery->data,
8682 racl_subquery->data,
8683 initacl_subquery->data,
8684 initracl_subquery->data);
8685
8686 destroyPQExpBuffer(acl_subquery);
8687 destroyPQExpBuffer(racl_subquery);
8688 destroyPQExpBuffer(initacl_subquery);
8689 destroyPQExpBuffer(initracl_subquery);
8690 }
8691 else if (fout->remoteVersion >= 90100)
8692 {
8693 appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
8694 "(%s fdwowner) AS rolname, "
8695 "fdwhandler::pg_catalog.regproc, "
8696 "fdwvalidator::pg_catalog.regproc, fdwacl, "
8697 "NULL as rfdwacl, "
8698 "NULL as initfdwacl, NULL AS initrfdwacl, "
8699 "array_to_string(ARRAY("
8700 "SELECT quote_ident(option_name) || ' ' || "
8701 "quote_literal(option_value) "
8702 "FROM pg_options_to_table(fdwoptions) "
8703 "ORDER BY option_name"
8704 "), E',\n ') AS fdwoptions "
8705 "FROM pg_foreign_data_wrapper",
8706 username_subquery);
8707 }
8708 else
8709 {
8710 appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
8711 "(%s fdwowner) AS rolname, "
8712 "'-' AS fdwhandler, "
8713 "fdwvalidator::pg_catalog.regproc, fdwacl, "
8714 "NULL as rfdwacl, "
8715 "NULL as initfdwacl, NULL AS initrfdwacl, "
8716 "array_to_string(ARRAY("
8717 "SELECT quote_ident(option_name) || ' ' || "
8718 "quote_literal(option_value) "
8719 "FROM pg_options_to_table(fdwoptions) "
8720 "ORDER BY option_name"
8721 "), E',\n ') AS fdwoptions "
8722 "FROM pg_foreign_data_wrapper",
8723 username_subquery);
8724 }
8725
8726 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8727
8728 ntups = PQntuples(res);
8729 *numForeignDataWrappers = ntups;
8730
8731 fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
8732
8733 i_tableoid = PQfnumber(res, "tableoid");
8734 i_oid = PQfnumber(res, "oid");
8735 i_fdwname = PQfnumber(res, "fdwname");
8736 i_rolname = PQfnumber(res, "rolname");
8737 i_fdwhandler = PQfnumber(res, "fdwhandler");
8738 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
8739 i_fdwacl = PQfnumber(res, "fdwacl");
8740 i_rfdwacl = PQfnumber(res, "rfdwacl");
8741 i_initfdwacl = PQfnumber(res, "initfdwacl");
8742 i_initrfdwacl = PQfnumber(res, "initrfdwacl");
8743 i_fdwoptions = PQfnumber(res, "fdwoptions");
8744
8745 for (i = 0; i < ntups; i++)
8746 {
8747 fdwinfo[i].dobj.objType = DO_FDW;
8748 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8749 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8750 AssignDumpId(&fdwinfo[i].dobj);
8751 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
8752 fdwinfo[i].dobj.namespace = NULL;
8753 fdwinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
8754 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
8755 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
8756 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
8757 fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
8758 fdwinfo[i].rfdwacl = pg_strdup(PQgetvalue(res, i, i_rfdwacl));
8759 fdwinfo[i].initfdwacl = pg_strdup(PQgetvalue(res, i, i_initfdwacl));
8760 fdwinfo[i].initrfdwacl = pg_strdup(PQgetvalue(res, i, i_initrfdwacl));
8761
8762 /* Decide whether we want to dump it */
8763 selectDumpableObject(&(fdwinfo[i].dobj), fout);
8764
8765 /* Do not try to dump ACL if no ACL exists. */
8766 if (PQgetisnull(res, i, i_fdwacl) && PQgetisnull(res, i, i_rfdwacl) &&
8767 PQgetisnull(res, i, i_initfdwacl) &&
8768 PQgetisnull(res, i, i_initrfdwacl))
8769 fdwinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8770 }
8771
8772 PQclear(res);
8773
8774 destroyPQExpBuffer(query);
8775
8776 return fdwinfo;
8777 }
8778
8779 /*
8780 * getForeignServers:
8781 * read all foreign servers in the system catalogs and return
8782 * them in the ForeignServerInfo * structure
8783 *
8784 * numForeignServers is set to the number of servers read in
8785 */
8786 ForeignServerInfo *
getForeignServers(Archive * fout,int * numForeignServers)8787 getForeignServers(Archive *fout, int *numForeignServers)
8788 {
8789 DumpOptions *dopt = fout->dopt;
8790 PGresult *res;
8791 int ntups;
8792 int i;
8793 PQExpBuffer query;
8794 ForeignServerInfo *srvinfo;
8795 int i_tableoid;
8796 int i_oid;
8797 int i_srvname;
8798 int i_rolname;
8799 int i_srvfdw;
8800 int i_srvtype;
8801 int i_srvversion;
8802 int i_srvacl;
8803 int i_rsrvacl;
8804 int i_initsrvacl;
8805 int i_initrsrvacl;
8806 int i_srvoptions;
8807
8808 /* Before 8.4, there are no foreign servers */
8809 if (fout->remoteVersion < 80400)
8810 {
8811 *numForeignServers = 0;
8812 return NULL;
8813 }
8814
8815 query = createPQExpBuffer();
8816
8817 if (fout->remoteVersion >= 90600)
8818 {
8819 PQExpBuffer acl_subquery = createPQExpBuffer();
8820 PQExpBuffer racl_subquery = createPQExpBuffer();
8821 PQExpBuffer initacl_subquery = createPQExpBuffer();
8822 PQExpBuffer initracl_subquery = createPQExpBuffer();
8823
8824 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
8825 initracl_subquery, "f.srvacl", "f.srvowner", "'S'",
8826 dopt->binary_upgrade);
8827
8828 appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.srvname, "
8829 "(%s f.srvowner) AS rolname, "
8830 "f.srvfdw, f.srvtype, f.srvversion, "
8831 "%s AS srvacl, "
8832 "%s AS rsrvacl, "
8833 "%s AS initsrvacl, "
8834 "%s AS initrsrvacl, "
8835 "array_to_string(ARRAY("
8836 "SELECT quote_ident(option_name) || ' ' || "
8837 "quote_literal(option_value) "
8838 "FROM pg_options_to_table(f.srvoptions) "
8839 "ORDER BY option_name"
8840 "), E',\n ') AS srvoptions "
8841 "FROM pg_foreign_server f "
8842 "LEFT JOIN pg_init_privs pip "
8843 "ON (f.oid = pip.objoid "
8844 "AND pip.classoid = 'pg_foreign_server'::regclass "
8845 "AND pip.objsubid = 0) ",
8846 username_subquery,
8847 acl_subquery->data,
8848 racl_subquery->data,
8849 initacl_subquery->data,
8850 initracl_subquery->data);
8851
8852 destroyPQExpBuffer(acl_subquery);
8853 destroyPQExpBuffer(racl_subquery);
8854 destroyPQExpBuffer(initacl_subquery);
8855 destroyPQExpBuffer(initracl_subquery);
8856 }
8857 else
8858 {
8859 appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
8860 "(%s srvowner) AS rolname, "
8861 "srvfdw, srvtype, srvversion, srvacl, "
8862 "NULL AS rsrvacl, "
8863 "NULL AS initsrvacl, NULL AS initrsrvacl, "
8864 "array_to_string(ARRAY("
8865 "SELECT quote_ident(option_name) || ' ' || "
8866 "quote_literal(option_value) "
8867 "FROM pg_options_to_table(srvoptions) "
8868 "ORDER BY option_name"
8869 "), E',\n ') AS srvoptions "
8870 "FROM pg_foreign_server",
8871 username_subquery);
8872 }
8873
8874 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8875
8876 ntups = PQntuples(res);
8877 *numForeignServers = ntups;
8878
8879 srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
8880
8881 i_tableoid = PQfnumber(res, "tableoid");
8882 i_oid = PQfnumber(res, "oid");
8883 i_srvname = PQfnumber(res, "srvname");
8884 i_rolname = PQfnumber(res, "rolname");
8885 i_srvfdw = PQfnumber(res, "srvfdw");
8886 i_srvtype = PQfnumber(res, "srvtype");
8887 i_srvversion = PQfnumber(res, "srvversion");
8888 i_srvacl = PQfnumber(res, "srvacl");
8889 i_rsrvacl = PQfnumber(res, "rsrvacl");
8890 i_initsrvacl = PQfnumber(res, "initsrvacl");
8891 i_initrsrvacl = PQfnumber(res, "initrsrvacl");
8892 i_srvoptions = PQfnumber(res, "srvoptions");
8893
8894 for (i = 0; i < ntups; i++)
8895 {
8896 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
8897 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8898 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8899 AssignDumpId(&srvinfo[i].dobj);
8900 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
8901 srvinfo[i].dobj.namespace = NULL;
8902 srvinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
8903 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
8904 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
8905 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
8906 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
8907 srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl));
8908 srvinfo[i].rsrvacl = pg_strdup(PQgetvalue(res, i, i_rsrvacl));
8909 srvinfo[i].initsrvacl = pg_strdup(PQgetvalue(res, i, i_initsrvacl));
8910 srvinfo[i].initrsrvacl = pg_strdup(PQgetvalue(res, i, i_initrsrvacl));
8911
8912 /* Decide whether we want to dump it */
8913 selectDumpableObject(&(srvinfo[i].dobj), fout);
8914
8915 /* Do not try to dump ACL if no ACL exists. */
8916 if (PQgetisnull(res, i, i_srvacl) && PQgetisnull(res, i, i_rsrvacl) &&
8917 PQgetisnull(res, i, i_initsrvacl) &&
8918 PQgetisnull(res, i, i_initrsrvacl))
8919 srvinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8920 }
8921
8922 PQclear(res);
8923
8924 destroyPQExpBuffer(query);
8925
8926 return srvinfo;
8927 }
8928
8929 /*
8930 * getDefaultACLs:
8931 * read all default ACL information in the system catalogs and return
8932 * them in the DefaultACLInfo structure
8933 *
8934 * numDefaultACLs is set to the number of ACLs read in
8935 */
8936 DefaultACLInfo *
getDefaultACLs(Archive * fout,int * numDefaultACLs)8937 getDefaultACLs(Archive *fout, int *numDefaultACLs)
8938 {
8939 DumpOptions *dopt = fout->dopt;
8940 DefaultACLInfo *daclinfo;
8941 PQExpBuffer query;
8942 PGresult *res;
8943 int i_oid;
8944 int i_tableoid;
8945 int i_defaclrole;
8946 int i_defaclnamespace;
8947 int i_defaclobjtype;
8948 int i_defaclacl;
8949 int i_rdefaclacl;
8950 int i_initdefaclacl;
8951 int i_initrdefaclacl;
8952 int i,
8953 ntups;
8954
8955 if (fout->remoteVersion < 90000)
8956 {
8957 *numDefaultACLs = 0;
8958 return NULL;
8959 }
8960
8961 query = createPQExpBuffer();
8962
8963 if (fout->remoteVersion >= 90600)
8964 {
8965 PQExpBuffer acl_subquery = createPQExpBuffer();
8966 PQExpBuffer racl_subquery = createPQExpBuffer();
8967 PQExpBuffer initacl_subquery = createPQExpBuffer();
8968 PQExpBuffer initracl_subquery = createPQExpBuffer();
8969
8970 /*
8971 * Global entries (with defaclnamespace=0) replace the hard-wired
8972 * default ACL for their object type. We should dump them as deltas
8973 * from the default ACL, since that will be used as a starting point
8974 * for interpreting the ALTER DEFAULT PRIVILEGES commands. On the
8975 * other hand, non-global entries can only add privileges not revoke
8976 * them. We must dump those as-is (i.e., as deltas from an empty
8977 * ACL). We implement that by passing NULL as the object type for
8978 * acldefault(), which works because acldefault() is STRICT.
8979 *
8980 * We can use defaclobjtype as the object type for acldefault(),
8981 * except for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be
8982 * converted to 's'.
8983 */
8984 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
8985 initracl_subquery, "defaclacl", "defaclrole",
8986 "CASE WHEN defaclnamespace = 0 THEN"
8987 " CASE WHEN defaclobjtype = 'S' THEN 's'::\"char\""
8988 " ELSE defaclobjtype END "
8989 "ELSE NULL END",
8990 dopt->binary_upgrade);
8991
8992 appendPQExpBuffer(query, "SELECT d.oid, d.tableoid, "
8993 "(%s d.defaclrole) AS defaclrole, "
8994 "d.defaclnamespace, "
8995 "d.defaclobjtype, "
8996 "%s AS defaclacl, "
8997 "%s AS rdefaclacl, "
8998 "%s AS initdefaclacl, "
8999 "%s AS initrdefaclacl "
9000 "FROM pg_default_acl d "
9001 "LEFT JOIN pg_init_privs pip ON "
9002 "(d.oid = pip.objoid "
9003 "AND pip.classoid = 'pg_default_acl'::regclass "
9004 "AND pip.objsubid = 0) ",
9005 username_subquery,
9006 acl_subquery->data,
9007 racl_subquery->data,
9008 initacl_subquery->data,
9009 initracl_subquery->data);
9010
9011 destroyPQExpBuffer(acl_subquery);
9012 destroyPQExpBuffer(racl_subquery);
9013 destroyPQExpBuffer(initacl_subquery);
9014 destroyPQExpBuffer(initracl_subquery);
9015 }
9016 else
9017 {
9018 appendPQExpBuffer(query, "SELECT oid, tableoid, "
9019 "(%s defaclrole) AS defaclrole, "
9020 "defaclnamespace, "
9021 "defaclobjtype, "
9022 "defaclacl, "
9023 "NULL AS rdefaclacl, "
9024 "NULL AS initdefaclacl, "
9025 "NULL AS initrdefaclacl "
9026 "FROM pg_default_acl",
9027 username_subquery);
9028 }
9029
9030 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9031
9032 ntups = PQntuples(res);
9033 *numDefaultACLs = ntups;
9034
9035 daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
9036
9037 i_oid = PQfnumber(res, "oid");
9038 i_tableoid = PQfnumber(res, "tableoid");
9039 i_defaclrole = PQfnumber(res, "defaclrole");
9040 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
9041 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
9042 i_defaclacl = PQfnumber(res, "defaclacl");
9043 i_rdefaclacl = PQfnumber(res, "rdefaclacl");
9044 i_initdefaclacl = PQfnumber(res, "initdefaclacl");
9045 i_initrdefaclacl = PQfnumber(res, "initrdefaclacl");
9046
9047 for (i = 0; i < ntups; i++)
9048 {
9049 Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
9050
9051 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
9052 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9053 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9054 AssignDumpId(&daclinfo[i].dobj);
9055 /* cheesy ... is it worth coming up with a better object name? */
9056 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
9057
9058 if (nspid != InvalidOid)
9059 daclinfo[i].dobj.namespace = findNamespace(fout, nspid);
9060 else
9061 daclinfo[i].dobj.namespace = NULL;
9062
9063 daclinfo[i].defaclrole = pg_strdup(PQgetvalue(res, i, i_defaclrole));
9064 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
9065 daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
9066 daclinfo[i].rdefaclacl = pg_strdup(PQgetvalue(res, i, i_rdefaclacl));
9067 daclinfo[i].initdefaclacl = pg_strdup(PQgetvalue(res, i, i_initdefaclacl));
9068 daclinfo[i].initrdefaclacl = pg_strdup(PQgetvalue(res, i, i_initrdefaclacl));
9069
9070 /* Decide whether we want to dump it */
9071 selectDumpableDefaultACL(&(daclinfo[i]), dopt);
9072 }
9073
9074 PQclear(res);
9075
9076 destroyPQExpBuffer(query);
9077
9078 return daclinfo;
9079 }
9080
9081 /*
9082 * dumpComment --
9083 *
9084 * This routine is used to dump any comments associated with the
9085 * object handed to this routine. The routine takes the object type
9086 * and object name (ready to print, except for schema decoration), plus
9087 * the namespace and owner of the object (for labeling the ArchiveEntry),
9088 * plus catalog ID and subid which are the lookup key for pg_description,
9089 * plus the dump ID for the object (for setting a dependency).
9090 * If a matching pg_description entry is found, it is dumped.
9091 *
9092 * Note: in some cases, such as comments for triggers and rules, the "type"
9093 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
9094 * but it doesn't seem worth complicating the API for all callers to make
9095 * it cleaner.
9096 *
9097 * Note: although this routine takes a dumpId for dependency purposes,
9098 * that purpose is just to mark the dependency in the emitted dump file
9099 * for possible future use by pg_restore. We do NOT use it for determining
9100 * ordering of the comment in the dump file, because this routine is called
9101 * after dependency sorting occurs. This routine should be called just after
9102 * calling ArchiveEntry() for the specified object.
9103 */
9104 static void
dumpComment(Archive * fout,const char * type,const char * name,const char * namespace,const char * owner,CatalogId catalogId,int subid,DumpId dumpId)9105 dumpComment(Archive *fout, const char *type, const char *name,
9106 const char *namespace, const char *owner,
9107 CatalogId catalogId, int subid, DumpId dumpId)
9108 {
9109 DumpOptions *dopt = fout->dopt;
9110 CommentItem *comments;
9111 int ncomments;
9112
9113 /* Comments are schema not data ... except blob comments are data */
9114 if (strcmp(type, "LARGE OBJECT") != 0)
9115 {
9116 if (dopt->dataOnly)
9117 return;
9118 }
9119 else
9120 {
9121 /* We do dump blob comments in binary-upgrade mode */
9122 if (dopt->schemaOnly && !dopt->binary_upgrade)
9123 return;
9124 }
9125
9126 /* Search for comments associated with catalogId, using table */
9127 ncomments = findComments(fout, catalogId.tableoid, catalogId.oid,
9128 &comments);
9129
9130 /* Is there one matching the subid? */
9131 while (ncomments > 0)
9132 {
9133 if (comments->objsubid == subid)
9134 break;
9135 comments++;
9136 ncomments--;
9137 }
9138
9139 /* If a comment exists, build COMMENT ON statement */
9140 if (ncomments > 0)
9141 {
9142 PQExpBuffer query = createPQExpBuffer();
9143 PQExpBuffer tag = createPQExpBuffer();
9144
9145 appendPQExpBuffer(query, "COMMENT ON %s ", type);
9146 if (namespace && *namespace)
9147 appendPQExpBuffer(query, "%s.", fmtId(namespace));
9148 appendPQExpBuffer(query, "%s IS ", name);
9149 appendStringLiteralAH(query, comments->descr, fout);
9150 appendPQExpBufferStr(query, ";\n");
9151
9152 appendPQExpBuffer(tag, "%s %s", type, name);
9153
9154 /*
9155 * We mark comments as SECTION_NONE because they really belong in the
9156 * same section as their parent, whether that is pre-data or
9157 * post-data.
9158 */
9159 ArchiveEntry(fout, nilCatalogId, createDumpId(),
9160 tag->data, namespace, NULL, owner,
9161 false, "COMMENT", SECTION_NONE,
9162 query->data, "", NULL,
9163 &(dumpId), 1,
9164 NULL, NULL);
9165
9166 destroyPQExpBuffer(query);
9167 destroyPQExpBuffer(tag);
9168 }
9169 }
9170
9171 /*
9172 * dumpTableComment --
9173 *
9174 * As above, but dump comments for both the specified table (or view)
9175 * and its columns.
9176 */
9177 static void
dumpTableComment(Archive * fout,TableInfo * tbinfo,const char * reltypename)9178 dumpTableComment(Archive *fout, TableInfo *tbinfo,
9179 const char *reltypename)
9180 {
9181 DumpOptions *dopt = fout->dopt;
9182 CommentItem *comments;
9183 int ncomments;
9184 PQExpBuffer query;
9185 PQExpBuffer tag;
9186
9187 /* Comments are SCHEMA not data */
9188 if (dopt->dataOnly)
9189 return;
9190
9191 /* Search for comments associated with relation, using table */
9192 ncomments = findComments(fout,
9193 tbinfo->dobj.catId.tableoid,
9194 tbinfo->dobj.catId.oid,
9195 &comments);
9196
9197 /* If comments exist, build COMMENT ON statements */
9198 if (ncomments <= 0)
9199 return;
9200
9201 query = createPQExpBuffer();
9202 tag = createPQExpBuffer();
9203
9204 while (ncomments > 0)
9205 {
9206 const char *descr = comments->descr;
9207 int objsubid = comments->objsubid;
9208
9209 if (objsubid == 0)
9210 {
9211 resetPQExpBuffer(tag);
9212 appendPQExpBuffer(tag, "%s %s", reltypename,
9213 fmtId(tbinfo->dobj.name));
9214
9215 resetPQExpBuffer(query);
9216 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
9217 fmtQualifiedDumpable(tbinfo));
9218 appendStringLiteralAH(query, descr, fout);
9219 appendPQExpBufferStr(query, ";\n");
9220
9221 ArchiveEntry(fout, nilCatalogId, createDumpId(),
9222 tag->data,
9223 tbinfo->dobj.namespace->dobj.name,
9224 NULL, tbinfo->rolname,
9225 false, "COMMENT", SECTION_NONE,
9226 query->data, "", NULL,
9227 &(tbinfo->dobj.dumpId), 1,
9228 NULL, NULL);
9229 }
9230 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
9231 {
9232 resetPQExpBuffer(tag);
9233 appendPQExpBuffer(tag, "COLUMN %s.",
9234 fmtId(tbinfo->dobj.name));
9235 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
9236
9237 resetPQExpBuffer(query);
9238 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
9239 fmtQualifiedDumpable(tbinfo));
9240 appendPQExpBuffer(query, "%s IS ",
9241 fmtId(tbinfo->attnames[objsubid - 1]));
9242 appendStringLiteralAH(query, descr, fout);
9243 appendPQExpBufferStr(query, ";\n");
9244
9245 ArchiveEntry(fout, nilCatalogId, createDumpId(),
9246 tag->data,
9247 tbinfo->dobj.namespace->dobj.name,
9248 NULL, tbinfo->rolname,
9249 false, "COMMENT", SECTION_NONE,
9250 query->data, "", NULL,
9251 &(tbinfo->dobj.dumpId), 1,
9252 NULL, NULL);
9253 }
9254
9255 comments++;
9256 ncomments--;
9257 }
9258
9259 destroyPQExpBuffer(query);
9260 destroyPQExpBuffer(tag);
9261 }
9262
9263 /*
9264 * findComments --
9265 *
9266 * Find the comment(s), if any, associated with the given object. All the
9267 * objsubid values associated with the given classoid/objoid are found with
9268 * one search.
9269 */
9270 static int
findComments(Archive * fout,Oid classoid,Oid objoid,CommentItem ** items)9271 findComments(Archive *fout, Oid classoid, Oid objoid,
9272 CommentItem **items)
9273 {
9274 /* static storage for table of comments */
9275 static CommentItem *comments = NULL;
9276 static int ncomments = -1;
9277
9278 CommentItem *middle = NULL;
9279 CommentItem *low;
9280 CommentItem *high;
9281 int nmatch;
9282
9283 /* Get comments if we didn't already */
9284 if (ncomments < 0)
9285 ncomments = collectComments(fout, &comments);
9286
9287 /*
9288 * Do binary search to find some item matching the object.
9289 */
9290 low = &comments[0];
9291 high = &comments[ncomments - 1];
9292 while (low <= high)
9293 {
9294 middle = low + (high - low) / 2;
9295
9296 if (classoid < middle->classoid)
9297 high = middle - 1;
9298 else if (classoid > middle->classoid)
9299 low = middle + 1;
9300 else if (objoid < middle->objoid)
9301 high = middle - 1;
9302 else if (objoid > middle->objoid)
9303 low = middle + 1;
9304 else
9305 break; /* found a match */
9306 }
9307
9308 if (low > high) /* no matches */
9309 {
9310 *items = NULL;
9311 return 0;
9312 }
9313
9314 /*
9315 * Now determine how many items match the object. The search loop
9316 * invariant still holds: only items between low and high inclusive could
9317 * match.
9318 */
9319 nmatch = 1;
9320 while (middle > low)
9321 {
9322 if (classoid != middle[-1].classoid ||
9323 objoid != middle[-1].objoid)
9324 break;
9325 middle--;
9326 nmatch++;
9327 }
9328
9329 *items = middle;
9330
9331 middle += nmatch;
9332 while (middle <= high)
9333 {
9334 if (classoid != middle->classoid ||
9335 objoid != middle->objoid)
9336 break;
9337 middle++;
9338 nmatch++;
9339 }
9340
9341 return nmatch;
9342 }
9343
9344 /*
9345 * collectComments --
9346 *
9347 * Construct a table of all comments available for database objects.
9348 * We used to do per-object queries for the comments, but it's much faster
9349 * to pull them all over at once, and on most databases the memory cost
9350 * isn't high.
9351 *
9352 * The table is sorted by classoid/objid/objsubid for speed in lookup.
9353 */
9354 static int
collectComments(Archive * fout,CommentItem ** items)9355 collectComments(Archive *fout, CommentItem **items)
9356 {
9357 PGresult *res;
9358 PQExpBuffer query;
9359 int i_description;
9360 int i_classoid;
9361 int i_objoid;
9362 int i_objsubid;
9363 int ntups;
9364 int i;
9365 CommentItem *comments;
9366
9367 query = createPQExpBuffer();
9368
9369 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
9370 "FROM pg_catalog.pg_description "
9371 "ORDER BY classoid, objoid, objsubid");
9372
9373 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9374
9375 /* Construct lookup table containing OIDs in numeric form */
9376
9377 i_description = PQfnumber(res, "description");
9378 i_classoid = PQfnumber(res, "classoid");
9379 i_objoid = PQfnumber(res, "objoid");
9380 i_objsubid = PQfnumber(res, "objsubid");
9381
9382 ntups = PQntuples(res);
9383
9384 comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
9385
9386 for (i = 0; i < ntups; i++)
9387 {
9388 comments[i].descr = PQgetvalue(res, i, i_description);
9389 comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
9390 comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
9391 comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
9392 }
9393
9394 /* Do NOT free the PGresult since we are keeping pointers into it */
9395 destroyPQExpBuffer(query);
9396
9397 *items = comments;
9398 return ntups;
9399 }
9400
9401 /*
9402 * dumpDumpableObject
9403 *
9404 * This routine and its subsidiaries are responsible for creating
9405 * ArchiveEntries (TOC objects) for each object to be dumped.
9406 */
9407 static void
dumpDumpableObject(Archive * fout,DumpableObject * dobj)9408 dumpDumpableObject(Archive *fout, DumpableObject *dobj)
9409 {
9410 switch (dobj->objType)
9411 {
9412 case DO_NAMESPACE:
9413 dumpNamespace(fout, (NamespaceInfo *) dobj);
9414 break;
9415 case DO_EXTENSION:
9416 dumpExtension(fout, (ExtensionInfo *) dobj);
9417 break;
9418 case DO_TYPE:
9419 dumpType(fout, (TypeInfo *) dobj);
9420 break;
9421 case DO_SHELL_TYPE:
9422 dumpShellType(fout, (ShellTypeInfo *) dobj);
9423 break;
9424 case DO_FUNC:
9425 dumpFunc(fout, (FuncInfo *) dobj);
9426 break;
9427 case DO_AGG:
9428 dumpAgg(fout, (AggInfo *) dobj);
9429 break;
9430 case DO_OPERATOR:
9431 dumpOpr(fout, (OprInfo *) dobj);
9432 break;
9433 case DO_ACCESS_METHOD:
9434 dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
9435 break;
9436 case DO_OPCLASS:
9437 dumpOpclass(fout, (OpclassInfo *) dobj);
9438 break;
9439 case DO_OPFAMILY:
9440 dumpOpfamily(fout, (OpfamilyInfo *) dobj);
9441 break;
9442 case DO_COLLATION:
9443 dumpCollation(fout, (CollInfo *) dobj);
9444 break;
9445 case DO_CONVERSION:
9446 dumpConversion(fout, (ConvInfo *) dobj);
9447 break;
9448 case DO_TABLE:
9449 dumpTable(fout, (TableInfo *) dobj);
9450 break;
9451 case DO_ATTRDEF:
9452 dumpAttrDef(fout, (AttrDefInfo *) dobj);
9453 break;
9454 case DO_INDEX:
9455 dumpIndex(fout, (IndxInfo *) dobj);
9456 break;
9457 case DO_STATSEXT:
9458 dumpStatisticsExt(fout, (StatsExtInfo *) dobj);
9459 break;
9460 case DO_REFRESH_MATVIEW:
9461 refreshMatViewData(fout, (TableDataInfo *) dobj);
9462 break;
9463 case DO_RULE:
9464 dumpRule(fout, (RuleInfo *) dobj);
9465 break;
9466 case DO_TRIGGER:
9467 dumpTrigger(fout, (TriggerInfo *) dobj);
9468 break;
9469 case DO_EVENT_TRIGGER:
9470 dumpEventTrigger(fout, (EventTriggerInfo *) dobj);
9471 break;
9472 case DO_CONSTRAINT:
9473 dumpConstraint(fout, (ConstraintInfo *) dobj);
9474 break;
9475 case DO_FK_CONSTRAINT:
9476 dumpConstraint(fout, (ConstraintInfo *) dobj);
9477 break;
9478 case DO_PROCLANG:
9479 dumpProcLang(fout, (ProcLangInfo *) dobj);
9480 break;
9481 case DO_CAST:
9482 dumpCast(fout, (CastInfo *) dobj);
9483 break;
9484 case DO_TRANSFORM:
9485 dumpTransform(fout, (TransformInfo *) dobj);
9486 break;
9487 case DO_SEQUENCE_SET:
9488 dumpSequenceData(fout, (TableDataInfo *) dobj);
9489 break;
9490 case DO_TABLE_DATA:
9491 dumpTableData(fout, (TableDataInfo *) dobj);
9492 break;
9493 case DO_DUMMY_TYPE:
9494 /* table rowtypes and array types are never dumped separately */
9495 break;
9496 case DO_TSPARSER:
9497 dumpTSParser(fout, (TSParserInfo *) dobj);
9498 break;
9499 case DO_TSDICT:
9500 dumpTSDictionary(fout, (TSDictInfo *) dobj);
9501 break;
9502 case DO_TSTEMPLATE:
9503 dumpTSTemplate(fout, (TSTemplateInfo *) dobj);
9504 break;
9505 case DO_TSCONFIG:
9506 dumpTSConfig(fout, (TSConfigInfo *) dobj);
9507 break;
9508 case DO_FDW:
9509 dumpForeignDataWrapper(fout, (FdwInfo *) dobj);
9510 break;
9511 case DO_FOREIGN_SERVER:
9512 dumpForeignServer(fout, (ForeignServerInfo *) dobj);
9513 break;
9514 case DO_DEFAULT_ACL:
9515 dumpDefaultACL(fout, (DefaultACLInfo *) dobj);
9516 break;
9517 case DO_BLOB:
9518 dumpBlob(fout, (BlobInfo *) dobj);
9519 break;
9520 case DO_BLOB_DATA:
9521 if (dobj->dump & DUMP_COMPONENT_DATA)
9522 ArchiveEntry(fout, dobj->catId, dobj->dumpId,
9523 dobj->name, NULL, NULL, "",
9524 false, "BLOBS", SECTION_DATA,
9525 "", "", NULL,
9526 NULL, 0,
9527 dumpBlobs, NULL);
9528 break;
9529 case DO_POLICY:
9530 dumpPolicy(fout, (PolicyInfo *) dobj);
9531 break;
9532 case DO_PUBLICATION:
9533 dumpPublication(fout, (PublicationInfo *) dobj);
9534 break;
9535 case DO_PUBLICATION_REL:
9536 dumpPublicationTable(fout, (PublicationRelInfo *) dobj);
9537 break;
9538 case DO_SUBSCRIPTION:
9539 dumpSubscription(fout, (SubscriptionInfo *) dobj);
9540 break;
9541 case DO_PRE_DATA_BOUNDARY:
9542 case DO_POST_DATA_BOUNDARY:
9543 /* never dumped, nothing to do */
9544 break;
9545 }
9546 }
9547
9548 /*
9549 * dumpNamespace
9550 * writes out to fout the queries to recreate a user-defined namespace
9551 */
9552 static void
dumpNamespace(Archive * fout,NamespaceInfo * nspinfo)9553 dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
9554 {
9555 DumpOptions *dopt = fout->dopt;
9556 PQExpBuffer q;
9557 PQExpBuffer delq;
9558 char *qnspname;
9559
9560 /* Skip if not to be dumped */
9561 if (!nspinfo->dobj.dump || dopt->dataOnly)
9562 return;
9563
9564 q = createPQExpBuffer();
9565 delq = createPQExpBuffer();
9566
9567 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
9568
9569 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
9570
9571 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
9572
9573 if (dopt->binary_upgrade)
9574 binary_upgrade_extension_member(q, &nspinfo->dobj,
9575 "SCHEMA", qnspname, NULL);
9576
9577 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
9578 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
9579 nspinfo->dobj.name,
9580 NULL, NULL,
9581 nspinfo->rolname,
9582 false, "SCHEMA", SECTION_PRE_DATA,
9583 q->data, delq->data, NULL,
9584 NULL, 0,
9585 NULL, NULL);
9586
9587 /* Dump Schema Comments and Security Labels */
9588 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
9589 dumpComment(fout, "SCHEMA", qnspname,
9590 NULL, nspinfo->rolname,
9591 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
9592
9593 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
9594 dumpSecLabel(fout, "SCHEMA", qnspname,
9595 NULL, nspinfo->rolname,
9596 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
9597
9598 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
9599 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
9600 qnspname, NULL, NULL,
9601 nspinfo->rolname, nspinfo->nspacl, nspinfo->rnspacl,
9602 nspinfo->initnspacl, nspinfo->initrnspacl);
9603
9604 free(qnspname);
9605
9606 destroyPQExpBuffer(q);
9607 destroyPQExpBuffer(delq);
9608 }
9609
9610 /*
9611 * dumpExtension
9612 * writes out to fout the queries to recreate an extension
9613 */
9614 static void
dumpExtension(Archive * fout,ExtensionInfo * extinfo)9615 dumpExtension(Archive *fout, ExtensionInfo *extinfo)
9616 {
9617 DumpOptions *dopt = fout->dopt;
9618 PQExpBuffer q;
9619 PQExpBuffer delq;
9620 char *qextname;
9621
9622 /* Skip if not to be dumped */
9623 if (!extinfo->dobj.dump || dopt->dataOnly)
9624 return;
9625
9626 q = createPQExpBuffer();
9627 delq = createPQExpBuffer();
9628
9629 qextname = pg_strdup(fmtId(extinfo->dobj.name));
9630
9631 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
9632
9633 if (!dopt->binary_upgrade)
9634 {
9635 /*
9636 * In a regular dump, we use IF NOT EXISTS so that there isn't a
9637 * problem if the extension already exists in the target database;
9638 * this is essential for installed-by-default extensions such as
9639 * plpgsql.
9640 *
9641 * In binary-upgrade mode, that doesn't work well, so instead we skip
9642 * built-in extensions based on their OIDs; see
9643 * selectDumpableExtension.
9644 */
9645 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
9646 qextname, fmtId(extinfo->namespace));
9647 }
9648 else
9649 {
9650 int i;
9651 int n;
9652
9653 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
9654
9655 /*
9656 * We unconditionally create the extension, so we must drop it if it
9657 * exists. This could happen if the user deleted 'plpgsql' and then
9658 * readded it, causing its oid to be greater than g_last_builtin_oid.
9659 * The g_last_builtin_oid test was kept to avoid repeatedly dropping
9660 * and recreating extensions like 'plpgsql'.
9661 */
9662 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
9663
9664 appendPQExpBufferStr(q,
9665 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
9666 appendStringLiteralAH(q, extinfo->dobj.name, fout);
9667 appendPQExpBufferStr(q, ", ");
9668 appendStringLiteralAH(q, extinfo->namespace, fout);
9669 appendPQExpBufferStr(q, ", ");
9670 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
9671 appendStringLiteralAH(q, extinfo->extversion, fout);
9672 appendPQExpBufferStr(q, ", ");
9673
9674 /*
9675 * Note that we're pushing extconfig (an OID array) back into
9676 * pg_extension exactly as-is. This is OK because pg_class OIDs are
9677 * preserved in binary upgrade.
9678 */
9679 if (strlen(extinfo->extconfig) > 2)
9680 appendStringLiteralAH(q, extinfo->extconfig, fout);
9681 else
9682 appendPQExpBufferStr(q, "NULL");
9683 appendPQExpBufferStr(q, ", ");
9684 if (strlen(extinfo->extcondition) > 2)
9685 appendStringLiteralAH(q, extinfo->extcondition, fout);
9686 else
9687 appendPQExpBufferStr(q, "NULL");
9688 appendPQExpBufferStr(q, ", ");
9689 appendPQExpBufferStr(q, "ARRAY[");
9690 n = 0;
9691 for (i = 0; i < extinfo->dobj.nDeps; i++)
9692 {
9693 DumpableObject *extobj;
9694
9695 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
9696 if (extobj && extobj->objType == DO_EXTENSION)
9697 {
9698 if (n++ > 0)
9699 appendPQExpBufferChar(q, ',');
9700 appendStringLiteralAH(q, extobj->name, fout);
9701 }
9702 }
9703 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
9704 appendPQExpBufferStr(q, ");\n");
9705 }
9706
9707 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
9708 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
9709 extinfo->dobj.name,
9710 NULL, NULL,
9711 "",
9712 false, "EXTENSION", SECTION_PRE_DATA,
9713 q->data, delq->data, NULL,
9714 NULL, 0,
9715 NULL, NULL);
9716
9717 /* Dump Extension Comments and Security Labels */
9718 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
9719 dumpComment(fout, "EXTENSION", qextname,
9720 NULL, "",
9721 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
9722
9723 if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
9724 dumpSecLabel(fout, "EXTENSION", qextname,
9725 NULL, "",
9726 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
9727
9728 free(qextname);
9729
9730 destroyPQExpBuffer(q);
9731 destroyPQExpBuffer(delq);
9732 }
9733
9734 /*
9735 * dumpType
9736 * writes out to fout the queries to recreate a user-defined type
9737 */
9738 static void
dumpType(Archive * fout,TypeInfo * tyinfo)9739 dumpType(Archive *fout, TypeInfo *tyinfo)
9740 {
9741 DumpOptions *dopt = fout->dopt;
9742
9743 /* Skip if not to be dumped */
9744 if (!tyinfo->dobj.dump || dopt->dataOnly)
9745 return;
9746
9747 /* Dump out in proper style */
9748 if (tyinfo->typtype == TYPTYPE_BASE)
9749 dumpBaseType(fout, tyinfo);
9750 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
9751 dumpDomain(fout, tyinfo);
9752 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
9753 dumpCompositeType(fout, tyinfo);
9754 else if (tyinfo->typtype == TYPTYPE_ENUM)
9755 dumpEnumType(fout, tyinfo);
9756 else if (tyinfo->typtype == TYPTYPE_RANGE)
9757 dumpRangeType(fout, tyinfo);
9758 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
9759 dumpUndefinedType(fout, tyinfo);
9760 else
9761 write_msg(NULL, "WARNING: typtype of data type \"%s\" appears to be invalid\n",
9762 tyinfo->dobj.name);
9763 }
9764
9765 /*
9766 * dumpEnumType
9767 * writes out to fout the queries to recreate a user-defined enum type
9768 */
9769 static void
dumpEnumType(Archive * fout,TypeInfo * tyinfo)9770 dumpEnumType(Archive *fout, TypeInfo *tyinfo)
9771 {
9772 DumpOptions *dopt = fout->dopt;
9773 PQExpBuffer q = createPQExpBuffer();
9774 PQExpBuffer delq = createPQExpBuffer();
9775 PQExpBuffer query = createPQExpBuffer();
9776 PGresult *res;
9777 int num,
9778 i;
9779 Oid enum_oid;
9780 char *qtypname;
9781 char *qualtypname;
9782 char *label;
9783
9784 if (fout->remoteVersion >= 90100)
9785 appendPQExpBuffer(query, "SELECT oid, enumlabel "
9786 "FROM pg_catalog.pg_enum "
9787 "WHERE enumtypid = '%u'"
9788 "ORDER BY enumsortorder",
9789 tyinfo->dobj.catId.oid);
9790 else
9791 appendPQExpBuffer(query, "SELECT oid, enumlabel "
9792 "FROM pg_catalog.pg_enum "
9793 "WHERE enumtypid = '%u'"
9794 "ORDER BY oid",
9795 tyinfo->dobj.catId.oid);
9796
9797 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9798
9799 num = PQntuples(res);
9800
9801 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
9802 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
9803
9804 /*
9805 * CASCADE shouldn't be required here as for normal types since the I/O
9806 * functions are generic and do not get dropped.
9807 */
9808 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
9809
9810 if (dopt->binary_upgrade)
9811 binary_upgrade_set_type_oids_by_type_oid(fout, q,
9812 tyinfo->dobj.catId.oid);
9813
9814 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
9815 qualtypname);
9816
9817 if (!dopt->binary_upgrade)
9818 {
9819 /* Labels with server-assigned oids */
9820 for (i = 0; i < num; i++)
9821 {
9822 label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
9823 if (i > 0)
9824 appendPQExpBufferChar(q, ',');
9825 appendPQExpBufferStr(q, "\n ");
9826 appendStringLiteralAH(q, label, fout);
9827 }
9828 }
9829
9830 appendPQExpBufferStr(q, "\n);\n");
9831
9832 if (dopt->binary_upgrade)
9833 {
9834 /* Labels with dump-assigned (preserved) oids */
9835 for (i = 0; i < num; i++)
9836 {
9837 enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
9838 label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
9839
9840 if (i == 0)
9841 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
9842 appendPQExpBuffer(q,
9843 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
9844 enum_oid);
9845 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
9846 appendStringLiteralAH(q, label, fout);
9847 appendPQExpBufferStr(q, ";\n\n");
9848 }
9849 }
9850
9851 if (dopt->binary_upgrade)
9852 binary_upgrade_extension_member(q, &tyinfo->dobj,
9853 "TYPE", qtypname,
9854 tyinfo->dobj.namespace->dobj.name);
9855
9856 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
9857 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
9858 tyinfo->dobj.name,
9859 tyinfo->dobj.namespace->dobj.name,
9860 NULL,
9861 tyinfo->rolname, false,
9862 "TYPE", SECTION_PRE_DATA,
9863 q->data, delq->data, NULL,
9864 NULL, 0,
9865 NULL, NULL);
9866
9867 /* Dump Type Comments and Security Labels */
9868 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
9869 dumpComment(fout, "TYPE", qtypname,
9870 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
9871 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
9872
9873 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
9874 dumpSecLabel(fout, "TYPE", qtypname,
9875 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
9876 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
9877
9878 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
9879 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
9880 qtypname, NULL,
9881 tyinfo->dobj.namespace->dobj.name,
9882 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
9883 tyinfo->inittypacl, tyinfo->initrtypacl);
9884
9885 PQclear(res);
9886 destroyPQExpBuffer(q);
9887 destroyPQExpBuffer(delq);
9888 destroyPQExpBuffer(query);
9889 free(qtypname);
9890 free(qualtypname);
9891 }
9892
9893 /*
9894 * dumpRangeType
9895 * writes out to fout the queries to recreate a user-defined range type
9896 */
9897 static void
dumpRangeType(Archive * fout,TypeInfo * tyinfo)9898 dumpRangeType(Archive *fout, TypeInfo *tyinfo)
9899 {
9900 DumpOptions *dopt = fout->dopt;
9901 PQExpBuffer q = createPQExpBuffer();
9902 PQExpBuffer delq = createPQExpBuffer();
9903 PQExpBuffer query = createPQExpBuffer();
9904 PGresult *res;
9905 Oid collationOid;
9906 char *qtypname;
9907 char *qualtypname;
9908 char *procname;
9909
9910 appendPQExpBuffer(query,
9911 "SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
9912 "opc.opcname AS opcname, "
9913 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
9914 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
9915 "opc.opcdefault, "
9916 "CASE WHEN rngcollation = st.typcollation THEN 0 "
9917 " ELSE rngcollation END AS collation, "
9918 "rngcanonical, rngsubdiff "
9919 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
9920 " pg_catalog.pg_opclass opc "
9921 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
9922 "rngtypid = '%u'",
9923 tyinfo->dobj.catId.oid);
9924
9925 res = ExecuteSqlQueryForSingleRow(fout, query->data);
9926
9927 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
9928 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
9929
9930 /*
9931 * CASCADE shouldn't be required here as for normal types since the I/O
9932 * functions are generic and do not get dropped.
9933 */
9934 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
9935
9936 if (dopt->binary_upgrade)
9937 binary_upgrade_set_type_oids_by_type_oid(fout,
9938 q, tyinfo->dobj.catId.oid);
9939
9940 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
9941 qualtypname);
9942
9943 appendPQExpBuffer(q, "\n subtype = %s",
9944 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
9945
9946 /* print subtype_opclass only if not default for subtype */
9947 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
9948 {
9949 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
9950 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
9951
9952 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
9953 fmtId(nspname));
9954 appendPQExpBufferStr(q, fmtId(opcname));
9955 }
9956
9957 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
9958 if (OidIsValid(collationOid))
9959 {
9960 CollInfo *coll = findCollationByOid(collationOid);
9961
9962 if (coll)
9963 appendPQExpBuffer(q, ",\n collation = %s",
9964 fmtQualifiedDumpable(coll));
9965 }
9966
9967 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
9968 if (strcmp(procname, "-") != 0)
9969 appendPQExpBuffer(q, ",\n canonical = %s", procname);
9970
9971 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
9972 if (strcmp(procname, "-") != 0)
9973 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
9974
9975 appendPQExpBufferStr(q, "\n);\n");
9976
9977 if (dopt->binary_upgrade)
9978 binary_upgrade_extension_member(q, &tyinfo->dobj,
9979 "TYPE", qtypname,
9980 tyinfo->dobj.namespace->dobj.name);
9981
9982 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
9983 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
9984 tyinfo->dobj.name,
9985 tyinfo->dobj.namespace->dobj.name,
9986 NULL,
9987 tyinfo->rolname, false,
9988 "TYPE", SECTION_PRE_DATA,
9989 q->data, delq->data, NULL,
9990 NULL, 0,
9991 NULL, NULL);
9992
9993 /* Dump Type Comments and Security Labels */
9994 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
9995 dumpComment(fout, "TYPE", qtypname,
9996 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
9997 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
9998
9999 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10000 dumpSecLabel(fout, "TYPE", qtypname,
10001 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10002 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10003
10004 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10005 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
10006 qtypname, NULL,
10007 tyinfo->dobj.namespace->dobj.name,
10008 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10009 tyinfo->inittypacl, tyinfo->initrtypacl);
10010
10011 PQclear(res);
10012 destroyPQExpBuffer(q);
10013 destroyPQExpBuffer(delq);
10014 destroyPQExpBuffer(query);
10015 free(qtypname);
10016 free(qualtypname);
10017 }
10018
10019 /*
10020 * dumpUndefinedType
10021 * writes out to fout the queries to recreate a !typisdefined type
10022 *
10023 * This is a shell type, but we use different terminology to distinguish
10024 * this case from where we have to emit a shell type definition to break
10025 * circular dependencies. An undefined type shouldn't ever have anything
10026 * depending on it.
10027 */
10028 static void
dumpUndefinedType(Archive * fout,TypeInfo * tyinfo)10029 dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
10030 {
10031 DumpOptions *dopt = fout->dopt;
10032 PQExpBuffer q = createPQExpBuffer();
10033 PQExpBuffer delq = createPQExpBuffer();
10034 char *qtypname;
10035 char *qualtypname;
10036
10037 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10038 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10039
10040 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
10041
10042 if (dopt->binary_upgrade)
10043 binary_upgrade_set_type_oids_by_type_oid(fout,
10044 q, tyinfo->dobj.catId.oid);
10045
10046 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
10047 qualtypname);
10048
10049 if (dopt->binary_upgrade)
10050 binary_upgrade_extension_member(q, &tyinfo->dobj,
10051 "TYPE", qtypname,
10052 tyinfo->dobj.namespace->dobj.name);
10053
10054 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10055 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10056 tyinfo->dobj.name,
10057 tyinfo->dobj.namespace->dobj.name,
10058 NULL,
10059 tyinfo->rolname, false,
10060 "TYPE", SECTION_PRE_DATA,
10061 q->data, delq->data, NULL,
10062 NULL, 0,
10063 NULL, NULL);
10064
10065 /* Dump Type Comments and Security Labels */
10066 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10067 dumpComment(fout, "TYPE", qtypname,
10068 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10069 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10070
10071 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10072 dumpSecLabel(fout, "TYPE", qtypname,
10073 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10074 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10075
10076 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10077 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
10078 qtypname, NULL,
10079 tyinfo->dobj.namespace->dobj.name,
10080 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10081 tyinfo->inittypacl, tyinfo->initrtypacl);
10082
10083 destroyPQExpBuffer(q);
10084 destroyPQExpBuffer(delq);
10085 free(qtypname);
10086 free(qualtypname);
10087 }
10088
10089 /*
10090 * dumpBaseType
10091 * writes out to fout the queries to recreate a user-defined base type
10092 */
10093 static void
dumpBaseType(Archive * fout,TypeInfo * tyinfo)10094 dumpBaseType(Archive *fout, TypeInfo *tyinfo)
10095 {
10096 DumpOptions *dopt = fout->dopt;
10097 PQExpBuffer q = createPQExpBuffer();
10098 PQExpBuffer delq = createPQExpBuffer();
10099 PQExpBuffer query = createPQExpBuffer();
10100 PGresult *res;
10101 char *qtypname;
10102 char *qualtypname;
10103 char *typlen;
10104 char *typinput;
10105 char *typoutput;
10106 char *typreceive;
10107 char *typsend;
10108 char *typmodin;
10109 char *typmodout;
10110 char *typanalyze;
10111 Oid typreceiveoid;
10112 Oid typsendoid;
10113 Oid typmodinoid;
10114 Oid typmodoutoid;
10115 Oid typanalyzeoid;
10116 char *typcategory;
10117 char *typispreferred;
10118 char *typdelim;
10119 char *typbyval;
10120 char *typalign;
10121 char *typstorage;
10122 char *typcollatable;
10123 char *typdefault;
10124 bool typdefault_is_literal = false;
10125
10126 /* Fetch type-specific details */
10127 if (fout->remoteVersion >= 90100)
10128 {
10129 appendPQExpBuffer(query, "SELECT typlen, "
10130 "typinput, typoutput, typreceive, typsend, "
10131 "typmodin, typmodout, typanalyze, "
10132 "typreceive::pg_catalog.oid AS typreceiveoid, "
10133 "typsend::pg_catalog.oid AS typsendoid, "
10134 "typmodin::pg_catalog.oid AS typmodinoid, "
10135 "typmodout::pg_catalog.oid AS typmodoutoid, "
10136 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
10137 "typcategory, typispreferred, "
10138 "typdelim, typbyval, typalign, typstorage, "
10139 "(typcollation <> 0) AS typcollatable, "
10140 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault "
10141 "FROM pg_catalog.pg_type "
10142 "WHERE oid = '%u'::pg_catalog.oid",
10143 tyinfo->dobj.catId.oid);
10144 }
10145 else if (fout->remoteVersion >= 80400)
10146 {
10147 appendPQExpBuffer(query, "SELECT typlen, "
10148 "typinput, typoutput, typreceive, typsend, "
10149 "typmodin, typmodout, typanalyze, "
10150 "typreceive::pg_catalog.oid AS typreceiveoid, "
10151 "typsend::pg_catalog.oid AS typsendoid, "
10152 "typmodin::pg_catalog.oid AS typmodinoid, "
10153 "typmodout::pg_catalog.oid AS typmodoutoid, "
10154 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
10155 "typcategory, typispreferred, "
10156 "typdelim, typbyval, typalign, typstorage, "
10157 "false AS typcollatable, "
10158 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault "
10159 "FROM pg_catalog.pg_type "
10160 "WHERE oid = '%u'::pg_catalog.oid",
10161 tyinfo->dobj.catId.oid);
10162 }
10163 else if (fout->remoteVersion >= 80300)
10164 {
10165 /* Before 8.4, pg_get_expr does not allow 0 for its second arg */
10166 appendPQExpBuffer(query, "SELECT typlen, "
10167 "typinput, typoutput, typreceive, typsend, "
10168 "typmodin, typmodout, typanalyze, "
10169 "typreceive::pg_catalog.oid AS typreceiveoid, "
10170 "typsend::pg_catalog.oid AS typsendoid, "
10171 "typmodin::pg_catalog.oid AS typmodinoid, "
10172 "typmodout::pg_catalog.oid AS typmodoutoid, "
10173 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
10174 "'U' AS typcategory, false AS typispreferred, "
10175 "typdelim, typbyval, typalign, typstorage, "
10176 "false AS typcollatable, "
10177 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault "
10178 "FROM pg_catalog.pg_type "
10179 "WHERE oid = '%u'::pg_catalog.oid",
10180 tyinfo->dobj.catId.oid);
10181 }
10182 else
10183 {
10184 appendPQExpBuffer(query, "SELECT typlen, "
10185 "typinput, typoutput, typreceive, typsend, "
10186 "'-' AS typmodin, '-' AS typmodout, "
10187 "typanalyze, "
10188 "typreceive::pg_catalog.oid AS typreceiveoid, "
10189 "typsend::pg_catalog.oid AS typsendoid, "
10190 "0 AS typmodinoid, 0 AS typmodoutoid, "
10191 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
10192 "'U' AS typcategory, false AS typispreferred, "
10193 "typdelim, typbyval, typalign, typstorage, "
10194 "false AS typcollatable, "
10195 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault "
10196 "FROM pg_catalog.pg_type "
10197 "WHERE oid = '%u'::pg_catalog.oid",
10198 tyinfo->dobj.catId.oid);
10199 }
10200
10201 res = ExecuteSqlQueryForSingleRow(fout, query->data);
10202
10203 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
10204 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
10205 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
10206 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
10207 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
10208 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
10209 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
10210 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
10211 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
10212 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
10213 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
10214 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
10215 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
10216 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
10217 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
10218 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
10219 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
10220 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
10221 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
10222 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
10223 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
10224 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
10225 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
10226 {
10227 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
10228 typdefault_is_literal = true; /* it needs quotes */
10229 }
10230 else
10231 typdefault = NULL;
10232
10233 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10234 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10235
10236 /*
10237 * The reason we include CASCADE is that the circular dependency between
10238 * the type and its I/O functions makes it impossible to drop the type any
10239 * other way.
10240 */
10241 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
10242
10243 /* We might already have a shell type, but setting pg_type_oid is harmless */
10244 if (dopt->binary_upgrade)
10245 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10246 tyinfo->dobj.catId.oid);
10247
10248 appendPQExpBuffer(q,
10249 "CREATE TYPE %s (\n"
10250 " INTERNALLENGTH = %s",
10251 qualtypname,
10252 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
10253
10254 /* regproc result is sufficiently quoted already */
10255 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
10256 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
10257 if (OidIsValid(typreceiveoid))
10258 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
10259 if (OidIsValid(typsendoid))
10260 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
10261 if (OidIsValid(typmodinoid))
10262 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
10263 if (OidIsValid(typmodoutoid))
10264 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
10265 if (OidIsValid(typanalyzeoid))
10266 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
10267
10268 if (strcmp(typcollatable, "t") == 0)
10269 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
10270
10271 if (typdefault != NULL)
10272 {
10273 appendPQExpBufferStr(q, ",\n DEFAULT = ");
10274 if (typdefault_is_literal)
10275 appendStringLiteralAH(q, typdefault, fout);
10276 else
10277 appendPQExpBufferStr(q, typdefault);
10278 }
10279
10280 if (OidIsValid(tyinfo->typelem))
10281 appendPQExpBuffer(q, ",\n ELEMENT = %s",
10282 getFormattedTypeName(fout, tyinfo->typelem,
10283 zeroAsOpaque));
10284
10285 if (strcmp(typcategory, "U") != 0)
10286 {
10287 appendPQExpBufferStr(q, ",\n CATEGORY = ");
10288 appendStringLiteralAH(q, typcategory, fout);
10289 }
10290
10291 if (strcmp(typispreferred, "t") == 0)
10292 appendPQExpBufferStr(q, ",\n PREFERRED = true");
10293
10294 if (typdelim && strcmp(typdelim, ",") != 0)
10295 {
10296 appendPQExpBufferStr(q, ",\n DELIMITER = ");
10297 appendStringLiteralAH(q, typdelim, fout);
10298 }
10299
10300 if (strcmp(typalign, "c") == 0)
10301 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
10302 else if (strcmp(typalign, "s") == 0)
10303 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
10304 else if (strcmp(typalign, "i") == 0)
10305 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
10306 else if (strcmp(typalign, "d") == 0)
10307 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
10308
10309 if (strcmp(typstorage, "p") == 0)
10310 appendPQExpBufferStr(q, ",\n STORAGE = plain");
10311 else if (strcmp(typstorage, "e") == 0)
10312 appendPQExpBufferStr(q, ",\n STORAGE = external");
10313 else if (strcmp(typstorage, "x") == 0)
10314 appendPQExpBufferStr(q, ",\n STORAGE = extended");
10315 else if (strcmp(typstorage, "m") == 0)
10316 appendPQExpBufferStr(q, ",\n STORAGE = main");
10317
10318 if (strcmp(typbyval, "t") == 0)
10319 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
10320
10321 appendPQExpBufferStr(q, "\n);\n");
10322
10323 if (dopt->binary_upgrade)
10324 binary_upgrade_extension_member(q, &tyinfo->dobj,
10325 "TYPE", qtypname,
10326 tyinfo->dobj.namespace->dobj.name);
10327
10328 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10329 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10330 tyinfo->dobj.name,
10331 tyinfo->dobj.namespace->dobj.name,
10332 NULL,
10333 tyinfo->rolname, false,
10334 "TYPE", SECTION_PRE_DATA,
10335 q->data, delq->data, NULL,
10336 NULL, 0,
10337 NULL, NULL);
10338
10339 /* Dump Type Comments and Security Labels */
10340 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10341 dumpComment(fout, "TYPE", qtypname,
10342 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10343 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10344
10345 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10346 dumpSecLabel(fout, "TYPE", qtypname,
10347 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10348 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10349
10350 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10351 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
10352 qtypname, NULL,
10353 tyinfo->dobj.namespace->dobj.name,
10354 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10355 tyinfo->inittypacl, tyinfo->initrtypacl);
10356
10357 PQclear(res);
10358 destroyPQExpBuffer(q);
10359 destroyPQExpBuffer(delq);
10360 destroyPQExpBuffer(query);
10361 free(qtypname);
10362 free(qualtypname);
10363 }
10364
10365 /*
10366 * dumpDomain
10367 * writes out to fout the queries to recreate a user-defined domain
10368 */
10369 static void
dumpDomain(Archive * fout,TypeInfo * tyinfo)10370 dumpDomain(Archive *fout, TypeInfo *tyinfo)
10371 {
10372 DumpOptions *dopt = fout->dopt;
10373 PQExpBuffer q = createPQExpBuffer();
10374 PQExpBuffer delq = createPQExpBuffer();
10375 PQExpBuffer query = createPQExpBuffer();
10376 PGresult *res;
10377 int i;
10378 char *qtypname;
10379 char *qualtypname;
10380 char *typnotnull;
10381 char *typdefn;
10382 char *typdefault;
10383 Oid typcollation;
10384 bool typdefault_is_literal = false;
10385
10386 /* Fetch domain specific details */
10387 if (fout->remoteVersion >= 90100)
10388 {
10389 /* typcollation is new in 9.1 */
10390 appendPQExpBuffer(query, "SELECT t.typnotnull, "
10391 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
10392 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
10393 "t.typdefault, "
10394 "CASE WHEN t.typcollation <> u.typcollation "
10395 "THEN t.typcollation ELSE 0 END AS typcollation "
10396 "FROM pg_catalog.pg_type t "
10397 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
10398 "WHERE t.oid = '%u'::pg_catalog.oid",
10399 tyinfo->dobj.catId.oid);
10400 }
10401 else
10402 {
10403 appendPQExpBuffer(query, "SELECT typnotnull, "
10404 "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, "
10405 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
10406 "typdefault, 0 AS typcollation "
10407 "FROM pg_catalog.pg_type "
10408 "WHERE oid = '%u'::pg_catalog.oid",
10409 tyinfo->dobj.catId.oid);
10410 }
10411
10412 res = ExecuteSqlQueryForSingleRow(fout, query->data);
10413
10414 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
10415 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
10416 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
10417 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
10418 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
10419 {
10420 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
10421 typdefault_is_literal = true; /* it needs quotes */
10422 }
10423 else
10424 typdefault = NULL;
10425 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
10426
10427 if (dopt->binary_upgrade)
10428 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10429 tyinfo->dobj.catId.oid);
10430
10431 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10432 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10433
10434 appendPQExpBuffer(q,
10435 "CREATE DOMAIN %s AS %s",
10436 qualtypname,
10437 typdefn);
10438
10439 /* Print collation only if different from base type's collation */
10440 if (OidIsValid(typcollation))
10441 {
10442 CollInfo *coll;
10443
10444 coll = findCollationByOid(typcollation);
10445 if (coll)
10446 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
10447 }
10448
10449 if (typnotnull[0] == 't')
10450 appendPQExpBufferStr(q, " NOT NULL");
10451
10452 if (typdefault != NULL)
10453 {
10454 appendPQExpBufferStr(q, " DEFAULT ");
10455 if (typdefault_is_literal)
10456 appendStringLiteralAH(q, typdefault, fout);
10457 else
10458 appendPQExpBufferStr(q, typdefault);
10459 }
10460
10461 PQclear(res);
10462
10463 /*
10464 * Add any CHECK constraints for the domain
10465 */
10466 for (i = 0; i < tyinfo->nDomChecks; i++)
10467 {
10468 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
10469
10470 if (!domcheck->separate)
10471 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
10472 fmtId(domcheck->dobj.name), domcheck->condef);
10473 }
10474
10475 appendPQExpBufferStr(q, ";\n");
10476
10477 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
10478
10479 if (dopt->binary_upgrade)
10480 binary_upgrade_extension_member(q, &tyinfo->dobj,
10481 "DOMAIN", qtypname,
10482 tyinfo->dobj.namespace->dobj.name);
10483
10484 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10485 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10486 tyinfo->dobj.name,
10487 tyinfo->dobj.namespace->dobj.name,
10488 NULL,
10489 tyinfo->rolname, false,
10490 "DOMAIN", SECTION_PRE_DATA,
10491 q->data, delq->data, NULL,
10492 NULL, 0,
10493 NULL, NULL);
10494
10495 /* Dump Domain Comments and Security Labels */
10496 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10497 dumpComment(fout, "DOMAIN", qtypname,
10498 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10499 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10500
10501 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10502 dumpSecLabel(fout, "DOMAIN", qtypname,
10503 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10504 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10505
10506 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10507 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
10508 qtypname, NULL,
10509 tyinfo->dobj.namespace->dobj.name,
10510 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10511 tyinfo->inittypacl, tyinfo->initrtypacl);
10512
10513 /* Dump any per-constraint comments */
10514 for (i = 0; i < tyinfo->nDomChecks; i++)
10515 {
10516 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
10517 PQExpBuffer conprefix = createPQExpBuffer();
10518
10519 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
10520 fmtId(domcheck->dobj.name));
10521
10522 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10523 dumpComment(fout, conprefix->data, qtypname,
10524 tyinfo->dobj.namespace->dobj.name,
10525 tyinfo->rolname,
10526 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
10527
10528 destroyPQExpBuffer(conprefix);
10529 }
10530
10531 destroyPQExpBuffer(q);
10532 destroyPQExpBuffer(delq);
10533 destroyPQExpBuffer(query);
10534 free(qtypname);
10535 free(qualtypname);
10536 }
10537
10538 /*
10539 * dumpCompositeType
10540 * writes out to fout the queries to recreate a user-defined stand-alone
10541 * composite type
10542 */
10543 static void
dumpCompositeType(Archive * fout,TypeInfo * tyinfo)10544 dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
10545 {
10546 DumpOptions *dopt = fout->dopt;
10547 PQExpBuffer q = createPQExpBuffer();
10548 PQExpBuffer dropped = createPQExpBuffer();
10549 PQExpBuffer delq = createPQExpBuffer();
10550 PQExpBuffer query = createPQExpBuffer();
10551 PGresult *res;
10552 char *qtypname;
10553 char *qualtypname;
10554 int ntups;
10555 int i_attname;
10556 int i_atttypdefn;
10557 int i_attlen;
10558 int i_attalign;
10559 int i_attisdropped;
10560 int i_attcollation;
10561 int i;
10562 int actual_atts;
10563
10564 /* Fetch type specific details */
10565 if (fout->remoteVersion >= 90100)
10566 {
10567 /*
10568 * attcollation is new in 9.1. Since we only want to dump COLLATE
10569 * clauses for attributes whose collation is different from their
10570 * type's default, we use a CASE here to suppress uninteresting
10571 * attcollations cheaply. atttypid will be 0 for dropped columns;
10572 * collation does not matter for those.
10573 */
10574 appendPQExpBuffer(query, "SELECT a.attname, "
10575 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
10576 "a.attlen, a.attalign, a.attisdropped, "
10577 "CASE WHEN a.attcollation <> at.typcollation "
10578 "THEN a.attcollation ELSE 0 END AS attcollation "
10579 "FROM pg_catalog.pg_type ct "
10580 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
10581 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
10582 "WHERE ct.oid = '%u'::pg_catalog.oid "
10583 "ORDER BY a.attnum ",
10584 tyinfo->dobj.catId.oid);
10585 }
10586 else
10587 {
10588 /*
10589 * Since ALTER TYPE could not drop columns until 9.1, attisdropped
10590 * should always be false.
10591 */
10592 appendPQExpBuffer(query, "SELECT a.attname, "
10593 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
10594 "a.attlen, a.attalign, a.attisdropped, "
10595 "0 AS attcollation "
10596 "FROM pg_catalog.pg_type ct, pg_catalog.pg_attribute a "
10597 "WHERE ct.oid = '%u'::pg_catalog.oid "
10598 "AND a.attrelid = ct.typrelid "
10599 "ORDER BY a.attnum ",
10600 tyinfo->dobj.catId.oid);
10601 }
10602
10603 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10604
10605 ntups = PQntuples(res);
10606
10607 i_attname = PQfnumber(res, "attname");
10608 i_atttypdefn = PQfnumber(res, "atttypdefn");
10609 i_attlen = PQfnumber(res, "attlen");
10610 i_attalign = PQfnumber(res, "attalign");
10611 i_attisdropped = PQfnumber(res, "attisdropped");
10612 i_attcollation = PQfnumber(res, "attcollation");
10613
10614 if (dopt->binary_upgrade)
10615 {
10616 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10617 tyinfo->dobj.catId.oid);
10618 binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
10619 }
10620
10621 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10622 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10623
10624 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
10625 qualtypname);
10626
10627 actual_atts = 0;
10628 for (i = 0; i < ntups; i++)
10629 {
10630 char *attname;
10631 char *atttypdefn;
10632 char *attlen;
10633 char *attalign;
10634 bool attisdropped;
10635 Oid attcollation;
10636
10637 attname = PQgetvalue(res, i, i_attname);
10638 atttypdefn = PQgetvalue(res, i, i_atttypdefn);
10639 attlen = PQgetvalue(res, i, i_attlen);
10640 attalign = PQgetvalue(res, i, i_attalign);
10641 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
10642 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
10643
10644 if (attisdropped && !dopt->binary_upgrade)
10645 continue;
10646
10647 /* Format properly if not first attr */
10648 if (actual_atts++ > 0)
10649 appendPQExpBufferChar(q, ',');
10650 appendPQExpBufferStr(q, "\n\t");
10651
10652 if (!attisdropped)
10653 {
10654 appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
10655
10656 /* Add collation if not default for the column type */
10657 if (OidIsValid(attcollation))
10658 {
10659 CollInfo *coll;
10660
10661 coll = findCollationByOid(attcollation);
10662 if (coll)
10663 appendPQExpBuffer(q, " COLLATE %s",
10664 fmtQualifiedDumpable(coll));
10665 }
10666 }
10667 else
10668 {
10669 /*
10670 * This is a dropped attribute and we're in binary_upgrade mode.
10671 * Insert a placeholder for it in the CREATE TYPE command, and set
10672 * length and alignment with direct UPDATE to the catalogs
10673 * afterwards. See similar code in dumpTableSchema().
10674 */
10675 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
10676
10677 /* stash separately for insertion after the CREATE TYPE */
10678 appendPQExpBufferStr(dropped,
10679 "\n-- For binary upgrade, recreate dropped column.\n");
10680 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
10681 "SET attlen = %s, "
10682 "attalign = '%s', attbyval = false\n"
10683 "WHERE attname = ", attlen, attalign);
10684 appendStringLiteralAH(dropped, attname, fout);
10685 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
10686 appendStringLiteralAH(dropped, qualtypname, fout);
10687 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
10688
10689 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
10690 qualtypname);
10691 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
10692 fmtId(attname));
10693 }
10694 }
10695 appendPQExpBufferStr(q, "\n);\n");
10696 appendPQExpBufferStr(q, dropped->data);
10697
10698 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
10699
10700 if (dopt->binary_upgrade)
10701 binary_upgrade_extension_member(q, &tyinfo->dobj,
10702 "TYPE", qtypname,
10703 tyinfo->dobj.namespace->dobj.name);
10704
10705 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10706 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10707 tyinfo->dobj.name,
10708 tyinfo->dobj.namespace->dobj.name,
10709 NULL,
10710 tyinfo->rolname, false,
10711 "TYPE", SECTION_PRE_DATA,
10712 q->data, delq->data, NULL,
10713 NULL, 0,
10714 NULL, NULL);
10715
10716
10717 /* Dump Type Comments and Security Labels */
10718 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10719 dumpComment(fout, "TYPE", qtypname,
10720 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10721 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10722
10723 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10724 dumpSecLabel(fout, "TYPE", qtypname,
10725 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10726 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10727
10728 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10729 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
10730 qtypname, NULL,
10731 tyinfo->dobj.namespace->dobj.name,
10732 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10733 tyinfo->inittypacl, tyinfo->initrtypacl);
10734
10735 PQclear(res);
10736 destroyPQExpBuffer(q);
10737 destroyPQExpBuffer(dropped);
10738 destroyPQExpBuffer(delq);
10739 destroyPQExpBuffer(query);
10740 free(qtypname);
10741 free(qualtypname);
10742
10743 /* Dump any per-column comments */
10744 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10745 dumpCompositeTypeColComments(fout, tyinfo);
10746 }
10747
10748 /*
10749 * dumpCompositeTypeColComments
10750 * writes out to fout the queries to recreate comments on the columns of
10751 * a user-defined stand-alone composite type
10752 */
10753 static void
dumpCompositeTypeColComments(Archive * fout,TypeInfo * tyinfo)10754 dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo)
10755 {
10756 CommentItem *comments;
10757 int ncomments;
10758 PGresult *res;
10759 PQExpBuffer query;
10760 PQExpBuffer target;
10761 Oid pgClassOid;
10762 int i;
10763 int ntups;
10764 int i_attname;
10765 int i_attnum;
10766
10767 query = createPQExpBuffer();
10768
10769 appendPQExpBuffer(query,
10770 "SELECT c.tableoid, a.attname, a.attnum "
10771 "FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a "
10772 "WHERE c.oid = '%u' AND c.oid = a.attrelid "
10773 " AND NOT a.attisdropped "
10774 "ORDER BY a.attnum ",
10775 tyinfo->typrelid);
10776
10777 /* Fetch column attnames */
10778 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10779
10780 ntups = PQntuples(res);
10781 if (ntups < 1)
10782 {
10783 PQclear(res);
10784 destroyPQExpBuffer(query);
10785 return;
10786 }
10787
10788 pgClassOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "tableoid")));
10789
10790 /* Search for comments associated with type's pg_class OID */
10791 ncomments = findComments(fout,
10792 pgClassOid,
10793 tyinfo->typrelid,
10794 &comments);
10795
10796 /* If no comments exist, we're done */
10797 if (ncomments <= 0)
10798 {
10799 PQclear(res);
10800 destroyPQExpBuffer(query);
10801 return;
10802 }
10803
10804 /* Build COMMENT ON statements */
10805 target = createPQExpBuffer();
10806
10807 i_attnum = PQfnumber(res, "attnum");
10808 i_attname = PQfnumber(res, "attname");
10809 while (ncomments > 0)
10810 {
10811 const char *attname;
10812
10813 attname = NULL;
10814 for (i = 0; i < ntups; i++)
10815 {
10816 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid)
10817 {
10818 attname = PQgetvalue(res, i, i_attname);
10819 break;
10820 }
10821 }
10822 if (attname) /* just in case we don't find it */
10823 {
10824 const char *descr = comments->descr;
10825
10826 resetPQExpBuffer(target);
10827 appendPQExpBuffer(target, "COLUMN %s.",
10828 fmtId(tyinfo->dobj.name));
10829 appendPQExpBufferStr(target, fmtId(attname));
10830
10831 resetPQExpBuffer(query);
10832 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
10833 fmtQualifiedDumpable(tyinfo));
10834 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
10835 appendStringLiteralAH(query, descr, fout);
10836 appendPQExpBufferStr(query, ";\n");
10837
10838 ArchiveEntry(fout, nilCatalogId, createDumpId(),
10839 target->data,
10840 tyinfo->dobj.namespace->dobj.name,
10841 NULL, tyinfo->rolname,
10842 false, "COMMENT", SECTION_NONE,
10843 query->data, "", NULL,
10844 &(tyinfo->dobj.dumpId), 1,
10845 NULL, NULL);
10846 }
10847
10848 comments++;
10849 ncomments--;
10850 }
10851
10852 PQclear(res);
10853 destroyPQExpBuffer(query);
10854 destroyPQExpBuffer(target);
10855 }
10856
10857 /*
10858 * dumpShellType
10859 * writes out to fout the queries to create a shell type
10860 *
10861 * We dump a shell definition in advance of the I/O functions for the type.
10862 */
10863 static void
dumpShellType(Archive * fout,ShellTypeInfo * stinfo)10864 dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
10865 {
10866 DumpOptions *dopt = fout->dopt;
10867 PQExpBuffer q;
10868
10869 /* Skip if not to be dumped */
10870 if (!stinfo->dobj.dump || dopt->dataOnly)
10871 return;
10872
10873 q = createPQExpBuffer();
10874
10875 /*
10876 * Note the lack of a DROP command for the shell type; any required DROP
10877 * is driven off the base type entry, instead. This interacts with
10878 * _printTocEntry()'s use of the presence of a DROP command to decide
10879 * whether an entry needs an ALTER OWNER command. We don't want to alter
10880 * the shell type's owner immediately on creation; that should happen only
10881 * after it's filled in, otherwise the backend complains.
10882 */
10883
10884 if (dopt->binary_upgrade)
10885 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10886 stinfo->baseType->dobj.catId.oid);
10887
10888 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
10889 fmtQualifiedDumpable(stinfo));
10890
10891 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10892 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
10893 stinfo->dobj.name,
10894 stinfo->dobj.namespace->dobj.name,
10895 NULL,
10896 stinfo->baseType->rolname, false,
10897 "SHELL TYPE", SECTION_PRE_DATA,
10898 q->data, "", NULL,
10899 NULL, 0,
10900 NULL, NULL);
10901
10902 destroyPQExpBuffer(q);
10903 }
10904
10905 /*
10906 * dumpProcLang
10907 * writes out to fout the queries to recreate a user-defined
10908 * procedural language
10909 */
10910 static void
dumpProcLang(Archive * fout,ProcLangInfo * plang)10911 dumpProcLang(Archive *fout, ProcLangInfo *plang)
10912 {
10913 DumpOptions *dopt = fout->dopt;
10914 PQExpBuffer defqry;
10915 PQExpBuffer delqry;
10916 bool useParams;
10917 char *qlanname;
10918 FuncInfo *funcInfo;
10919 FuncInfo *inlineInfo = NULL;
10920 FuncInfo *validatorInfo = NULL;
10921
10922 /* Skip if not to be dumped */
10923 if (!plang->dobj.dump || dopt->dataOnly)
10924 return;
10925
10926 /*
10927 * Try to find the support function(s). It is not an error if we don't
10928 * find them --- if the functions are in the pg_catalog schema, as is
10929 * standard in 8.1 and up, then we won't have loaded them. (In this case
10930 * we will emit a parameterless CREATE LANGUAGE command, which will
10931 * require PL template knowledge in the backend to reload.)
10932 */
10933
10934 funcInfo = findFuncByOid(plang->lanplcallfoid);
10935 if (funcInfo != NULL && !funcInfo->dobj.dump)
10936 funcInfo = NULL; /* treat not-dumped same as not-found */
10937
10938 if (OidIsValid(plang->laninline))
10939 {
10940 inlineInfo = findFuncByOid(plang->laninline);
10941 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
10942 inlineInfo = NULL;
10943 }
10944
10945 if (OidIsValid(plang->lanvalidator))
10946 {
10947 validatorInfo = findFuncByOid(plang->lanvalidator);
10948 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
10949 validatorInfo = NULL;
10950 }
10951
10952 /*
10953 * If the functions are dumpable then emit a traditional CREATE LANGUAGE
10954 * with parameters. Otherwise, we'll write a parameterless command, which
10955 * will rely on data from pg_pltemplate.
10956 */
10957 useParams = (funcInfo != NULL &&
10958 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
10959 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
10960
10961 defqry = createPQExpBuffer();
10962 delqry = createPQExpBuffer();
10963
10964 qlanname = pg_strdup(fmtId(plang->dobj.name));
10965
10966 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
10967 qlanname);
10968
10969 if (useParams)
10970 {
10971 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
10972 plang->lanpltrusted ? "TRUSTED " : "",
10973 qlanname);
10974 appendPQExpBuffer(defqry, " HANDLER %s",
10975 fmtQualifiedDumpable(funcInfo));
10976 if (OidIsValid(plang->laninline))
10977 appendPQExpBuffer(defqry, " INLINE %s",
10978 fmtQualifiedDumpable(inlineInfo));
10979 if (OidIsValid(plang->lanvalidator))
10980 appendPQExpBuffer(defqry, " VALIDATOR %s",
10981 fmtQualifiedDumpable(validatorInfo));
10982 }
10983 else
10984 {
10985 /*
10986 * If not dumping parameters, then use CREATE OR REPLACE so that the
10987 * command will not fail if the language is preinstalled in the target
10988 * database. We restrict the use of REPLACE to this case so as to
10989 * eliminate the risk of replacing a language with incompatible
10990 * parameter settings: this command will only succeed at all if there
10991 * is a pg_pltemplate entry, and if there is one, the existing entry
10992 * must match it too.
10993 */
10994 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
10995 qlanname);
10996 }
10997 appendPQExpBufferStr(defqry, ";\n");
10998
10999 if (dopt->binary_upgrade)
11000 binary_upgrade_extension_member(defqry, &plang->dobj,
11001 "LANGUAGE", qlanname, NULL);
11002
11003 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
11004 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
11005 plang->dobj.name,
11006 NULL, NULL, plang->lanowner,
11007 false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA,
11008 defqry->data, delqry->data, NULL,
11009 NULL, 0,
11010 NULL, NULL);
11011
11012 /* Dump Proc Lang Comments and Security Labels */
11013 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
11014 dumpComment(fout, "LANGUAGE", qlanname,
11015 NULL, plang->lanowner,
11016 plang->dobj.catId, 0, plang->dobj.dumpId);
11017
11018 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
11019 dumpSecLabel(fout, "LANGUAGE", qlanname,
11020 NULL, plang->lanowner,
11021 plang->dobj.catId, 0, plang->dobj.dumpId);
11022
11023 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
11024 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
11025 qlanname, NULL, NULL,
11026 plang->lanowner, plang->lanacl, plang->rlanacl,
11027 plang->initlanacl, plang->initrlanacl);
11028
11029 free(qlanname);
11030
11031 destroyPQExpBuffer(defqry);
11032 destroyPQExpBuffer(delqry);
11033 }
11034
11035 /*
11036 * format_function_arguments: generate function name and argument list
11037 *
11038 * This is used when we can rely on pg_get_function_arguments to format
11039 * the argument list. Note, however, that pg_get_function_arguments
11040 * does not special-case zero-argument aggregates.
11041 */
11042 static char *
format_function_arguments(FuncInfo * finfo,char * funcargs,bool is_agg)11043 format_function_arguments(FuncInfo *finfo, char *funcargs, bool is_agg)
11044 {
11045 PQExpBufferData fn;
11046
11047 initPQExpBuffer(&fn);
11048 appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
11049 if (is_agg && finfo->nargs == 0)
11050 appendPQExpBufferStr(&fn, "(*)");
11051 else
11052 appendPQExpBuffer(&fn, "(%s)", funcargs);
11053 return fn.data;
11054 }
11055
11056 /*
11057 * format_function_arguments_old: generate function name and argument list
11058 *
11059 * The argument type names are qualified if needed. The function name
11060 * is never qualified.
11061 *
11062 * This is used only with pre-8.4 servers, so we aren't expecting to see
11063 * VARIADIC or TABLE arguments, nor are there any defaults for arguments.
11064 *
11065 * Any or all of allargtypes, argmodes, argnames may be NULL.
11066 */
11067 static char *
format_function_arguments_old(Archive * fout,FuncInfo * finfo,int nallargs,char ** allargtypes,char ** argmodes,char ** argnames)11068 format_function_arguments_old(Archive *fout,
11069 FuncInfo *finfo, int nallargs,
11070 char **allargtypes,
11071 char **argmodes,
11072 char **argnames)
11073 {
11074 PQExpBufferData fn;
11075 int j;
11076
11077 initPQExpBuffer(&fn);
11078 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
11079 for (j = 0; j < nallargs; j++)
11080 {
11081 Oid typid;
11082 const char *typname;
11083 const char *argmode;
11084 const char *argname;
11085
11086 typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j];
11087 typname = getFormattedTypeName(fout, typid, zeroAsOpaque);
11088
11089 if (argmodes)
11090 {
11091 switch (argmodes[j][0])
11092 {
11093 case PROARGMODE_IN:
11094 argmode = "";
11095 break;
11096 case PROARGMODE_OUT:
11097 argmode = "OUT ";
11098 break;
11099 case PROARGMODE_INOUT:
11100 argmode = "INOUT ";
11101 break;
11102 default:
11103 write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
11104 argmode = "";
11105 break;
11106 }
11107 }
11108 else
11109 argmode = "";
11110
11111 argname = argnames ? argnames[j] : (char *) NULL;
11112 if (argname && argname[0] == '\0')
11113 argname = NULL;
11114
11115 appendPQExpBuffer(&fn, "%s%s%s%s%s",
11116 (j > 0) ? ", " : "",
11117 argmode,
11118 argname ? fmtId(argname) : "",
11119 argname ? " " : "",
11120 typname);
11121 }
11122 appendPQExpBufferChar(&fn, ')');
11123 return fn.data;
11124 }
11125
11126 /*
11127 * format_function_signature: generate function name and argument list
11128 *
11129 * This is like format_function_arguments_old except that only a minimal
11130 * list of input argument types is generated; this is sufficient to
11131 * reference the function, but not to define it.
11132 *
11133 * If honor_quotes is false then the function name is never quoted.
11134 * This is appropriate for use in TOC tags, but not in SQL commands.
11135 */
11136 static char *
format_function_signature(Archive * fout,FuncInfo * finfo,bool honor_quotes)11137 format_function_signature(Archive *fout, FuncInfo *finfo, bool honor_quotes)
11138 {
11139 PQExpBufferData fn;
11140 int j;
11141
11142 initPQExpBuffer(&fn);
11143 if (honor_quotes)
11144 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
11145 else
11146 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
11147 for (j = 0; j < finfo->nargs; j++)
11148 {
11149 if (j > 0)
11150 appendPQExpBufferStr(&fn, ", ");
11151
11152 appendPQExpBufferStr(&fn,
11153 getFormattedTypeName(fout, finfo->argtypes[j],
11154 zeroAsOpaque));
11155 }
11156 appendPQExpBufferChar(&fn, ')');
11157 return fn.data;
11158 }
11159
11160
11161 /*
11162 * dumpFunc:
11163 * dump out one function
11164 */
11165 static void
dumpFunc(Archive * fout,FuncInfo * finfo)11166 dumpFunc(Archive *fout, FuncInfo *finfo)
11167 {
11168 DumpOptions *dopt = fout->dopt;
11169 PQExpBuffer query;
11170 PQExpBuffer q;
11171 PQExpBuffer delqry;
11172 PQExpBuffer asPart;
11173 PGresult *res;
11174 char *funcsig; /* identity signature */
11175 char *funcfullsig = NULL; /* full signature */
11176 char *funcsig_tag;
11177 char *proretset;
11178 char *prosrc;
11179 char *probin;
11180 char *funcargs;
11181 char *funciargs;
11182 char *funcresult;
11183 char *proallargtypes;
11184 char *proargmodes;
11185 char *proargnames;
11186 char *protrftypes;
11187 char *proiswindow;
11188 char *provolatile;
11189 char *proisstrict;
11190 char *prosecdef;
11191 char *proleakproof;
11192 char *proconfig;
11193 char *procost;
11194 char *prorows;
11195 char *proparallel;
11196 char *lanname;
11197 int nallargs;
11198 char **allargtypes = NULL;
11199 char **argmodes = NULL;
11200 char **argnames = NULL;
11201 char **configitems = NULL;
11202 int nconfigitems = 0;
11203 int i;
11204
11205 /* Skip if not to be dumped */
11206 if (!finfo->dobj.dump || dopt->dataOnly)
11207 return;
11208
11209 query = createPQExpBuffer();
11210 q = createPQExpBuffer();
11211 delqry = createPQExpBuffer();
11212 asPart = createPQExpBuffer();
11213
11214 /* Fetch function-specific details */
11215 if (fout->remoteVersion >= 90600)
11216 {
11217 /*
11218 * proparallel was added in 9.6
11219 */
11220 appendPQExpBuffer(query,
11221 "SELECT proretset, prosrc, probin, "
11222 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11223 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11224 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11225 "array_to_string(protrftypes, ' ') AS protrftypes, "
11226 "proiswindow, provolatile, proisstrict, prosecdef, "
11227 "proleakproof, proconfig, procost, prorows, "
11228 "proparallel, "
11229 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11230 "FROM pg_catalog.pg_proc "
11231 "WHERE oid = '%u'::pg_catalog.oid",
11232 finfo->dobj.catId.oid);
11233 }
11234 else if (fout->remoteVersion >= 90500)
11235 {
11236 /*
11237 * protrftypes was added in 9.5
11238 */
11239 appendPQExpBuffer(query,
11240 "SELECT proretset, prosrc, probin, "
11241 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11242 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11243 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11244 "array_to_string(protrftypes, ' ') AS protrftypes, "
11245 "proiswindow, provolatile, proisstrict, prosecdef, "
11246 "proleakproof, proconfig, procost, prorows, "
11247 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11248 "FROM pg_catalog.pg_proc "
11249 "WHERE oid = '%u'::pg_catalog.oid",
11250 finfo->dobj.catId.oid);
11251 }
11252 else if (fout->remoteVersion >= 90200)
11253 {
11254 /*
11255 * proleakproof was added in 9.2
11256 */
11257 appendPQExpBuffer(query,
11258 "SELECT proretset, prosrc, probin, "
11259 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11260 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11261 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11262 "proiswindow, provolatile, proisstrict, prosecdef, "
11263 "proleakproof, proconfig, procost, prorows, "
11264 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11265 "FROM pg_catalog.pg_proc "
11266 "WHERE oid = '%u'::pg_catalog.oid",
11267 finfo->dobj.catId.oid);
11268 }
11269 else if (fout->remoteVersion >= 80400)
11270 {
11271 /*
11272 * In 8.4 and up we rely on pg_get_function_arguments and
11273 * pg_get_function_result instead of examining proallargtypes etc.
11274 */
11275 appendPQExpBuffer(query,
11276 "SELECT proretset, prosrc, probin, "
11277 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11278 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11279 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11280 "proiswindow, provolatile, proisstrict, prosecdef, "
11281 "false AS proleakproof, "
11282 " proconfig, procost, prorows, "
11283 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11284 "FROM pg_catalog.pg_proc "
11285 "WHERE oid = '%u'::pg_catalog.oid",
11286 finfo->dobj.catId.oid);
11287 }
11288 else if (fout->remoteVersion >= 80300)
11289 {
11290 appendPQExpBuffer(query,
11291 "SELECT proretset, prosrc, probin, "
11292 "proallargtypes, proargmodes, proargnames, "
11293 "false AS proiswindow, "
11294 "provolatile, proisstrict, prosecdef, "
11295 "false AS proleakproof, "
11296 "proconfig, procost, prorows, "
11297 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11298 "FROM pg_catalog.pg_proc "
11299 "WHERE oid = '%u'::pg_catalog.oid",
11300 finfo->dobj.catId.oid);
11301 }
11302 else if (fout->remoteVersion >= 80100)
11303 {
11304 appendPQExpBuffer(query,
11305 "SELECT proretset, prosrc, probin, "
11306 "proallargtypes, proargmodes, proargnames, "
11307 "false AS proiswindow, "
11308 "provolatile, proisstrict, prosecdef, "
11309 "false AS proleakproof, "
11310 "null AS proconfig, 0 AS procost, 0 AS prorows, "
11311 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11312 "FROM pg_catalog.pg_proc "
11313 "WHERE oid = '%u'::pg_catalog.oid",
11314 finfo->dobj.catId.oid);
11315 }
11316 else
11317 {
11318 appendPQExpBuffer(query,
11319 "SELECT proretset, prosrc, probin, "
11320 "null AS proallargtypes, "
11321 "null AS proargmodes, "
11322 "proargnames, "
11323 "false AS proiswindow, "
11324 "provolatile, proisstrict, prosecdef, "
11325 "false AS proleakproof, "
11326 "null AS proconfig, 0 AS procost, 0 AS prorows, "
11327 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11328 "FROM pg_catalog.pg_proc "
11329 "WHERE oid = '%u'::pg_catalog.oid",
11330 finfo->dobj.catId.oid);
11331 }
11332
11333 res = ExecuteSqlQueryForSingleRow(fout, query->data);
11334
11335 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
11336 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
11337 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
11338 if (fout->remoteVersion >= 80400)
11339 {
11340 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
11341 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
11342 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
11343 proallargtypes = proargmodes = proargnames = NULL;
11344 }
11345 else
11346 {
11347 proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes"));
11348 proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
11349 proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
11350 funcargs = funciargs = funcresult = NULL;
11351 }
11352 if (PQfnumber(res, "protrftypes") != -1)
11353 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
11354 else
11355 protrftypes = NULL;
11356 proiswindow = PQgetvalue(res, 0, PQfnumber(res, "proiswindow"));
11357 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
11358 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
11359 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
11360 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
11361 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
11362 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
11363 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
11364
11365 if (PQfnumber(res, "proparallel") != -1)
11366 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
11367 else
11368 proparallel = NULL;
11369
11370 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
11371
11372 /*
11373 * See backend/commands/functioncmds.c for details of how the 'AS' clause
11374 * is used. In 8.4 and up, an unused probin is NULL (here ""); previous
11375 * versions would set it to "-". There are no known cases in which prosrc
11376 * is unused, so the tests below for "-" are probably useless.
11377 */
11378 if (probin[0] != '\0' && strcmp(probin, "-") != 0)
11379 {
11380 appendPQExpBufferStr(asPart, "AS ");
11381 appendStringLiteralAH(asPart, probin, fout);
11382 if (strcmp(prosrc, "-") != 0)
11383 {
11384 appendPQExpBufferStr(asPart, ", ");
11385
11386 /*
11387 * where we have bin, use dollar quoting if allowed and src
11388 * contains quote or backslash; else use regular quoting.
11389 */
11390 if (dopt->disable_dollar_quoting ||
11391 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
11392 appendStringLiteralAH(asPart, prosrc, fout);
11393 else
11394 appendStringLiteralDQ(asPart, prosrc, NULL);
11395 }
11396 }
11397 else
11398 {
11399 if (strcmp(prosrc, "-") != 0)
11400 {
11401 appendPQExpBufferStr(asPart, "AS ");
11402 /* with no bin, dollar quote src unconditionally if allowed */
11403 if (dopt->disable_dollar_quoting)
11404 appendStringLiteralAH(asPart, prosrc, fout);
11405 else
11406 appendStringLiteralDQ(asPart, prosrc, NULL);
11407 }
11408 }
11409
11410 nallargs = finfo->nargs; /* unless we learn different from allargs */
11411
11412 if (proallargtypes && *proallargtypes)
11413 {
11414 int nitems = 0;
11415
11416 if (!parsePGArray(proallargtypes, &allargtypes, &nitems) ||
11417 nitems < finfo->nargs)
11418 {
11419 write_msg(NULL, "WARNING: could not parse proallargtypes array\n");
11420 if (allargtypes)
11421 free(allargtypes);
11422 allargtypes = NULL;
11423 }
11424 else
11425 nallargs = nitems;
11426 }
11427
11428 if (proargmodes && *proargmodes)
11429 {
11430 int nitems = 0;
11431
11432 if (!parsePGArray(proargmodes, &argmodes, &nitems) ||
11433 nitems != nallargs)
11434 {
11435 write_msg(NULL, "WARNING: could not parse proargmodes array\n");
11436 if (argmodes)
11437 free(argmodes);
11438 argmodes = NULL;
11439 }
11440 }
11441
11442 if (proargnames && *proargnames)
11443 {
11444 int nitems = 0;
11445
11446 if (!parsePGArray(proargnames, &argnames, &nitems) ||
11447 nitems != nallargs)
11448 {
11449 write_msg(NULL, "WARNING: could not parse proargnames array\n");
11450 if (argnames)
11451 free(argnames);
11452 argnames = NULL;
11453 }
11454 }
11455
11456 if (proconfig && *proconfig)
11457 {
11458 if (!parsePGArray(proconfig, &configitems, &nconfigitems))
11459 {
11460 write_msg(NULL, "WARNING: could not parse proconfig array\n");
11461 if (configitems)
11462 free(configitems);
11463 configitems = NULL;
11464 nconfigitems = 0;
11465 }
11466 }
11467
11468 if (funcargs)
11469 {
11470 /* 8.4 or later; we rely on server-side code for most of the work */
11471 funcfullsig = format_function_arguments(finfo, funcargs, false);
11472 funcsig = format_function_arguments(finfo, funciargs, false);
11473 }
11474 else
11475 /* pre-8.4, do it ourselves */
11476 funcsig = format_function_arguments_old(fout,
11477 finfo, nallargs, allargtypes,
11478 argmodes, argnames);
11479
11480 funcsig_tag = format_function_signature(fout, finfo, false);
11481
11482 appendPQExpBuffer(delqry, "DROP FUNCTION %s.%s;\n",
11483 fmtId(finfo->dobj.namespace->dobj.name),
11484 funcsig);
11485
11486 appendPQExpBuffer(q, "CREATE FUNCTION %s.%s ",
11487 fmtId(finfo->dobj.namespace->dobj.name),
11488 funcfullsig ? funcfullsig :
11489 funcsig);
11490
11491 if (funcresult)
11492 appendPQExpBuffer(q, "RETURNS %s", funcresult);
11493 else
11494 appendPQExpBuffer(q, "RETURNS %s%s",
11495 (proretset[0] == 't') ? "SETOF " : "",
11496 getFormattedTypeName(fout, finfo->prorettype,
11497 zeroAsOpaque));
11498
11499 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
11500
11501 if (protrftypes != NULL && strcmp(protrftypes, "") != 0)
11502 {
11503 Oid *typeids = palloc(FUNC_MAX_ARGS * sizeof(Oid));
11504 int i;
11505
11506 appendPQExpBufferStr(q, " TRANSFORM ");
11507 parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
11508 for (i = 0; typeids[i]; i++)
11509 {
11510 if (i != 0)
11511 appendPQExpBufferStr(q, ", ");
11512 appendPQExpBuffer(q, "FOR TYPE %s",
11513 getFormattedTypeName(fout, typeids[i], zeroAsNone));
11514 }
11515 }
11516
11517 if (proiswindow[0] == 't')
11518 appendPQExpBufferStr(q, " WINDOW");
11519
11520 if (provolatile[0] != PROVOLATILE_VOLATILE)
11521 {
11522 if (provolatile[0] == PROVOLATILE_IMMUTABLE)
11523 appendPQExpBufferStr(q, " IMMUTABLE");
11524 else if (provolatile[0] == PROVOLATILE_STABLE)
11525 appendPQExpBufferStr(q, " STABLE");
11526 else if (provolatile[0] != PROVOLATILE_VOLATILE)
11527 exit_horribly(NULL, "unrecognized provolatile value for function \"%s\"\n",
11528 finfo->dobj.name);
11529 }
11530
11531 if (proisstrict[0] == 't')
11532 appendPQExpBufferStr(q, " STRICT");
11533
11534 if (prosecdef[0] == 't')
11535 appendPQExpBufferStr(q, " SECURITY DEFINER");
11536
11537 if (proleakproof[0] == 't')
11538 appendPQExpBufferStr(q, " LEAKPROOF");
11539
11540 /*
11541 * COST and ROWS are emitted only if present and not default, so as not to
11542 * break backwards-compatibility of the dump without need. Keep this code
11543 * in sync with the defaults in functioncmds.c.
11544 */
11545 if (strcmp(procost, "0") != 0)
11546 {
11547 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
11548 {
11549 /* default cost is 1 */
11550 if (strcmp(procost, "1") != 0)
11551 appendPQExpBuffer(q, " COST %s", procost);
11552 }
11553 else
11554 {
11555 /* default cost is 100 */
11556 if (strcmp(procost, "100") != 0)
11557 appendPQExpBuffer(q, " COST %s", procost);
11558 }
11559 }
11560 if (proretset[0] == 't' &&
11561 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
11562 appendPQExpBuffer(q, " ROWS %s", prorows);
11563
11564 if (proparallel != NULL && proparallel[0] != PROPARALLEL_UNSAFE)
11565 {
11566 if (proparallel[0] == PROPARALLEL_SAFE)
11567 appendPQExpBufferStr(q, " PARALLEL SAFE");
11568 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
11569 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
11570 else if (proparallel[0] != PROPARALLEL_UNSAFE)
11571 exit_horribly(NULL, "unrecognized proparallel value for function \"%s\"\n",
11572 finfo->dobj.name);
11573 }
11574
11575 for (i = 0; i < nconfigitems; i++)
11576 {
11577 /* we feel free to scribble on configitems[] here */
11578 char *configitem = configitems[i];
11579 char *pos;
11580
11581 pos = strchr(configitem, '=');
11582 if (pos == NULL)
11583 continue;
11584 *pos++ = '\0';
11585 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
11586
11587 /*
11588 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
11589 * by flatten_set_variable_args() before they were put into the
11590 * proconfig array. However, because the quoting rules used there
11591 * aren't exactly like SQL's, we have to break the list value apart
11592 * and then quote the elements as string literals. (The elements may
11593 * be double-quoted as-is, but we can't just feed them to the SQL
11594 * parser; it would do the wrong thing with elements that are
11595 * zero-length or longer than NAMEDATALEN.)
11596 *
11597 * Variables that are not so marked should just be emitted as simple
11598 * string literals. If the variable is not known to
11599 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
11600 * to use GUC_LIST_QUOTE for extension variables.
11601 */
11602 if (variable_is_guc_list_quote(configitem))
11603 {
11604 char **namelist;
11605 char **nameptr;
11606
11607 /* Parse string into list of identifiers */
11608 /* this shouldn't fail really */
11609 if (SplitGUCList(pos, ',', &namelist))
11610 {
11611 for (nameptr = namelist; *nameptr; nameptr++)
11612 {
11613 if (nameptr != namelist)
11614 appendPQExpBufferStr(q, ", ");
11615 appendStringLiteralAH(q, *nameptr, fout);
11616 }
11617 }
11618 pg_free(namelist);
11619 }
11620 else
11621 appendStringLiteralAH(q, pos, fout);
11622 }
11623
11624 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
11625
11626 append_depends_on_extension(fout, q, &finfo->dobj,
11627 "pg_catalog.pg_proc", "FUNCTION",
11628 psprintf("%s.%s",
11629 fmtId(finfo->dobj.namespace->dobj.name),
11630 funcsig));
11631
11632 if (dopt->binary_upgrade)
11633 binary_upgrade_extension_member(q, &finfo->dobj,
11634 "FUNCTION", funcsig,
11635 finfo->dobj.namespace->dobj.name);
11636
11637 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11638 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
11639 funcsig_tag,
11640 finfo->dobj.namespace->dobj.name,
11641 NULL,
11642 finfo->rolname, false,
11643 "FUNCTION", SECTION_PRE_DATA,
11644 q->data, delqry->data, NULL,
11645 NULL, 0,
11646 NULL, NULL);
11647
11648 /* Dump Function Comments and Security Labels */
11649 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11650 dumpComment(fout, "FUNCTION", funcsig,
11651 finfo->dobj.namespace->dobj.name, finfo->rolname,
11652 finfo->dobj.catId, 0, finfo->dobj.dumpId);
11653
11654 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11655 dumpSecLabel(fout, "FUNCTION", funcsig,
11656 finfo->dobj.namespace->dobj.name, finfo->rolname,
11657 finfo->dobj.catId, 0, finfo->dobj.dumpId);
11658
11659 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
11660 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, "FUNCTION",
11661 funcsig, NULL,
11662 finfo->dobj.namespace->dobj.name,
11663 finfo->rolname, finfo->proacl, finfo->rproacl,
11664 finfo->initproacl, finfo->initrproacl);
11665
11666 PQclear(res);
11667
11668 destroyPQExpBuffer(query);
11669 destroyPQExpBuffer(q);
11670 destroyPQExpBuffer(delqry);
11671 destroyPQExpBuffer(asPart);
11672 free(funcsig);
11673 if (funcfullsig)
11674 free(funcfullsig);
11675 free(funcsig_tag);
11676 if (allargtypes)
11677 free(allargtypes);
11678 if (argmodes)
11679 free(argmodes);
11680 if (argnames)
11681 free(argnames);
11682 if (configitems)
11683 free(configitems);
11684 }
11685
11686
11687 /*
11688 * Dump a user-defined cast
11689 */
11690 static void
dumpCast(Archive * fout,CastInfo * cast)11691 dumpCast(Archive *fout, CastInfo *cast)
11692 {
11693 DumpOptions *dopt = fout->dopt;
11694 PQExpBuffer defqry;
11695 PQExpBuffer delqry;
11696 PQExpBuffer labelq;
11697 PQExpBuffer castargs;
11698 FuncInfo *funcInfo = NULL;
11699 const char *sourceType;
11700 const char *targetType;
11701
11702 /* Skip if not to be dumped */
11703 if (!cast->dobj.dump || dopt->dataOnly)
11704 return;
11705
11706 /* Cannot dump if we don't have the cast function's info */
11707 if (OidIsValid(cast->castfunc))
11708 {
11709 funcInfo = findFuncByOid(cast->castfunc);
11710 if (funcInfo == NULL)
11711 exit_horribly(NULL, "could not find function definition for function with OID %u\n",
11712 cast->castfunc);
11713 }
11714
11715 defqry = createPQExpBuffer();
11716 delqry = createPQExpBuffer();
11717 labelq = createPQExpBuffer();
11718 castargs = createPQExpBuffer();
11719
11720 sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
11721 targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
11722 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
11723 sourceType, targetType);
11724
11725 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
11726 sourceType, targetType);
11727
11728 switch (cast->castmethod)
11729 {
11730 case COERCION_METHOD_BINARY:
11731 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
11732 break;
11733 case COERCION_METHOD_INOUT:
11734 appendPQExpBufferStr(defqry, "WITH INOUT");
11735 break;
11736 case COERCION_METHOD_FUNCTION:
11737 if (funcInfo)
11738 {
11739 char *fsig = format_function_signature(fout, funcInfo, true);
11740
11741 /*
11742 * Always qualify the function name (format_function_signature
11743 * won't qualify it).
11744 */
11745 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
11746 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
11747 free(fsig);
11748 }
11749 else
11750 write_msg(NULL, "WARNING: bogus value in pg_cast.castfunc or pg_cast.castmethod field\n");
11751 break;
11752 default:
11753 write_msg(NULL, "WARNING: bogus value in pg_cast.castmethod field\n");
11754 }
11755
11756 if (cast->castcontext == 'a')
11757 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
11758 else if (cast->castcontext == 'i')
11759 appendPQExpBufferStr(defqry, " AS IMPLICIT");
11760 appendPQExpBufferStr(defqry, ";\n");
11761
11762 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
11763 sourceType, targetType);
11764
11765 appendPQExpBuffer(castargs, "(%s AS %s)",
11766 sourceType, targetType);
11767
11768 if (dopt->binary_upgrade)
11769 binary_upgrade_extension_member(defqry, &cast->dobj,
11770 "CAST", castargs->data, NULL);
11771
11772 if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
11773 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
11774 labelq->data,
11775 NULL, NULL, "",
11776 false, "CAST", SECTION_PRE_DATA,
11777 defqry->data, delqry->data, NULL,
11778 NULL, 0,
11779 NULL, NULL);
11780
11781 /* Dump Cast Comments */
11782 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
11783 dumpComment(fout, "CAST", castargs->data,
11784 NULL, "",
11785 cast->dobj.catId, 0, cast->dobj.dumpId);
11786
11787 destroyPQExpBuffer(defqry);
11788 destroyPQExpBuffer(delqry);
11789 destroyPQExpBuffer(labelq);
11790 destroyPQExpBuffer(castargs);
11791 }
11792
11793 /*
11794 * Dump a transform
11795 */
11796 static void
dumpTransform(Archive * fout,TransformInfo * transform)11797 dumpTransform(Archive *fout, TransformInfo *transform)
11798 {
11799 DumpOptions *dopt = fout->dopt;
11800 PQExpBuffer defqry;
11801 PQExpBuffer delqry;
11802 PQExpBuffer labelq;
11803 PQExpBuffer transformargs;
11804 FuncInfo *fromsqlFuncInfo = NULL;
11805 FuncInfo *tosqlFuncInfo = NULL;
11806 char *lanname;
11807 const char *transformType;
11808
11809 /* Skip if not to be dumped */
11810 if (!transform->dobj.dump || dopt->dataOnly)
11811 return;
11812
11813 /* Cannot dump if we don't have the transform functions' info */
11814 if (OidIsValid(transform->trffromsql))
11815 {
11816 fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
11817 if (fromsqlFuncInfo == NULL)
11818 exit_horribly(NULL, "could not find function definition for function with OID %u\n",
11819 transform->trffromsql);
11820 }
11821 if (OidIsValid(transform->trftosql))
11822 {
11823 tosqlFuncInfo = findFuncByOid(transform->trftosql);
11824 if (tosqlFuncInfo == NULL)
11825 exit_horribly(NULL, "could not find function definition for function with OID %u\n",
11826 transform->trftosql);
11827 }
11828
11829 defqry = createPQExpBuffer();
11830 delqry = createPQExpBuffer();
11831 labelq = createPQExpBuffer();
11832 transformargs = createPQExpBuffer();
11833
11834 lanname = get_language_name(fout, transform->trflang);
11835 transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
11836
11837 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
11838 transformType, lanname);
11839
11840 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
11841 transformType, lanname);
11842
11843 if (!transform->trffromsql && !transform->trftosql)
11844 write_msg(NULL, "WARNING: bogus transform definition, at least one of trffromsql and trftosql should be nonzero\n");
11845
11846 if (transform->trffromsql)
11847 {
11848 if (fromsqlFuncInfo)
11849 {
11850 char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
11851
11852 /*
11853 * Always qualify the function name (format_function_signature
11854 * won't qualify it).
11855 */
11856 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
11857 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
11858 free(fsig);
11859 }
11860 else
11861 write_msg(NULL, "WARNING: bogus value in pg_transform.trffromsql field\n");
11862 }
11863
11864 if (transform->trftosql)
11865 {
11866 if (transform->trffromsql)
11867 appendPQExpBuffer(defqry, ", ");
11868
11869 if (tosqlFuncInfo)
11870 {
11871 char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
11872
11873 /*
11874 * Always qualify the function name (format_function_signature
11875 * won't qualify it).
11876 */
11877 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
11878 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
11879 free(fsig);
11880 }
11881 else
11882 write_msg(NULL, "WARNING: bogus value in pg_transform.trftosql field\n");
11883 }
11884
11885 appendPQExpBuffer(defqry, ");\n");
11886
11887 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
11888 transformType, lanname);
11889
11890 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
11891 transformType, lanname);
11892
11893 if (dopt->binary_upgrade)
11894 binary_upgrade_extension_member(defqry, &transform->dobj,
11895 "TRANSFORM", transformargs->data, NULL);
11896
11897 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
11898 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
11899 labelq->data,
11900 NULL, NULL, "",
11901 false, "TRANSFORM", SECTION_PRE_DATA,
11902 defqry->data, delqry->data, NULL,
11903 transform->dobj.dependencies, transform->dobj.nDeps,
11904 NULL, NULL);
11905
11906 /* Dump Transform Comments */
11907 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
11908 dumpComment(fout, "TRANSFORM", transformargs->data,
11909 NULL, "",
11910 transform->dobj.catId, 0, transform->dobj.dumpId);
11911
11912 free(lanname);
11913 destroyPQExpBuffer(defqry);
11914 destroyPQExpBuffer(delqry);
11915 destroyPQExpBuffer(labelq);
11916 destroyPQExpBuffer(transformargs);
11917 }
11918
11919
11920 /*
11921 * dumpOpr
11922 * write out a single operator definition
11923 */
11924 static void
dumpOpr(Archive * fout,OprInfo * oprinfo)11925 dumpOpr(Archive *fout, OprInfo *oprinfo)
11926 {
11927 DumpOptions *dopt = fout->dopt;
11928 PQExpBuffer query;
11929 PQExpBuffer q;
11930 PQExpBuffer delq;
11931 PQExpBuffer oprid;
11932 PQExpBuffer details;
11933 PGresult *res;
11934 int i_oprkind;
11935 int i_oprcode;
11936 int i_oprleft;
11937 int i_oprright;
11938 int i_oprcom;
11939 int i_oprnegate;
11940 int i_oprrest;
11941 int i_oprjoin;
11942 int i_oprcanmerge;
11943 int i_oprcanhash;
11944 char *oprkind;
11945 char *oprcode;
11946 char *oprleft;
11947 char *oprright;
11948 char *oprcom;
11949 char *oprnegate;
11950 char *oprrest;
11951 char *oprjoin;
11952 char *oprcanmerge;
11953 char *oprcanhash;
11954 char *oprregproc;
11955 char *oprref;
11956
11957 /* Skip if not to be dumped */
11958 if (!oprinfo->dobj.dump || dopt->dataOnly)
11959 return;
11960
11961 /*
11962 * some operators are invalid because they were the result of user
11963 * defining operators before commutators exist
11964 */
11965 if (!OidIsValid(oprinfo->oprcode))
11966 return;
11967
11968 query = createPQExpBuffer();
11969 q = createPQExpBuffer();
11970 delq = createPQExpBuffer();
11971 oprid = createPQExpBuffer();
11972 details = createPQExpBuffer();
11973
11974 if (fout->remoteVersion >= 80300)
11975 {
11976 appendPQExpBuffer(query, "SELECT oprkind, "
11977 "oprcode::pg_catalog.regprocedure, "
11978 "oprleft::pg_catalog.regtype, "
11979 "oprright::pg_catalog.regtype, "
11980 "oprcom, "
11981 "oprnegate, "
11982 "oprrest::pg_catalog.regprocedure, "
11983 "oprjoin::pg_catalog.regprocedure, "
11984 "oprcanmerge, oprcanhash "
11985 "FROM pg_catalog.pg_operator "
11986 "WHERE oid = '%u'::pg_catalog.oid",
11987 oprinfo->dobj.catId.oid);
11988 }
11989 else
11990 {
11991 appendPQExpBuffer(query, "SELECT oprkind, "
11992 "oprcode::pg_catalog.regprocedure, "
11993 "oprleft::pg_catalog.regtype, "
11994 "oprright::pg_catalog.regtype, "
11995 "oprcom, "
11996 "oprnegate, "
11997 "oprrest::pg_catalog.regprocedure, "
11998 "oprjoin::pg_catalog.regprocedure, "
11999 "(oprlsortop != 0) AS oprcanmerge, "
12000 "oprcanhash "
12001 "FROM pg_catalog.pg_operator "
12002 "WHERE oid = '%u'::pg_catalog.oid",
12003 oprinfo->dobj.catId.oid);
12004 }
12005
12006 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12007
12008 i_oprkind = PQfnumber(res, "oprkind");
12009 i_oprcode = PQfnumber(res, "oprcode");
12010 i_oprleft = PQfnumber(res, "oprleft");
12011 i_oprright = PQfnumber(res, "oprright");
12012 i_oprcom = PQfnumber(res, "oprcom");
12013 i_oprnegate = PQfnumber(res, "oprnegate");
12014 i_oprrest = PQfnumber(res, "oprrest");
12015 i_oprjoin = PQfnumber(res, "oprjoin");
12016 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
12017 i_oprcanhash = PQfnumber(res, "oprcanhash");
12018
12019 oprkind = PQgetvalue(res, 0, i_oprkind);
12020 oprcode = PQgetvalue(res, 0, i_oprcode);
12021 oprleft = PQgetvalue(res, 0, i_oprleft);
12022 oprright = PQgetvalue(res, 0, i_oprright);
12023 oprcom = PQgetvalue(res, 0, i_oprcom);
12024 oprnegate = PQgetvalue(res, 0, i_oprnegate);
12025 oprrest = PQgetvalue(res, 0, i_oprrest);
12026 oprjoin = PQgetvalue(res, 0, i_oprjoin);
12027 oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
12028 oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
12029
12030 oprregproc = convertRegProcReference(fout, oprcode);
12031 if (oprregproc)
12032 {
12033 appendPQExpBuffer(details, " PROCEDURE = %s", oprregproc);
12034 free(oprregproc);
12035 }
12036
12037 appendPQExpBuffer(oprid, "%s (",
12038 oprinfo->dobj.name);
12039
12040 /*
12041 * right unary means there's a left arg and left unary means there's a
12042 * right arg
12043 */
12044 if (strcmp(oprkind, "r") == 0 ||
12045 strcmp(oprkind, "b") == 0)
12046 {
12047 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
12048 appendPQExpBufferStr(oprid, oprleft);
12049 }
12050 else
12051 appendPQExpBufferStr(oprid, "NONE");
12052
12053 if (strcmp(oprkind, "l") == 0 ||
12054 strcmp(oprkind, "b") == 0)
12055 {
12056 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
12057 appendPQExpBuffer(oprid, ", %s)", oprright);
12058 }
12059 else
12060 appendPQExpBufferStr(oprid, ", NONE)");
12061
12062 oprref = getFormattedOperatorName(fout, oprcom);
12063 if (oprref)
12064 {
12065 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
12066 free(oprref);
12067 }
12068
12069 oprref = getFormattedOperatorName(fout, oprnegate);
12070 if (oprref)
12071 {
12072 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
12073 free(oprref);
12074 }
12075
12076 if (strcmp(oprcanmerge, "t") == 0)
12077 appendPQExpBufferStr(details, ",\n MERGES");
12078
12079 if (strcmp(oprcanhash, "t") == 0)
12080 appendPQExpBufferStr(details, ",\n HASHES");
12081
12082 oprregproc = convertRegProcReference(fout, oprrest);
12083 if (oprregproc)
12084 {
12085 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
12086 free(oprregproc);
12087 }
12088
12089 oprregproc = convertRegProcReference(fout, oprjoin);
12090 if (oprregproc)
12091 {
12092 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
12093 free(oprregproc);
12094 }
12095
12096 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
12097 fmtId(oprinfo->dobj.namespace->dobj.name),
12098 oprid->data);
12099
12100 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
12101 fmtId(oprinfo->dobj.namespace->dobj.name),
12102 oprinfo->dobj.name, details->data);
12103
12104 if (dopt->binary_upgrade)
12105 binary_upgrade_extension_member(q, &oprinfo->dobj,
12106 "OPERATOR", oprid->data,
12107 oprinfo->dobj.namespace->dobj.name);
12108
12109 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12110 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
12111 oprinfo->dobj.name,
12112 oprinfo->dobj.namespace->dobj.name,
12113 NULL,
12114 oprinfo->rolname,
12115 false, "OPERATOR", SECTION_PRE_DATA,
12116 q->data, delq->data, NULL,
12117 NULL, 0,
12118 NULL, NULL);
12119
12120 /* Dump Operator Comments */
12121 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12122 dumpComment(fout, "OPERATOR", oprid->data,
12123 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
12124 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
12125
12126 PQclear(res);
12127
12128 destroyPQExpBuffer(query);
12129 destroyPQExpBuffer(q);
12130 destroyPQExpBuffer(delq);
12131 destroyPQExpBuffer(oprid);
12132 destroyPQExpBuffer(details);
12133 }
12134
12135 /*
12136 * Convert a function reference obtained from pg_operator
12137 *
12138 * Returns allocated string of what to print, or NULL if function references
12139 * is InvalidOid. Returned string is expected to be free'd by the caller.
12140 *
12141 * The input is a REGPROCEDURE display; we have to strip the argument-types
12142 * part.
12143 */
12144 static char *
convertRegProcReference(Archive * fout,const char * proc)12145 convertRegProcReference(Archive *fout, const char *proc)
12146 {
12147 char *name;
12148 char *paren;
12149 bool inquote;
12150
12151 /* In all cases "-" means a null reference */
12152 if (strcmp(proc, "-") == 0)
12153 return NULL;
12154
12155 name = pg_strdup(proc);
12156 /* find non-double-quoted left paren */
12157 inquote = false;
12158 for (paren = name; *paren; paren++)
12159 {
12160 if (*paren == '(' && !inquote)
12161 {
12162 *paren = '\0';
12163 break;
12164 }
12165 if (*paren == '"')
12166 inquote = !inquote;
12167 }
12168 return name;
12169 }
12170
12171 /*
12172 * getFormattedOperatorName - retrieve the operator name for the
12173 * given operator OID (presented in string form).
12174 *
12175 * Returns an allocated string, or NULL if the given OID is invalid.
12176 * Caller is responsible for free'ing result string.
12177 *
12178 * What we produce has the format "OPERATOR(schema.oprname)". This is only
12179 * useful in commands where the operator's argument types can be inferred from
12180 * context. We always schema-qualify the name, though. The predecessor to
12181 * this code tried to skip the schema qualification if possible, but that led
12182 * to wrong results in corner cases, such as if an operator and its negator
12183 * are in different schemas.
12184 */
12185 static char *
getFormattedOperatorName(Archive * fout,const char * oproid)12186 getFormattedOperatorName(Archive *fout, const char *oproid)
12187 {
12188 OprInfo *oprInfo;
12189
12190 /* In all cases "0" means a null reference */
12191 if (strcmp(oproid, "0") == 0)
12192 return NULL;
12193
12194 oprInfo = findOprByOid(atooid(oproid));
12195 if (oprInfo == NULL)
12196 {
12197 write_msg(NULL, "WARNING: could not find operator with OID %s\n",
12198 oproid);
12199 return NULL;
12200 }
12201
12202 return psprintf("OPERATOR(%s.%s)",
12203 fmtId(oprInfo->dobj.namespace->dobj.name),
12204 oprInfo->dobj.name);
12205 }
12206
12207 /*
12208 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
12209 *
12210 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
12211 * argument lists of these functions are predetermined. Note that the
12212 * caller should ensure we are in the proper schema, because the results
12213 * are search path dependent!
12214 */
12215 static char *
convertTSFunction(Archive * fout,Oid funcOid)12216 convertTSFunction(Archive *fout, Oid funcOid)
12217 {
12218 char *result;
12219 char query[128];
12220 PGresult *res;
12221
12222 snprintf(query, sizeof(query),
12223 "SELECT '%u'::pg_catalog.regproc", funcOid);
12224 res = ExecuteSqlQueryForSingleRow(fout, query);
12225
12226 result = pg_strdup(PQgetvalue(res, 0, 0));
12227
12228 PQclear(res);
12229
12230 return result;
12231 }
12232
12233 /*
12234 * dumpAccessMethod
12235 * write out a single access method definition
12236 */
12237 static void
dumpAccessMethod(Archive * fout,AccessMethodInfo * aminfo)12238 dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
12239 {
12240 DumpOptions *dopt = fout->dopt;
12241 PQExpBuffer q;
12242 PQExpBuffer delq;
12243 char *qamname;
12244
12245 /* Skip if not to be dumped */
12246 if (!aminfo->dobj.dump || dopt->dataOnly)
12247 return;
12248
12249 q = createPQExpBuffer();
12250 delq = createPQExpBuffer();
12251
12252 qamname = pg_strdup(fmtId(aminfo->dobj.name));
12253
12254 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
12255
12256 switch (aminfo->amtype)
12257 {
12258 case AMTYPE_INDEX:
12259 appendPQExpBuffer(q, "TYPE INDEX ");
12260 break;
12261 default:
12262 write_msg(NULL, "WARNING: invalid type \"%c\" of access method \"%s\"\n",
12263 aminfo->amtype, qamname);
12264 destroyPQExpBuffer(q);
12265 destroyPQExpBuffer(delq);
12266 free(qamname);
12267 return;
12268 }
12269
12270 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
12271
12272 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
12273 qamname);
12274
12275 if (dopt->binary_upgrade)
12276 binary_upgrade_extension_member(q, &aminfo->dobj,
12277 "ACCESS METHOD", qamname, NULL);
12278
12279 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12280 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
12281 aminfo->dobj.name,
12282 NULL,
12283 NULL,
12284 "",
12285 false, "ACCESS METHOD", SECTION_PRE_DATA,
12286 q->data, delq->data, NULL,
12287 NULL, 0,
12288 NULL, NULL);
12289
12290 /* Dump Access Method Comments */
12291 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12292 dumpComment(fout, "ACCESS METHOD", qamname,
12293 NULL, "",
12294 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
12295
12296 destroyPQExpBuffer(q);
12297 destroyPQExpBuffer(delq);
12298 free(qamname);
12299 }
12300
12301 /*
12302 * dumpOpclass
12303 * write out a single operator class definition
12304 */
12305 static void
dumpOpclass(Archive * fout,OpclassInfo * opcinfo)12306 dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
12307 {
12308 DumpOptions *dopt = fout->dopt;
12309 PQExpBuffer query;
12310 PQExpBuffer q;
12311 PQExpBuffer delq;
12312 PQExpBuffer nameusing;
12313 PGresult *res;
12314 int ntups;
12315 int i_opcintype;
12316 int i_opckeytype;
12317 int i_opcdefault;
12318 int i_opcfamily;
12319 int i_opcfamilyname;
12320 int i_opcfamilynsp;
12321 int i_amname;
12322 int i_amopstrategy;
12323 int i_amopreqcheck;
12324 int i_amopopr;
12325 int i_sortfamily;
12326 int i_sortfamilynsp;
12327 int i_amprocnum;
12328 int i_amproc;
12329 int i_amproclefttype;
12330 int i_amprocrighttype;
12331 char *opcintype;
12332 char *opckeytype;
12333 char *opcdefault;
12334 char *opcfamily;
12335 char *opcfamilyname;
12336 char *opcfamilynsp;
12337 char *amname;
12338 char *amopstrategy;
12339 char *amopreqcheck;
12340 char *amopopr;
12341 char *sortfamily;
12342 char *sortfamilynsp;
12343 char *amprocnum;
12344 char *amproc;
12345 char *amproclefttype;
12346 char *amprocrighttype;
12347 bool needComma;
12348 int i;
12349
12350 /* Skip if not to be dumped */
12351 if (!opcinfo->dobj.dump || dopt->dataOnly)
12352 return;
12353
12354 query = createPQExpBuffer();
12355 q = createPQExpBuffer();
12356 delq = createPQExpBuffer();
12357 nameusing = createPQExpBuffer();
12358
12359 /* Get additional fields from the pg_opclass row */
12360 if (fout->remoteVersion >= 80300)
12361 {
12362 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
12363 "opckeytype::pg_catalog.regtype, "
12364 "opcdefault, opcfamily, "
12365 "opfname AS opcfamilyname, "
12366 "nspname AS opcfamilynsp, "
12367 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
12368 "FROM pg_catalog.pg_opclass c "
12369 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
12370 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
12371 "WHERE c.oid = '%u'::pg_catalog.oid",
12372 opcinfo->dobj.catId.oid);
12373 }
12374 else
12375 {
12376 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
12377 "opckeytype::pg_catalog.regtype, "
12378 "opcdefault, NULL AS opcfamily, "
12379 "NULL AS opcfamilyname, "
12380 "NULL AS opcfamilynsp, "
12381 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
12382 "FROM pg_catalog.pg_opclass "
12383 "WHERE oid = '%u'::pg_catalog.oid",
12384 opcinfo->dobj.catId.oid);
12385 }
12386
12387 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12388
12389 i_opcintype = PQfnumber(res, "opcintype");
12390 i_opckeytype = PQfnumber(res, "opckeytype");
12391 i_opcdefault = PQfnumber(res, "opcdefault");
12392 i_opcfamily = PQfnumber(res, "opcfamily");
12393 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
12394 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
12395 i_amname = PQfnumber(res, "amname");
12396
12397 /* opcintype may still be needed after we PQclear res */
12398 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
12399 opckeytype = PQgetvalue(res, 0, i_opckeytype);
12400 opcdefault = PQgetvalue(res, 0, i_opcdefault);
12401 /* opcfamily will still be needed after we PQclear res */
12402 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
12403 opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
12404 opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
12405 /* amname will still be needed after we PQclear res */
12406 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
12407
12408 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
12409 fmtQualifiedDumpable(opcinfo));
12410 appendPQExpBuffer(delq, " USING %s;\n",
12411 fmtId(amname));
12412
12413 /* Build the fixed portion of the CREATE command */
12414 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
12415 fmtQualifiedDumpable(opcinfo));
12416 if (strcmp(opcdefault, "t") == 0)
12417 appendPQExpBufferStr(q, "DEFAULT ");
12418 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
12419 opcintype,
12420 fmtId(amname));
12421 if (strlen(opcfamilyname) > 0)
12422 {
12423 appendPQExpBufferStr(q, " FAMILY ");
12424 appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
12425 appendPQExpBufferStr(q, fmtId(opcfamilyname));
12426 }
12427 appendPQExpBufferStr(q, " AS\n ");
12428
12429 needComma = false;
12430
12431 if (strcmp(opckeytype, "-") != 0)
12432 {
12433 appendPQExpBuffer(q, "STORAGE %s",
12434 opckeytype);
12435 needComma = true;
12436 }
12437
12438 PQclear(res);
12439
12440 /*
12441 * Now fetch and print the OPERATOR entries (pg_amop rows).
12442 *
12443 * Print only those opfamily members that are tied to the opclass by
12444 * pg_depend entries.
12445 *
12446 * XXX RECHECK is gone as of 8.4, but we'll still print it if dumping an
12447 * older server's opclass in which it is used. This is to avoid
12448 * hard-to-detect breakage if a newer pg_dump is used to dump from an
12449 * older server and then reload into that old version. This can go away
12450 * once 8.3 is so old as to not be of interest to anyone.
12451 */
12452 resetPQExpBuffer(query);
12453
12454 if (fout->remoteVersion >= 90100)
12455 {
12456 appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
12457 "amopopr::pg_catalog.regoperator, "
12458 "opfname AS sortfamily, "
12459 "nspname AS sortfamilynsp "
12460 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
12461 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
12462 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
12463 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
12464 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
12465 "AND refobjid = '%u'::pg_catalog.oid "
12466 "AND amopfamily = '%s'::pg_catalog.oid "
12467 "ORDER BY amopstrategy",
12468 opcinfo->dobj.catId.oid,
12469 opcfamily);
12470 }
12471 else if (fout->remoteVersion >= 80400)
12472 {
12473 appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
12474 "amopopr::pg_catalog.regoperator, "
12475 "NULL AS sortfamily, "
12476 "NULL AS sortfamilynsp "
12477 "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
12478 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
12479 "AND refobjid = '%u'::pg_catalog.oid "
12480 "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass "
12481 "AND objid = ao.oid "
12482 "ORDER BY amopstrategy",
12483 opcinfo->dobj.catId.oid);
12484 }
12485 else if (fout->remoteVersion >= 80300)
12486 {
12487 appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
12488 "amopopr::pg_catalog.regoperator, "
12489 "NULL AS sortfamily, "
12490 "NULL AS sortfamilynsp "
12491 "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
12492 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
12493 "AND refobjid = '%u'::pg_catalog.oid "
12494 "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass "
12495 "AND objid = ao.oid "
12496 "ORDER BY amopstrategy",
12497 opcinfo->dobj.catId.oid);
12498 }
12499 else
12500 {
12501 /*
12502 * Here, we print all entries since there are no opfamilies and hence
12503 * no loose operators to worry about.
12504 */
12505 appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
12506 "amopopr::pg_catalog.regoperator, "
12507 "NULL AS sortfamily, "
12508 "NULL AS sortfamilynsp "
12509 "FROM pg_catalog.pg_amop "
12510 "WHERE amopclaid = '%u'::pg_catalog.oid "
12511 "ORDER BY amopstrategy",
12512 opcinfo->dobj.catId.oid);
12513 }
12514
12515 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12516
12517 ntups = PQntuples(res);
12518
12519 i_amopstrategy = PQfnumber(res, "amopstrategy");
12520 i_amopreqcheck = PQfnumber(res, "amopreqcheck");
12521 i_amopopr = PQfnumber(res, "amopopr");
12522 i_sortfamily = PQfnumber(res, "sortfamily");
12523 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
12524
12525 for (i = 0; i < ntups; i++)
12526 {
12527 amopstrategy = PQgetvalue(res, i, i_amopstrategy);
12528 amopreqcheck = PQgetvalue(res, i, i_amopreqcheck);
12529 amopopr = PQgetvalue(res, i, i_amopopr);
12530 sortfamily = PQgetvalue(res, i, i_sortfamily);
12531 sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
12532
12533 if (needComma)
12534 appendPQExpBufferStr(q, " ,\n ");
12535
12536 appendPQExpBuffer(q, "OPERATOR %s %s",
12537 amopstrategy, amopopr);
12538
12539 if (strlen(sortfamily) > 0)
12540 {
12541 appendPQExpBufferStr(q, " FOR ORDER BY ");
12542 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
12543 appendPQExpBufferStr(q, fmtId(sortfamily));
12544 }
12545
12546 if (strcmp(amopreqcheck, "t") == 0)
12547 appendPQExpBufferStr(q, " RECHECK");
12548
12549 needComma = true;
12550 }
12551
12552 PQclear(res);
12553
12554 /*
12555 * Now fetch and print the FUNCTION entries (pg_amproc rows).
12556 *
12557 * Print only those opfamily members that are tied to the opclass by
12558 * pg_depend entries.
12559 *
12560 * We print the amproclefttype/amprocrighttype even though in most cases
12561 * the backend could deduce the right values, because of the corner case
12562 * of a btree sort support function for a cross-type comparison. That's
12563 * only allowed in 9.2 and later, but for simplicity print them in all
12564 * versions that have the columns.
12565 */
12566 resetPQExpBuffer(query);
12567
12568 if (fout->remoteVersion >= 80300)
12569 {
12570 appendPQExpBuffer(query, "SELECT amprocnum, "
12571 "amproc::pg_catalog.regprocedure, "
12572 "amproclefttype::pg_catalog.regtype, "
12573 "amprocrighttype::pg_catalog.regtype "
12574 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
12575 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
12576 "AND refobjid = '%u'::pg_catalog.oid "
12577 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
12578 "AND objid = ap.oid "
12579 "ORDER BY amprocnum",
12580 opcinfo->dobj.catId.oid);
12581 }
12582 else
12583 {
12584 appendPQExpBuffer(query, "SELECT amprocnum, "
12585 "amproc::pg_catalog.regprocedure, "
12586 "'' AS amproclefttype, "
12587 "'' AS amprocrighttype "
12588 "FROM pg_catalog.pg_amproc "
12589 "WHERE amopclaid = '%u'::pg_catalog.oid "
12590 "ORDER BY amprocnum",
12591 opcinfo->dobj.catId.oid);
12592 }
12593
12594 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12595
12596 ntups = PQntuples(res);
12597
12598 i_amprocnum = PQfnumber(res, "amprocnum");
12599 i_amproc = PQfnumber(res, "amproc");
12600 i_amproclefttype = PQfnumber(res, "amproclefttype");
12601 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
12602
12603 for (i = 0; i < ntups; i++)
12604 {
12605 amprocnum = PQgetvalue(res, i, i_amprocnum);
12606 amproc = PQgetvalue(res, i, i_amproc);
12607 amproclefttype = PQgetvalue(res, i, i_amproclefttype);
12608 amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
12609
12610 if (needComma)
12611 appendPQExpBufferStr(q, " ,\n ");
12612
12613 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
12614
12615 if (*amproclefttype && *amprocrighttype)
12616 appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
12617
12618 appendPQExpBuffer(q, " %s", amproc);
12619
12620 needComma = true;
12621 }
12622
12623 PQclear(res);
12624
12625 /*
12626 * If needComma is still false it means we haven't added anything after
12627 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
12628 * clause with the same datatype. This isn't sanctioned by the
12629 * documentation, but actually DefineOpClass will treat it as a no-op.
12630 */
12631 if (!needComma)
12632 appendPQExpBuffer(q, "STORAGE %s", opcintype);
12633
12634 appendPQExpBufferStr(q, ";\n");
12635
12636 appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
12637 appendPQExpBuffer(nameusing, " USING %s",
12638 fmtId(amname));
12639
12640 if (dopt->binary_upgrade)
12641 binary_upgrade_extension_member(q, &opcinfo->dobj,
12642 "OPERATOR CLASS", nameusing->data,
12643 opcinfo->dobj.namespace->dobj.name);
12644
12645 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12646 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
12647 opcinfo->dobj.name,
12648 opcinfo->dobj.namespace->dobj.name,
12649 NULL,
12650 opcinfo->rolname,
12651 false, "OPERATOR CLASS", SECTION_PRE_DATA,
12652 q->data, delq->data, NULL,
12653 NULL, 0,
12654 NULL, NULL);
12655
12656 /* Dump Operator Class Comments */
12657 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12658 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
12659 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
12660 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
12661
12662 free(opcintype);
12663 free(opcfamily);
12664 free(amname);
12665 destroyPQExpBuffer(query);
12666 destroyPQExpBuffer(q);
12667 destroyPQExpBuffer(delq);
12668 destroyPQExpBuffer(nameusing);
12669 }
12670
12671 /*
12672 * dumpOpfamily
12673 * write out a single operator family definition
12674 *
12675 * Note: this also dumps any "loose" operator members that aren't bound to a
12676 * specific opclass within the opfamily.
12677 */
12678 static void
dumpOpfamily(Archive * fout,OpfamilyInfo * opfinfo)12679 dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
12680 {
12681 DumpOptions *dopt = fout->dopt;
12682 PQExpBuffer query;
12683 PQExpBuffer q;
12684 PQExpBuffer delq;
12685 PQExpBuffer nameusing;
12686 PGresult *res;
12687 PGresult *res_ops;
12688 PGresult *res_procs;
12689 int ntups;
12690 int i_amname;
12691 int i_amopstrategy;
12692 int i_amopreqcheck;
12693 int i_amopopr;
12694 int i_sortfamily;
12695 int i_sortfamilynsp;
12696 int i_amprocnum;
12697 int i_amproc;
12698 int i_amproclefttype;
12699 int i_amprocrighttype;
12700 char *amname;
12701 char *amopstrategy;
12702 char *amopreqcheck;
12703 char *amopopr;
12704 char *sortfamily;
12705 char *sortfamilynsp;
12706 char *amprocnum;
12707 char *amproc;
12708 char *amproclefttype;
12709 char *amprocrighttype;
12710 bool needComma;
12711 int i;
12712
12713 /* Skip if not to be dumped */
12714 if (!opfinfo->dobj.dump || dopt->dataOnly)
12715 return;
12716
12717 query = createPQExpBuffer();
12718 q = createPQExpBuffer();
12719 delq = createPQExpBuffer();
12720 nameusing = createPQExpBuffer();
12721
12722 /*
12723 * Fetch only those opfamily members that are tied directly to the
12724 * opfamily by pg_depend entries.
12725 *
12726 * XXX RECHECK is gone as of 8.4, but we'll still print it if dumping an
12727 * older server's opclass in which it is used. This is to avoid
12728 * hard-to-detect breakage if a newer pg_dump is used to dump from an
12729 * older server and then reload into that old version. This can go away
12730 * once 8.3 is so old as to not be of interest to anyone.
12731 */
12732 if (fout->remoteVersion >= 90100)
12733 {
12734 appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
12735 "amopopr::pg_catalog.regoperator, "
12736 "opfname AS sortfamily, "
12737 "nspname AS sortfamilynsp "
12738 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
12739 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
12740 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
12741 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
12742 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
12743 "AND refobjid = '%u'::pg_catalog.oid "
12744 "AND amopfamily = '%u'::pg_catalog.oid "
12745 "ORDER BY amopstrategy",
12746 opfinfo->dobj.catId.oid,
12747 opfinfo->dobj.catId.oid);
12748 }
12749 else if (fout->remoteVersion >= 80400)
12750 {
12751 appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
12752 "amopopr::pg_catalog.regoperator, "
12753 "NULL AS sortfamily, "
12754 "NULL AS sortfamilynsp "
12755 "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
12756 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
12757 "AND refobjid = '%u'::pg_catalog.oid "
12758 "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass "
12759 "AND objid = ao.oid "
12760 "ORDER BY amopstrategy",
12761 opfinfo->dobj.catId.oid);
12762 }
12763 else
12764 {
12765 appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
12766 "amopopr::pg_catalog.regoperator, "
12767 "NULL AS sortfamily, "
12768 "NULL AS sortfamilynsp "
12769 "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
12770 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
12771 "AND refobjid = '%u'::pg_catalog.oid "
12772 "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass "
12773 "AND objid = ao.oid "
12774 "ORDER BY amopstrategy",
12775 opfinfo->dobj.catId.oid);
12776 }
12777
12778 res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12779
12780 resetPQExpBuffer(query);
12781
12782 appendPQExpBuffer(query, "SELECT amprocnum, "
12783 "amproc::pg_catalog.regprocedure, "
12784 "amproclefttype::pg_catalog.regtype, "
12785 "amprocrighttype::pg_catalog.regtype "
12786 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
12787 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
12788 "AND refobjid = '%u'::pg_catalog.oid "
12789 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
12790 "AND objid = ap.oid "
12791 "ORDER BY amprocnum",
12792 opfinfo->dobj.catId.oid);
12793
12794 res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12795
12796 /* Get additional fields from the pg_opfamily row */
12797 resetPQExpBuffer(query);
12798
12799 appendPQExpBuffer(query, "SELECT "
12800 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
12801 "FROM pg_catalog.pg_opfamily "
12802 "WHERE oid = '%u'::pg_catalog.oid",
12803 opfinfo->dobj.catId.oid);
12804
12805 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12806
12807 i_amname = PQfnumber(res, "amname");
12808
12809 /* amname will still be needed after we PQclear res */
12810 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
12811
12812 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
12813 fmtQualifiedDumpable(opfinfo));
12814 appendPQExpBuffer(delq, " USING %s;\n",
12815 fmtId(amname));
12816
12817 /* Build the fixed portion of the CREATE command */
12818 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
12819 fmtQualifiedDumpable(opfinfo));
12820 appendPQExpBuffer(q, " USING %s;\n",
12821 fmtId(amname));
12822
12823 PQclear(res);
12824
12825 /* Do we need an ALTER to add loose members? */
12826 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
12827 {
12828 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
12829 fmtQualifiedDumpable(opfinfo));
12830 appendPQExpBuffer(q, " USING %s ADD\n ",
12831 fmtId(amname));
12832
12833 needComma = false;
12834
12835 /*
12836 * Now fetch and print the OPERATOR entries (pg_amop rows).
12837 */
12838 ntups = PQntuples(res_ops);
12839
12840 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
12841 i_amopreqcheck = PQfnumber(res_ops, "amopreqcheck");
12842 i_amopopr = PQfnumber(res_ops, "amopopr");
12843 i_sortfamily = PQfnumber(res_ops, "sortfamily");
12844 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
12845
12846 for (i = 0; i < ntups; i++)
12847 {
12848 amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
12849 amopreqcheck = PQgetvalue(res_ops, i, i_amopreqcheck);
12850 amopopr = PQgetvalue(res_ops, i, i_amopopr);
12851 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
12852 sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
12853
12854 if (needComma)
12855 appendPQExpBufferStr(q, " ,\n ");
12856
12857 appendPQExpBuffer(q, "OPERATOR %s %s",
12858 amopstrategy, amopopr);
12859
12860 if (strlen(sortfamily) > 0)
12861 {
12862 appendPQExpBufferStr(q, " FOR ORDER BY ");
12863 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
12864 appendPQExpBufferStr(q, fmtId(sortfamily));
12865 }
12866
12867 if (strcmp(amopreqcheck, "t") == 0)
12868 appendPQExpBufferStr(q, " RECHECK");
12869
12870 needComma = true;
12871 }
12872
12873 /*
12874 * Now fetch and print the FUNCTION entries (pg_amproc rows).
12875 */
12876 ntups = PQntuples(res_procs);
12877
12878 i_amprocnum = PQfnumber(res_procs, "amprocnum");
12879 i_amproc = PQfnumber(res_procs, "amproc");
12880 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
12881 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
12882
12883 for (i = 0; i < ntups; i++)
12884 {
12885 amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
12886 amproc = PQgetvalue(res_procs, i, i_amproc);
12887 amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
12888 amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
12889
12890 if (needComma)
12891 appendPQExpBufferStr(q, " ,\n ");
12892
12893 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
12894 amprocnum, amproclefttype, amprocrighttype,
12895 amproc);
12896
12897 needComma = true;
12898 }
12899
12900 appendPQExpBufferStr(q, ";\n");
12901 }
12902
12903 appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
12904 appendPQExpBuffer(nameusing, " USING %s",
12905 fmtId(amname));
12906
12907 if (dopt->binary_upgrade)
12908 binary_upgrade_extension_member(q, &opfinfo->dobj,
12909 "OPERATOR FAMILY", nameusing->data,
12910 opfinfo->dobj.namespace->dobj.name);
12911
12912 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12913 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
12914 opfinfo->dobj.name,
12915 opfinfo->dobj.namespace->dobj.name,
12916 NULL,
12917 opfinfo->rolname,
12918 false, "OPERATOR FAMILY", SECTION_PRE_DATA,
12919 q->data, delq->data, NULL,
12920 NULL, 0,
12921 NULL, NULL);
12922
12923 /* Dump Operator Family Comments */
12924 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12925 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
12926 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
12927 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
12928
12929 free(amname);
12930 PQclear(res_ops);
12931 PQclear(res_procs);
12932 destroyPQExpBuffer(query);
12933 destroyPQExpBuffer(q);
12934 destroyPQExpBuffer(delq);
12935 destroyPQExpBuffer(nameusing);
12936 }
12937
12938 /*
12939 * dumpCollation
12940 * write out a single collation definition
12941 */
12942 static void
dumpCollation(Archive * fout,CollInfo * collinfo)12943 dumpCollation(Archive *fout, CollInfo *collinfo)
12944 {
12945 DumpOptions *dopt = fout->dopt;
12946 PQExpBuffer query;
12947 PQExpBuffer q;
12948 PQExpBuffer delq;
12949 char *qcollname;
12950 PGresult *res;
12951 int i_collprovider;
12952 int i_collcollate;
12953 int i_collctype;
12954 const char *collprovider;
12955 const char *collcollate;
12956 const char *collctype;
12957
12958 /* Skip if not to be dumped */
12959 if (!collinfo->dobj.dump || dopt->dataOnly)
12960 return;
12961
12962 query = createPQExpBuffer();
12963 q = createPQExpBuffer();
12964 delq = createPQExpBuffer();
12965
12966 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
12967
12968 /* Get collation-specific details */
12969 if (fout->remoteVersion >= 100000)
12970 appendPQExpBuffer(query, "SELECT "
12971 "collprovider, "
12972 "collcollate, "
12973 "collctype, "
12974 "collversion "
12975 "FROM pg_catalog.pg_collation c "
12976 "WHERE c.oid = '%u'::pg_catalog.oid",
12977 collinfo->dobj.catId.oid);
12978 else
12979 appendPQExpBuffer(query, "SELECT "
12980 "'c' AS collprovider, "
12981 "collcollate, "
12982 "collctype, "
12983 "NULL AS collversion "
12984 "FROM pg_catalog.pg_collation c "
12985 "WHERE c.oid = '%u'::pg_catalog.oid",
12986 collinfo->dobj.catId.oid);
12987
12988 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12989
12990 i_collprovider = PQfnumber(res, "collprovider");
12991 i_collcollate = PQfnumber(res, "collcollate");
12992 i_collctype = PQfnumber(res, "collctype");
12993
12994 collprovider = PQgetvalue(res, 0, i_collprovider);
12995 collcollate = PQgetvalue(res, 0, i_collcollate);
12996 collctype = PQgetvalue(res, 0, i_collctype);
12997
12998 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
12999 fmtQualifiedDumpable(collinfo));
13000
13001 appendPQExpBuffer(q, "CREATE COLLATION %s (",
13002 fmtQualifiedDumpable(collinfo));
13003
13004 appendPQExpBufferStr(q, "provider = ");
13005 if (collprovider[0] == 'c')
13006 appendPQExpBufferStr(q, "libc");
13007 else if (collprovider[0] == 'i')
13008 appendPQExpBufferStr(q, "icu");
13009 else if (collprovider[0] == 'd')
13010 /* to allow dumping pg_catalog; not accepted on input */
13011 appendPQExpBufferStr(q, "default");
13012 else
13013 exit_horribly(NULL,
13014 "unrecognized collation provider: %s\n",
13015 collprovider);
13016
13017 if (strcmp(collcollate, collctype) == 0)
13018 {
13019 appendPQExpBufferStr(q, ", locale = ");
13020 appendStringLiteralAH(q, collcollate, fout);
13021 }
13022 else
13023 {
13024 appendPQExpBufferStr(q, ", lc_collate = ");
13025 appendStringLiteralAH(q, collcollate, fout);
13026 appendPQExpBufferStr(q, ", lc_ctype = ");
13027 appendStringLiteralAH(q, collctype, fout);
13028 }
13029
13030 /*
13031 * For binary upgrade, carry over the collation version. For normal
13032 * dump/restore, omit the version, so that it is computed upon restore.
13033 */
13034 if (dopt->binary_upgrade)
13035 {
13036 int i_collversion;
13037
13038 i_collversion = PQfnumber(res, "collversion");
13039 if (!PQgetisnull(res, 0, i_collversion))
13040 {
13041 appendPQExpBufferStr(q, ", version = ");
13042 appendStringLiteralAH(q,
13043 PQgetvalue(res, 0, i_collversion),
13044 fout);
13045 }
13046 }
13047
13048 appendPQExpBufferStr(q, ");\n");
13049
13050 if (dopt->binary_upgrade)
13051 binary_upgrade_extension_member(q, &collinfo->dobj,
13052 "COLLATION", qcollname,
13053 collinfo->dobj.namespace->dobj.name);
13054
13055 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13056 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
13057 collinfo->dobj.name,
13058 collinfo->dobj.namespace->dobj.name,
13059 NULL,
13060 collinfo->rolname,
13061 false, "COLLATION", SECTION_PRE_DATA,
13062 q->data, delq->data, NULL,
13063 NULL, 0,
13064 NULL, NULL);
13065
13066 /* Dump Collation Comments */
13067 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13068 dumpComment(fout, "COLLATION", qcollname,
13069 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
13070 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
13071
13072 PQclear(res);
13073
13074 destroyPQExpBuffer(query);
13075 destroyPQExpBuffer(q);
13076 destroyPQExpBuffer(delq);
13077 free(qcollname);
13078 }
13079
13080 /*
13081 * dumpConversion
13082 * write out a single conversion definition
13083 */
13084 static void
dumpConversion(Archive * fout,ConvInfo * convinfo)13085 dumpConversion(Archive *fout, ConvInfo *convinfo)
13086 {
13087 DumpOptions *dopt = fout->dopt;
13088 PQExpBuffer query;
13089 PQExpBuffer q;
13090 PQExpBuffer delq;
13091 char *qconvname;
13092 PGresult *res;
13093 int i_conforencoding;
13094 int i_contoencoding;
13095 int i_conproc;
13096 int i_condefault;
13097 const char *conforencoding;
13098 const char *contoencoding;
13099 const char *conproc;
13100 bool condefault;
13101
13102 /* Skip if not to be dumped */
13103 if (!convinfo->dobj.dump || dopt->dataOnly)
13104 return;
13105
13106 query = createPQExpBuffer();
13107 q = createPQExpBuffer();
13108 delq = createPQExpBuffer();
13109
13110 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
13111
13112 /* Get conversion-specific details */
13113 appendPQExpBuffer(query, "SELECT "
13114 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
13115 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
13116 "conproc, condefault "
13117 "FROM pg_catalog.pg_conversion c "
13118 "WHERE c.oid = '%u'::pg_catalog.oid",
13119 convinfo->dobj.catId.oid);
13120
13121 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13122
13123 i_conforencoding = PQfnumber(res, "conforencoding");
13124 i_contoencoding = PQfnumber(res, "contoencoding");
13125 i_conproc = PQfnumber(res, "conproc");
13126 i_condefault = PQfnumber(res, "condefault");
13127
13128 conforencoding = PQgetvalue(res, 0, i_conforencoding);
13129 contoencoding = PQgetvalue(res, 0, i_contoencoding);
13130 conproc = PQgetvalue(res, 0, i_conproc);
13131 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
13132
13133 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
13134 fmtQualifiedDumpable(convinfo));
13135
13136 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
13137 (condefault) ? "DEFAULT " : "",
13138 fmtQualifiedDumpable(convinfo));
13139 appendStringLiteralAH(q, conforencoding, fout);
13140 appendPQExpBufferStr(q, " TO ");
13141 appendStringLiteralAH(q, contoencoding, fout);
13142 /* regproc output is already sufficiently quoted */
13143 appendPQExpBuffer(q, " FROM %s;\n", conproc);
13144
13145 if (dopt->binary_upgrade)
13146 binary_upgrade_extension_member(q, &convinfo->dobj,
13147 "CONVERSION", qconvname,
13148 convinfo->dobj.namespace->dobj.name);
13149
13150 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13151 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
13152 convinfo->dobj.name,
13153 convinfo->dobj.namespace->dobj.name,
13154 NULL,
13155 convinfo->rolname,
13156 false, "CONVERSION", SECTION_PRE_DATA,
13157 q->data, delq->data, NULL,
13158 NULL, 0,
13159 NULL, NULL);
13160
13161 /* Dump Conversion Comments */
13162 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13163 dumpComment(fout, "CONVERSION", qconvname,
13164 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
13165 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
13166
13167 PQclear(res);
13168
13169 destroyPQExpBuffer(query);
13170 destroyPQExpBuffer(q);
13171 destroyPQExpBuffer(delq);
13172 free(qconvname);
13173 }
13174
13175 /*
13176 * format_aggregate_signature: generate aggregate name and argument list
13177 *
13178 * The argument type names are qualified if needed. The aggregate name
13179 * is never qualified.
13180 */
13181 static char *
format_aggregate_signature(AggInfo * agginfo,Archive * fout,bool honor_quotes)13182 format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
13183 {
13184 PQExpBufferData buf;
13185 int j;
13186
13187 initPQExpBuffer(&buf);
13188 if (honor_quotes)
13189 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
13190 else
13191 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
13192
13193 if (agginfo->aggfn.nargs == 0)
13194 appendPQExpBuffer(&buf, "(*)");
13195 else
13196 {
13197 appendPQExpBufferChar(&buf, '(');
13198 for (j = 0; j < agginfo->aggfn.nargs; j++)
13199 appendPQExpBuffer(&buf, "%s%s",
13200 (j > 0) ? ", " : "",
13201 getFormattedTypeName(fout,
13202 agginfo->aggfn.argtypes[j],
13203 zeroAsOpaque));
13204 appendPQExpBufferChar(&buf, ')');
13205 }
13206 return buf.data;
13207 }
13208
13209 /*
13210 * dumpAgg
13211 * write out a single aggregate definition
13212 */
13213 static void
dumpAgg(Archive * fout,AggInfo * agginfo)13214 dumpAgg(Archive *fout, AggInfo *agginfo)
13215 {
13216 DumpOptions *dopt = fout->dopt;
13217 PQExpBuffer query;
13218 PQExpBuffer q;
13219 PQExpBuffer delq;
13220 PQExpBuffer details;
13221 char *aggsig; /* identity signature */
13222 char *aggfullsig = NULL; /* full signature */
13223 char *aggsig_tag;
13224 PGresult *res;
13225 int i_aggtransfn;
13226 int i_aggfinalfn;
13227 int i_aggcombinefn;
13228 int i_aggserialfn;
13229 int i_aggdeserialfn;
13230 int i_aggmtransfn;
13231 int i_aggminvtransfn;
13232 int i_aggmfinalfn;
13233 int i_aggfinalextra;
13234 int i_aggmfinalextra;
13235 int i_aggsortop;
13236 int i_hypothetical;
13237 int i_aggtranstype;
13238 int i_aggtransspace;
13239 int i_aggmtranstype;
13240 int i_aggmtransspace;
13241 int i_agginitval;
13242 int i_aggminitval;
13243 int i_convertok;
13244 int i_proparallel;
13245 const char *aggtransfn;
13246 const char *aggfinalfn;
13247 const char *aggcombinefn;
13248 const char *aggserialfn;
13249 const char *aggdeserialfn;
13250 const char *aggmtransfn;
13251 const char *aggminvtransfn;
13252 const char *aggmfinalfn;
13253 bool aggfinalextra;
13254 bool aggmfinalextra;
13255 const char *aggsortop;
13256 char *aggsortconvop;
13257 bool hypothetical;
13258 const char *aggtranstype;
13259 const char *aggtransspace;
13260 const char *aggmtranstype;
13261 const char *aggmtransspace;
13262 const char *agginitval;
13263 const char *aggminitval;
13264 bool convertok;
13265 const char *proparallel;
13266
13267 /* Skip if not to be dumped */
13268 if (!agginfo->aggfn.dobj.dump || dopt->dataOnly)
13269 return;
13270
13271 query = createPQExpBuffer();
13272 q = createPQExpBuffer();
13273 delq = createPQExpBuffer();
13274 details = createPQExpBuffer();
13275
13276 /* Get aggregate-specific details */
13277 if (fout->remoteVersion >= 90600)
13278 {
13279 appendPQExpBuffer(query, "SELECT aggtransfn, "
13280 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13281 "aggcombinefn, aggserialfn, aggdeserialfn, aggmtransfn, "
13282 "aggminvtransfn, aggmfinalfn, aggmtranstype::pg_catalog.regtype, "
13283 "aggfinalextra, aggmfinalextra, "
13284 "aggsortop, "
13285 "(aggkind = 'h') AS hypothetical, "
13286 "aggtransspace, agginitval, "
13287 "aggmtransspace, aggminitval, "
13288 "true AS convertok, "
13289 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
13290 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs, "
13291 "p.proparallel "
13292 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13293 "WHERE a.aggfnoid = p.oid "
13294 "AND p.oid = '%u'::pg_catalog.oid",
13295 agginfo->aggfn.dobj.catId.oid);
13296 }
13297 else if (fout->remoteVersion >= 90400)
13298 {
13299 appendPQExpBuffer(query, "SELECT aggtransfn, "
13300 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13301 "'-' AS aggcombinefn, '-' AS aggserialfn, "
13302 "'-' AS aggdeserialfn, aggmtransfn, aggminvtransfn, "
13303 "aggmfinalfn, aggmtranstype::pg_catalog.regtype, "
13304 "aggfinalextra, aggmfinalextra, "
13305 "aggsortop, "
13306 "(aggkind = 'h') AS hypothetical, "
13307 "aggtransspace, agginitval, "
13308 "aggmtransspace, aggminitval, "
13309 "true AS convertok, "
13310 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
13311 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs "
13312 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13313 "WHERE a.aggfnoid = p.oid "
13314 "AND p.oid = '%u'::pg_catalog.oid",
13315 agginfo->aggfn.dobj.catId.oid);
13316 }
13317 else if (fout->remoteVersion >= 80400)
13318 {
13319 appendPQExpBuffer(query, "SELECT aggtransfn, "
13320 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13321 "'-' AS aggcombinefn, '-' AS aggserialfn, "
13322 "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
13323 "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
13324 "0 AS aggmtranstype, false AS aggfinalextra, "
13325 "false AS aggmfinalextra, "
13326 "aggsortop, "
13327 "false AS hypothetical, "
13328 "0 AS aggtransspace, agginitval, "
13329 "0 AS aggmtransspace, NULL AS aggminitval, "
13330 "true AS convertok, "
13331 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
13332 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs "
13333 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13334 "WHERE a.aggfnoid = p.oid "
13335 "AND p.oid = '%u'::pg_catalog.oid",
13336 agginfo->aggfn.dobj.catId.oid);
13337 }
13338 else if (fout->remoteVersion >= 80100)
13339 {
13340 appendPQExpBuffer(query, "SELECT aggtransfn, "
13341 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13342 "'-' AS aggcombinefn, '-' AS aggserialfn, "
13343 "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
13344 "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
13345 "0 AS aggmtranstype, false AS aggfinalextra, "
13346 "false AS aggmfinalextra, "
13347 "aggsortop, "
13348 "false AS hypothetical, "
13349 "0 AS aggtransspace, agginitval, "
13350 "0 AS aggmtransspace, NULL AS aggminitval, "
13351 "true AS convertok "
13352 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13353 "WHERE a.aggfnoid = p.oid "
13354 "AND p.oid = '%u'::pg_catalog.oid",
13355 agginfo->aggfn.dobj.catId.oid);
13356 }
13357 else
13358 {
13359 appendPQExpBuffer(query, "SELECT aggtransfn, "
13360 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13361 "'-' AS aggcombinefn, '-' AS aggserialfn, "
13362 "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
13363 "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
13364 "0 AS aggmtranstype, false AS aggfinalextra, "
13365 "false AS aggmfinalextra, 0 AS aggsortop, "
13366 "false AS hypothetical, "
13367 "0 AS aggtransspace, agginitval, "
13368 "0 AS aggmtransspace, NULL AS aggminitval, "
13369 "true AS convertok "
13370 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13371 "WHERE a.aggfnoid = p.oid "
13372 "AND p.oid = '%u'::pg_catalog.oid",
13373 agginfo->aggfn.dobj.catId.oid);
13374 }
13375
13376 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13377
13378 i_aggtransfn = PQfnumber(res, "aggtransfn");
13379 i_aggfinalfn = PQfnumber(res, "aggfinalfn");
13380 i_aggcombinefn = PQfnumber(res, "aggcombinefn");
13381 i_aggserialfn = PQfnumber(res, "aggserialfn");
13382 i_aggdeserialfn = PQfnumber(res, "aggdeserialfn");
13383 i_aggmtransfn = PQfnumber(res, "aggmtransfn");
13384 i_aggminvtransfn = PQfnumber(res, "aggminvtransfn");
13385 i_aggmfinalfn = PQfnumber(res, "aggmfinalfn");
13386 i_aggfinalextra = PQfnumber(res, "aggfinalextra");
13387 i_aggmfinalextra = PQfnumber(res, "aggmfinalextra");
13388 i_aggsortop = PQfnumber(res, "aggsortop");
13389 i_hypothetical = PQfnumber(res, "hypothetical");
13390 i_aggtranstype = PQfnumber(res, "aggtranstype");
13391 i_aggtransspace = PQfnumber(res, "aggtransspace");
13392 i_aggmtranstype = PQfnumber(res, "aggmtranstype");
13393 i_aggmtransspace = PQfnumber(res, "aggmtransspace");
13394 i_agginitval = PQfnumber(res, "agginitval");
13395 i_aggminitval = PQfnumber(res, "aggminitval");
13396 i_convertok = PQfnumber(res, "convertok");
13397 i_proparallel = PQfnumber(res, "proparallel");
13398
13399 aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
13400 aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
13401 aggcombinefn = PQgetvalue(res, 0, i_aggcombinefn);
13402 aggserialfn = PQgetvalue(res, 0, i_aggserialfn);
13403 aggdeserialfn = PQgetvalue(res, 0, i_aggdeserialfn);
13404 aggmtransfn = PQgetvalue(res, 0, i_aggmtransfn);
13405 aggminvtransfn = PQgetvalue(res, 0, i_aggminvtransfn);
13406 aggmfinalfn = PQgetvalue(res, 0, i_aggmfinalfn);
13407 aggfinalextra = (PQgetvalue(res, 0, i_aggfinalextra)[0] == 't');
13408 aggmfinalextra = (PQgetvalue(res, 0, i_aggmfinalextra)[0] == 't');
13409 aggsortop = PQgetvalue(res, 0, i_aggsortop);
13410 hypothetical = (PQgetvalue(res, 0, i_hypothetical)[0] == 't');
13411 aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
13412 aggtransspace = PQgetvalue(res, 0, i_aggtransspace);
13413 aggmtranstype = PQgetvalue(res, 0, i_aggmtranstype);
13414 aggmtransspace = PQgetvalue(res, 0, i_aggmtransspace);
13415 agginitval = PQgetvalue(res, 0, i_agginitval);
13416 aggminitval = PQgetvalue(res, 0, i_aggminitval);
13417 convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
13418
13419 if (fout->remoteVersion >= 80400)
13420 {
13421 /* 8.4 or later; we rely on server-side code for most of the work */
13422 char *funcargs;
13423 char *funciargs;
13424
13425 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13426 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13427 aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
13428 aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
13429 }
13430 else
13431 /* pre-8.4, do it ourselves */
13432 aggsig = format_aggregate_signature(agginfo, fout, true);
13433
13434 aggsig_tag = format_aggregate_signature(agginfo, fout, false);
13435
13436 if (i_proparallel != -1)
13437 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13438 else
13439 proparallel = NULL;
13440
13441 if (!convertok)
13442 {
13443 write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
13444 aggsig);
13445
13446 if (aggfullsig)
13447 free(aggfullsig);
13448
13449 free(aggsig);
13450
13451 return;
13452 }
13453
13454 /* regproc and regtype output is already sufficiently quoted */
13455 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
13456 aggtransfn, aggtranstype);
13457
13458 if (strcmp(aggtransspace, "0") != 0)
13459 {
13460 appendPQExpBuffer(details, ",\n SSPACE = %s",
13461 aggtransspace);
13462 }
13463
13464 if (!PQgetisnull(res, 0, i_agginitval))
13465 {
13466 appendPQExpBufferStr(details, ",\n INITCOND = ");
13467 appendStringLiteralAH(details, agginitval, fout);
13468 }
13469
13470 if (strcmp(aggfinalfn, "-") != 0)
13471 {
13472 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
13473 aggfinalfn);
13474 if (aggfinalextra)
13475 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
13476 }
13477
13478 if (strcmp(aggcombinefn, "-") != 0)
13479 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
13480
13481 if (strcmp(aggserialfn, "-") != 0)
13482 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
13483
13484 if (strcmp(aggdeserialfn, "-") != 0)
13485 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
13486
13487 if (strcmp(aggmtransfn, "-") != 0)
13488 {
13489 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
13490 aggmtransfn,
13491 aggminvtransfn,
13492 aggmtranstype);
13493 }
13494
13495 if (strcmp(aggmtransspace, "0") != 0)
13496 {
13497 appendPQExpBuffer(details, ",\n MSSPACE = %s",
13498 aggmtransspace);
13499 }
13500
13501 if (!PQgetisnull(res, 0, i_aggminitval))
13502 {
13503 appendPQExpBufferStr(details, ",\n MINITCOND = ");
13504 appendStringLiteralAH(details, aggminitval, fout);
13505 }
13506
13507 if (strcmp(aggmfinalfn, "-") != 0)
13508 {
13509 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
13510 aggmfinalfn);
13511 if (aggmfinalextra)
13512 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
13513 }
13514
13515 aggsortconvop = getFormattedOperatorName(fout, aggsortop);
13516 if (aggsortconvop)
13517 {
13518 appendPQExpBuffer(details, ",\n SORTOP = %s",
13519 aggsortconvop);
13520 free(aggsortconvop);
13521 }
13522
13523 if (hypothetical)
13524 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
13525
13526 if (proparallel != NULL && proparallel[0] != PROPARALLEL_UNSAFE)
13527 {
13528 if (proparallel[0] == PROPARALLEL_SAFE)
13529 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
13530 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13531 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
13532 else if (proparallel[0] != PROPARALLEL_UNSAFE)
13533 exit_horribly(NULL, "unrecognized proparallel value for function \"%s\"\n",
13534 agginfo->aggfn.dobj.name);
13535 }
13536
13537 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
13538 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
13539 aggsig);
13540
13541 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
13542 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
13543 aggfullsig ? aggfullsig : aggsig, details->data);
13544
13545 if (dopt->binary_upgrade)
13546 binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
13547 "AGGREGATE", aggsig,
13548 agginfo->aggfn.dobj.namespace->dobj.name);
13549
13550 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
13551 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
13552 agginfo->aggfn.dobj.dumpId,
13553 aggsig_tag,
13554 agginfo->aggfn.dobj.namespace->dobj.name,
13555 NULL,
13556 agginfo->aggfn.rolname,
13557 false, "AGGREGATE", SECTION_PRE_DATA,
13558 q->data, delq->data, NULL,
13559 NULL, 0,
13560 NULL, NULL);
13561
13562 /* Dump Aggregate Comments */
13563 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
13564 dumpComment(fout, "AGGREGATE", aggsig,
13565 agginfo->aggfn.dobj.namespace->dobj.name,
13566 agginfo->aggfn.rolname,
13567 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
13568
13569 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
13570 dumpSecLabel(fout, "AGGREGATE", aggsig,
13571 agginfo->aggfn.dobj.namespace->dobj.name,
13572 agginfo->aggfn.rolname,
13573 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
13574
13575 /*
13576 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
13577 * command look like a function's GRANT; in particular this affects the
13578 * syntax for zero-argument aggregates and ordered-set aggregates.
13579 */
13580 free(aggsig);
13581
13582 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
13583
13584 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
13585 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
13586 "FUNCTION", aggsig, NULL,
13587 agginfo->aggfn.dobj.namespace->dobj.name,
13588 agginfo->aggfn.rolname, agginfo->aggfn.proacl,
13589 agginfo->aggfn.rproacl,
13590 agginfo->aggfn.initproacl, agginfo->aggfn.initrproacl);
13591
13592 free(aggsig);
13593 if (aggfullsig)
13594 free(aggfullsig);
13595 free(aggsig_tag);
13596
13597 PQclear(res);
13598
13599 destroyPQExpBuffer(query);
13600 destroyPQExpBuffer(q);
13601 destroyPQExpBuffer(delq);
13602 destroyPQExpBuffer(details);
13603 }
13604
13605 /*
13606 * dumpTSParser
13607 * write out a single text search parser
13608 */
13609 static void
dumpTSParser(Archive * fout,TSParserInfo * prsinfo)13610 dumpTSParser(Archive *fout, TSParserInfo *prsinfo)
13611 {
13612 DumpOptions *dopt = fout->dopt;
13613 PQExpBuffer q;
13614 PQExpBuffer delq;
13615 char *qprsname;
13616
13617 /* Skip if not to be dumped */
13618 if (!prsinfo->dobj.dump || dopt->dataOnly)
13619 return;
13620
13621 q = createPQExpBuffer();
13622 delq = createPQExpBuffer();
13623
13624 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
13625
13626 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
13627 fmtQualifiedDumpable(prsinfo));
13628
13629 appendPQExpBuffer(q, " START = %s,\n",
13630 convertTSFunction(fout, prsinfo->prsstart));
13631 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
13632 convertTSFunction(fout, prsinfo->prstoken));
13633 appendPQExpBuffer(q, " END = %s,\n",
13634 convertTSFunction(fout, prsinfo->prsend));
13635 if (prsinfo->prsheadline != InvalidOid)
13636 appendPQExpBuffer(q, " HEADLINE = %s,\n",
13637 convertTSFunction(fout, prsinfo->prsheadline));
13638 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
13639 convertTSFunction(fout, prsinfo->prslextype));
13640
13641 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
13642 fmtQualifiedDumpable(prsinfo));
13643
13644 if (dopt->binary_upgrade)
13645 binary_upgrade_extension_member(q, &prsinfo->dobj,
13646 "TEXT SEARCH PARSER", qprsname,
13647 prsinfo->dobj.namespace->dobj.name);
13648
13649 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13650 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
13651 prsinfo->dobj.name,
13652 prsinfo->dobj.namespace->dobj.name,
13653 NULL,
13654 "",
13655 false, "TEXT SEARCH PARSER", SECTION_PRE_DATA,
13656 q->data, delq->data, NULL,
13657 NULL, 0,
13658 NULL, NULL);
13659
13660 /* Dump Parser Comments */
13661 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13662 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
13663 prsinfo->dobj.namespace->dobj.name, "",
13664 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
13665
13666 destroyPQExpBuffer(q);
13667 destroyPQExpBuffer(delq);
13668 free(qprsname);
13669 }
13670
13671 /*
13672 * dumpTSDictionary
13673 * write out a single text search dictionary
13674 */
13675 static void
dumpTSDictionary(Archive * fout,TSDictInfo * dictinfo)13676 dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo)
13677 {
13678 DumpOptions *dopt = fout->dopt;
13679 PQExpBuffer q;
13680 PQExpBuffer delq;
13681 PQExpBuffer query;
13682 char *qdictname;
13683 PGresult *res;
13684 char *nspname;
13685 char *tmplname;
13686
13687 /* Skip if not to be dumped */
13688 if (!dictinfo->dobj.dump || dopt->dataOnly)
13689 return;
13690
13691 q = createPQExpBuffer();
13692 delq = createPQExpBuffer();
13693 query = createPQExpBuffer();
13694
13695 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
13696
13697 /* Fetch name and namespace of the dictionary's template */
13698 appendPQExpBuffer(query, "SELECT nspname, tmplname "
13699 "FROM pg_ts_template p, pg_namespace n "
13700 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
13701 dictinfo->dicttemplate);
13702 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13703 nspname = PQgetvalue(res, 0, 0);
13704 tmplname = PQgetvalue(res, 0, 1);
13705
13706 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
13707 fmtQualifiedDumpable(dictinfo));
13708
13709 appendPQExpBufferStr(q, " TEMPLATE = ");
13710 appendPQExpBuffer(q, "%s.", fmtId(nspname));
13711 appendPQExpBufferStr(q, fmtId(tmplname));
13712
13713 PQclear(res);
13714
13715 /* the dictinitoption can be dumped straight into the command */
13716 if (dictinfo->dictinitoption)
13717 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
13718
13719 appendPQExpBufferStr(q, " );\n");
13720
13721 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
13722 fmtQualifiedDumpable(dictinfo));
13723
13724 if (dopt->binary_upgrade)
13725 binary_upgrade_extension_member(q, &dictinfo->dobj,
13726 "TEXT SEARCH DICTIONARY", qdictname,
13727 dictinfo->dobj.namespace->dobj.name);
13728
13729 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13730 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
13731 dictinfo->dobj.name,
13732 dictinfo->dobj.namespace->dobj.name,
13733 NULL,
13734 dictinfo->rolname,
13735 false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA,
13736 q->data, delq->data, NULL,
13737 NULL, 0,
13738 NULL, NULL);
13739
13740 /* Dump Dictionary Comments */
13741 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13742 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
13743 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
13744 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
13745
13746 destroyPQExpBuffer(q);
13747 destroyPQExpBuffer(delq);
13748 destroyPQExpBuffer(query);
13749 free(qdictname);
13750 }
13751
13752 /*
13753 * dumpTSTemplate
13754 * write out a single text search template
13755 */
13756 static void
dumpTSTemplate(Archive * fout,TSTemplateInfo * tmplinfo)13757 dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo)
13758 {
13759 DumpOptions *dopt = fout->dopt;
13760 PQExpBuffer q;
13761 PQExpBuffer delq;
13762 char *qtmplname;
13763
13764 /* Skip if not to be dumped */
13765 if (!tmplinfo->dobj.dump || dopt->dataOnly)
13766 return;
13767
13768 q = createPQExpBuffer();
13769 delq = createPQExpBuffer();
13770
13771 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
13772
13773 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
13774 fmtQualifiedDumpable(tmplinfo));
13775
13776 if (tmplinfo->tmplinit != InvalidOid)
13777 appendPQExpBuffer(q, " INIT = %s,\n",
13778 convertTSFunction(fout, tmplinfo->tmplinit));
13779 appendPQExpBuffer(q, " LEXIZE = %s );\n",
13780 convertTSFunction(fout, tmplinfo->tmpllexize));
13781
13782 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
13783 fmtQualifiedDumpable(tmplinfo));
13784
13785 if (dopt->binary_upgrade)
13786 binary_upgrade_extension_member(q, &tmplinfo->dobj,
13787 "TEXT SEARCH TEMPLATE", qtmplname,
13788 tmplinfo->dobj.namespace->dobj.name);
13789
13790 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13791 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
13792 tmplinfo->dobj.name,
13793 tmplinfo->dobj.namespace->dobj.name,
13794 NULL,
13795 "",
13796 false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA,
13797 q->data, delq->data, NULL,
13798 NULL, 0,
13799 NULL, NULL);
13800
13801 /* Dump Template Comments */
13802 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13803 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
13804 tmplinfo->dobj.namespace->dobj.name, "",
13805 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
13806
13807 destroyPQExpBuffer(q);
13808 destroyPQExpBuffer(delq);
13809 free(qtmplname);
13810 }
13811
13812 /*
13813 * dumpTSConfig
13814 * write out a single text search configuration
13815 */
13816 static void
dumpTSConfig(Archive * fout,TSConfigInfo * cfginfo)13817 dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo)
13818 {
13819 DumpOptions *dopt = fout->dopt;
13820 PQExpBuffer q;
13821 PQExpBuffer delq;
13822 PQExpBuffer query;
13823 char *qcfgname;
13824 PGresult *res;
13825 char *nspname;
13826 char *prsname;
13827 int ntups,
13828 i;
13829 int i_tokenname;
13830 int i_dictname;
13831
13832 /* Skip if not to be dumped */
13833 if (!cfginfo->dobj.dump || dopt->dataOnly)
13834 return;
13835
13836 q = createPQExpBuffer();
13837 delq = createPQExpBuffer();
13838 query = createPQExpBuffer();
13839
13840 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
13841
13842 /* Fetch name and namespace of the config's parser */
13843 appendPQExpBuffer(query, "SELECT nspname, prsname "
13844 "FROM pg_ts_parser p, pg_namespace n "
13845 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
13846 cfginfo->cfgparser);
13847 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13848 nspname = PQgetvalue(res, 0, 0);
13849 prsname = PQgetvalue(res, 0, 1);
13850
13851 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
13852 fmtQualifiedDumpable(cfginfo));
13853
13854 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
13855 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
13856
13857 PQclear(res);
13858
13859 resetPQExpBuffer(query);
13860 appendPQExpBuffer(query,
13861 "SELECT\n"
13862 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
13863 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
13864 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
13865 "FROM pg_catalog.pg_ts_config_map AS m\n"
13866 "WHERE m.mapcfg = '%u'\n"
13867 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
13868 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
13869
13870 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13871 ntups = PQntuples(res);
13872
13873 i_tokenname = PQfnumber(res, "tokenname");
13874 i_dictname = PQfnumber(res, "dictname");
13875
13876 for (i = 0; i < ntups; i++)
13877 {
13878 char *tokenname = PQgetvalue(res, i, i_tokenname);
13879 char *dictname = PQgetvalue(res, i, i_dictname);
13880
13881 if (i == 0 ||
13882 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
13883 {
13884 /* starting a new token type, so start a new command */
13885 if (i > 0)
13886 appendPQExpBufferStr(q, ";\n");
13887 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
13888 fmtQualifiedDumpable(cfginfo));
13889 /* tokenname needs quoting, dictname does NOT */
13890 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
13891 fmtId(tokenname), dictname);
13892 }
13893 else
13894 appendPQExpBuffer(q, ", %s", dictname);
13895 }
13896
13897 if (ntups > 0)
13898 appendPQExpBufferStr(q, ";\n");
13899
13900 PQclear(res);
13901
13902 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
13903 fmtQualifiedDumpable(cfginfo));
13904
13905 if (dopt->binary_upgrade)
13906 binary_upgrade_extension_member(q, &cfginfo->dobj,
13907 "TEXT SEARCH CONFIGURATION", qcfgname,
13908 cfginfo->dobj.namespace->dobj.name);
13909
13910 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13911 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
13912 cfginfo->dobj.name,
13913 cfginfo->dobj.namespace->dobj.name,
13914 NULL,
13915 cfginfo->rolname,
13916 false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA,
13917 q->data, delq->data, NULL,
13918 NULL, 0,
13919 NULL, NULL);
13920
13921 /* Dump Configuration Comments */
13922 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13923 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
13924 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
13925 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
13926
13927 destroyPQExpBuffer(q);
13928 destroyPQExpBuffer(delq);
13929 destroyPQExpBuffer(query);
13930 free(qcfgname);
13931 }
13932
13933 /*
13934 * dumpForeignDataWrapper
13935 * write out a single foreign-data wrapper definition
13936 */
13937 static void
dumpForeignDataWrapper(Archive * fout,FdwInfo * fdwinfo)13938 dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo)
13939 {
13940 DumpOptions *dopt = fout->dopt;
13941 PQExpBuffer q;
13942 PQExpBuffer delq;
13943 char *qfdwname;
13944
13945 /* Skip if not to be dumped */
13946 if (!fdwinfo->dobj.dump || dopt->dataOnly)
13947 return;
13948
13949 q = createPQExpBuffer();
13950 delq = createPQExpBuffer();
13951
13952 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
13953
13954 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
13955 qfdwname);
13956
13957 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
13958 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
13959
13960 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
13961 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
13962
13963 if (strlen(fdwinfo->fdwoptions) > 0)
13964 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
13965
13966 appendPQExpBufferStr(q, ";\n");
13967
13968 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
13969 qfdwname);
13970
13971 if (dopt->binary_upgrade)
13972 binary_upgrade_extension_member(q, &fdwinfo->dobj,
13973 "FOREIGN DATA WRAPPER", qfdwname,
13974 NULL);
13975
13976 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13977 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
13978 fdwinfo->dobj.name,
13979 NULL,
13980 NULL,
13981 fdwinfo->rolname,
13982 false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA,
13983 q->data, delq->data, NULL,
13984 NULL, 0,
13985 NULL, NULL);
13986
13987 /* Handle the ACL */
13988 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
13989 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
13990 "FOREIGN DATA WRAPPER", qfdwname, NULL,
13991 NULL, fdwinfo->rolname,
13992 fdwinfo->fdwacl, fdwinfo->rfdwacl,
13993 fdwinfo->initfdwacl, fdwinfo->initrfdwacl);
13994
13995 /* Dump Foreign Data Wrapper Comments */
13996 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13997 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
13998 NULL, fdwinfo->rolname,
13999 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
14000
14001 free(qfdwname);
14002
14003 destroyPQExpBuffer(q);
14004 destroyPQExpBuffer(delq);
14005 }
14006
14007 /*
14008 * dumpForeignServer
14009 * write out a foreign server definition
14010 */
14011 static void
dumpForeignServer(Archive * fout,ForeignServerInfo * srvinfo)14012 dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo)
14013 {
14014 DumpOptions *dopt = fout->dopt;
14015 PQExpBuffer q;
14016 PQExpBuffer delq;
14017 PQExpBuffer query;
14018 PGresult *res;
14019 char *qsrvname;
14020 char *fdwname;
14021
14022 /* Skip if not to be dumped */
14023 if (!srvinfo->dobj.dump || dopt->dataOnly)
14024 return;
14025
14026 q = createPQExpBuffer();
14027 delq = createPQExpBuffer();
14028 query = createPQExpBuffer();
14029
14030 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
14031
14032 /* look up the foreign-data wrapper */
14033 appendPQExpBuffer(query, "SELECT fdwname "
14034 "FROM pg_foreign_data_wrapper w "
14035 "WHERE w.oid = '%u'",
14036 srvinfo->srvfdw);
14037 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14038 fdwname = PQgetvalue(res, 0, 0);
14039
14040 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
14041 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
14042 {
14043 appendPQExpBufferStr(q, " TYPE ");
14044 appendStringLiteralAH(q, srvinfo->srvtype, fout);
14045 }
14046 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
14047 {
14048 appendPQExpBufferStr(q, " VERSION ");
14049 appendStringLiteralAH(q, srvinfo->srvversion, fout);
14050 }
14051
14052 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
14053 appendPQExpBufferStr(q, fmtId(fdwname));
14054
14055 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
14056 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
14057
14058 appendPQExpBufferStr(q, ";\n");
14059
14060 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
14061 qsrvname);
14062
14063 if (dopt->binary_upgrade)
14064 binary_upgrade_extension_member(q, &srvinfo->dobj,
14065 "SERVER", qsrvname, NULL);
14066
14067 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14068 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
14069 srvinfo->dobj.name,
14070 NULL,
14071 NULL,
14072 srvinfo->rolname,
14073 false, "SERVER", SECTION_PRE_DATA,
14074 q->data, delq->data, NULL,
14075 NULL, 0,
14076 NULL, NULL);
14077
14078 /* Handle the ACL */
14079 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
14080 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
14081 "FOREIGN SERVER", qsrvname, NULL,
14082 NULL, srvinfo->rolname,
14083 srvinfo->srvacl, srvinfo->rsrvacl,
14084 srvinfo->initsrvacl, srvinfo->initrsrvacl);
14085
14086 /* Dump user mappings */
14087 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
14088 dumpUserMappings(fout,
14089 srvinfo->dobj.name, NULL,
14090 srvinfo->rolname,
14091 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
14092
14093 /* Dump Foreign Server Comments */
14094 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14095 dumpComment(fout, "SERVER", qsrvname,
14096 NULL, srvinfo->rolname,
14097 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
14098
14099 free(qsrvname);
14100
14101 destroyPQExpBuffer(q);
14102 destroyPQExpBuffer(delq);
14103 destroyPQExpBuffer(query);
14104 }
14105
14106 /*
14107 * dumpUserMappings
14108 *
14109 * This routine is used to dump any user mappings associated with the
14110 * server handed to this routine. Should be called after ArchiveEntry()
14111 * for the server.
14112 */
14113 static void
dumpUserMappings(Archive * fout,const char * servername,const char * namespace,const char * owner,CatalogId catalogId,DumpId dumpId)14114 dumpUserMappings(Archive *fout,
14115 const char *servername, const char *namespace,
14116 const char *owner,
14117 CatalogId catalogId, DumpId dumpId)
14118 {
14119 PQExpBuffer q;
14120 PQExpBuffer delq;
14121 PQExpBuffer query;
14122 PQExpBuffer tag;
14123 PGresult *res;
14124 int ntups;
14125 int i_usename;
14126 int i_umoptions;
14127 int i;
14128
14129 q = createPQExpBuffer();
14130 tag = createPQExpBuffer();
14131 delq = createPQExpBuffer();
14132 query = createPQExpBuffer();
14133
14134 /*
14135 * We read from the publicly accessible view pg_user_mappings, so as not
14136 * to fail if run by a non-superuser. Note that the view will show
14137 * umoptions as null if the user hasn't got privileges for the associated
14138 * server; this means that pg_dump will dump such a mapping, but with no
14139 * OPTIONS clause. A possible alternative is to skip such mappings
14140 * altogether, but it's not clear that that's an improvement.
14141 */
14142 appendPQExpBuffer(query,
14143 "SELECT usename, "
14144 "array_to_string(ARRAY("
14145 "SELECT quote_ident(option_name) || ' ' || "
14146 "quote_literal(option_value) "
14147 "FROM pg_options_to_table(umoptions) "
14148 "ORDER BY option_name"
14149 "), E',\n ') AS umoptions "
14150 "FROM pg_user_mappings "
14151 "WHERE srvid = '%u' "
14152 "ORDER BY usename",
14153 catalogId.oid);
14154
14155 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14156
14157 ntups = PQntuples(res);
14158 i_usename = PQfnumber(res, "usename");
14159 i_umoptions = PQfnumber(res, "umoptions");
14160
14161 for (i = 0; i < ntups; i++)
14162 {
14163 char *usename;
14164 char *umoptions;
14165
14166 usename = PQgetvalue(res, i, i_usename);
14167 umoptions = PQgetvalue(res, i, i_umoptions);
14168
14169 resetPQExpBuffer(q);
14170 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
14171 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
14172
14173 if (umoptions && strlen(umoptions) > 0)
14174 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
14175
14176 appendPQExpBufferStr(q, ";\n");
14177
14178 resetPQExpBuffer(delq);
14179 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
14180 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
14181
14182 resetPQExpBuffer(tag);
14183 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
14184 usename, servername);
14185
14186 ArchiveEntry(fout, nilCatalogId, createDumpId(),
14187 tag->data,
14188 namespace,
14189 NULL,
14190 owner, false,
14191 "USER MAPPING", SECTION_PRE_DATA,
14192 q->data, delq->data, NULL,
14193 &dumpId, 1,
14194 NULL, NULL);
14195 }
14196
14197 PQclear(res);
14198
14199 destroyPQExpBuffer(query);
14200 destroyPQExpBuffer(delq);
14201 destroyPQExpBuffer(tag);
14202 destroyPQExpBuffer(q);
14203 }
14204
14205 /*
14206 * Write out default privileges information
14207 */
14208 static void
dumpDefaultACL(Archive * fout,DefaultACLInfo * daclinfo)14209 dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
14210 {
14211 DumpOptions *dopt = fout->dopt;
14212 PQExpBuffer q;
14213 PQExpBuffer tag;
14214 const char *type;
14215
14216 /* Skip if not to be dumped */
14217 if (!daclinfo->dobj.dump || dopt->dataOnly || dopt->aclsSkip)
14218 return;
14219
14220 q = createPQExpBuffer();
14221 tag = createPQExpBuffer();
14222
14223 switch (daclinfo->defaclobjtype)
14224 {
14225 case DEFACLOBJ_RELATION:
14226 type = "TABLES";
14227 break;
14228 case DEFACLOBJ_SEQUENCE:
14229 type = "SEQUENCES";
14230 break;
14231 case DEFACLOBJ_FUNCTION:
14232 type = "FUNCTIONS";
14233 break;
14234 case DEFACLOBJ_TYPE:
14235 type = "TYPES";
14236 break;
14237 case DEFACLOBJ_NAMESPACE:
14238 type = "SCHEMAS";
14239 break;
14240 default:
14241 /* shouldn't get here */
14242 exit_horribly(NULL,
14243 "unrecognized object type in default privileges: %d\n",
14244 (int) daclinfo->defaclobjtype);
14245 type = ""; /* keep compiler quiet */
14246 }
14247
14248 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
14249
14250 /* build the actual command(s) for this tuple */
14251 if (!buildDefaultACLCommands(type,
14252 daclinfo->dobj.namespace != NULL ?
14253 daclinfo->dobj.namespace->dobj.name : NULL,
14254 daclinfo->defaclacl,
14255 daclinfo->rdefaclacl,
14256 daclinfo->initdefaclacl,
14257 daclinfo->initrdefaclacl,
14258 daclinfo->defaclrole,
14259 fout->remoteVersion,
14260 q))
14261 exit_horribly(NULL, "could not parse default ACL list (%s)\n",
14262 daclinfo->defaclacl);
14263
14264 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
14265 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
14266 tag->data,
14267 daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL,
14268 NULL,
14269 daclinfo->defaclrole,
14270 false, "DEFAULT ACL", SECTION_POST_DATA,
14271 q->data, "", NULL,
14272 NULL, 0,
14273 NULL, NULL);
14274
14275 destroyPQExpBuffer(tag);
14276 destroyPQExpBuffer(q);
14277 }
14278
14279 /*----------
14280 * Write out grant/revoke information
14281 *
14282 * 'objDumpId' is the dump ID of the underlying object.
14283 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
14284 * or InvalidDumpId if there is no need for a second dependency.
14285 * 'type' must be one of
14286 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
14287 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
14288 * 'name' is the formatted name of the object. Must be quoted etc. already.
14289 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
14290 * (Currently we assume that subname is only provided for table columns.)
14291 * 'nspname' is the namespace the object is in (NULL if none).
14292 * 'owner' is the owner, NULL if there is no owner (for languages).
14293 * 'acls' contains the ACL string of the object from the appropriate system
14294 * catalog field; it will be passed to buildACLCommands for building the
14295 * appropriate GRANT commands.
14296 * 'racls' contains the ACL string of any initial-but-now-revoked ACLs of the
14297 * object; it will be passed to buildACLCommands for building the
14298 * appropriate REVOKE commands.
14299 * 'initacls' In binary-upgrade mode, ACL string of the object's initial
14300 * privileges, to be recorded into pg_init_privs
14301 * 'initracls' In binary-upgrade mode, ACL string of the object's
14302 * revoked-from-default privileges, to be recorded into pg_init_privs
14303 *
14304 * NB: initacls/initracls are needed because extensions can set privileges on
14305 * an object during the extension's script file and we record those into
14306 * pg_init_privs as that object's initial privileges.
14307 *
14308 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
14309 * no ACL entry was created.
14310 *----------
14311 */
14312 static DumpId
dumpACL(Archive * fout,DumpId objDumpId,DumpId altDumpId,const char * type,const char * name,const char * subname,const char * nspname,const char * owner,const char * acls,const char * racls,const char * initacls,const char * initracls)14313 dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
14314 const char *type, const char *name, const char *subname,
14315 const char *nspname, const char *owner,
14316 const char *acls, const char *racls,
14317 const char *initacls, const char *initracls)
14318 {
14319 DumpId aclDumpId = InvalidDumpId;
14320 DumpOptions *dopt = fout->dopt;
14321 PQExpBuffer sql;
14322
14323 /* Do nothing if ACL dump is not enabled */
14324 if (dopt->aclsSkip)
14325 return InvalidDumpId;
14326
14327 /* --data-only skips ACLs *except* BLOB ACLs */
14328 if (dopt->dataOnly && strcmp(type, "LARGE OBJECT") != 0)
14329 return InvalidDumpId;
14330
14331 sql = createPQExpBuffer();
14332
14333 /*
14334 * Check to see if this object has had any initial ACLs included for it.
14335 * If so, we are in binary upgrade mode and these are the ACLs to turn
14336 * into GRANT and REVOKE statements to set and record the initial
14337 * privileges for an extension object. Let the backend know that these
14338 * are to be recorded by calling binary_upgrade_set_record_init_privs()
14339 * before and after.
14340 */
14341 if (strlen(initacls) != 0 || strlen(initracls) != 0)
14342 {
14343 appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
14344 if (!buildACLCommands(name, subname, nspname, type,
14345 initacls, initracls, owner,
14346 "", fout->remoteVersion, sql))
14347 exit_horribly(NULL,
14348 "could not parse initial GRANT ACL list (%s) or initial REVOKE ACL list (%s) for object \"%s\" (%s)\n",
14349 initacls, initracls, name, type);
14350 appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
14351 }
14352
14353 if (!buildACLCommands(name, subname, nspname, type,
14354 acls, racls, owner,
14355 "", fout->remoteVersion, sql))
14356 exit_horribly(NULL,
14357 "could not parse GRANT ACL list (%s) or REVOKE ACL list (%s) for object \"%s\" (%s)\n",
14358 acls, racls, name, type);
14359
14360 if (sql->len > 0)
14361 {
14362 PQExpBuffer tag = createPQExpBuffer();
14363 DumpId aclDeps[2];
14364 int nDeps = 0;
14365
14366 if (subname)
14367 appendPQExpBuffer(tag, "COLUMN %s.%s", name, subname);
14368 else
14369 appendPQExpBuffer(tag, "%s %s", type, name);
14370
14371 aclDeps[nDeps++] = objDumpId;
14372 if (altDumpId != InvalidDumpId)
14373 aclDeps[nDeps++] = altDumpId;
14374
14375 aclDumpId = createDumpId();
14376
14377 ArchiveEntry(fout, nilCatalogId, aclDumpId,
14378 tag->data, nspname,
14379 NULL,
14380 owner ? owner : "",
14381 false, "ACL", SECTION_NONE,
14382 sql->data, "", NULL,
14383 aclDeps, nDeps,
14384 NULL, NULL);
14385
14386 destroyPQExpBuffer(tag);
14387 }
14388
14389 destroyPQExpBuffer(sql);
14390
14391 return aclDumpId;
14392 }
14393
14394 /*
14395 * dumpSecLabel
14396 *
14397 * This routine is used to dump any security labels associated with the
14398 * object handed to this routine. The routine takes the object type
14399 * and object name (ready to print, except for schema decoration), plus
14400 * the namespace and owner of the object (for labeling the ArchiveEntry),
14401 * plus catalog ID and subid which are the lookup key for pg_seclabel,
14402 * plus the dump ID for the object (for setting a dependency).
14403 * If a matching pg_seclabel entry is found, it is dumped.
14404 *
14405 * Note: although this routine takes a dumpId for dependency purposes,
14406 * that purpose is just to mark the dependency in the emitted dump file
14407 * for possible future use by pg_restore. We do NOT use it for determining
14408 * ordering of the label in the dump file, because this routine is called
14409 * after dependency sorting occurs. This routine should be called just after
14410 * calling ArchiveEntry() for the specified object.
14411 */
14412 static void
dumpSecLabel(Archive * fout,const char * type,const char * name,const char * namespace,const char * owner,CatalogId catalogId,int subid,DumpId dumpId)14413 dumpSecLabel(Archive *fout, const char *type, const char *name,
14414 const char *namespace, const char *owner,
14415 CatalogId catalogId, int subid, DumpId dumpId)
14416 {
14417 DumpOptions *dopt = fout->dopt;
14418 SecLabelItem *labels;
14419 int nlabels;
14420 int i;
14421 PQExpBuffer query;
14422
14423 /* do nothing, if --no-security-labels is supplied */
14424 if (dopt->no_security_labels)
14425 return;
14426
14427 /* Security labels are schema not data ... except blob labels are data */
14428 if (strcmp(type, "LARGE OBJECT") != 0)
14429 {
14430 if (dopt->dataOnly)
14431 return;
14432 }
14433 else
14434 {
14435 /* We do dump blob security labels in binary-upgrade mode */
14436 if (dopt->schemaOnly && !dopt->binary_upgrade)
14437 return;
14438 }
14439
14440 /* Search for security labels associated with catalogId, using table */
14441 nlabels = findSecLabels(fout, catalogId.tableoid, catalogId.oid, &labels);
14442
14443 query = createPQExpBuffer();
14444
14445 for (i = 0; i < nlabels; i++)
14446 {
14447 /*
14448 * Ignore label entries for which the subid doesn't match.
14449 */
14450 if (labels[i].objsubid != subid)
14451 continue;
14452
14453 appendPQExpBuffer(query,
14454 "SECURITY LABEL FOR %s ON %s ",
14455 fmtId(labels[i].provider), type);
14456 if (namespace && *namespace)
14457 appendPQExpBuffer(query, "%s.", fmtId(namespace));
14458 appendPQExpBuffer(query, "%s IS ", name);
14459 appendStringLiteralAH(query, labels[i].label, fout);
14460 appendPQExpBufferStr(query, ";\n");
14461 }
14462
14463 if (query->len > 0)
14464 {
14465 PQExpBuffer tag = createPQExpBuffer();
14466
14467 appendPQExpBuffer(tag, "%s %s", type, name);
14468 ArchiveEntry(fout, nilCatalogId, createDumpId(),
14469 tag->data, namespace, NULL, owner,
14470 false, "SECURITY LABEL", SECTION_NONE,
14471 query->data, "", NULL,
14472 &(dumpId), 1,
14473 NULL, NULL);
14474 destroyPQExpBuffer(tag);
14475 }
14476
14477 destroyPQExpBuffer(query);
14478 }
14479
14480 /*
14481 * dumpTableSecLabel
14482 *
14483 * As above, but dump security label for both the specified table (or view)
14484 * and its columns.
14485 */
14486 static void
dumpTableSecLabel(Archive * fout,TableInfo * tbinfo,const char * reltypename)14487 dumpTableSecLabel(Archive *fout, TableInfo *tbinfo, const char *reltypename)
14488 {
14489 DumpOptions *dopt = fout->dopt;
14490 SecLabelItem *labels;
14491 int nlabels;
14492 int i;
14493 PQExpBuffer query;
14494 PQExpBuffer target;
14495
14496 /* do nothing, if --no-security-labels is supplied */
14497 if (dopt->no_security_labels)
14498 return;
14499
14500 /* SecLabel are SCHEMA not data */
14501 if (dopt->dataOnly)
14502 return;
14503
14504 /* Search for comments associated with relation, using table */
14505 nlabels = findSecLabels(fout,
14506 tbinfo->dobj.catId.tableoid,
14507 tbinfo->dobj.catId.oid,
14508 &labels);
14509
14510 /* If security labels exist, build SECURITY LABEL statements */
14511 if (nlabels <= 0)
14512 return;
14513
14514 query = createPQExpBuffer();
14515 target = createPQExpBuffer();
14516
14517 for (i = 0; i < nlabels; i++)
14518 {
14519 const char *colname;
14520 const char *provider = labels[i].provider;
14521 const char *label = labels[i].label;
14522 int objsubid = labels[i].objsubid;
14523
14524 resetPQExpBuffer(target);
14525 if (objsubid == 0)
14526 {
14527 appendPQExpBuffer(target, "%s %s", reltypename,
14528 fmtQualifiedDumpable(tbinfo));
14529 }
14530 else
14531 {
14532 colname = getAttrName(objsubid, tbinfo);
14533 /* first fmtXXX result must be consumed before calling again */
14534 appendPQExpBuffer(target, "COLUMN %s",
14535 fmtQualifiedDumpable(tbinfo));
14536 appendPQExpBuffer(target, ".%s", fmtId(colname));
14537 }
14538 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
14539 fmtId(provider), target->data);
14540 appendStringLiteralAH(query, label, fout);
14541 appendPQExpBufferStr(query, ";\n");
14542 }
14543 if (query->len > 0)
14544 {
14545 resetPQExpBuffer(target);
14546 appendPQExpBuffer(target, "%s %s", reltypename,
14547 fmtId(tbinfo->dobj.name));
14548 ArchiveEntry(fout, nilCatalogId, createDumpId(),
14549 target->data,
14550 tbinfo->dobj.namespace->dobj.name,
14551 NULL, tbinfo->rolname,
14552 false, "SECURITY LABEL", SECTION_NONE,
14553 query->data, "", NULL,
14554 &(tbinfo->dobj.dumpId), 1,
14555 NULL, NULL);
14556 }
14557 destroyPQExpBuffer(query);
14558 destroyPQExpBuffer(target);
14559 }
14560
14561 /*
14562 * findSecLabels
14563 *
14564 * Find the security label(s), if any, associated with the given object.
14565 * All the objsubid values associated with the given classoid/objoid are
14566 * found with one search.
14567 */
14568 static int
findSecLabels(Archive * fout,Oid classoid,Oid objoid,SecLabelItem ** items)14569 findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items)
14570 {
14571 /* static storage for table of security labels */
14572 static SecLabelItem *labels = NULL;
14573 static int nlabels = -1;
14574
14575 SecLabelItem *middle = NULL;
14576 SecLabelItem *low;
14577 SecLabelItem *high;
14578 int nmatch;
14579
14580 /* Get security labels if we didn't already */
14581 if (nlabels < 0)
14582 nlabels = collectSecLabels(fout, &labels);
14583
14584 if (nlabels <= 0) /* no labels, so no match is possible */
14585 {
14586 *items = NULL;
14587 return 0;
14588 }
14589
14590 /*
14591 * Do binary search to find some item matching the object.
14592 */
14593 low = &labels[0];
14594 high = &labels[nlabels - 1];
14595 while (low <= high)
14596 {
14597 middle = low + (high - low) / 2;
14598
14599 if (classoid < middle->classoid)
14600 high = middle - 1;
14601 else if (classoid > middle->classoid)
14602 low = middle + 1;
14603 else if (objoid < middle->objoid)
14604 high = middle - 1;
14605 else if (objoid > middle->objoid)
14606 low = middle + 1;
14607 else
14608 break; /* found a match */
14609 }
14610
14611 if (low > high) /* no matches */
14612 {
14613 *items = NULL;
14614 return 0;
14615 }
14616
14617 /*
14618 * Now determine how many items match the object. The search loop
14619 * invariant still holds: only items between low and high inclusive could
14620 * match.
14621 */
14622 nmatch = 1;
14623 while (middle > low)
14624 {
14625 if (classoid != middle[-1].classoid ||
14626 objoid != middle[-1].objoid)
14627 break;
14628 middle--;
14629 nmatch++;
14630 }
14631
14632 *items = middle;
14633
14634 middle += nmatch;
14635 while (middle <= high)
14636 {
14637 if (classoid != middle->classoid ||
14638 objoid != middle->objoid)
14639 break;
14640 middle++;
14641 nmatch++;
14642 }
14643
14644 return nmatch;
14645 }
14646
14647 /*
14648 * collectSecLabels
14649 *
14650 * Construct a table of all security labels available for database objects.
14651 * It's much faster to pull them all at once.
14652 *
14653 * The table is sorted by classoid/objid/objsubid for speed in lookup.
14654 */
14655 static int
collectSecLabels(Archive * fout,SecLabelItem ** items)14656 collectSecLabels(Archive *fout, SecLabelItem **items)
14657 {
14658 PGresult *res;
14659 PQExpBuffer query;
14660 int i_label;
14661 int i_provider;
14662 int i_classoid;
14663 int i_objoid;
14664 int i_objsubid;
14665 int ntups;
14666 int i;
14667 SecLabelItem *labels;
14668
14669 query = createPQExpBuffer();
14670
14671 appendPQExpBufferStr(query,
14672 "SELECT label, provider, classoid, objoid, objsubid "
14673 "FROM pg_catalog.pg_seclabel "
14674 "ORDER BY classoid, objoid, objsubid");
14675
14676 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14677
14678 /* Construct lookup table containing OIDs in numeric form */
14679 i_label = PQfnumber(res, "label");
14680 i_provider = PQfnumber(res, "provider");
14681 i_classoid = PQfnumber(res, "classoid");
14682 i_objoid = PQfnumber(res, "objoid");
14683 i_objsubid = PQfnumber(res, "objsubid");
14684
14685 ntups = PQntuples(res);
14686
14687 labels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
14688
14689 for (i = 0; i < ntups; i++)
14690 {
14691 labels[i].label = PQgetvalue(res, i, i_label);
14692 labels[i].provider = PQgetvalue(res, i, i_provider);
14693 labels[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
14694 labels[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
14695 labels[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
14696 }
14697
14698 /* Do NOT free the PGresult since we are keeping pointers into it */
14699 destroyPQExpBuffer(query);
14700
14701 *items = labels;
14702 return ntups;
14703 }
14704
14705 /*
14706 * dumpTable
14707 * write out to fout the declarations (not data) of a user-defined table
14708 */
14709 static void
dumpTable(Archive * fout,TableInfo * tbinfo)14710 dumpTable(Archive *fout, TableInfo *tbinfo)
14711 {
14712 DumpOptions *dopt = fout->dopt;
14713 DumpId tableAclDumpId = InvalidDumpId;
14714 char *namecopy;
14715
14716 /*
14717 * noop if we are not dumping anything about this table, or if we are
14718 * doing a data-only dump
14719 */
14720 if (!tbinfo->dobj.dump || dopt->dataOnly)
14721 return;
14722
14723 if (tbinfo->relkind == RELKIND_SEQUENCE)
14724 dumpSequence(fout, tbinfo);
14725 else
14726 dumpTableSchema(fout, tbinfo);
14727
14728 /* Handle the ACL here */
14729 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
14730 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
14731 {
14732 const char *objtype =
14733 (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
14734
14735 tableAclDumpId =
14736 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
14737 objtype, namecopy, NULL,
14738 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
14739 tbinfo->relacl, tbinfo->rrelacl,
14740 tbinfo->initrelacl, tbinfo->initrrelacl);
14741 }
14742
14743 /*
14744 * Handle column ACLs, if any. Note: we pull these with a separate query
14745 * rather than trying to fetch them during getTableAttrs, so that we won't
14746 * miss ACLs on system columns.
14747 */
14748 if (fout->remoteVersion >= 80400 && tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
14749 {
14750 PQExpBuffer query = createPQExpBuffer();
14751 PGresult *res;
14752 int i;
14753
14754 if (fout->remoteVersion >= 90600)
14755 {
14756 PQExpBuffer acl_subquery = createPQExpBuffer();
14757 PQExpBuffer racl_subquery = createPQExpBuffer();
14758 PQExpBuffer initacl_subquery = createPQExpBuffer();
14759 PQExpBuffer initracl_subquery = createPQExpBuffer();
14760
14761 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
14762 initracl_subquery, "at.attacl", "c.relowner", "'c'",
14763 dopt->binary_upgrade);
14764
14765 appendPQExpBuffer(query,
14766 "SELECT at.attname, "
14767 "%s AS attacl, "
14768 "%s AS rattacl, "
14769 "%s AS initattacl, "
14770 "%s AS initrattacl "
14771 "FROM pg_catalog.pg_attribute at "
14772 "JOIN pg_catalog.pg_class c ON (at.attrelid = c.oid) "
14773 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
14774 "(at.attrelid = pip.objoid "
14775 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
14776 "AND at.attnum = pip.objsubid) "
14777 "WHERE at.attrelid = '%u'::pg_catalog.oid AND "
14778 "NOT at.attisdropped "
14779 "AND ("
14780 "%s IS NOT NULL OR "
14781 "%s IS NOT NULL OR "
14782 "%s IS NOT NULL OR "
14783 "%s IS NOT NULL)"
14784 "ORDER BY at.attnum",
14785 acl_subquery->data,
14786 racl_subquery->data,
14787 initacl_subquery->data,
14788 initracl_subquery->data,
14789 tbinfo->dobj.catId.oid,
14790 acl_subquery->data,
14791 racl_subquery->data,
14792 initacl_subquery->data,
14793 initracl_subquery->data);
14794
14795 destroyPQExpBuffer(acl_subquery);
14796 destroyPQExpBuffer(racl_subquery);
14797 destroyPQExpBuffer(initacl_subquery);
14798 destroyPQExpBuffer(initracl_subquery);
14799 }
14800 else
14801 {
14802 appendPQExpBuffer(query,
14803 "SELECT attname, attacl, NULL as rattacl, "
14804 "NULL AS initattacl, NULL AS initrattacl "
14805 "FROM pg_catalog.pg_attribute "
14806 "WHERE attrelid = '%u'::pg_catalog.oid AND NOT attisdropped "
14807 "AND attacl IS NOT NULL "
14808 "ORDER BY attnum",
14809 tbinfo->dobj.catId.oid);
14810 }
14811
14812 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14813
14814 for (i = 0; i < PQntuples(res); i++)
14815 {
14816 char *attname = PQgetvalue(res, i, 0);
14817 char *attacl = PQgetvalue(res, i, 1);
14818 char *rattacl = PQgetvalue(res, i, 2);
14819 char *initattacl = PQgetvalue(res, i, 3);
14820 char *initrattacl = PQgetvalue(res, i, 4);
14821 char *attnamecopy;
14822
14823 attnamecopy = pg_strdup(fmtId(attname));
14824
14825 /*
14826 * Column's GRANT type is always TABLE. Each column ACL depends
14827 * on the table-level ACL, since we can restore column ACLs in
14828 * parallel but the table-level ACL has to be done first.
14829 */
14830 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
14831 "TABLE", namecopy, attnamecopy,
14832 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
14833 attacl, rattacl, initattacl, initrattacl);
14834 free(attnamecopy);
14835 }
14836 PQclear(res);
14837 destroyPQExpBuffer(query);
14838 }
14839
14840 free(namecopy);
14841
14842 return;
14843 }
14844
14845 /*
14846 * Create the AS clause for a view or materialized view. The semicolon is
14847 * stripped because a materialized view must add a WITH NO DATA clause.
14848 *
14849 * This returns a new buffer which must be freed by the caller.
14850 */
14851 static PQExpBuffer
createViewAsClause(Archive * fout,TableInfo * tbinfo)14852 createViewAsClause(Archive *fout, TableInfo *tbinfo)
14853 {
14854 PQExpBuffer query = createPQExpBuffer();
14855 PQExpBuffer result = createPQExpBuffer();
14856 PGresult *res;
14857 int len;
14858
14859 /* Fetch the view definition */
14860 appendPQExpBuffer(query,
14861 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
14862 tbinfo->dobj.catId.oid);
14863
14864 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14865
14866 if (PQntuples(res) != 1)
14867 {
14868 if (PQntuples(res) < 1)
14869 exit_horribly(NULL, "query to obtain definition of view \"%s\" returned no data\n",
14870 tbinfo->dobj.name);
14871 else
14872 exit_horribly(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
14873 tbinfo->dobj.name);
14874 }
14875
14876 len = PQgetlength(res, 0, 0);
14877
14878 if (len == 0)
14879 exit_horribly(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
14880 tbinfo->dobj.name);
14881
14882 /* Strip off the trailing semicolon so that other things may follow. */
14883 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
14884 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
14885
14886 PQclear(res);
14887 destroyPQExpBuffer(query);
14888
14889 return result;
14890 }
14891
14892 /*
14893 * Create a dummy AS clause for a view. This is used when the real view
14894 * definition has to be postponed because of circular dependencies.
14895 * We must duplicate the view's external properties -- column names and types
14896 * (including collation) -- so that it works for subsequent references.
14897 *
14898 * This returns a new buffer which must be freed by the caller.
14899 */
14900 static PQExpBuffer
createDummyViewAsClause(Archive * fout,TableInfo * tbinfo)14901 createDummyViewAsClause(Archive *fout, TableInfo *tbinfo)
14902 {
14903 PQExpBuffer result = createPQExpBuffer();
14904 int j;
14905
14906 appendPQExpBufferStr(result, "SELECT");
14907
14908 for (j = 0; j < tbinfo->numatts; j++)
14909 {
14910 if (j > 0)
14911 appendPQExpBufferChar(result, ',');
14912 appendPQExpBufferStr(result, "\n ");
14913
14914 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
14915
14916 /*
14917 * Must add collation if not default for the type, because CREATE OR
14918 * REPLACE VIEW won't change it
14919 */
14920 if (OidIsValid(tbinfo->attcollation[j]))
14921 {
14922 CollInfo *coll;
14923
14924 coll = findCollationByOid(tbinfo->attcollation[j]);
14925 if (coll)
14926 appendPQExpBuffer(result, " COLLATE %s",
14927 fmtQualifiedDumpable(coll));
14928 }
14929
14930 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
14931 }
14932
14933 return result;
14934 }
14935
14936 /*
14937 * dumpTableSchema
14938 * write the declaration (not data) of one user-defined table or view
14939 */
14940 static void
dumpTableSchema(Archive * fout,TableInfo * tbinfo)14941 dumpTableSchema(Archive *fout, TableInfo *tbinfo)
14942 {
14943 DumpOptions *dopt = fout->dopt;
14944 PQExpBuffer q = createPQExpBuffer();
14945 PQExpBuffer delq = createPQExpBuffer();
14946 char *qrelname;
14947 char *qualrelname;
14948 int numParents;
14949 TableInfo **parents;
14950 int actual_atts; /* number of attrs in this CREATE statement */
14951 const char *reltypename;
14952 char *storage;
14953 char *srvname;
14954 char *ftoptions;
14955 int j,
14956 k;
14957
14958 /* We had better have loaded per-column details about this table */
14959 Assert(tbinfo->interesting);
14960
14961 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
14962 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
14963
14964 if (dopt->binary_upgrade)
14965 binary_upgrade_set_type_oids_by_rel_oid(fout, q,
14966 tbinfo->dobj.catId.oid);
14967
14968 /* Is it a table or a view? */
14969 if (tbinfo->relkind == RELKIND_VIEW)
14970 {
14971 PQExpBuffer result;
14972
14973 /*
14974 * Note: keep this code in sync with the is_view case in dumpRule()
14975 */
14976
14977 reltypename = "VIEW";
14978
14979 appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
14980
14981 if (dopt->binary_upgrade)
14982 binary_upgrade_set_pg_class_oids(fout, q,
14983 tbinfo->dobj.catId.oid, false);
14984
14985 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
14986
14987 if (tbinfo->dummy_view)
14988 result = createDummyViewAsClause(fout, tbinfo);
14989 else
14990 {
14991 if (nonemptyReloptions(tbinfo->reloptions))
14992 {
14993 appendPQExpBufferStr(q, " WITH (");
14994 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
14995 appendPQExpBufferChar(q, ')');
14996 }
14997 result = createViewAsClause(fout, tbinfo);
14998 }
14999 appendPQExpBuffer(q, " AS\n%s", result->data);
15000 destroyPQExpBuffer(result);
15001
15002 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
15003 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
15004 appendPQExpBufferStr(q, ";\n");
15005 }
15006 else
15007 {
15008 switch (tbinfo->relkind)
15009 {
15010 case RELKIND_FOREIGN_TABLE:
15011 {
15012 PQExpBuffer query = createPQExpBuffer();
15013 PGresult *res;
15014 int i_srvname;
15015 int i_ftoptions;
15016
15017 reltypename = "FOREIGN TABLE";
15018
15019 /* retrieve name of foreign server and generic options */
15020 appendPQExpBuffer(query,
15021 "SELECT fs.srvname, "
15022 "pg_catalog.array_to_string(ARRAY("
15023 "SELECT pg_catalog.quote_ident(option_name) || "
15024 "' ' || pg_catalog.quote_literal(option_value) "
15025 "FROM pg_catalog.pg_options_to_table(ftoptions) "
15026 "ORDER BY option_name"
15027 "), E',\n ') AS ftoptions "
15028 "FROM pg_catalog.pg_foreign_table ft "
15029 "JOIN pg_catalog.pg_foreign_server fs "
15030 "ON (fs.oid = ft.ftserver) "
15031 "WHERE ft.ftrelid = '%u'",
15032 tbinfo->dobj.catId.oid);
15033 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15034 i_srvname = PQfnumber(res, "srvname");
15035 i_ftoptions = PQfnumber(res, "ftoptions");
15036 srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
15037 ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
15038 PQclear(res);
15039 destroyPQExpBuffer(query);
15040 break;
15041 }
15042 case RELKIND_MATVIEW:
15043 reltypename = "MATERIALIZED VIEW";
15044 srvname = NULL;
15045 ftoptions = NULL;
15046 break;
15047 default:
15048 reltypename = "TABLE";
15049 srvname = NULL;
15050 ftoptions = NULL;
15051 }
15052
15053 numParents = tbinfo->numParents;
15054 parents = tbinfo->parents;
15055
15056 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
15057
15058 if (dopt->binary_upgrade)
15059 binary_upgrade_set_pg_class_oids(fout, q,
15060 tbinfo->dobj.catId.oid, false);
15061
15062 appendPQExpBuffer(q, "CREATE %s%s %s",
15063 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
15064 "UNLOGGED " : "",
15065 reltypename,
15066 qualrelname);
15067
15068 /*
15069 * Attach to type, if reloftype; except in case of a binary upgrade,
15070 * we dump the table normally and attach it to the type afterward.
15071 */
15072 if (tbinfo->reloftype && !dopt->binary_upgrade)
15073 appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
15074
15075 if (tbinfo->relkind != RELKIND_MATVIEW)
15076 {
15077 /* Dump the attributes */
15078 actual_atts = 0;
15079 for (j = 0; j < tbinfo->numatts; j++)
15080 {
15081 /*
15082 * Normally, dump if it's locally defined in this table, and
15083 * not dropped. But for binary upgrade, we'll dump all the
15084 * columns, and then fix up the dropped and nonlocal cases
15085 * below.
15086 */
15087 if (shouldPrintColumn(dopt, tbinfo, j))
15088 {
15089 bool print_default;
15090 bool print_notnull;
15091
15092 /*
15093 * Default value --- suppress if to be printed separately.
15094 */
15095 print_default = (tbinfo->attrdefs[j] != NULL &&
15096 !tbinfo->attrdefs[j]->separate);
15097
15098 /*
15099 * Not Null constraint --- suppress if inherited, except
15100 * if partition, or in binary-upgrade case where that
15101 * won't work.
15102 */
15103 print_notnull = (tbinfo->notnull[j] &&
15104 (!tbinfo->inhNotNull[j] ||
15105 tbinfo->ispartition || dopt->binary_upgrade));
15106
15107 /*
15108 * Skip column if fully defined by reloftype, except in
15109 * binary upgrade
15110 */
15111 if (tbinfo->reloftype && !print_default && !print_notnull &&
15112 !dopt->binary_upgrade)
15113 continue;
15114
15115 /* Format properly if not first attr */
15116 if (actual_atts == 0)
15117 appendPQExpBufferStr(q, " (");
15118 else
15119 appendPQExpBufferChar(q, ',');
15120 appendPQExpBufferStr(q, "\n ");
15121 actual_atts++;
15122
15123 /* Attribute name */
15124 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
15125
15126 if (tbinfo->attisdropped[j])
15127 {
15128 /*
15129 * ALTER TABLE DROP COLUMN clears
15130 * pg_attribute.atttypid, so we will not have gotten a
15131 * valid type name; insert INTEGER as a stopgap. We'll
15132 * clean things up later.
15133 */
15134 appendPQExpBufferStr(q, " INTEGER /* dummy */");
15135 /* and skip to the next column */
15136 continue;
15137 }
15138
15139 /*
15140 * Attribute type; print it except when creating a typed
15141 * table ('OF type_name'), but in binary-upgrade mode,
15142 * print it in that case too.
15143 */
15144 if (dopt->binary_upgrade || !tbinfo->reloftype)
15145 {
15146 appendPQExpBuffer(q, " %s",
15147 tbinfo->atttypnames[j]);
15148 }
15149
15150 /* Add collation if not default for the type */
15151 if (OidIsValid(tbinfo->attcollation[j]))
15152 {
15153 CollInfo *coll;
15154
15155 coll = findCollationByOid(tbinfo->attcollation[j]);
15156 if (coll)
15157 appendPQExpBuffer(q, " COLLATE %s",
15158 fmtQualifiedDumpable(coll));
15159 }
15160
15161 if (print_default)
15162 appendPQExpBuffer(q, " DEFAULT %s",
15163 tbinfo->attrdefs[j]->adef_expr);
15164
15165 if (print_notnull)
15166 appendPQExpBufferStr(q, " NOT NULL");
15167 }
15168 }
15169
15170 /*
15171 * Add non-inherited CHECK constraints, if any.
15172 *
15173 * For partitions, we need to include check constraints even if
15174 * they're not defined locally, because the ALTER TABLE ATTACH
15175 * PARTITION that we'll emit later expects the constraint to be
15176 * there. (No need to fix conislocal: ATTACH PARTITION does that)
15177 */
15178 for (j = 0; j < tbinfo->ncheck; j++)
15179 {
15180 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
15181
15182 if (constr->separate ||
15183 (!constr->conislocal && !tbinfo->ispartition))
15184 continue;
15185
15186 if (actual_atts == 0)
15187 appendPQExpBufferStr(q, " (\n ");
15188 else
15189 appendPQExpBufferStr(q, ",\n ");
15190
15191 appendPQExpBuffer(q, "CONSTRAINT %s ",
15192 fmtId(constr->dobj.name));
15193 appendPQExpBufferStr(q, constr->condef);
15194
15195 actual_atts++;
15196 }
15197
15198 if (actual_atts)
15199 appendPQExpBufferStr(q, "\n)");
15200 else if (!(tbinfo->reloftype && !dopt->binary_upgrade))
15201 {
15202 /*
15203 * No attributes? we must have a parenthesized attribute list,
15204 * even though empty, when not using the OF TYPE syntax.
15205 */
15206 appendPQExpBufferStr(q, " (\n)");
15207 }
15208
15209 /*
15210 * Emit the INHERITS clause (not for partitions), except in
15211 * binary-upgrade mode.
15212 */
15213 if (numParents > 0 && !tbinfo->ispartition &&
15214 !dopt->binary_upgrade)
15215 {
15216 appendPQExpBufferStr(q, "\nINHERITS (");
15217 for (k = 0; k < numParents; k++)
15218 {
15219 TableInfo *parentRel = parents[k];
15220
15221 if (k > 0)
15222 appendPQExpBufferStr(q, ", ");
15223 appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
15224 }
15225 appendPQExpBufferChar(q, ')');
15226 }
15227
15228 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
15229 appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef);
15230
15231 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
15232 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
15233 }
15234
15235 if (nonemptyReloptions(tbinfo->reloptions) ||
15236 nonemptyReloptions(tbinfo->toast_reloptions))
15237 {
15238 bool addcomma = false;
15239
15240 appendPQExpBufferStr(q, "\nWITH (");
15241 if (nonemptyReloptions(tbinfo->reloptions))
15242 {
15243 addcomma = true;
15244 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
15245 }
15246 if (nonemptyReloptions(tbinfo->toast_reloptions))
15247 {
15248 if (addcomma)
15249 appendPQExpBufferStr(q, ", ");
15250 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
15251 fout);
15252 }
15253 appendPQExpBufferChar(q, ')');
15254 }
15255
15256 /* Dump generic options if any */
15257 if (ftoptions && ftoptions[0])
15258 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
15259
15260 /*
15261 * For materialized views, create the AS clause just like a view. At
15262 * this point, we always mark the view as not populated.
15263 */
15264 if (tbinfo->relkind == RELKIND_MATVIEW)
15265 {
15266 PQExpBuffer result;
15267
15268 result = createViewAsClause(fout, tbinfo);
15269 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
15270 result->data);
15271 destroyPQExpBuffer(result);
15272 }
15273 else
15274 appendPQExpBufferStr(q, ";\n");
15275
15276 /* Materialized views can depend on extensions */
15277 if (tbinfo->relkind == RELKIND_MATVIEW)
15278 append_depends_on_extension(fout, q, &tbinfo->dobj,
15279 "pg_catalog.pg_class",
15280 tbinfo->relkind == RELKIND_MATVIEW ?
15281 "MATERIALIZED VIEW" : "INDEX",
15282 qualrelname);
15283
15284 /*
15285 * To create binary-compatible heap files, we have to ensure the same
15286 * physical column order, including dropped columns, as in the
15287 * original. Therefore, we create dropped columns above and drop them
15288 * here, also updating their attlen/attalign values so that the
15289 * dropped column can be skipped properly. (We do not bother with
15290 * restoring the original attbyval setting.) Also, inheritance
15291 * relationships are set up by doing ALTER TABLE INHERIT rather than
15292 * using an INHERITS clause --- the latter would possibly mess up the
15293 * column order. That also means we have to take care about setting
15294 * attislocal correctly, plus fix up any inherited CHECK constraints.
15295 * Analogously, we set up typed tables using ALTER TABLE / OF here.
15296 *
15297 * We process foreign and partitioned tables here, even though they
15298 * lack heap storage, because they can participate in inheritance
15299 * relationships and we want this stuff to be consistent across the
15300 * inheritance tree. We can exclude indexes, toast tables, sequences
15301 * and matviews, even though they have storage, because we don't
15302 * support altering or dropping columns in them, nor can they be part
15303 * of inheritance trees.
15304 */
15305 if (dopt->binary_upgrade &&
15306 (tbinfo->relkind == RELKIND_RELATION ||
15307 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
15308 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
15309 {
15310 for (j = 0; j < tbinfo->numatts; j++)
15311 {
15312 if (tbinfo->attisdropped[j])
15313 {
15314 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n");
15315 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n"
15316 "SET attlen = %d, "
15317 "attalign = '%c', attbyval = false\n"
15318 "WHERE attname = ",
15319 tbinfo->attlen[j],
15320 tbinfo->attalign[j]);
15321 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
15322 appendPQExpBufferStr(q, "\n AND attrelid = ");
15323 appendStringLiteralAH(q, qualrelname, fout);
15324 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15325
15326 if (tbinfo->relkind == RELKIND_RELATION ||
15327 tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
15328 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15329 qualrelname);
15330 else
15331 appendPQExpBuffer(q, "ALTER FOREIGN TABLE ONLY %s ",
15332 qualrelname);
15333 appendPQExpBuffer(q, "DROP COLUMN %s;\n",
15334 fmtId(tbinfo->attnames[j]));
15335 }
15336 else if (!tbinfo->attislocal[j])
15337 {
15338 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n");
15339 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
15340 "SET attislocal = false\n"
15341 "WHERE attname = ");
15342 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
15343 appendPQExpBufferStr(q, "\n AND attrelid = ");
15344 appendStringLiteralAH(q, qualrelname, fout);
15345 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15346 }
15347 }
15348
15349 /*
15350 * Add inherited CHECK constraints, if any.
15351 *
15352 * For partitions, they were already dumped, and conislocal
15353 * doesn't need fixing.
15354 */
15355 for (k = 0; k < tbinfo->ncheck; k++)
15356 {
15357 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
15358
15359 if (constr->separate || constr->conislocal || tbinfo->ispartition)
15360 continue;
15361
15362 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n");
15363 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15364 qualrelname);
15365 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
15366 fmtId(constr->dobj.name));
15367 appendPQExpBuffer(q, "%s;\n", constr->condef);
15368 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
15369 "SET conislocal = false\n"
15370 "WHERE contype = 'c' AND conname = ");
15371 appendStringLiteralAH(q, constr->dobj.name, fout);
15372 appendPQExpBufferStr(q, "\n AND conrelid = ");
15373 appendStringLiteralAH(q, qualrelname, fout);
15374 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15375 }
15376
15377 if (numParents > 0 && !tbinfo->ispartition)
15378 {
15379 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
15380 for (k = 0; k < numParents; k++)
15381 {
15382 TableInfo *parentRel = parents[k];
15383
15384 appendPQExpBuffer(q, "ALTER TABLE ONLY %s INHERIT %s;\n",
15385 qualrelname,
15386 fmtQualifiedDumpable(parentRel));
15387 }
15388 }
15389
15390 if (tbinfo->reloftype)
15391 {
15392 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
15393 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
15394 qualrelname,
15395 tbinfo->reloftype);
15396 }
15397 }
15398
15399 /*
15400 * For partitioned tables, emit the ATTACH PARTITION clause. Note
15401 * that we always want to create partitions this way instead of using
15402 * CREATE TABLE .. PARTITION OF, mainly to preserve a possible column
15403 * layout discrepancy with the parent, but also to ensure it gets the
15404 * correct tablespace setting if it differs from the parent's.
15405 */
15406 if (tbinfo->ispartition)
15407 {
15408 /* With partitions there can only be one parent */
15409 if (tbinfo->numParents != 1)
15410 exit_horribly(NULL, "invalid number of parents %d for table \"%s\"\n",
15411 tbinfo->numParents, tbinfo->dobj.name);
15412
15413 /* Perform ALTER TABLE on the parent */
15414 appendPQExpBuffer(q,
15415 "ALTER TABLE ONLY %s ATTACH PARTITION %s %s;\n",
15416 fmtQualifiedDumpable(parents[0]),
15417 qualrelname, tbinfo->partbound);
15418 }
15419
15420 /*
15421 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
15422 * relminmxid of all vacuumable relations. (While vacuum.c processes
15423 * TOAST tables semi-independently, here we see them only as children
15424 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
15425 * child toast table is handled below.)
15426 */
15427 if (dopt->binary_upgrade &&
15428 (tbinfo->relkind == RELKIND_RELATION ||
15429 tbinfo->relkind == RELKIND_MATVIEW))
15430 {
15431 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
15432 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
15433 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
15434 "WHERE oid = ",
15435 tbinfo->frozenxid, tbinfo->minmxid);
15436 appendStringLiteralAH(q, qualrelname, fout);
15437 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15438
15439 if (tbinfo->toast_oid)
15440 {
15441 /*
15442 * The toast table will have the same OID at restore, so we
15443 * can safely target it by OID.
15444 */
15445 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
15446 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
15447 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
15448 "WHERE oid = '%u';\n",
15449 tbinfo->toast_frozenxid,
15450 tbinfo->toast_minmxid, tbinfo->toast_oid);
15451 }
15452 }
15453
15454 /*
15455 * In binary_upgrade mode, restore matviews' populated status by
15456 * poking pg_class directly. This is pretty ugly, but we can't use
15457 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
15458 * matview is not populated even though this matview is; in any case,
15459 * we want to transfer the matview's heap storage, not run REFRESH.
15460 */
15461 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
15462 tbinfo->relispopulated)
15463 {
15464 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
15465 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
15466 "SET relispopulated = 't'\n"
15467 "WHERE oid = ");
15468 appendStringLiteralAH(q, qualrelname, fout);
15469 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15470 }
15471
15472 /*
15473 * Dump additional per-column properties that we can't handle in the
15474 * main CREATE TABLE command.
15475 */
15476 for (j = 0; j < tbinfo->numatts; j++)
15477 {
15478 /* None of this applies to dropped columns */
15479 if (tbinfo->attisdropped[j])
15480 continue;
15481
15482 /*
15483 * If we didn't dump the column definition explicitly above, and
15484 * it is NOT NULL and did not inherit that property from a parent,
15485 * we have to mark it separately.
15486 */
15487 if (!shouldPrintColumn(dopt, tbinfo, j) &&
15488 tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
15489 {
15490 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15491 qualrelname);
15492 appendPQExpBuffer(q, "ALTER COLUMN %s SET NOT NULL;\n",
15493 fmtId(tbinfo->attnames[j]));
15494 }
15495
15496 /*
15497 * Dump per-column statistics information. We only issue an ALTER
15498 * TABLE statement if the attstattarget entry for this column is
15499 * non-negative (i.e. it's not the default value)
15500 */
15501 if (tbinfo->attstattarget[j] >= 0)
15502 {
15503 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15504 qualrelname);
15505 appendPQExpBuffer(q, "ALTER COLUMN %s ",
15506 fmtId(tbinfo->attnames[j]));
15507 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
15508 tbinfo->attstattarget[j]);
15509 }
15510
15511 /*
15512 * Dump per-column storage information. The statement is only
15513 * dumped if the storage has been changed from the type's default.
15514 */
15515 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
15516 {
15517 switch (tbinfo->attstorage[j])
15518 {
15519 case 'p':
15520 storage = "PLAIN";
15521 break;
15522 case 'e':
15523 storage = "EXTERNAL";
15524 break;
15525 case 'm':
15526 storage = "MAIN";
15527 break;
15528 case 'x':
15529 storage = "EXTENDED";
15530 break;
15531 default:
15532 storage = NULL;
15533 }
15534
15535 /*
15536 * Only dump the statement if it's a storage type we recognize
15537 */
15538 if (storage != NULL)
15539 {
15540 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15541 qualrelname);
15542 appendPQExpBuffer(q, "ALTER COLUMN %s ",
15543 fmtId(tbinfo->attnames[j]));
15544 appendPQExpBuffer(q, "SET STORAGE %s;\n",
15545 storage);
15546 }
15547 }
15548
15549 /*
15550 * Dump per-column attributes.
15551 */
15552 if (tbinfo->attoptions[j] && tbinfo->attoptions[j][0] != '\0')
15553 {
15554 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15555 qualrelname);
15556 appendPQExpBuffer(q, "ALTER COLUMN %s ",
15557 fmtId(tbinfo->attnames[j]));
15558 appendPQExpBuffer(q, "SET (%s);\n",
15559 tbinfo->attoptions[j]);
15560 }
15561
15562 /*
15563 * Dump per-column fdw options.
15564 */
15565 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
15566 tbinfo->attfdwoptions[j] &&
15567 tbinfo->attfdwoptions[j][0] != '\0')
15568 {
15569 appendPQExpBuffer(q, "ALTER FOREIGN TABLE %s ",
15570 qualrelname);
15571 appendPQExpBuffer(q, "ALTER COLUMN %s ",
15572 fmtId(tbinfo->attnames[j]));
15573 appendPQExpBuffer(q, "OPTIONS (\n %s\n);\n",
15574 tbinfo->attfdwoptions[j]);
15575 }
15576 }
15577 }
15578
15579 /*
15580 * dump properties we only have ALTER TABLE syntax for
15581 */
15582 if ((tbinfo->relkind == RELKIND_RELATION ||
15583 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
15584 tbinfo->relkind == RELKIND_MATVIEW) &&
15585 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
15586 {
15587 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
15588 {
15589 /* nothing to do, will be set when the index is dumped */
15590 }
15591 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
15592 {
15593 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
15594 qualrelname);
15595 }
15596 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
15597 {
15598 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
15599 qualrelname);
15600 }
15601 }
15602
15603 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE && tbinfo->hasoids)
15604 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s SET WITH OIDS;\n",
15605 qualrelname);
15606
15607 if (tbinfo->forcerowsec)
15608 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
15609 qualrelname);
15610
15611 if (dopt->binary_upgrade)
15612 binary_upgrade_extension_member(q, &tbinfo->dobj,
15613 reltypename, qrelname,
15614 tbinfo->dobj.namespace->dobj.name);
15615
15616 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15617 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
15618 tbinfo->dobj.name,
15619 tbinfo->dobj.namespace->dobj.name,
15620 (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
15621 tbinfo->rolname,
15622 (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
15623 reltypename,
15624 tbinfo->postponed_def ?
15625 SECTION_POST_DATA : SECTION_PRE_DATA,
15626 q->data, delq->data, NULL,
15627 NULL, 0,
15628 NULL, NULL);
15629
15630
15631 /* Dump Table Comments */
15632 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15633 dumpTableComment(fout, tbinfo, reltypename);
15634
15635 /* Dump Table Security Labels */
15636 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
15637 dumpTableSecLabel(fout, tbinfo, reltypename);
15638
15639 /* Dump comments on inlined table constraints */
15640 for (j = 0; j < tbinfo->ncheck; j++)
15641 {
15642 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
15643
15644 if (constr->separate || !constr->conislocal)
15645 continue;
15646
15647 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15648 dumpTableConstraintComment(fout, constr);
15649 }
15650
15651 destroyPQExpBuffer(q);
15652 destroyPQExpBuffer(delq);
15653 free(qrelname);
15654 free(qualrelname);
15655 }
15656
15657 /*
15658 * dumpAttrDef --- dump an attribute's default-value declaration
15659 */
15660 static void
dumpAttrDef(Archive * fout,AttrDefInfo * adinfo)15661 dumpAttrDef(Archive *fout, AttrDefInfo *adinfo)
15662 {
15663 DumpOptions *dopt = fout->dopt;
15664 TableInfo *tbinfo = adinfo->adtable;
15665 int adnum = adinfo->adnum;
15666 PQExpBuffer q;
15667 PQExpBuffer delq;
15668 char *qualrelname;
15669 char *tag;
15670
15671 /* Skip if table definition not to be dumped */
15672 if (!tbinfo->dobj.dump || dopt->dataOnly)
15673 return;
15674
15675 /* Skip if not "separate"; it was dumped in the table's definition */
15676 if (!adinfo->separate)
15677 return;
15678
15679 q = createPQExpBuffer();
15680 delq = createPQExpBuffer();
15681
15682 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
15683
15684 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15685 qualrelname);
15686 appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
15687 fmtId(tbinfo->attnames[adnum - 1]),
15688 adinfo->adef_expr);
15689
15690 appendPQExpBuffer(delq, "ALTER TABLE %s ",
15691 qualrelname);
15692 appendPQExpBuffer(delq, "ALTER COLUMN %s DROP DEFAULT;\n",
15693 fmtId(tbinfo->attnames[adnum - 1]));
15694
15695 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
15696
15697 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15698 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
15699 tag,
15700 tbinfo->dobj.namespace->dobj.name,
15701 NULL,
15702 tbinfo->rolname,
15703 false, "DEFAULT", SECTION_PRE_DATA,
15704 q->data, delq->data, NULL,
15705 NULL, 0,
15706 NULL, NULL);
15707
15708 free(tag);
15709 destroyPQExpBuffer(q);
15710 destroyPQExpBuffer(delq);
15711 free(qualrelname);
15712 }
15713
15714 /*
15715 * getAttrName: extract the correct name for an attribute
15716 *
15717 * The array tblInfo->attnames[] only provides names of user attributes;
15718 * if a system attribute number is supplied, we have to fake it.
15719 * We also do a little bit of bounds checking for safety's sake.
15720 */
15721 static const char *
getAttrName(int attrnum,TableInfo * tblInfo)15722 getAttrName(int attrnum, TableInfo *tblInfo)
15723 {
15724 if (attrnum > 0 && attrnum <= tblInfo->numatts)
15725 return tblInfo->attnames[attrnum - 1];
15726 switch (attrnum)
15727 {
15728 case SelfItemPointerAttributeNumber:
15729 return "ctid";
15730 case ObjectIdAttributeNumber:
15731 return "oid";
15732 case MinTransactionIdAttributeNumber:
15733 return "xmin";
15734 case MinCommandIdAttributeNumber:
15735 return "cmin";
15736 case MaxTransactionIdAttributeNumber:
15737 return "xmax";
15738 case MaxCommandIdAttributeNumber:
15739 return "cmax";
15740 case TableOidAttributeNumber:
15741 return "tableoid";
15742 }
15743 exit_horribly(NULL, "invalid column number %d for table \"%s\"\n",
15744 attrnum, tblInfo->dobj.name);
15745 return NULL; /* keep compiler quiet */
15746 }
15747
15748 /*
15749 * dumpIndex
15750 * write out to fout a user-defined index
15751 */
15752 static void
dumpIndex(Archive * fout,IndxInfo * indxinfo)15753 dumpIndex(Archive *fout, IndxInfo *indxinfo)
15754 {
15755 DumpOptions *dopt = fout->dopt;
15756 TableInfo *tbinfo = indxinfo->indextable;
15757 bool is_constraint = (indxinfo->indexconstraint != 0);
15758 PQExpBuffer q;
15759 PQExpBuffer delq;
15760 char *qindxname;
15761 char *qqindxname;
15762
15763 if (dopt->dataOnly)
15764 return;
15765
15766 q = createPQExpBuffer();
15767 delq = createPQExpBuffer();
15768
15769 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
15770 qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
15771
15772 /*
15773 * If there's an associated constraint, don't dump the index per se, but
15774 * do dump any comment for it. (This is safe because dependency ordering
15775 * will have ensured the constraint is emitted first.) Note that the
15776 * emitted comment has to be shown as depending on the constraint, not the
15777 * index, in such cases.
15778 */
15779 if (!is_constraint)
15780 {
15781 if (dopt->binary_upgrade)
15782 binary_upgrade_set_pg_class_oids(fout, q,
15783 indxinfo->dobj.catId.oid, true);
15784
15785 /* Plain secondary index */
15786 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
15787
15788 /*
15789 * Append ALTER TABLE commands as needed to set properties that we
15790 * only have ALTER TABLE syntax for. Keep this in sync with the
15791 * similar code in dumpConstraint!
15792 */
15793
15794 /* If the index is clustered, we need to record that. */
15795 if (indxinfo->indisclustered)
15796 {
15797 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
15798 fmtQualifiedDumpable(tbinfo));
15799 /* index name is not qualified in this syntax */
15800 appendPQExpBuffer(q, " ON %s;\n",
15801 qindxname);
15802 }
15803
15804 /* Indexes can depend on extensions */
15805 append_depends_on_extension(fout, q, &indxinfo->dobj,
15806 "pg_catalog.pg_class",
15807 "INDEX", qqindxname);
15808
15809 /* If the index defines identity, we need to record that. */
15810 if (indxinfo->indisreplident)
15811 {
15812 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
15813 fmtQualifiedDumpable(tbinfo));
15814 /* index name is not qualified in this syntax */
15815 appendPQExpBuffer(q, " INDEX %s;\n",
15816 qindxname);
15817 }
15818
15819 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
15820
15821 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15822 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
15823 indxinfo->dobj.name,
15824 tbinfo->dobj.namespace->dobj.name,
15825 indxinfo->tablespace,
15826 tbinfo->rolname, false,
15827 "INDEX", SECTION_POST_DATA,
15828 q->data, delq->data, NULL,
15829 NULL, 0,
15830 NULL, NULL);
15831 }
15832
15833 /* Dump Index Comments */
15834 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15835 dumpComment(fout, "INDEX", qindxname,
15836 tbinfo->dobj.namespace->dobj.name,
15837 tbinfo->rolname,
15838 indxinfo->dobj.catId, 0,
15839 is_constraint ? indxinfo->indexconstraint :
15840 indxinfo->dobj.dumpId);
15841
15842 destroyPQExpBuffer(q);
15843 destroyPQExpBuffer(delq);
15844 free(qindxname);
15845 free(qqindxname);
15846 }
15847
15848 /*
15849 * dumpStatisticsExt
15850 * write out to fout an extended statistics object
15851 */
15852 static void
dumpStatisticsExt(Archive * fout,StatsExtInfo * statsextinfo)15853 dumpStatisticsExt(Archive *fout, StatsExtInfo *statsextinfo)
15854 {
15855 DumpOptions *dopt = fout->dopt;
15856 PQExpBuffer q;
15857 PQExpBuffer delq;
15858 PQExpBuffer query;
15859 char *qstatsextname;
15860 PGresult *res;
15861 char *stxdef;
15862
15863 /* Skip if not to be dumped */
15864 if (!statsextinfo->dobj.dump || dopt->dataOnly)
15865 return;
15866
15867 q = createPQExpBuffer();
15868 delq = createPQExpBuffer();
15869 query = createPQExpBuffer();
15870
15871 qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
15872
15873 appendPQExpBuffer(query, "SELECT "
15874 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
15875 statsextinfo->dobj.catId.oid);
15876
15877 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15878
15879 stxdef = PQgetvalue(res, 0, 0);
15880
15881 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
15882 appendPQExpBuffer(q, "%s;\n", stxdef);
15883
15884 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
15885 fmtQualifiedDumpable(statsextinfo));
15886
15887 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15888 ArchiveEntry(fout, statsextinfo->dobj.catId,
15889 statsextinfo->dobj.dumpId,
15890 statsextinfo->dobj.name,
15891 statsextinfo->dobj.namespace->dobj.name,
15892 NULL,
15893 statsextinfo->rolname, false,
15894 "STATISTICS", SECTION_POST_DATA,
15895 q->data, delq->data, NULL,
15896 NULL, 0,
15897 NULL, NULL);
15898
15899 /* Dump Statistics Comments */
15900 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15901 dumpComment(fout, "STATISTICS", qstatsextname,
15902 statsextinfo->dobj.namespace->dobj.name,
15903 statsextinfo->rolname,
15904 statsextinfo->dobj.catId, 0,
15905 statsextinfo->dobj.dumpId);
15906
15907 PQclear(res);
15908 destroyPQExpBuffer(q);
15909 destroyPQExpBuffer(delq);
15910 destroyPQExpBuffer(query);
15911 free(qstatsextname);
15912 }
15913
15914 /*
15915 * dumpConstraint
15916 * write out to fout a user-defined constraint
15917 */
15918 static void
dumpConstraint(Archive * fout,ConstraintInfo * coninfo)15919 dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
15920 {
15921 DumpOptions *dopt = fout->dopt;
15922 TableInfo *tbinfo = coninfo->contable;
15923 PQExpBuffer q;
15924 PQExpBuffer delq;
15925 char *tag = NULL;
15926
15927 /* Skip if not to be dumped */
15928 if (!coninfo->dobj.dump || dopt->dataOnly)
15929 return;
15930
15931 q = createPQExpBuffer();
15932 delq = createPQExpBuffer();
15933
15934 if (coninfo->contype == 'p' ||
15935 coninfo->contype == 'u' ||
15936 coninfo->contype == 'x')
15937 {
15938 /* Index-related constraint */
15939 IndxInfo *indxinfo;
15940 int k;
15941
15942 indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
15943
15944 if (indxinfo == NULL)
15945 exit_horribly(NULL, "missing index for constraint \"%s\"\n",
15946 coninfo->dobj.name);
15947
15948 if (dopt->binary_upgrade)
15949 binary_upgrade_set_pg_class_oids(fout, q,
15950 indxinfo->dobj.catId.oid, true);
15951
15952 appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
15953 fmtQualifiedDumpable(tbinfo));
15954 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
15955 fmtId(coninfo->dobj.name));
15956
15957 if (coninfo->condef)
15958 {
15959 /* pg_get_constraintdef should have provided everything */
15960 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
15961 }
15962 else
15963 {
15964 appendPQExpBuffer(q, "%s (",
15965 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
15966 for (k = 0; k < indxinfo->indnkeys; k++)
15967 {
15968 int indkey = (int) indxinfo->indkeys[k];
15969 const char *attname;
15970
15971 if (indkey == InvalidAttrNumber)
15972 break;
15973 attname = getAttrName(indkey, tbinfo);
15974
15975 appendPQExpBuffer(q, "%s%s",
15976 (k == 0) ? "" : ", ",
15977 fmtId(attname));
15978 }
15979
15980 appendPQExpBufferChar(q, ')');
15981
15982 if (nonemptyReloptions(indxinfo->indreloptions))
15983 {
15984 appendPQExpBufferStr(q, " WITH (");
15985 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
15986 appendPQExpBufferChar(q, ')');
15987 }
15988
15989 if (coninfo->condeferrable)
15990 {
15991 appendPQExpBufferStr(q, " DEFERRABLE");
15992 if (coninfo->condeferred)
15993 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
15994 }
15995
15996 appendPQExpBufferStr(q, ";\n");
15997 }
15998
15999 /*
16000 * Append ALTER TABLE commands as needed to set properties that we
16001 * only have ALTER TABLE syntax for. Keep this in sync with the
16002 * similar code in dumpIndex!
16003 */
16004
16005 /* If the index is clustered, we need to record that. */
16006 if (indxinfo->indisclustered)
16007 {
16008 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
16009 fmtQualifiedDumpable(tbinfo));
16010 /* index name is not qualified in this syntax */
16011 appendPQExpBuffer(q, " ON %s;\n",
16012 fmtId(indxinfo->dobj.name));
16013 }
16014
16015 /* If the index defines identity, we need to record that. */
16016 if (indxinfo->indisreplident)
16017 {
16018 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
16019 fmtQualifiedDumpable(tbinfo));
16020 /* index name is not qualified in this syntax */
16021 appendPQExpBuffer(q, " INDEX %s;\n",
16022 fmtId(indxinfo->dobj.name));
16023 }
16024
16025 /* Indexes can depend on extensions */
16026 append_depends_on_extension(fout, q, &indxinfo->dobj,
16027 "pg_catalog.pg_class", "INDEX",
16028 fmtQualifiedDumpable(indxinfo));
16029
16030 appendPQExpBuffer(delq, "ALTER TABLE ONLY %s ",
16031 fmtQualifiedDumpable(tbinfo));
16032 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
16033 fmtId(coninfo->dobj.name));
16034
16035 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
16036
16037 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16038 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
16039 tag,
16040 tbinfo->dobj.namespace->dobj.name,
16041 indxinfo->tablespace,
16042 tbinfo->rolname, false,
16043 "CONSTRAINT", SECTION_POST_DATA,
16044 q->data, delq->data, NULL,
16045 NULL, 0,
16046 NULL, NULL);
16047 }
16048 else if (coninfo->contype == 'f')
16049 {
16050 /*
16051 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
16052 * current table data is not processed
16053 */
16054 appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
16055 fmtQualifiedDumpable(tbinfo));
16056 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
16057 fmtId(coninfo->dobj.name),
16058 coninfo->condef);
16059
16060 appendPQExpBuffer(delq, "ALTER TABLE ONLY %s ",
16061 fmtQualifiedDumpable(tbinfo));
16062 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
16063 fmtId(coninfo->dobj.name));
16064
16065 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
16066
16067 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16068 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
16069 tag,
16070 tbinfo->dobj.namespace->dobj.name,
16071 NULL,
16072 tbinfo->rolname, false,
16073 "FK CONSTRAINT", SECTION_POST_DATA,
16074 q->data, delq->data, NULL,
16075 NULL, 0,
16076 NULL, NULL);
16077 }
16078 else if (coninfo->contype == 'c' && tbinfo)
16079 {
16080 /* CHECK constraint on a table */
16081
16082 /* Ignore if not to be dumped separately, or if it was inherited */
16083 if (coninfo->separate && coninfo->conislocal)
16084 {
16085 /* not ONLY since we want it to propagate to children */
16086 appendPQExpBuffer(q, "ALTER TABLE %s\n",
16087 fmtQualifiedDumpable(tbinfo));
16088 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
16089 fmtId(coninfo->dobj.name),
16090 coninfo->condef);
16091
16092 appendPQExpBuffer(delq, "ALTER TABLE %s ",
16093 fmtQualifiedDumpable(tbinfo));
16094 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
16095 fmtId(coninfo->dobj.name));
16096
16097 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
16098
16099 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16100 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
16101 tag,
16102 tbinfo->dobj.namespace->dobj.name,
16103 NULL,
16104 tbinfo->rolname, false,
16105 "CHECK CONSTRAINT", SECTION_POST_DATA,
16106 q->data, delq->data, NULL,
16107 NULL, 0,
16108 NULL, NULL);
16109 }
16110 }
16111 else if (coninfo->contype == 'c' && tbinfo == NULL)
16112 {
16113 /* CHECK constraint on a domain */
16114 TypeInfo *tyinfo = coninfo->condomain;
16115
16116 /* Ignore if not to be dumped separately */
16117 if (coninfo->separate)
16118 {
16119 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
16120 fmtQualifiedDumpable(tyinfo));
16121 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
16122 fmtId(coninfo->dobj.name),
16123 coninfo->condef);
16124
16125 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
16126 fmtQualifiedDumpable(tyinfo));
16127 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
16128 fmtId(coninfo->dobj.name));
16129
16130 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
16131
16132 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16133 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
16134 tag,
16135 tyinfo->dobj.namespace->dobj.name,
16136 NULL,
16137 tyinfo->rolname, false,
16138 "CHECK CONSTRAINT", SECTION_POST_DATA,
16139 q->data, delq->data, NULL,
16140 NULL, 0,
16141 NULL, NULL);
16142 }
16143 }
16144 else
16145 {
16146 exit_horribly(NULL, "unrecognized constraint type: %c\n",
16147 coninfo->contype);
16148 }
16149
16150 /* Dump Constraint Comments --- only works for table constraints */
16151 if (tbinfo && coninfo->separate &&
16152 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16153 dumpTableConstraintComment(fout, coninfo);
16154
16155 free(tag);
16156 destroyPQExpBuffer(q);
16157 destroyPQExpBuffer(delq);
16158 }
16159
16160 /*
16161 * dumpTableConstraintComment --- dump a constraint's comment if any
16162 *
16163 * This is split out because we need the function in two different places
16164 * depending on whether the constraint is dumped as part of CREATE TABLE
16165 * or as a separate ALTER command.
16166 */
16167 static void
dumpTableConstraintComment(Archive * fout,ConstraintInfo * coninfo)16168 dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo)
16169 {
16170 TableInfo *tbinfo = coninfo->contable;
16171 PQExpBuffer conprefix = createPQExpBuffer();
16172 char *qtabname;
16173
16174 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
16175
16176 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
16177 fmtId(coninfo->dobj.name));
16178
16179 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16180 dumpComment(fout, conprefix->data, qtabname,
16181 tbinfo->dobj.namespace->dobj.name,
16182 tbinfo->rolname,
16183 coninfo->dobj.catId, 0,
16184 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
16185
16186 destroyPQExpBuffer(conprefix);
16187 free(qtabname);
16188 }
16189
16190 /*
16191 * findLastBuiltinOid_V71 -
16192 *
16193 * find the last built in oid
16194 *
16195 * For 7.1 through 8.0, we do this by retrieving datlastsysoid from the
16196 * pg_database entry for the current database.
16197 */
16198 static Oid
findLastBuiltinOid_V71(Archive * fout,const char * dbname)16199 findLastBuiltinOid_V71(Archive *fout, const char *dbname)
16200 {
16201 PGresult *res;
16202 Oid last_oid;
16203 PQExpBuffer query = createPQExpBuffer();
16204
16205 resetPQExpBuffer(query);
16206 appendPQExpBufferStr(query, "SELECT datlastsysoid from pg_database where datname = ");
16207 appendStringLiteralAH(query, dbname, fout);
16208
16209 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16210 last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
16211 PQclear(res);
16212 destroyPQExpBuffer(query);
16213
16214 return last_oid;
16215 }
16216
16217 /*
16218 * dumpSequence
16219 * write the declaration (not data) of one user-defined sequence
16220 */
16221 static void
dumpSequence(Archive * fout,TableInfo * tbinfo)16222 dumpSequence(Archive *fout, TableInfo *tbinfo)
16223 {
16224 DumpOptions *dopt = fout->dopt;
16225 PGresult *res;
16226 char *startv,
16227 *incby,
16228 *maxv,
16229 *minv,
16230 *cache,
16231 *seqtype;
16232 bool cycled;
16233 bool is_ascending;
16234 int64 default_minv,
16235 default_maxv;
16236 char bufm[32],
16237 bufx[32];
16238 PQExpBuffer query = createPQExpBuffer();
16239 PQExpBuffer delqry = createPQExpBuffer();
16240 char *qseqname;
16241
16242 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
16243
16244 if (fout->remoteVersion >= 100000)
16245 {
16246 appendPQExpBuffer(query,
16247 "SELECT format_type(seqtypid, NULL), "
16248 "seqstart, seqincrement, "
16249 "seqmax, seqmin, "
16250 "seqcache, seqcycle "
16251 "FROM pg_catalog.pg_sequence "
16252 "WHERE seqrelid = '%u'::oid",
16253 tbinfo->dobj.catId.oid);
16254 }
16255 else if (fout->remoteVersion >= 80400)
16256 {
16257 /*
16258 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
16259 *
16260 * Note: it might seem that 'bigint' potentially needs to be
16261 * schema-qualified, but actually that's a keyword.
16262 */
16263 appendPQExpBuffer(query,
16264 "SELECT 'bigint' AS sequence_type, "
16265 "start_value, increment_by, max_value, min_value, "
16266 "cache_value, is_cycled FROM %s",
16267 fmtQualifiedDumpable(tbinfo));
16268 }
16269 else
16270 {
16271 appendPQExpBuffer(query,
16272 "SELECT 'bigint' AS sequence_type, "
16273 "0 AS start_value, increment_by, max_value, min_value, "
16274 "cache_value, is_cycled FROM %s",
16275 fmtQualifiedDumpable(tbinfo));
16276 }
16277
16278 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16279
16280 if (PQntuples(res) != 1)
16281 {
16282 write_msg(NULL, ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)\n",
16283 "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
16284 PQntuples(res)),
16285 tbinfo->dobj.name, PQntuples(res));
16286 exit_nicely(1);
16287 }
16288
16289 seqtype = PQgetvalue(res, 0, 0);
16290 startv = PQgetvalue(res, 0, 1);
16291 incby = PQgetvalue(res, 0, 2);
16292 maxv = PQgetvalue(res, 0, 3);
16293 minv = PQgetvalue(res, 0, 4);
16294 cache = PQgetvalue(res, 0, 5);
16295 cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
16296
16297 /* Calculate default limits for a sequence of this type */
16298 is_ascending = (incby[0] != '-');
16299 if (strcmp(seqtype, "smallint") == 0)
16300 {
16301 default_minv = is_ascending ? 1 : PG_INT16_MIN;
16302 default_maxv = is_ascending ? PG_INT16_MAX : -1;
16303 }
16304 else if (strcmp(seqtype, "integer") == 0)
16305 {
16306 default_minv = is_ascending ? 1 : PG_INT32_MIN;
16307 default_maxv = is_ascending ? PG_INT32_MAX : -1;
16308 }
16309 else if (strcmp(seqtype, "bigint") == 0)
16310 {
16311 default_minv = is_ascending ? 1 : PG_INT64_MIN;
16312 default_maxv = is_ascending ? PG_INT64_MAX : -1;
16313 }
16314 else
16315 {
16316 exit_horribly(NULL, "unrecognized sequence type: %s\n", seqtype);
16317 default_minv = default_maxv = 0; /* keep compiler quiet */
16318 }
16319
16320 /*
16321 * 64-bit strtol() isn't very portable, so convert the limits to strings
16322 * and compare that way.
16323 */
16324 snprintf(bufm, sizeof(bufm), INT64_FORMAT, default_minv);
16325 snprintf(bufx, sizeof(bufx), INT64_FORMAT, default_maxv);
16326
16327 /* Don't print minv/maxv if they match the respective default limit */
16328 if (strcmp(minv, bufm) == 0)
16329 minv = NULL;
16330 if (strcmp(maxv, bufx) == 0)
16331 maxv = NULL;
16332
16333 /*
16334 * Identity sequences are not to be dropped separately.
16335 */
16336 if (!tbinfo->is_identity_sequence)
16337 {
16338 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
16339 fmtQualifiedDumpable(tbinfo));
16340 }
16341
16342 resetPQExpBuffer(query);
16343
16344 if (dopt->binary_upgrade)
16345 {
16346 binary_upgrade_set_pg_class_oids(fout, query,
16347 tbinfo->dobj.catId.oid, false);
16348 binary_upgrade_set_type_oids_by_rel_oid(fout, query,
16349 tbinfo->dobj.catId.oid);
16350 }
16351
16352 if (tbinfo->is_identity_sequence)
16353 {
16354 TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab);
16355
16356 appendPQExpBuffer(query,
16357 "ALTER TABLE %s ",
16358 fmtQualifiedDumpable(owning_tab));
16359 appendPQExpBuffer(query,
16360 "ALTER COLUMN %s ADD GENERATED ",
16361 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
16362 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
16363 appendPQExpBuffer(query, "ALWAYS");
16364 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
16365 appendPQExpBuffer(query, "BY DEFAULT");
16366 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
16367 fmtQualifiedDumpable(tbinfo));
16368 }
16369 else
16370 {
16371 appendPQExpBuffer(query,
16372 "CREATE SEQUENCE %s\n",
16373 fmtQualifiedDumpable(tbinfo));
16374
16375 if (strcmp(seqtype, "bigint") != 0)
16376 appendPQExpBuffer(query, " AS %s\n", seqtype);
16377 }
16378
16379 if (fout->remoteVersion >= 80400)
16380 appendPQExpBuffer(query, " START WITH %s\n", startv);
16381
16382 appendPQExpBuffer(query, " INCREMENT BY %s\n", incby);
16383
16384 if (minv)
16385 appendPQExpBuffer(query, " MINVALUE %s\n", minv);
16386 else
16387 appendPQExpBufferStr(query, " NO MINVALUE\n");
16388
16389 if (maxv)
16390 appendPQExpBuffer(query, " MAXVALUE %s\n", maxv);
16391 else
16392 appendPQExpBufferStr(query, " NO MAXVALUE\n");
16393
16394 appendPQExpBuffer(query,
16395 " CACHE %s%s",
16396 cache, (cycled ? "\n CYCLE" : ""));
16397
16398 if (tbinfo->is_identity_sequence)
16399 appendPQExpBufferStr(query, "\n);\n");
16400 else
16401 appendPQExpBufferStr(query, ";\n");
16402
16403 /* binary_upgrade: no need to clear TOAST table oid */
16404
16405 if (dopt->binary_upgrade)
16406 binary_upgrade_extension_member(query, &tbinfo->dobj,
16407 "SEQUENCE", qseqname,
16408 tbinfo->dobj.namespace->dobj.name);
16409
16410 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16411 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
16412 tbinfo->dobj.name,
16413 tbinfo->dobj.namespace->dobj.name,
16414 NULL,
16415 tbinfo->rolname,
16416 false, "SEQUENCE", SECTION_PRE_DATA,
16417 query->data, delqry->data, NULL,
16418 NULL, 0,
16419 NULL, NULL);
16420
16421 /*
16422 * If the sequence is owned by a table column, emit the ALTER for it as a
16423 * separate TOC entry immediately following the sequence's own entry. It's
16424 * OK to do this rather than using full sorting logic, because the
16425 * dependency that tells us it's owned will have forced the table to be
16426 * created first. We can't just include the ALTER in the TOC entry
16427 * because it will fail if we haven't reassigned the sequence owner to
16428 * match the table's owner.
16429 *
16430 * We need not schema-qualify the table reference because both sequence
16431 * and table must be in the same schema.
16432 */
16433 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
16434 {
16435 TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab);
16436
16437 if (owning_tab == NULL)
16438 exit_horribly(NULL, "failed sanity check, parent table with OID %u of sequence with OID %u not found\n",
16439 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
16440
16441 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
16442 {
16443 resetPQExpBuffer(query);
16444 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
16445 fmtQualifiedDumpable(tbinfo));
16446 appendPQExpBuffer(query, " OWNED BY %s",
16447 fmtQualifiedDumpable(owning_tab));
16448 appendPQExpBuffer(query, ".%s;\n",
16449 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
16450
16451 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16452 ArchiveEntry(fout, nilCatalogId, createDumpId(),
16453 tbinfo->dobj.name,
16454 tbinfo->dobj.namespace->dobj.name,
16455 NULL,
16456 tbinfo->rolname,
16457 false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
16458 query->data, "", NULL,
16459 &(tbinfo->dobj.dumpId), 1,
16460 NULL, NULL);
16461 }
16462 }
16463
16464 /* Dump Sequence Comments and Security Labels */
16465 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16466 dumpComment(fout, "SEQUENCE", qseqname,
16467 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
16468 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
16469
16470 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
16471 dumpSecLabel(fout, "SEQUENCE", qseqname,
16472 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
16473 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
16474
16475 PQclear(res);
16476
16477 destroyPQExpBuffer(query);
16478 destroyPQExpBuffer(delqry);
16479 free(qseqname);
16480 }
16481
16482 /*
16483 * dumpSequenceData
16484 * write the data of one user-defined sequence
16485 */
16486 static void
dumpSequenceData(Archive * fout,TableDataInfo * tdinfo)16487 dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
16488 {
16489 TableInfo *tbinfo = tdinfo->tdtable;
16490 PGresult *res;
16491 char *last;
16492 bool called;
16493 PQExpBuffer query = createPQExpBuffer();
16494
16495 appendPQExpBuffer(query,
16496 "SELECT last_value, is_called FROM %s",
16497 fmtQualifiedDumpable(tbinfo));
16498
16499 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16500
16501 if (PQntuples(res) != 1)
16502 {
16503 write_msg(NULL, ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)\n",
16504 "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
16505 PQntuples(res)),
16506 tbinfo->dobj.name, PQntuples(res));
16507 exit_nicely(1);
16508 }
16509
16510 last = PQgetvalue(res, 0, 0);
16511 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
16512
16513 resetPQExpBuffer(query);
16514 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
16515 appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
16516 appendPQExpBuffer(query, ", %s, %s);\n",
16517 last, (called ? "true" : "false"));
16518
16519 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
16520 ArchiveEntry(fout, nilCatalogId, createDumpId(),
16521 tbinfo->dobj.name,
16522 tbinfo->dobj.namespace->dobj.name,
16523 NULL,
16524 tbinfo->rolname,
16525 false, "SEQUENCE SET", SECTION_DATA,
16526 query->data, "", NULL,
16527 &(tbinfo->dobj.dumpId), 1,
16528 NULL, NULL);
16529
16530 PQclear(res);
16531
16532 destroyPQExpBuffer(query);
16533 }
16534
16535 /*
16536 * dumpTrigger
16537 * write the declaration of one user-defined table trigger
16538 */
16539 static void
dumpTrigger(Archive * fout,TriggerInfo * tginfo)16540 dumpTrigger(Archive *fout, TriggerInfo *tginfo)
16541 {
16542 DumpOptions *dopt = fout->dopt;
16543 TableInfo *tbinfo = tginfo->tgtable;
16544 PQExpBuffer query;
16545 PQExpBuffer delqry;
16546 PQExpBuffer trigprefix;
16547 PQExpBuffer trigidentity;
16548 char *qtabname;
16549 char *tgargs;
16550 size_t lentgargs;
16551 const char *p;
16552 int findx;
16553 char *tag;
16554
16555 /*
16556 * we needn't check dobj.dump because TriggerInfo wouldn't have been
16557 * created in the first place for non-dumpable triggers
16558 */
16559 if (dopt->dataOnly)
16560 return;
16561
16562 query = createPQExpBuffer();
16563 delqry = createPQExpBuffer();
16564 trigprefix = createPQExpBuffer();
16565 trigidentity = createPQExpBuffer();
16566
16567 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
16568
16569 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
16570 appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
16571
16572 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
16573
16574 if (tginfo->tgdef)
16575 {
16576 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
16577 }
16578 else
16579 {
16580 if (tginfo->tgisconstraint)
16581 {
16582 appendPQExpBufferStr(query, "CREATE CONSTRAINT TRIGGER ");
16583 appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname));
16584 }
16585 else
16586 {
16587 appendPQExpBufferStr(query, "CREATE TRIGGER ");
16588 appendPQExpBufferStr(query, fmtId(tginfo->dobj.name));
16589 }
16590 appendPQExpBufferStr(query, "\n ");
16591
16592 /* Trigger type */
16593 if (TRIGGER_FOR_BEFORE(tginfo->tgtype))
16594 appendPQExpBufferStr(query, "BEFORE");
16595 else if (TRIGGER_FOR_AFTER(tginfo->tgtype))
16596 appendPQExpBufferStr(query, "AFTER");
16597 else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype))
16598 appendPQExpBufferStr(query, "INSTEAD OF");
16599 else
16600 {
16601 write_msg(NULL, "unexpected tgtype value: %d\n", tginfo->tgtype);
16602 exit_nicely(1);
16603 }
16604
16605 findx = 0;
16606 if (TRIGGER_FOR_INSERT(tginfo->tgtype))
16607 {
16608 appendPQExpBufferStr(query, " INSERT");
16609 findx++;
16610 }
16611 if (TRIGGER_FOR_DELETE(tginfo->tgtype))
16612 {
16613 if (findx > 0)
16614 appendPQExpBufferStr(query, " OR DELETE");
16615 else
16616 appendPQExpBufferStr(query, " DELETE");
16617 findx++;
16618 }
16619 if (TRIGGER_FOR_UPDATE(tginfo->tgtype))
16620 {
16621 if (findx > 0)
16622 appendPQExpBufferStr(query, " OR UPDATE");
16623 else
16624 appendPQExpBufferStr(query, " UPDATE");
16625 findx++;
16626 }
16627 if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype))
16628 {
16629 if (findx > 0)
16630 appendPQExpBufferStr(query, " OR TRUNCATE");
16631 else
16632 appendPQExpBufferStr(query, " TRUNCATE");
16633 findx++;
16634 }
16635 appendPQExpBuffer(query, " ON %s\n",
16636 fmtQualifiedDumpable(tbinfo));
16637
16638 if (tginfo->tgisconstraint)
16639 {
16640 if (OidIsValid(tginfo->tgconstrrelid))
16641 {
16642 /* regclass output is already quoted */
16643 appendPQExpBuffer(query, " FROM %s\n ",
16644 tginfo->tgconstrrelname);
16645 }
16646 if (!tginfo->tgdeferrable)
16647 appendPQExpBufferStr(query, "NOT ");
16648 appendPQExpBufferStr(query, "DEFERRABLE INITIALLY ");
16649 if (tginfo->tginitdeferred)
16650 appendPQExpBufferStr(query, "DEFERRED\n");
16651 else
16652 appendPQExpBufferStr(query, "IMMEDIATE\n");
16653 }
16654
16655 if (TRIGGER_FOR_ROW(tginfo->tgtype))
16656 appendPQExpBufferStr(query, " FOR EACH ROW\n ");
16657 else
16658 appendPQExpBufferStr(query, " FOR EACH STATEMENT\n ");
16659
16660 /* regproc output is already sufficiently quoted */
16661 appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
16662 tginfo->tgfname);
16663
16664 tgargs = (char *) PQunescapeBytea((unsigned char *) tginfo->tgargs,
16665 &lentgargs);
16666 p = tgargs;
16667 for (findx = 0; findx < tginfo->tgnargs; findx++)
16668 {
16669 /* find the embedded null that terminates this trigger argument */
16670 size_t tlen = strlen(p);
16671
16672 if (p + tlen >= tgargs + lentgargs)
16673 {
16674 /* hm, not found before end of bytea value... */
16675 write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
16676 tginfo->tgargs,
16677 tginfo->dobj.name,
16678 tbinfo->dobj.name);
16679 exit_nicely(1);
16680 }
16681
16682 if (findx > 0)
16683 appendPQExpBufferStr(query, ", ");
16684 appendStringLiteralAH(query, p, fout);
16685 p += tlen + 1;
16686 }
16687 free(tgargs);
16688 appendPQExpBufferStr(query, ");\n");
16689 }
16690
16691 /* Triggers can depend on extensions */
16692 append_depends_on_extension(fout, query, &tginfo->dobj,
16693 "pg_catalog.pg_trigger", "TRIGGER",
16694 trigidentity->data);
16695
16696 if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
16697 {
16698 appendPQExpBuffer(query, "\nALTER TABLE %s ",
16699 fmtQualifiedDumpable(tbinfo));
16700 switch (tginfo->tgenabled)
16701 {
16702 case 'D':
16703 case 'f':
16704 appendPQExpBufferStr(query, "DISABLE");
16705 break;
16706 case 'A':
16707 appendPQExpBufferStr(query, "ENABLE ALWAYS");
16708 break;
16709 case 'R':
16710 appendPQExpBufferStr(query, "ENABLE REPLICA");
16711 break;
16712 default:
16713 appendPQExpBufferStr(query, "ENABLE");
16714 break;
16715 }
16716 appendPQExpBuffer(query, " TRIGGER %s;\n",
16717 fmtId(tginfo->dobj.name));
16718 }
16719
16720 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
16721 fmtId(tginfo->dobj.name));
16722
16723 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
16724
16725 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16726 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
16727 tag,
16728 tbinfo->dobj.namespace->dobj.name,
16729 NULL,
16730 tbinfo->rolname, false,
16731 "TRIGGER", SECTION_POST_DATA,
16732 query->data, delqry->data, NULL,
16733 NULL, 0,
16734 NULL, NULL);
16735
16736 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16737 dumpComment(fout, trigprefix->data, qtabname,
16738 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
16739 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
16740
16741 free(tag);
16742 destroyPQExpBuffer(query);
16743 destroyPQExpBuffer(delqry);
16744 destroyPQExpBuffer(trigprefix);
16745 destroyPQExpBuffer(trigidentity);
16746 free(qtabname);
16747 }
16748
16749 /*
16750 * dumpEventTrigger
16751 * write the declaration of one user-defined event trigger
16752 */
16753 static void
dumpEventTrigger(Archive * fout,EventTriggerInfo * evtinfo)16754 dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo)
16755 {
16756 DumpOptions *dopt = fout->dopt;
16757 PQExpBuffer query;
16758 PQExpBuffer delqry;
16759 char *qevtname;
16760
16761 /* Skip if not to be dumped */
16762 if (!evtinfo->dobj.dump || dopt->dataOnly)
16763 return;
16764
16765 query = createPQExpBuffer();
16766 delqry = createPQExpBuffer();
16767
16768 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
16769
16770 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
16771 appendPQExpBufferStr(query, qevtname);
16772 appendPQExpBufferStr(query, " ON ");
16773 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
16774
16775 if (strcmp("", evtinfo->evttags) != 0)
16776 {
16777 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
16778 appendPQExpBufferStr(query, evtinfo->evttags);
16779 appendPQExpBufferChar(query, ')');
16780 }
16781
16782 appendPQExpBufferStr(query, "\n EXECUTE PROCEDURE ");
16783 appendPQExpBufferStr(query, evtinfo->evtfname);
16784 appendPQExpBufferStr(query, "();\n");
16785
16786 if (evtinfo->evtenabled != 'O')
16787 {
16788 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
16789 qevtname);
16790 switch (evtinfo->evtenabled)
16791 {
16792 case 'D':
16793 appendPQExpBufferStr(query, "DISABLE");
16794 break;
16795 case 'A':
16796 appendPQExpBufferStr(query, "ENABLE ALWAYS");
16797 break;
16798 case 'R':
16799 appendPQExpBufferStr(query, "ENABLE REPLICA");
16800 break;
16801 default:
16802 appendPQExpBufferStr(query, "ENABLE");
16803 break;
16804 }
16805 appendPQExpBufferStr(query, ";\n");
16806 }
16807
16808 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
16809 qevtname);
16810
16811 if (dopt->binary_upgrade)
16812 binary_upgrade_extension_member(query, &evtinfo->dobj,
16813 "EVENT TRIGGER", qevtname, NULL);
16814
16815 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16816 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
16817 evtinfo->dobj.name, NULL, NULL,
16818 evtinfo->evtowner, false,
16819 "EVENT TRIGGER", SECTION_POST_DATA,
16820 query->data, delqry->data, NULL,
16821 NULL, 0,
16822 NULL, NULL);
16823
16824 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16825 dumpComment(fout, "EVENT TRIGGER", qevtname,
16826 NULL, evtinfo->evtowner,
16827 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
16828
16829 destroyPQExpBuffer(query);
16830 destroyPQExpBuffer(delqry);
16831 free(qevtname);
16832 }
16833
16834 /*
16835 * dumpRule
16836 * Dump a rule
16837 */
16838 static void
dumpRule(Archive * fout,RuleInfo * rinfo)16839 dumpRule(Archive *fout, RuleInfo *rinfo)
16840 {
16841 DumpOptions *dopt = fout->dopt;
16842 TableInfo *tbinfo = rinfo->ruletable;
16843 bool is_view;
16844 PQExpBuffer query;
16845 PQExpBuffer cmd;
16846 PQExpBuffer delcmd;
16847 PQExpBuffer ruleprefix;
16848 char *qtabname;
16849 PGresult *res;
16850 char *tag;
16851
16852 /* Skip if not to be dumped */
16853 if (!rinfo->dobj.dump || dopt->dataOnly)
16854 return;
16855
16856 /*
16857 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
16858 * we do not want to dump it as a separate object.
16859 */
16860 if (!rinfo->separate)
16861 return;
16862
16863 /*
16864 * If it's an ON SELECT rule, we want to print it as a view definition,
16865 * instead of a rule.
16866 */
16867 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
16868
16869 query = createPQExpBuffer();
16870 cmd = createPQExpBuffer();
16871 delcmd = createPQExpBuffer();
16872 ruleprefix = createPQExpBuffer();
16873
16874 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
16875
16876 if (is_view)
16877 {
16878 PQExpBuffer result;
16879
16880 /*
16881 * We need OR REPLACE here because we'll be replacing a dummy view.
16882 * Otherwise this should look largely like the regular view dump code.
16883 */
16884 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
16885 fmtQualifiedDumpable(tbinfo));
16886 if (nonemptyReloptions(tbinfo->reloptions))
16887 {
16888 appendPQExpBufferStr(cmd, " WITH (");
16889 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
16890 appendPQExpBufferChar(cmd, ')');
16891 }
16892 result = createViewAsClause(fout, tbinfo);
16893 appendPQExpBuffer(cmd, " AS\n%s", result->data);
16894 destroyPQExpBuffer(result);
16895 if (tbinfo->checkoption != NULL)
16896 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
16897 tbinfo->checkoption);
16898 appendPQExpBufferStr(cmd, ";\n");
16899 }
16900 else
16901 {
16902 /* In the rule case, just print pg_get_ruledef's result verbatim */
16903 appendPQExpBuffer(query,
16904 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
16905 rinfo->dobj.catId.oid);
16906
16907 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16908
16909 if (PQntuples(res) != 1)
16910 {
16911 write_msg(NULL, "query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned\n",
16912 rinfo->dobj.name, tbinfo->dobj.name);
16913 exit_nicely(1);
16914 }
16915
16916 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
16917
16918 PQclear(res);
16919 }
16920
16921 /*
16922 * Add the command to alter the rules replication firing semantics if it
16923 * differs from the default.
16924 */
16925 if (rinfo->ev_enabled != 'O')
16926 {
16927 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
16928 switch (rinfo->ev_enabled)
16929 {
16930 case 'A':
16931 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
16932 fmtId(rinfo->dobj.name));
16933 break;
16934 case 'R':
16935 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
16936 fmtId(rinfo->dobj.name));
16937 break;
16938 case 'D':
16939 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
16940 fmtId(rinfo->dobj.name));
16941 break;
16942 }
16943 }
16944
16945 if (is_view)
16946 {
16947 /*
16948 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
16949 * REPLACE VIEW to replace the rule with something with minimal
16950 * dependencies.
16951 */
16952 PQExpBuffer result;
16953
16954 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
16955 fmtQualifiedDumpable(tbinfo));
16956 result = createDummyViewAsClause(fout, tbinfo);
16957 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
16958 destroyPQExpBuffer(result);
16959 }
16960 else
16961 {
16962 appendPQExpBuffer(delcmd, "DROP RULE %s ",
16963 fmtId(rinfo->dobj.name));
16964 appendPQExpBuffer(delcmd, "ON %s;\n",
16965 fmtQualifiedDumpable(tbinfo));
16966 }
16967
16968 appendPQExpBuffer(ruleprefix, "RULE %s ON",
16969 fmtId(rinfo->dobj.name));
16970
16971 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
16972
16973 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16974 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
16975 tag,
16976 tbinfo->dobj.namespace->dobj.name,
16977 NULL,
16978 tbinfo->rolname, false,
16979 "RULE", SECTION_POST_DATA,
16980 cmd->data, delcmd->data, NULL,
16981 NULL, 0,
16982 NULL, NULL);
16983
16984 /* Dump rule comments */
16985 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16986 dumpComment(fout, ruleprefix->data, qtabname,
16987 tbinfo->dobj.namespace->dobj.name,
16988 tbinfo->rolname,
16989 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
16990
16991 free(tag);
16992 destroyPQExpBuffer(query);
16993 destroyPQExpBuffer(cmd);
16994 destroyPQExpBuffer(delcmd);
16995 destroyPQExpBuffer(ruleprefix);
16996 free(qtabname);
16997 }
16998
16999 /*
17000 * getExtensionMembership --- obtain extension membership data
17001 *
17002 * We need to identify objects that are extension members as soon as they're
17003 * loaded, so that we can correctly determine whether they need to be dumped.
17004 * Generally speaking, extension member objects will get marked as *not* to
17005 * be dumped, as they will be recreated by the single CREATE EXTENSION
17006 * command. However, in binary upgrade mode we still need to dump the members
17007 * individually.
17008 */
17009 void
getExtensionMembership(Archive * fout,ExtensionInfo extinfo[],int numExtensions)17010 getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
17011 int numExtensions)
17012 {
17013 PQExpBuffer query;
17014 PGresult *res;
17015 int ntups,
17016 nextmembers,
17017 i;
17018 int i_classid,
17019 i_objid,
17020 i_refobjid;
17021 ExtensionMemberId *extmembers;
17022 ExtensionInfo *ext;
17023
17024 /* Nothing to do if no extensions */
17025 if (numExtensions == 0)
17026 return;
17027
17028 query = createPQExpBuffer();
17029
17030 /* refclassid constraint is redundant but may speed the search */
17031 appendPQExpBufferStr(query, "SELECT "
17032 "classid, objid, refobjid "
17033 "FROM pg_depend "
17034 "WHERE refclassid = 'pg_extension'::regclass "
17035 "AND deptype = 'e' "
17036 "ORDER BY 3");
17037
17038 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17039
17040 ntups = PQntuples(res);
17041
17042 i_classid = PQfnumber(res, "classid");
17043 i_objid = PQfnumber(res, "objid");
17044 i_refobjid = PQfnumber(res, "refobjid");
17045
17046 extmembers = (ExtensionMemberId *) pg_malloc(ntups * sizeof(ExtensionMemberId));
17047 nextmembers = 0;
17048
17049 /*
17050 * Accumulate data into extmembers[].
17051 *
17052 * Since we ordered the SELECT by referenced ID, we can expect that
17053 * multiple entries for the same extension will appear together; this
17054 * saves on searches.
17055 */
17056 ext = NULL;
17057
17058 for (i = 0; i < ntups; i++)
17059 {
17060 CatalogId objId;
17061 Oid extId;
17062
17063 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
17064 objId.oid = atooid(PQgetvalue(res, i, i_objid));
17065 extId = atooid(PQgetvalue(res, i, i_refobjid));
17066
17067 if (ext == NULL ||
17068 ext->dobj.catId.oid != extId)
17069 ext = findExtensionByOid(extId);
17070
17071 if (ext == NULL)
17072 {
17073 /* shouldn't happen */
17074 fprintf(stderr, "could not find referenced extension %u\n", extId);
17075 continue;
17076 }
17077
17078 extmembers[nextmembers].catId = objId;
17079 extmembers[nextmembers].ext = ext;
17080 nextmembers++;
17081 }
17082
17083 PQclear(res);
17084
17085 /* Remember the data for use later */
17086 setExtensionMembership(extmembers, nextmembers);
17087
17088 destroyPQExpBuffer(query);
17089 }
17090
17091 /*
17092 * processExtensionTables --- deal with extension configuration tables
17093 *
17094 * There are two parts to this process:
17095 *
17096 * 1. Identify and create dump records for extension configuration tables.
17097 *
17098 * Extensions can mark tables as "configuration", which means that the user
17099 * is able and expected to modify those tables after the extension has been
17100 * loaded. For these tables, we dump out only the data- the structure is
17101 * expected to be handled at CREATE EXTENSION time, including any indexes or
17102 * foreign keys, which brings us to-
17103 *
17104 * 2. Record FK dependencies between configuration tables.
17105 *
17106 * Due to the FKs being created at CREATE EXTENSION time and therefore before
17107 * the data is loaded, we have to work out what the best order for reloading
17108 * the data is, to avoid FK violations when the tables are restored. This is
17109 * not perfect- we can't handle circular dependencies and if any exist they
17110 * will cause an invalid dump to be produced (though at least all of the data
17111 * is included for a user to manually restore). This is currently documented
17112 * but perhaps we can provide a better solution in the future.
17113 */
17114 void
processExtensionTables(Archive * fout,ExtensionInfo extinfo[],int numExtensions)17115 processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
17116 int numExtensions)
17117 {
17118 DumpOptions *dopt = fout->dopt;
17119 PQExpBuffer query;
17120 PGresult *res;
17121 int ntups,
17122 i;
17123 int i_conrelid,
17124 i_confrelid;
17125
17126 /* Nothing to do if no extensions */
17127 if (numExtensions == 0)
17128 return;
17129
17130 /*
17131 * Identify extension configuration tables and create TableDataInfo
17132 * objects for them, ensuring their data will be dumped even though the
17133 * tables themselves won't be.
17134 *
17135 * Note that we create TableDataInfo objects even in schemaOnly mode, ie,
17136 * user data in a configuration table is treated like schema data. This
17137 * seems appropriate since system data in a config table would get
17138 * reloaded by CREATE EXTENSION.
17139 */
17140 for (i = 0; i < numExtensions; i++)
17141 {
17142 ExtensionInfo *curext = &(extinfo[i]);
17143 char *extconfig = curext->extconfig;
17144 char *extcondition = curext->extcondition;
17145 char **extconfigarray = NULL;
17146 char **extconditionarray = NULL;
17147 int nconfigitems;
17148 int nconditionitems;
17149
17150 if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) &&
17151 parsePGArray(extcondition, &extconditionarray, &nconditionitems) &&
17152 nconfigitems == nconditionitems)
17153 {
17154 int j;
17155
17156 for (j = 0; j < nconfigitems; j++)
17157 {
17158 TableInfo *configtbl;
17159 Oid configtbloid = atooid(extconfigarray[j]);
17160 bool dumpobj =
17161 curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
17162
17163 configtbl = findTableByOid(configtbloid);
17164 if (configtbl == NULL)
17165 continue;
17166
17167 /*
17168 * Tables of not-to-be-dumped extensions shouldn't be dumped
17169 * unless the table or its schema is explicitly included
17170 */
17171 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
17172 {
17173 /* check table explicitly requested */
17174 if (table_include_oids.head != NULL &&
17175 simple_oid_list_member(&table_include_oids,
17176 configtbloid))
17177 dumpobj = true;
17178
17179 /* check table's schema explicitly requested */
17180 if (configtbl->dobj.namespace->dobj.dump &
17181 DUMP_COMPONENT_DATA)
17182 dumpobj = true;
17183 }
17184
17185 /* check table excluded by an exclusion switch */
17186 if (table_exclude_oids.head != NULL &&
17187 simple_oid_list_member(&table_exclude_oids,
17188 configtbloid))
17189 dumpobj = false;
17190
17191 /* check schema excluded by an exclusion switch */
17192 if (simple_oid_list_member(&schema_exclude_oids,
17193 configtbl->dobj.namespace->dobj.catId.oid))
17194 dumpobj = false;
17195
17196 if (dumpobj)
17197 {
17198 /*
17199 * Note: config tables are dumped without OIDs regardless
17200 * of the --oids setting. This is because row filtering
17201 * conditions aren't compatible with dumping OIDs.
17202 */
17203 makeTableDataInfo(dopt, configtbl, false);
17204 if (configtbl->dataObj != NULL)
17205 {
17206 if (strlen(extconditionarray[j]) > 0)
17207 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
17208 }
17209 }
17210 }
17211 }
17212 if (extconfigarray)
17213 free(extconfigarray);
17214 if (extconditionarray)
17215 free(extconditionarray);
17216 }
17217
17218 /*
17219 * Now that all the TableInfoData objects have been created for all the
17220 * extensions, check their FK dependencies and register them to try and
17221 * dump the data out in an order that they can be restored in.
17222 *
17223 * Note that this is not a problem for user tables as their FKs are
17224 * recreated after the data has been loaded.
17225 */
17226
17227 query = createPQExpBuffer();
17228
17229 printfPQExpBuffer(query,
17230 "SELECT conrelid, confrelid "
17231 "FROM pg_constraint "
17232 "JOIN pg_depend ON (objid = confrelid) "
17233 "WHERE contype = 'f' "
17234 "AND refclassid = 'pg_extension'::regclass "
17235 "AND classid = 'pg_class'::regclass;");
17236
17237 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17238 ntups = PQntuples(res);
17239
17240 i_conrelid = PQfnumber(res, "conrelid");
17241 i_confrelid = PQfnumber(res, "confrelid");
17242
17243 /* Now get the dependencies and register them */
17244 for (i = 0; i < ntups; i++)
17245 {
17246 Oid conrelid,
17247 confrelid;
17248 TableInfo *reftable,
17249 *contable;
17250
17251 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
17252 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
17253 contable = findTableByOid(conrelid);
17254 reftable = findTableByOid(confrelid);
17255
17256 if (reftable == NULL ||
17257 reftable->dataObj == NULL ||
17258 contable == NULL ||
17259 contable->dataObj == NULL)
17260 continue;
17261
17262 /*
17263 * Make referencing TABLE_DATA object depend on the referenced table's
17264 * TABLE_DATA object.
17265 */
17266 addObjectDependency(&contable->dataObj->dobj,
17267 reftable->dataObj->dobj.dumpId);
17268 }
17269 PQclear(res);
17270 destroyPQExpBuffer(query);
17271 }
17272
17273 /*
17274 * getDependencies --- obtain available dependency data
17275 */
17276 static void
getDependencies(Archive * fout)17277 getDependencies(Archive *fout)
17278 {
17279 PQExpBuffer query;
17280 PGresult *res;
17281 int ntups,
17282 i;
17283 int i_classid,
17284 i_objid,
17285 i_refclassid,
17286 i_refobjid,
17287 i_deptype;
17288 DumpableObject *dobj,
17289 *refdobj;
17290
17291 if (g_verbose)
17292 write_msg(NULL, "reading dependency data\n");
17293
17294 query = createPQExpBuffer();
17295
17296 /*
17297 * Messy query to collect the dependency data we need. Note that we
17298 * ignore the sub-object column, so that dependencies of or on a column
17299 * look the same as dependencies of or on a whole table.
17300 *
17301 * PIN dependencies aren't interesting, and EXTENSION dependencies were
17302 * already processed by getExtensionMembership.
17303 */
17304 appendPQExpBufferStr(query, "SELECT "
17305 "classid, objid, refclassid, refobjid, deptype "
17306 "FROM pg_depend "
17307 "WHERE deptype != 'p' AND deptype != 'e'\n");
17308
17309 /*
17310 * Since we don't treat pg_amop entries as separate DumpableObjects, we
17311 * have to translate their dependencies into dependencies of their parent
17312 * opfamily. Ignore internal dependencies though, as those will point to
17313 * their parent opclass, which we needn't consider here (and if we did,
17314 * it'd just result in circular dependencies). Also, "loose" opfamily
17315 * entries will have dependencies on their parent opfamily, which we
17316 * should drop since they'd likewise become useless self-dependencies.
17317 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
17318 *
17319 * Skip this for pre-8.3 source servers: pg_opfamily doesn't exist there,
17320 * and the (known) cases where it would matter to have these dependencies
17321 * can't arise anyway.
17322 */
17323 if (fout->remoteVersion >= 80300)
17324 {
17325 appendPQExpBufferStr(query, "UNION ALL\n"
17326 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
17327 "FROM pg_depend d, pg_amop o "
17328 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
17329 "classid = 'pg_amop'::regclass AND objid = o.oid "
17330 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
17331
17332 /* Likewise for pg_amproc entries */
17333 appendPQExpBufferStr(query, "UNION ALL\n"
17334 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
17335 "FROM pg_depend d, pg_amproc p "
17336 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
17337 "classid = 'pg_amproc'::regclass AND objid = p.oid "
17338 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
17339 }
17340
17341 /* Sort the output for efficiency below */
17342 appendPQExpBufferStr(query, "ORDER BY 1,2");
17343
17344 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17345
17346 ntups = PQntuples(res);
17347
17348 i_classid = PQfnumber(res, "classid");
17349 i_objid = PQfnumber(res, "objid");
17350 i_refclassid = PQfnumber(res, "refclassid");
17351 i_refobjid = PQfnumber(res, "refobjid");
17352 i_deptype = PQfnumber(res, "deptype");
17353
17354 /*
17355 * Since we ordered the SELECT by referencing ID, we can expect that
17356 * multiple entries for the same object will appear together; this saves
17357 * on searches.
17358 */
17359 dobj = NULL;
17360
17361 for (i = 0; i < ntups; i++)
17362 {
17363 CatalogId objId;
17364 CatalogId refobjId;
17365 char deptype;
17366
17367 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
17368 objId.oid = atooid(PQgetvalue(res, i, i_objid));
17369 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
17370 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
17371 deptype = *(PQgetvalue(res, i, i_deptype));
17372
17373 if (dobj == NULL ||
17374 dobj->catId.tableoid != objId.tableoid ||
17375 dobj->catId.oid != objId.oid)
17376 dobj = findObjectByCatalogId(objId);
17377
17378 /*
17379 * Failure to find objects mentioned in pg_depend is not unexpected,
17380 * since for example we don't collect info about TOAST tables.
17381 */
17382 if (dobj == NULL)
17383 {
17384 #ifdef NOT_USED
17385 fprintf(stderr, "no referencing object %u %u\n",
17386 objId.tableoid, objId.oid);
17387 #endif
17388 continue;
17389 }
17390
17391 refdobj = findObjectByCatalogId(refobjId);
17392
17393 if (refdobj == NULL)
17394 {
17395 #ifdef NOT_USED
17396 fprintf(stderr, "no referenced object %u %u\n",
17397 refobjId.tableoid, refobjId.oid);
17398 #endif
17399 continue;
17400 }
17401
17402 /*
17403 * For 'x' dependencies, mark the object for later; we still add the
17404 * normal dependency, for possible ordering purposes. Currently
17405 * pg_dump_sort.c knows to put extensions ahead of all object types
17406 * that could possibly depend on them, but this is safer.
17407 */
17408 if (deptype == 'x')
17409 dobj->depends_on_ext = true;
17410
17411 /*
17412 * Ordinarily, table rowtypes have implicit dependencies on their
17413 * tables. However, for a composite type the implicit dependency goes
17414 * the other way in pg_depend; which is the right thing for DROP but
17415 * it doesn't produce the dependency ordering we need. So in that one
17416 * case, we reverse the direction of the dependency.
17417 */
17418 if (deptype == 'i' &&
17419 dobj->objType == DO_TABLE &&
17420 refdobj->objType == DO_TYPE)
17421 addObjectDependency(refdobj, dobj->dumpId);
17422 else
17423 /* normal case */
17424 addObjectDependency(dobj, refdobj->dumpId);
17425 }
17426
17427 PQclear(res);
17428
17429 destroyPQExpBuffer(query);
17430 }
17431
17432
17433 /*
17434 * createBoundaryObjects - create dummy DumpableObjects to represent
17435 * dump section boundaries.
17436 */
17437 static DumpableObject *
createBoundaryObjects(void)17438 createBoundaryObjects(void)
17439 {
17440 DumpableObject *dobjs;
17441
17442 dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
17443
17444 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
17445 dobjs[0].catId = nilCatalogId;
17446 AssignDumpId(dobjs + 0);
17447 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
17448
17449 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
17450 dobjs[1].catId = nilCatalogId;
17451 AssignDumpId(dobjs + 1);
17452 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
17453
17454 return dobjs;
17455 }
17456
17457 /*
17458 * addBoundaryDependencies - add dependencies as needed to enforce the dump
17459 * section boundaries.
17460 */
17461 static void
addBoundaryDependencies(DumpableObject ** dobjs,int numObjs,DumpableObject * boundaryObjs)17462 addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
17463 DumpableObject *boundaryObjs)
17464 {
17465 DumpableObject *preDataBound = boundaryObjs + 0;
17466 DumpableObject *postDataBound = boundaryObjs + 1;
17467 int i;
17468
17469 for (i = 0; i < numObjs; i++)
17470 {
17471 DumpableObject *dobj = dobjs[i];
17472
17473 /*
17474 * The classification of object types here must match the SECTION_xxx
17475 * values assigned during subsequent ArchiveEntry calls!
17476 */
17477 switch (dobj->objType)
17478 {
17479 case DO_NAMESPACE:
17480 case DO_EXTENSION:
17481 case DO_TYPE:
17482 case DO_SHELL_TYPE:
17483 case DO_FUNC:
17484 case DO_AGG:
17485 case DO_OPERATOR:
17486 case DO_ACCESS_METHOD:
17487 case DO_OPCLASS:
17488 case DO_OPFAMILY:
17489 case DO_COLLATION:
17490 case DO_CONVERSION:
17491 case DO_TABLE:
17492 case DO_ATTRDEF:
17493 case DO_PROCLANG:
17494 case DO_CAST:
17495 case DO_DUMMY_TYPE:
17496 case DO_TSPARSER:
17497 case DO_TSDICT:
17498 case DO_TSTEMPLATE:
17499 case DO_TSCONFIG:
17500 case DO_FDW:
17501 case DO_FOREIGN_SERVER:
17502 case DO_TRANSFORM:
17503 case DO_BLOB:
17504 /* Pre-data objects: must come before the pre-data boundary */
17505 addObjectDependency(preDataBound, dobj->dumpId);
17506 break;
17507 case DO_TABLE_DATA:
17508 case DO_SEQUENCE_SET:
17509 case DO_BLOB_DATA:
17510 /* Data objects: must come between the boundaries */
17511 addObjectDependency(dobj, preDataBound->dumpId);
17512 addObjectDependency(postDataBound, dobj->dumpId);
17513 break;
17514 case DO_INDEX:
17515 case DO_STATSEXT:
17516 case DO_REFRESH_MATVIEW:
17517 case DO_TRIGGER:
17518 case DO_EVENT_TRIGGER:
17519 case DO_DEFAULT_ACL:
17520 case DO_POLICY:
17521 case DO_PUBLICATION:
17522 case DO_PUBLICATION_REL:
17523 case DO_SUBSCRIPTION:
17524 /* Post-data objects: must come after the post-data boundary */
17525 addObjectDependency(dobj, postDataBound->dumpId);
17526 break;
17527 case DO_RULE:
17528 /* Rules are post-data, but only if dumped separately */
17529 if (((RuleInfo *) dobj)->separate)
17530 addObjectDependency(dobj, postDataBound->dumpId);
17531 break;
17532 case DO_CONSTRAINT:
17533 case DO_FK_CONSTRAINT:
17534 /* Constraints are post-data, but only if dumped separately */
17535 if (((ConstraintInfo *) dobj)->separate)
17536 addObjectDependency(dobj, postDataBound->dumpId);
17537 break;
17538 case DO_PRE_DATA_BOUNDARY:
17539 /* nothing to do */
17540 break;
17541 case DO_POST_DATA_BOUNDARY:
17542 /* must come after the pre-data boundary */
17543 addObjectDependency(dobj, preDataBound->dumpId);
17544 break;
17545 }
17546 }
17547 }
17548
17549
17550 /*
17551 * BuildArchiveDependencies - create dependency data for archive TOC entries
17552 *
17553 * The raw dependency data obtained by getDependencies() is not terribly
17554 * useful in an archive dump, because in many cases there are dependency
17555 * chains linking through objects that don't appear explicitly in the dump.
17556 * For example, a view will depend on its _RETURN rule while the _RETURN rule
17557 * will depend on other objects --- but the rule will not appear as a separate
17558 * object in the dump. We need to adjust the view's dependencies to include
17559 * whatever the rule depends on that is included in the dump.
17560 *
17561 * Just to make things more complicated, there are also "special" dependencies
17562 * such as the dependency of a TABLE DATA item on its TABLE, which we must
17563 * not rearrange because pg_restore knows that TABLE DATA only depends on
17564 * its table. In these cases we must leave the dependencies strictly as-is
17565 * even if they refer to not-to-be-dumped objects.
17566 *
17567 * To handle this, the convention is that "special" dependencies are created
17568 * during ArchiveEntry calls, and an archive TOC item that has any such
17569 * entries will not be touched here. Otherwise, we recursively search the
17570 * DumpableObject data structures to build the correct dependencies for each
17571 * archive TOC item.
17572 */
17573 static void
BuildArchiveDependencies(Archive * fout)17574 BuildArchiveDependencies(Archive *fout)
17575 {
17576 ArchiveHandle *AH = (ArchiveHandle *) fout;
17577 TocEntry *te;
17578
17579 /* Scan all TOC entries in the archive */
17580 for (te = AH->toc->next; te != AH->toc; te = te->next)
17581 {
17582 DumpableObject *dobj;
17583 DumpId *dependencies;
17584 int nDeps;
17585 int allocDeps;
17586
17587 /* No need to process entries that will not be dumped */
17588 if (te->reqs == 0)
17589 continue;
17590 /* Ignore entries that already have "special" dependencies */
17591 if (te->nDeps > 0)
17592 continue;
17593 /* Otherwise, look up the item's original DumpableObject, if any */
17594 dobj = findObjectByDumpId(te->dumpId);
17595 if (dobj == NULL)
17596 continue;
17597 /* No work if it has no dependencies */
17598 if (dobj->nDeps <= 0)
17599 continue;
17600 /* Set up work array */
17601 allocDeps = 64;
17602 dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
17603 nDeps = 0;
17604 /* Recursively find all dumpable dependencies */
17605 findDumpableDependencies(AH, dobj,
17606 &dependencies, &nDeps, &allocDeps);
17607 /* And save 'em ... */
17608 if (nDeps > 0)
17609 {
17610 dependencies = (DumpId *) pg_realloc(dependencies,
17611 nDeps * sizeof(DumpId));
17612 te->dependencies = dependencies;
17613 te->nDeps = nDeps;
17614 }
17615 else
17616 free(dependencies);
17617 }
17618 }
17619
17620 /* Recursive search subroutine for BuildArchiveDependencies */
17621 static void
findDumpableDependencies(ArchiveHandle * AH,DumpableObject * dobj,DumpId ** dependencies,int * nDeps,int * allocDeps)17622 findDumpableDependencies(ArchiveHandle *AH, DumpableObject *dobj,
17623 DumpId **dependencies, int *nDeps, int *allocDeps)
17624 {
17625 int i;
17626
17627 /*
17628 * Ignore section boundary objects: if we search through them, we'll
17629 * report lots of bogus dependencies.
17630 */
17631 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
17632 dobj->objType == DO_POST_DATA_BOUNDARY)
17633 return;
17634
17635 for (i = 0; i < dobj->nDeps; i++)
17636 {
17637 DumpId depid = dobj->dependencies[i];
17638
17639 if (TocIDRequired(AH, depid) != 0)
17640 {
17641 /* Object will be dumped, so just reference it as a dependency */
17642 if (*nDeps >= *allocDeps)
17643 {
17644 *allocDeps *= 2;
17645 *dependencies = (DumpId *) pg_realloc(*dependencies,
17646 *allocDeps * sizeof(DumpId));
17647 }
17648 (*dependencies)[*nDeps] = depid;
17649 (*nDeps)++;
17650 }
17651 else
17652 {
17653 /*
17654 * Object will not be dumped, so recursively consider its deps. We
17655 * rely on the assumption that sortDumpableObjects already broke
17656 * any dependency loops, else we might recurse infinitely.
17657 */
17658 DumpableObject *otherdobj = findObjectByDumpId(depid);
17659
17660 if (otherdobj)
17661 findDumpableDependencies(AH, otherdobj,
17662 dependencies, nDeps, allocDeps);
17663 }
17664 }
17665 }
17666
17667
17668 /*
17669 * getFormattedTypeName - retrieve a nicely-formatted type name for the
17670 * given type OID.
17671 *
17672 * This does not guarantee to schema-qualify the output, so it should not
17673 * be used to create the target object name for CREATE or ALTER commands.
17674 *
17675 * Note that the result is cached and must not be freed by the caller.
17676 */
17677 static const char *
getFormattedTypeName(Archive * fout,Oid oid,OidOptions opts)17678 getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
17679 {
17680 TypeInfo *typeInfo;
17681 char *result;
17682 PQExpBuffer query;
17683 PGresult *res;
17684
17685 if (oid == 0)
17686 {
17687 if ((opts & zeroAsOpaque) != 0)
17688 return g_opaque_type;
17689 else if ((opts & zeroAsAny) != 0)
17690 return "'any'";
17691 else if ((opts & zeroAsStar) != 0)
17692 return "*";
17693 else if ((opts & zeroAsNone) != 0)
17694 return "NONE";
17695 }
17696
17697 /* see if we have the result cached in the type's TypeInfo record */
17698 typeInfo = findTypeByOid(oid);
17699 if (typeInfo && typeInfo->ftypname)
17700 return typeInfo->ftypname;
17701
17702 query = createPQExpBuffer();
17703 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
17704 oid);
17705
17706 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17707
17708 /* result of format_type is already quoted */
17709 result = pg_strdup(PQgetvalue(res, 0, 0));
17710
17711 PQclear(res);
17712 destroyPQExpBuffer(query);
17713
17714 /*
17715 * Cache the result for re-use in later requests, if possible. If we
17716 * don't have a TypeInfo for the type, the string will be leaked once the
17717 * caller is done with it ... but that case really should not happen, so
17718 * leaking if it does seems acceptable.
17719 */
17720 if (typeInfo)
17721 typeInfo->ftypname = result;
17722
17723 return result;
17724 }
17725
17726 /*
17727 * Return a column list clause for the given relation.
17728 *
17729 * Special case: if there are no undropped columns in the relation, return
17730 * "", not an invalid "()" column list.
17731 */
17732 static const char *
fmtCopyColumnList(const TableInfo * ti,PQExpBuffer buffer)17733 fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
17734 {
17735 int numatts = ti->numatts;
17736 char **attnames = ti->attnames;
17737 bool *attisdropped = ti->attisdropped;
17738 bool needComma;
17739 int i;
17740
17741 appendPQExpBufferChar(buffer, '(');
17742 needComma = false;
17743 for (i = 0; i < numatts; i++)
17744 {
17745 if (attisdropped[i])
17746 continue;
17747 if (needComma)
17748 appendPQExpBufferStr(buffer, ", ");
17749 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
17750 needComma = true;
17751 }
17752
17753 if (!needComma)
17754 return ""; /* no undropped columns */
17755
17756 appendPQExpBufferChar(buffer, ')');
17757 return buffer->data;
17758 }
17759
17760 /*
17761 * Check if a reloptions array is nonempty.
17762 */
17763 static bool
nonemptyReloptions(const char * reloptions)17764 nonemptyReloptions(const char *reloptions)
17765 {
17766 /* Don't want to print it if it's just "{}" */
17767 return (reloptions != NULL && strlen(reloptions) > 2);
17768 }
17769
17770 /*
17771 * Format a reloptions array and append it to the given buffer.
17772 *
17773 * "prefix" is prepended to the option names; typically it's "" or "toast.".
17774 */
17775 static void
appendReloptionsArrayAH(PQExpBuffer buffer,const char * reloptions,const char * prefix,Archive * fout)17776 appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
17777 const char *prefix, Archive *fout)
17778 {
17779 bool res;
17780
17781 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
17782 fout->std_strings);
17783 if (!res)
17784 write_msg(NULL, "WARNING: could not parse reloptions array\n");
17785 }
17786