1 /*-------------------------------------------------------------------------
2  *
3  * common.c
4  *	Catalog routines used by pg_dump; long ago these were shared
5  *	by another dump tool, but not anymore.
6  *
7  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *	  src/bin/pg_dump/common.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres_fe.h"
17 
18 #include "pg_backup_archiver.h"
19 #include "pg_backup_utils.h"
20 #include "pg_dump.h"
21 
22 #include <ctype.h>
23 
24 #include "catalog/pg_class_d.h"
25 #include "fe_utils/string_utils.h"
26 
27 
28 /*
29  * Variables for mapping DumpId to DumpableObject
30  */
31 static DumpableObject **dumpIdMap = NULL;
32 static int	allocedDumpIds = 0;
33 static DumpId lastDumpId = 0;	/* Note: 0 is InvalidDumpId */
34 
35 /*
36  * Variables for mapping CatalogId to DumpableObject
37  */
38 static bool catalogIdMapValid = false;
39 static DumpableObject **catalogIdMap = NULL;
40 static int	numCatalogIds = 0;
41 
42 /*
43  * These variables are static to avoid the notational cruft of having to pass
44  * them into findTableByOid() and friends.  For each of these arrays, we build
45  * a sorted-by-OID index array immediately after the objects are fetched,
46  * and then we use binary search in findTableByOid() and friends.  (qsort'ing
47  * the object arrays themselves would be simpler, but it doesn't work because
48  * pg_dump.c may have already established pointers between items.)
49  */
50 static DumpableObject **tblinfoindex;
51 static DumpableObject **typinfoindex;
52 static DumpableObject **funinfoindex;
53 static DumpableObject **oprinfoindex;
54 static DumpableObject **collinfoindex;
55 static DumpableObject **nspinfoindex;
56 static DumpableObject **extinfoindex;
57 static DumpableObject **pubinfoindex;
58 static int	numTables;
59 static int	numTypes;
60 static int	numFuncs;
61 static int	numOperators;
62 static int	numCollations;
63 static int	numNamespaces;
64 static int	numExtensions;
65 static int	numPublications;
66 
67 /* This is an array of object identities, not actual DumpableObjects */
68 static ExtensionMemberId *extmembers;
69 static int	numextmembers;
70 
71 static void flagInhTables(Archive *fout, TableInfo *tbinfo, int numTables,
72 			  InhInfo *inhinfo, int numInherits);
73 static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, int numTables);
74 static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables);
75 static DumpableObject **buildIndexArray(void *objArray, int numObjs,
76 				Size objSize);
77 static int	DOCatalogIdCompare(const void *p1, const void *p2);
78 static int	ExtensionMemberIdCompare(const void *p1, const void *p2);
79 static void findParentsByOid(TableInfo *self,
80 				 InhInfo *inhinfo, int numInherits);
81 static int	strInArray(const char *pattern, char **arr, int arr_size);
82 static IndxInfo *findIndexByOid(Oid oid, DumpableObject **idxinfoindex,
83 			   int numIndexes);
84 
85 
86 /*
87  * getSchemaData
88  *	  Collect information about all potentially dumpable objects
89  */
90 TableInfo *
getSchemaData(Archive * fout,int * numTablesPtr)91 getSchemaData(Archive *fout, int *numTablesPtr)
92 {
93 	TableInfo  *tblinfo;
94 	TypeInfo   *typinfo;
95 	FuncInfo   *funinfo;
96 	OprInfo    *oprinfo;
97 	CollInfo   *collinfo;
98 	NamespaceInfo *nspinfo;
99 	ExtensionInfo *extinfo;
100 	PublicationInfo *pubinfo;
101 	InhInfo    *inhinfo;
102 	int			numAggregates;
103 	int			numInherits;
104 	int			numRules;
105 	int			numProcLangs;
106 	int			numCasts;
107 	int			numTransforms;
108 	int			numAccessMethods;
109 	int			numOpclasses;
110 	int			numOpfamilies;
111 	int			numConversions;
112 	int			numTSParsers;
113 	int			numTSTemplates;
114 	int			numTSDicts;
115 	int			numTSConfigs;
116 	int			numForeignDataWrappers;
117 	int			numForeignServers;
118 	int			numDefaultACLs;
119 	int			numEventTriggers;
120 
121 	/*
122 	 * We must read extensions and extension membership info first, because
123 	 * extension membership needs to be consultable during decisions about
124 	 * whether other objects are to be dumped.
125 	 */
126 	if (g_verbose)
127 		write_msg(NULL, "reading extensions\n");
128 	extinfo = getExtensions(fout, &numExtensions);
129 	extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo));
130 
131 	if (g_verbose)
132 		write_msg(NULL, "identifying extension members\n");
133 	getExtensionMembership(fout, extinfo, numExtensions);
134 
135 	if (g_verbose)
136 		write_msg(NULL, "reading schemas\n");
137 	nspinfo = getNamespaces(fout, &numNamespaces);
138 	nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo));
139 
140 	/*
141 	 * getTables should be done as soon as possible, so as to minimize the
142 	 * window between starting our transaction and acquiring per-table locks.
143 	 * However, we have to do getNamespaces first because the tables get
144 	 * linked to their containing namespaces during getTables.
145 	 */
146 	if (g_verbose)
147 		write_msg(NULL, "reading user-defined tables\n");
148 	tblinfo = getTables(fout, &numTables);
149 	tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo));
150 
151 	/* Do this after we've built tblinfoindex */
152 	getOwnedSeqs(fout, tblinfo, numTables);
153 
154 	if (g_verbose)
155 		write_msg(NULL, "reading user-defined functions\n");
156 	funinfo = getFuncs(fout, &numFuncs);
157 	funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo));
158 
159 	/* this must be after getTables and getFuncs */
160 	if (g_verbose)
161 		write_msg(NULL, "reading user-defined types\n");
162 	typinfo = getTypes(fout, &numTypes);
163 	typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo));
164 
165 	/* this must be after getFuncs, too */
166 	if (g_verbose)
167 		write_msg(NULL, "reading procedural languages\n");
168 	getProcLangs(fout, &numProcLangs);
169 
170 	if (g_verbose)
171 		write_msg(NULL, "reading user-defined aggregate functions\n");
172 	getAggregates(fout, &numAggregates);
173 
174 	if (g_verbose)
175 		write_msg(NULL, "reading user-defined operators\n");
176 	oprinfo = getOperators(fout, &numOperators);
177 	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
178 
179 	if (g_verbose)
180 		write_msg(NULL, "reading user-defined access methods\n");
181 	getAccessMethods(fout, &numAccessMethods);
182 
183 	if (g_verbose)
184 		write_msg(NULL, "reading user-defined operator classes\n");
185 	getOpclasses(fout, &numOpclasses);
186 
187 	if (g_verbose)
188 		write_msg(NULL, "reading user-defined operator families\n");
189 	getOpfamilies(fout, &numOpfamilies);
190 
191 	if (g_verbose)
192 		write_msg(NULL, "reading user-defined text search parsers\n");
193 	getTSParsers(fout, &numTSParsers);
194 
195 	if (g_verbose)
196 		write_msg(NULL, "reading user-defined text search templates\n");
197 	getTSTemplates(fout, &numTSTemplates);
198 
199 	if (g_verbose)
200 		write_msg(NULL, "reading user-defined text search dictionaries\n");
201 	getTSDictionaries(fout, &numTSDicts);
202 
203 	if (g_verbose)
204 		write_msg(NULL, "reading user-defined text search configurations\n");
205 	getTSConfigurations(fout, &numTSConfigs);
206 
207 	if (g_verbose)
208 		write_msg(NULL, "reading user-defined foreign-data wrappers\n");
209 	getForeignDataWrappers(fout, &numForeignDataWrappers);
210 
211 	if (g_verbose)
212 		write_msg(NULL, "reading user-defined foreign servers\n");
213 	getForeignServers(fout, &numForeignServers);
214 
215 	if (g_verbose)
216 		write_msg(NULL, "reading default privileges\n");
217 	getDefaultACLs(fout, &numDefaultACLs);
218 
219 	if (g_verbose)
220 		write_msg(NULL, "reading user-defined collations\n");
221 	collinfo = getCollations(fout, &numCollations);
222 	collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo));
223 
224 	if (g_verbose)
225 		write_msg(NULL, "reading user-defined conversions\n");
226 	getConversions(fout, &numConversions);
227 
228 	if (g_verbose)
229 		write_msg(NULL, "reading type casts\n");
230 	getCasts(fout, &numCasts);
231 
232 	if (g_verbose)
233 		write_msg(NULL, "reading transforms\n");
234 	getTransforms(fout, &numTransforms);
235 
236 	if (g_verbose)
237 		write_msg(NULL, "reading table inheritance information\n");
238 	inhinfo = getInherits(fout, &numInherits);
239 
240 	if (g_verbose)
241 		write_msg(NULL, "reading event triggers\n");
242 	getEventTriggers(fout, &numEventTriggers);
243 
244 	/* Identify extension configuration tables that should be dumped */
245 	if (g_verbose)
246 		write_msg(NULL, "finding extension tables\n");
247 	processExtensionTables(fout, extinfo, numExtensions);
248 
249 	/* Link tables to parents, mark parents of target tables interesting */
250 	if (g_verbose)
251 		write_msg(NULL, "finding inheritance relationships\n");
252 	flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits);
253 
254 	if (g_verbose)
255 		write_msg(NULL, "reading column info for interesting tables\n");
256 	getTableAttrs(fout, tblinfo, numTables);
257 
258 	if (g_verbose)
259 		write_msg(NULL, "flagging inherited columns in subtables\n");
260 	flagInhAttrs(fout->dopt, tblinfo, numTables);
261 
262 	if (g_verbose)
263 		write_msg(NULL, "reading indexes\n");
264 	getIndexes(fout, tblinfo, numTables);
265 
266 	if (g_verbose)
267 		write_msg(NULL, "flagging indexes in partitioned tables\n");
268 	flagInhIndexes(fout, tblinfo, numTables);
269 
270 	if (g_verbose)
271 		write_msg(NULL, "reading extended statistics\n");
272 	getExtendedStatistics(fout);
273 
274 	if (g_verbose)
275 		write_msg(NULL, "reading constraints\n");
276 	getConstraints(fout, tblinfo, numTables);
277 
278 	if (g_verbose)
279 		write_msg(NULL, "reading triggers\n");
280 	getTriggers(fout, tblinfo, numTables);
281 
282 	if (g_verbose)
283 		write_msg(NULL, "reading rewrite rules\n");
284 	getRules(fout, &numRules);
285 
286 	if (g_verbose)
287 		write_msg(NULL, "reading policies\n");
288 	getPolicies(fout, tblinfo, numTables);
289 
290 	if (g_verbose)
291 		write_msg(NULL, "reading publications\n");
292 	pubinfo = getPublications(fout, &numPublications);
293 	pubinfoindex = buildIndexArray(pubinfo, numPublications,
294 								   sizeof(PublicationInfo));
295 
296 	if (g_verbose)
297 		write_msg(NULL, "reading publication membership\n");
298 	getPublicationTables(fout, tblinfo, numTables);
299 
300 	if (g_verbose)
301 		write_msg(NULL, "reading subscriptions\n");
302 	getSubscriptions(fout);
303 
304 	*numTablesPtr = numTables;
305 	return tblinfo;
306 }
307 
308 /* flagInhTables -
309  *	 Fill in parent link fields of tables for which we need that information,
310  *	 and mark parents of target tables as interesting
311  *
312  * Note that only direct ancestors of targets are marked interesting.
313  * This is sufficient; we don't much care whether they inherited their
314  * attributes or not.
315  *
316  * modifies tblinfo
317  */
318 static void
flagInhTables(Archive * fout,TableInfo * tblinfo,int numTables,InhInfo * inhinfo,int numInherits)319 flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
320 			  InhInfo *inhinfo, int numInherits)
321 {
322 	DumpOptions *dopt = fout->dopt;
323 	int			i,
324 				j;
325 
326 	for (i = 0; i < numTables; i++)
327 	{
328 		bool		find_parents = true;
329 		bool		mark_parents = true;
330 
331 		/* Some kinds never have parents */
332 		if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
333 			tblinfo[i].relkind == RELKIND_VIEW ||
334 			tblinfo[i].relkind == RELKIND_MATVIEW)
335 			continue;
336 
337 		/*
338 		 * Normally, we don't bother computing anything for non-target tables,
339 		 * but if load-via-partition-root is specified, we gather information
340 		 * on every partition in the system so that getRootTableInfo can trace
341 		 * from any given to leaf partition all the way up to the root.  (We
342 		 * don't need to mark them as interesting for getTableAttrs, though.)
343 		 */
344 		if (!tblinfo[i].dobj.dump)
345 		{
346 			mark_parents = false;
347 
348 			if (!dopt->load_via_partition_root ||
349 				!tblinfo[i].ispartition)
350 				find_parents = false;
351 		}
352 
353 		/* If needed, find all the immediate parent tables. */
354 		if (find_parents)
355 			findParentsByOid(&tblinfo[i], inhinfo, numInherits);
356 
357 		/*
358 		 * If needed, mark the parents as interesting for getTableAttrs and
359 		 * getIndexes.
360 		 */
361 		if (mark_parents)
362 		{
363 			int			numParents = tblinfo[i].numParents;
364 			TableInfo **parents = tblinfo[i].parents;
365 
366 			for (j = 0; j < numParents; j++)
367 				parents[j]->interesting = true;
368 		}
369 	}
370 }
371 
372 /*
373  * flagInhIndexes -
374  *	 Create AttachIndexInfo objects for partitioned indexes, and add
375  *	 appropriate dependency links.
376  */
377 static void
flagInhIndexes(Archive * fout,TableInfo tblinfo[],int numTables)378 flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
379 {
380 	int			i,
381 				j,
382 				k;
383 	DumpableObject ***parentIndexArray;
384 
385 	parentIndexArray = (DumpableObject ***)
386 		pg_malloc0(getMaxDumpId() * sizeof(DumpableObject **));
387 
388 	for (i = 0; i < numTables; i++)
389 	{
390 		TableInfo  *parenttbl;
391 		IndexAttachInfo *attachinfo;
392 
393 		if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0)
394 			continue;
395 
396 		Assert(tblinfo[i].numParents == 1);
397 		parenttbl = tblinfo[i].parents[0];
398 
399 		/*
400 		 * We need access to each parent table's index list, but there is no
401 		 * index to cover them outside of this function.  To avoid having to
402 		 * sort every parent table's indexes each time we come across each of
403 		 * its partitions, create an indexed array for each parent the first
404 		 * time it is required.
405 		 */
406 		if (parentIndexArray[parenttbl->dobj.dumpId] == NULL)
407 			parentIndexArray[parenttbl->dobj.dumpId] =
408 				buildIndexArray(parenttbl->indexes,
409 								parenttbl->numIndexes,
410 								sizeof(IndxInfo));
411 
412 		attachinfo = (IndexAttachInfo *)
413 			pg_malloc0(tblinfo[i].numIndexes * sizeof(IndexAttachInfo));
414 		for (j = 0, k = 0; j < tblinfo[i].numIndexes; j++)
415 		{
416 			IndxInfo   *index = &(tblinfo[i].indexes[j]);
417 			IndxInfo   *parentidx;
418 
419 			if (index->parentidx == 0)
420 				continue;
421 
422 			parentidx = findIndexByOid(index->parentidx,
423 									   parentIndexArray[parenttbl->dobj.dumpId],
424 									   parenttbl->numIndexes);
425 			if (parentidx == NULL)
426 				continue;
427 
428 			attachinfo[k].dobj.objType = DO_INDEX_ATTACH;
429 			attachinfo[k].dobj.catId.tableoid = 0;
430 			attachinfo[k].dobj.catId.oid = 0;
431 			AssignDumpId(&attachinfo[k].dobj);
432 			attachinfo[k].dobj.name = pg_strdup(index->dobj.name);
433 			attachinfo[k].dobj.namespace = index->indextable->dobj.namespace;
434 			attachinfo[k].parentIdx = parentidx;
435 			attachinfo[k].partitionIdx = index;
436 
437 			/*
438 			 * We must state the DO_INDEX_ATTACH object's dependencies
439 			 * explicitly, since it will not match anything in pg_depend.
440 			 *
441 			 * Give it dependencies on both the partition index and the parent
442 			 * index, so that it will not be executed till both of those
443 			 * exist.  (There's no need to care what order those are created
444 			 * in.)
445 			 *
446 			 * In addition, give it dependencies on the indexes' underlying
447 			 * tables.  This does nothing of great value so far as serial
448 			 * restore ordering goes, but it ensures that a parallel restore
449 			 * will not try to run the ATTACH concurrently with other
450 			 * operations on those tables.
451 			 */
452 			addObjectDependency(&attachinfo[k].dobj, index->dobj.dumpId);
453 			addObjectDependency(&attachinfo[k].dobj, parentidx->dobj.dumpId);
454 			addObjectDependency(&attachinfo[k].dobj,
455 								index->indextable->dobj.dumpId);
456 			addObjectDependency(&attachinfo[k].dobj,
457 								parentidx->indextable->dobj.dumpId);
458 
459 			k++;
460 		}
461 	}
462 
463 	for (i = 0; i < numTables; i++)
464 		if (parentIndexArray[i])
465 			pg_free(parentIndexArray[i]);
466 	pg_free(parentIndexArray);
467 }
468 
469 /* flagInhAttrs -
470  *	 for each dumpable table in tblinfo, flag its inherited attributes
471  *
472  * What we need to do here is detect child columns that inherit NOT NULL
473  * bits from their parents (so that we needn't specify that again for the
474  * child) and child columns that have DEFAULT NULL when their parents had
475  * some non-null default.  In the latter case, we make up a dummy AttrDefInfo
476  * object so that we'll correctly emit the necessary DEFAULT NULL clause;
477  * otherwise the backend will apply an inherited default to the column.
478  *
479  * modifies tblinfo
480  */
481 static void
flagInhAttrs(DumpOptions * dopt,TableInfo * tblinfo,int numTables)482 flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
483 {
484 	int			i,
485 				j,
486 				k;
487 
488 	for (i = 0; i < numTables; i++)
489 	{
490 		TableInfo  *tbinfo = &(tblinfo[i]);
491 		int			numParents;
492 		TableInfo **parents;
493 
494 		/* Some kinds never have parents */
495 		if (tbinfo->relkind == RELKIND_SEQUENCE ||
496 			tbinfo->relkind == RELKIND_VIEW ||
497 			tbinfo->relkind == RELKIND_MATVIEW)
498 			continue;
499 
500 		/* Don't bother computing anything for non-target tables, either */
501 		if (!tbinfo->dobj.dump)
502 			continue;
503 
504 		numParents = tbinfo->numParents;
505 		parents = tbinfo->parents;
506 
507 		if (numParents == 0)
508 			continue;			/* nothing to see here, move along */
509 
510 		/* For each column, search for matching column names in parent(s) */
511 		for (j = 0; j < tbinfo->numatts; j++)
512 		{
513 			bool		foundNotNull;	/* Attr was NOT NULL in a parent */
514 			bool		foundDefault;	/* Found a default in a parent */
515 
516 			/* no point in examining dropped columns */
517 			if (tbinfo->attisdropped[j])
518 				continue;
519 
520 			foundNotNull = false;
521 			foundDefault = false;
522 			for (k = 0; k < numParents; k++)
523 			{
524 				TableInfo  *parent = parents[k];
525 				int			inhAttrInd;
526 
527 				inhAttrInd = strInArray(tbinfo->attnames[j],
528 										parent->attnames,
529 										parent->numatts);
530 				if (inhAttrInd >= 0)
531 				{
532 					foundNotNull |= parent->notnull[inhAttrInd];
533 					foundDefault |= (parent->attrdefs[inhAttrInd] != NULL);
534 				}
535 			}
536 
537 			/* Remember if we found inherited NOT NULL */
538 			tbinfo->inhNotNull[j] = foundNotNull;
539 
540 			/* Manufacture a DEFAULT NULL clause if necessary */
541 			if (foundDefault && tbinfo->attrdefs[j] == NULL)
542 			{
543 				AttrDefInfo *attrDef;
544 
545 				attrDef = (AttrDefInfo *) pg_malloc(sizeof(AttrDefInfo));
546 				attrDef->dobj.objType = DO_ATTRDEF;
547 				attrDef->dobj.catId.tableoid = 0;
548 				attrDef->dobj.catId.oid = 0;
549 				AssignDumpId(&attrDef->dobj);
550 				attrDef->dobj.name = pg_strdup(tbinfo->dobj.name);
551 				attrDef->dobj.namespace = tbinfo->dobj.namespace;
552 				attrDef->dobj.dump = tbinfo->dobj.dump;
553 
554 				attrDef->adtable = tbinfo;
555 				attrDef->adnum = j + 1;
556 				attrDef->adef_expr = pg_strdup("NULL");
557 
558 				/* Will column be dumped explicitly? */
559 				if (shouldPrintColumn(dopt, tbinfo, j))
560 				{
561 					attrDef->separate = false;
562 					/* No dependency needed: NULL cannot have dependencies */
563 				}
564 				else
565 				{
566 					/* column will be suppressed, print default separately */
567 					attrDef->separate = true;
568 					/* ensure it comes out after the table */
569 					addObjectDependency(&attrDef->dobj,
570 										tbinfo->dobj.dumpId);
571 				}
572 
573 				tbinfo->attrdefs[j] = attrDef;
574 			}
575 		}
576 	}
577 }
578 
579 /*
580  * AssignDumpId
581  *		Given a newly-created dumpable object, assign a dump ID,
582  *		and enter the object into the lookup table.
583  *
584  * The caller is expected to have filled in objType and catId,
585  * but not any of the other standard fields of a DumpableObject.
586  */
587 void
AssignDumpId(DumpableObject * dobj)588 AssignDumpId(DumpableObject *dobj)
589 {
590 	dobj->dumpId = ++lastDumpId;
591 	dobj->name = NULL;			/* must be set later */
592 	dobj->namespace = NULL;		/* may be set later */
593 	dobj->dump = DUMP_COMPONENT_ALL;	/* default assumption */
594 	dobj->ext_member = false;	/* default assumption */
595 	dobj->depends_on_ext = false;	/* default assumption */
596 	dobj->dependencies = NULL;
597 	dobj->nDeps = 0;
598 	dobj->allocDeps = 0;
599 
600 	while (dobj->dumpId >= allocedDumpIds)
601 	{
602 		int			newAlloc;
603 
604 		if (allocedDumpIds <= 0)
605 		{
606 			newAlloc = 256;
607 			dumpIdMap = (DumpableObject **)
608 				pg_malloc(newAlloc * sizeof(DumpableObject *));
609 		}
610 		else
611 		{
612 			newAlloc = allocedDumpIds * 2;
613 			dumpIdMap = (DumpableObject **)
614 				pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
615 		}
616 		memset(dumpIdMap + allocedDumpIds, 0,
617 			   (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
618 		allocedDumpIds = newAlloc;
619 	}
620 	dumpIdMap[dobj->dumpId] = dobj;
621 
622 	/* mark catalogIdMap invalid, but don't rebuild it yet */
623 	catalogIdMapValid = false;
624 }
625 
626 /*
627  * Assign a DumpId that's not tied to a DumpableObject.
628  *
629  * This is used when creating a "fixed" ArchiveEntry that doesn't need to
630  * participate in the sorting logic.
631  */
632 DumpId
createDumpId(void)633 createDumpId(void)
634 {
635 	return ++lastDumpId;
636 }
637 
638 /*
639  * Return the largest DumpId so far assigned
640  */
641 DumpId
getMaxDumpId(void)642 getMaxDumpId(void)
643 {
644 	return lastDumpId;
645 }
646 
647 /*
648  * Find a DumpableObject by dump ID
649  *
650  * Returns NULL for invalid ID
651  */
652 DumpableObject *
findObjectByDumpId(DumpId dumpId)653 findObjectByDumpId(DumpId dumpId)
654 {
655 	if (dumpId <= 0 || dumpId >= allocedDumpIds)
656 		return NULL;			/* out of range? */
657 	return dumpIdMap[dumpId];
658 }
659 
660 /*
661  * Find a DumpableObject by catalog ID
662  *
663  * Returns NULL for unknown ID
664  *
665  * We use binary search in a sorted list that is built on first call.
666  * If AssignDumpId() and findObjectByCatalogId() calls were freely intermixed,
667  * the code would work, but possibly be very slow.  In the current usage
668  * pattern that does not happen, indeed we build the list at most twice.
669  */
670 DumpableObject *
findObjectByCatalogId(CatalogId catalogId)671 findObjectByCatalogId(CatalogId catalogId)
672 {
673 	DumpableObject **low;
674 	DumpableObject **high;
675 
676 	if (!catalogIdMapValid)
677 	{
678 		if (catalogIdMap)
679 			free(catalogIdMap);
680 		getDumpableObjects(&catalogIdMap, &numCatalogIds);
681 		if (numCatalogIds > 1)
682 			qsort((void *) catalogIdMap, numCatalogIds,
683 				  sizeof(DumpableObject *), DOCatalogIdCompare);
684 		catalogIdMapValid = true;
685 	}
686 
687 	/*
688 	 * We could use bsearch() here, but the notational cruft of calling
689 	 * bsearch is nearly as bad as doing it ourselves; and the generalized
690 	 * bsearch function is noticeably slower as well.
691 	 */
692 	if (numCatalogIds <= 0)
693 		return NULL;
694 	low = catalogIdMap;
695 	high = catalogIdMap + (numCatalogIds - 1);
696 	while (low <= high)
697 	{
698 		DumpableObject **middle;
699 		int			difference;
700 
701 		middle = low + (high - low) / 2;
702 		/* comparison must match DOCatalogIdCompare, below */
703 		difference = oidcmp((*middle)->catId.oid, catalogId.oid);
704 		if (difference == 0)
705 			difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid);
706 		if (difference == 0)
707 			return *middle;
708 		else if (difference < 0)
709 			low = middle + 1;
710 		else
711 			high = middle - 1;
712 	}
713 	return NULL;
714 }
715 
716 /*
717  * Find a DumpableObject by OID, in a pre-sorted array of one type of object
718  *
719  * Returns NULL for unknown OID
720  */
721 static DumpableObject *
findObjectByOid(Oid oid,DumpableObject ** indexArray,int numObjs)722 findObjectByOid(Oid oid, DumpableObject **indexArray, int numObjs)
723 {
724 	DumpableObject **low;
725 	DumpableObject **high;
726 
727 	/*
728 	 * This is the same as findObjectByCatalogId except we assume we need not
729 	 * look at table OID because the objects are all the same type.
730 	 *
731 	 * We could use bsearch() here, but the notational cruft of calling
732 	 * bsearch is nearly as bad as doing it ourselves; and the generalized
733 	 * bsearch function is noticeably slower as well.
734 	 */
735 	if (numObjs <= 0)
736 		return NULL;
737 	low = indexArray;
738 	high = indexArray + (numObjs - 1);
739 	while (low <= high)
740 	{
741 		DumpableObject **middle;
742 		int			difference;
743 
744 		middle = low + (high - low) / 2;
745 		difference = oidcmp((*middle)->catId.oid, oid);
746 		if (difference == 0)
747 			return *middle;
748 		else if (difference < 0)
749 			low = middle + 1;
750 		else
751 			high = middle - 1;
752 	}
753 	return NULL;
754 }
755 
756 /*
757  * Build an index array of DumpableObject pointers, sorted by OID
758  */
759 static DumpableObject **
buildIndexArray(void * objArray,int numObjs,Size objSize)760 buildIndexArray(void *objArray, int numObjs, Size objSize)
761 {
762 	DumpableObject **ptrs;
763 	int			i;
764 
765 	ptrs = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
766 	for (i = 0; i < numObjs; i++)
767 		ptrs[i] = (DumpableObject *) ((char *) objArray + i * objSize);
768 
769 	/* We can use DOCatalogIdCompare to sort since its first key is OID */
770 	if (numObjs > 1)
771 		qsort((void *) ptrs, numObjs, sizeof(DumpableObject *),
772 			  DOCatalogIdCompare);
773 
774 	return ptrs;
775 }
776 
777 /*
778  * qsort comparator for pointers to DumpableObjects
779  */
780 static int
DOCatalogIdCompare(const void * p1,const void * p2)781 DOCatalogIdCompare(const void *p1, const void *p2)
782 {
783 	const DumpableObject *obj1 = *(DumpableObject *const *) p1;
784 	const DumpableObject *obj2 = *(DumpableObject *const *) p2;
785 	int			cmpval;
786 
787 	/*
788 	 * Compare OID first since it's usually unique, whereas there will only be
789 	 * a few distinct values of tableoid.
790 	 */
791 	cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
792 	if (cmpval == 0)
793 		cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
794 	return cmpval;
795 }
796 
797 /*
798  * Build an array of pointers to all known dumpable objects
799  *
800  * This simply creates a modifiable copy of the internal map.
801  */
802 void
getDumpableObjects(DumpableObject *** objs,int * numObjs)803 getDumpableObjects(DumpableObject ***objs, int *numObjs)
804 {
805 	int			i,
806 				j;
807 
808 	*objs = (DumpableObject **)
809 		pg_malloc(allocedDumpIds * sizeof(DumpableObject *));
810 	j = 0;
811 	for (i = 1; i < allocedDumpIds; i++)
812 	{
813 		if (dumpIdMap[i])
814 			(*objs)[j++] = dumpIdMap[i];
815 	}
816 	*numObjs = j;
817 }
818 
819 /*
820  * Add a dependency link to a DumpableObject
821  *
822  * Note: duplicate dependencies are currently not eliminated
823  */
824 void
addObjectDependency(DumpableObject * dobj,DumpId refId)825 addObjectDependency(DumpableObject *dobj, DumpId refId)
826 {
827 	if (dobj->nDeps >= dobj->allocDeps)
828 	{
829 		if (dobj->allocDeps <= 0)
830 		{
831 			dobj->allocDeps = 16;
832 			dobj->dependencies = (DumpId *)
833 				pg_malloc(dobj->allocDeps * sizeof(DumpId));
834 		}
835 		else
836 		{
837 			dobj->allocDeps *= 2;
838 			dobj->dependencies = (DumpId *)
839 				pg_realloc(dobj->dependencies,
840 						   dobj->allocDeps * sizeof(DumpId));
841 		}
842 	}
843 	dobj->dependencies[dobj->nDeps++] = refId;
844 }
845 
846 /*
847  * Remove a dependency link from a DumpableObject
848  *
849  * If there are multiple links, all are removed
850  */
851 void
removeObjectDependency(DumpableObject * dobj,DumpId refId)852 removeObjectDependency(DumpableObject *dobj, DumpId refId)
853 {
854 	int			i;
855 	int			j = 0;
856 
857 	for (i = 0; i < dobj->nDeps; i++)
858 	{
859 		if (dobj->dependencies[i] != refId)
860 			dobj->dependencies[j++] = dobj->dependencies[i];
861 	}
862 	dobj->nDeps = j;
863 }
864 
865 
866 /*
867  * findTableByOid
868  *	  finds the entry (in tblinfo) of the table with the given oid
869  *	  returns NULL if not found
870  */
871 TableInfo *
findTableByOid(Oid oid)872 findTableByOid(Oid oid)
873 {
874 	return (TableInfo *) findObjectByOid(oid, tblinfoindex, numTables);
875 }
876 
877 /*
878  * findTypeByOid
879  *	  finds the entry (in typinfo) of the type with the given oid
880  *	  returns NULL if not found
881  */
882 TypeInfo *
findTypeByOid(Oid oid)883 findTypeByOid(Oid oid)
884 {
885 	return (TypeInfo *) findObjectByOid(oid, typinfoindex, numTypes);
886 }
887 
888 /*
889  * findFuncByOid
890  *	  finds the entry (in funinfo) of the function with the given oid
891  *	  returns NULL if not found
892  */
893 FuncInfo *
findFuncByOid(Oid oid)894 findFuncByOid(Oid oid)
895 {
896 	return (FuncInfo *) findObjectByOid(oid, funinfoindex, numFuncs);
897 }
898 
899 /*
900  * findOprByOid
901  *	  finds the entry (in oprinfo) of the operator with the given oid
902  *	  returns NULL if not found
903  */
904 OprInfo *
findOprByOid(Oid oid)905 findOprByOid(Oid oid)
906 {
907 	return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators);
908 }
909 
910 /*
911  * findCollationByOid
912  *	  finds the entry (in collinfo) of the collation with the given oid
913  *	  returns NULL if not found
914  */
915 CollInfo *
findCollationByOid(Oid oid)916 findCollationByOid(Oid oid)
917 {
918 	return (CollInfo *) findObjectByOid(oid, collinfoindex, numCollations);
919 }
920 
921 /*
922  * findNamespaceByOid
923  *	  finds the entry (in nspinfo) of the namespace with the given oid
924  *	  returns NULL if not found
925  */
926 NamespaceInfo *
findNamespaceByOid(Oid oid)927 findNamespaceByOid(Oid oid)
928 {
929 	return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces);
930 }
931 
932 /*
933  * findExtensionByOid
934  *	  finds the entry (in extinfo) of the extension with the given oid
935  *	  returns NULL if not found
936  */
937 ExtensionInfo *
findExtensionByOid(Oid oid)938 findExtensionByOid(Oid oid)
939 {
940 	return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
941 }
942 
943 /*
944  * findPublicationByOid
945  *	  finds the entry (in pubinfo) of the publication with the given oid
946  *	  returns NULL if not found
947  */
948 PublicationInfo *
findPublicationByOid(Oid oid)949 findPublicationByOid(Oid oid)
950 {
951 	return (PublicationInfo *) findObjectByOid(oid, pubinfoindex, numPublications);
952 }
953 
954 /*
955  * findIndexByOid
956  *		find the entry of the index with the given oid
957  *
958  * This one's signature is different from the previous ones because we lack a
959  * global array of all indexes, so caller must pass their array as argument.
960  */
961 static IndxInfo *
findIndexByOid(Oid oid,DumpableObject ** idxinfoindex,int numIndexes)962 findIndexByOid(Oid oid, DumpableObject **idxinfoindex, int numIndexes)
963 {
964 	return (IndxInfo *) findObjectByOid(oid, idxinfoindex, numIndexes);
965 }
966 
967 /*
968  * setExtensionMembership
969  *	  accept and save data about which objects belong to extensions
970  */
971 void
setExtensionMembership(ExtensionMemberId * extmems,int nextmems)972 setExtensionMembership(ExtensionMemberId *extmems, int nextmems)
973 {
974 	/* Sort array in preparation for binary searches */
975 	if (nextmems > 1)
976 		qsort((void *) extmems, nextmems, sizeof(ExtensionMemberId),
977 			  ExtensionMemberIdCompare);
978 	/* And save */
979 	extmembers = extmems;
980 	numextmembers = nextmems;
981 }
982 
983 /*
984  * findOwningExtension
985  *	  return owning extension for specified catalog ID, or NULL if none
986  */
987 ExtensionInfo *
findOwningExtension(CatalogId catalogId)988 findOwningExtension(CatalogId catalogId)
989 {
990 	ExtensionMemberId *low;
991 	ExtensionMemberId *high;
992 
993 	/*
994 	 * We could use bsearch() here, but the notational cruft of calling
995 	 * bsearch is nearly as bad as doing it ourselves; and the generalized
996 	 * bsearch function is noticeably slower as well.
997 	 */
998 	if (numextmembers <= 0)
999 		return NULL;
1000 	low = extmembers;
1001 	high = extmembers + (numextmembers - 1);
1002 	while (low <= high)
1003 	{
1004 		ExtensionMemberId *middle;
1005 		int			difference;
1006 
1007 		middle = low + (high - low) / 2;
1008 		/* comparison must match ExtensionMemberIdCompare, below */
1009 		difference = oidcmp(middle->catId.oid, catalogId.oid);
1010 		if (difference == 0)
1011 			difference = oidcmp(middle->catId.tableoid, catalogId.tableoid);
1012 		if (difference == 0)
1013 			return middle->ext;
1014 		else if (difference < 0)
1015 			low = middle + 1;
1016 		else
1017 			high = middle - 1;
1018 	}
1019 	return NULL;
1020 }
1021 
1022 /*
1023  * qsort comparator for ExtensionMemberIds
1024  */
1025 static int
ExtensionMemberIdCompare(const void * p1,const void * p2)1026 ExtensionMemberIdCompare(const void *p1, const void *p2)
1027 {
1028 	const ExtensionMemberId *obj1 = (const ExtensionMemberId *) p1;
1029 	const ExtensionMemberId *obj2 = (const ExtensionMemberId *) p2;
1030 	int			cmpval;
1031 
1032 	/*
1033 	 * Compare OID first since it's usually unique, whereas there will only be
1034 	 * a few distinct values of tableoid.
1035 	 */
1036 	cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
1037 	if (cmpval == 0)
1038 		cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
1039 	return cmpval;
1040 }
1041 
1042 
1043 /*
1044  * findParentsByOid
1045  *	  find a table's parents in tblinfo[]
1046  */
1047 static void
findParentsByOid(TableInfo * self,InhInfo * inhinfo,int numInherits)1048 findParentsByOid(TableInfo *self,
1049 				 InhInfo *inhinfo, int numInherits)
1050 {
1051 	Oid			oid = self->dobj.catId.oid;
1052 	int			i,
1053 				j;
1054 	int			numParents;
1055 
1056 	numParents = 0;
1057 	for (i = 0; i < numInherits; i++)
1058 	{
1059 		if (inhinfo[i].inhrelid == oid)
1060 			numParents++;
1061 	}
1062 
1063 	self->numParents = numParents;
1064 
1065 	if (numParents > 0)
1066 	{
1067 		self->parents = (TableInfo **)
1068 			pg_malloc(sizeof(TableInfo *) * numParents);
1069 		j = 0;
1070 		for (i = 0; i < numInherits; i++)
1071 		{
1072 			if (inhinfo[i].inhrelid == oid)
1073 			{
1074 				TableInfo  *parent;
1075 
1076 				parent = findTableByOid(inhinfo[i].inhparent);
1077 				if (parent == NULL)
1078 				{
1079 					write_msg(NULL, "failed sanity check, parent OID %u of table \"%s\" (OID %u) not found\n",
1080 							  inhinfo[i].inhparent,
1081 							  self->dobj.name,
1082 							  oid);
1083 					exit_nicely(1);
1084 				}
1085 				self->parents[j++] = parent;
1086 			}
1087 		}
1088 	}
1089 	else
1090 		self->parents = NULL;
1091 }
1092 
1093 /*
1094  * parseOidArray
1095  *	  parse a string of numbers delimited by spaces into a character array
1096  *
1097  * Note: actually this is used for both Oids and potentially-signed
1098  * attribute numbers.  This should cause no trouble, but we could split
1099  * the function into two functions with different argument types if it does.
1100  */
1101 
1102 void
parseOidArray(const char * str,Oid * array,int arraysize)1103 parseOidArray(const char *str, Oid *array, int arraysize)
1104 {
1105 	int			j,
1106 				argNum;
1107 	char		temp[100];
1108 	char		s;
1109 
1110 	argNum = 0;
1111 	j = 0;
1112 	for (;;)
1113 	{
1114 		s = *str++;
1115 		if (s == ' ' || s == '\0')
1116 		{
1117 			if (j > 0)
1118 			{
1119 				if (argNum >= arraysize)
1120 				{
1121 					write_msg(NULL, "could not parse numeric array \"%s\": too many numbers\n", str);
1122 					exit_nicely(1);
1123 				}
1124 				temp[j] = '\0';
1125 				array[argNum++] = atooid(temp);
1126 				j = 0;
1127 			}
1128 			if (s == '\0')
1129 				break;
1130 		}
1131 		else
1132 		{
1133 			if (!(isdigit((unsigned char) s) || s == '-') ||
1134 				j >= sizeof(temp) - 1)
1135 			{
1136 				write_msg(NULL, "could not parse numeric array \"%s\": invalid character in number\n", str);
1137 				exit_nicely(1);
1138 			}
1139 			temp[j++] = s;
1140 		}
1141 	}
1142 
1143 	while (argNum < arraysize)
1144 		array[argNum++] = InvalidOid;
1145 }
1146 
1147 
1148 /*
1149  * strInArray:
1150  *	  takes in a string and a string array and the number of elements in the
1151  * string array.
1152  *	  returns the index if the string is somewhere in the array, -1 otherwise
1153  */
1154 
1155 static int
strInArray(const char * pattern,char ** arr,int arr_size)1156 strInArray(const char *pattern, char **arr, int arr_size)
1157 {
1158 	int			i;
1159 
1160 	for (i = 0; i < arr_size; i++)
1161 	{
1162 		if (strcmp(pattern, arr[i]) == 0)
1163 			return i;
1164 	}
1165 	return -1;
1166 }
1167