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