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