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, &quote_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, &copybuf, 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