1 /*-------------------------------------------------------------------------
2  *
3  * pg_dump_sort.c
4  *	  Sort the items of a dump into a safe order for dumping
5  *
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/pg_dump_sort.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 "catalog/pg_class_d.h"
23 
24 /* translator: this is a module name */
25 static const char *modulename = gettext_noop("sorter");
26 
27 /*
28  * Sort priority for database object types.
29  * Objects are sorted by type, and within a type by name.
30  *
31  * Triggers, event triggers, and materialized views are intentionally sorted
32  * late.  Triggers must be restored after all data modifications, so that
33  * they don't interfere with loading data.  Event triggers are restored
34  * next-to-last so that they don't interfere with object creations of any
35  * kind.  Matview refreshes are last because they should execute in the
36  * database's normal state (e.g., they must come after all ACLs are restored;
37  * also, if they choose to look at system catalogs, they should see the final
38  * restore state).  If you think to change this, see also the RestorePass
39  * mechanism in pg_backup_archiver.c.
40  *
41  * NOTE: object-type priorities must match the section assignments made in
42  * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
43  * POST_DATA objects must sort after DO_POST_DATA_BOUNDARY, and DATA objects
44  * must sort between them.
45  *
46  * Note: sortDataAndIndexObjectsBySize wants to have all DO_TABLE_DATA and
47  * DO_INDEX objects in contiguous chunks, so do not reuse the values for those
48  * for other object types.
49  */
50 static const int dbObjectTypePriority[] =
51 {
52 	1,							/* DO_NAMESPACE */
53 	4,							/* DO_EXTENSION */
54 	5,							/* DO_TYPE */
55 	5,							/* DO_SHELL_TYPE */
56 	6,							/* DO_FUNC */
57 	7,							/* DO_AGG */
58 	8,							/* DO_OPERATOR */
59 	8,							/* DO_ACCESS_METHOD */
60 	9,							/* DO_OPCLASS */
61 	9,							/* DO_OPFAMILY */
62 	3,							/* DO_COLLATION */
63 	11,							/* DO_CONVERSION */
64 	18,							/* DO_TABLE */
65 	20,							/* DO_ATTRDEF */
66 	28,							/* DO_INDEX */
67 	29,							/* DO_INDEX_ATTACH */
68 	30,							/* DO_STATSEXT */
69 	31,							/* DO_RULE */
70 	32,							/* DO_TRIGGER */
71 	27,							/* DO_CONSTRAINT */
72 	33,							/* DO_FK_CONSTRAINT */
73 	2,							/* DO_PROCLANG */
74 	10,							/* DO_CAST */
75 	23,							/* DO_TABLE_DATA */
76 	24,							/* DO_SEQUENCE_SET */
77 	19,							/* DO_DUMMY_TYPE */
78 	12,							/* DO_TSPARSER */
79 	14,							/* DO_TSDICT */
80 	13,							/* DO_TSTEMPLATE */
81 	15,							/* DO_TSCONFIG */
82 	16,							/* DO_FDW */
83 	17,							/* DO_FOREIGN_SERVER */
84 	38,							/* DO_DEFAULT_ACL --- done in ACL pass */
85 	3,							/* DO_TRANSFORM */
86 	21,							/* DO_BLOB */
87 	25,							/* DO_BLOB_DATA */
88 	22,							/* DO_PRE_DATA_BOUNDARY */
89 	26,							/* DO_POST_DATA_BOUNDARY */
90 	39,							/* DO_EVENT_TRIGGER --- next to last! */
91 	40,							/* DO_REFRESH_MATVIEW --- last! */
92 	34,							/* DO_POLICY */
93 	35,							/* DO_PUBLICATION */
94 	36,							/* DO_PUBLICATION_REL */
95 	37							/* DO_SUBSCRIPTION */
96 };
97 
98 static DumpId preDataBoundId;
99 static DumpId postDataBoundId;
100 
101 
102 static int	DOTypeNameCompare(const void *p1, const void *p2);
103 static bool TopoSort(DumpableObject **objs,
104 		 int numObjs,
105 		 DumpableObject **ordering,
106 		 int *nOrdering);
107 static void addHeapElement(int val, int *heap, int heapLength);
108 static int	removeHeapElement(int *heap, int heapLength);
109 static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
110 static int findLoop(DumpableObject *obj,
111 		 DumpId startPoint,
112 		 bool *processed,
113 		 DumpId *searchFailed,
114 		 DumpableObject **workspace,
115 		 int depth);
116 static void repairDependencyLoop(DumpableObject **loop,
117 					 int nLoop);
118 static void describeDumpableObject(DumpableObject *obj,
119 					   char *buf, int bufsize);
120 
121 static int	DOSizeCompare(const void *p1, const void *p2);
122 
123 static int
findFirstEqualType(DumpableObjectType type,DumpableObject ** objs,int numObjs)124 findFirstEqualType(DumpableObjectType type, DumpableObject **objs, int numObjs)
125 {
126 	int			i;
127 
128 	for (i = 0; i < numObjs; i++)
129 		if (objs[i]->objType == type)
130 			return i;
131 	return -1;
132 }
133 
134 static int
findFirstDifferentType(DumpableObjectType type,DumpableObject ** objs,int numObjs,int start)135 findFirstDifferentType(DumpableObjectType type, DumpableObject **objs, int numObjs, int start)
136 {
137 	int			i;
138 
139 	for (i = start; i < numObjs; i++)
140 		if (objs[i]->objType != type)
141 			return i;
142 	return numObjs - 1;
143 }
144 
145 /*
146  * When we do a parallel dump, we want to start with the largest items first.
147  *
148  * Say we have the objects in this order:
149  * ....DDDDD....III....
150  *
151  * with D = Table data, I = Index, . = other object
152  *
153  * This sorting function now takes each of the D or I blocks and sorts them
154  * according to their size.
155  */
156 void
sortDataAndIndexObjectsBySize(DumpableObject ** objs,int numObjs)157 sortDataAndIndexObjectsBySize(DumpableObject **objs, int numObjs)
158 {
159 	int			startIdx,
160 				endIdx;
161 	void	   *startPtr;
162 
163 	if (numObjs <= 1)
164 		return;
165 
166 	startIdx = findFirstEqualType(DO_TABLE_DATA, objs, numObjs);
167 	if (startIdx >= 0)
168 	{
169 		endIdx = findFirstDifferentType(DO_TABLE_DATA, objs, numObjs, startIdx);
170 		startPtr = objs + startIdx;
171 		qsort(startPtr, endIdx - startIdx, sizeof(DumpableObject *),
172 			  DOSizeCompare);
173 	}
174 
175 	startIdx = findFirstEqualType(DO_INDEX, objs, numObjs);
176 	if (startIdx >= 0)
177 	{
178 		endIdx = findFirstDifferentType(DO_INDEX, objs, numObjs, startIdx);
179 		startPtr = objs + startIdx;
180 		qsort(startPtr, endIdx - startIdx, sizeof(DumpableObject *),
181 			  DOSizeCompare);
182 	}
183 }
184 
185 static int
DOSizeCompare(const void * p1,const void * p2)186 DOSizeCompare(const void *p1, const void *p2)
187 {
188 	DumpableObject *obj1 = *(DumpableObject **) p1;
189 	DumpableObject *obj2 = *(DumpableObject **) p2;
190 	int			obj1_size = 0;
191 	int			obj2_size = 0;
192 
193 	if (obj1->objType == DO_TABLE_DATA)
194 		obj1_size = ((TableDataInfo *) obj1)->tdtable->relpages;
195 	if (obj1->objType == DO_INDEX)
196 		obj1_size = ((IndxInfo *) obj1)->relpages;
197 
198 	if (obj2->objType == DO_TABLE_DATA)
199 		obj2_size = ((TableDataInfo *) obj2)->tdtable->relpages;
200 	if (obj2->objType == DO_INDEX)
201 		obj2_size = ((IndxInfo *) obj2)->relpages;
202 
203 	/* we want to see the biggest item go first */
204 	if (obj1_size > obj2_size)
205 		return -1;
206 	if (obj2_size > obj1_size)
207 		return 1;
208 
209 	return 0;
210 }
211 
212 /*
213  * Sort the given objects into a type/name-based ordering
214  *
215  * Normally this is just the starting point for the dependency-based
216  * ordering.
217  */
218 void
sortDumpableObjectsByTypeName(DumpableObject ** objs,int numObjs)219 sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
220 {
221 	if (numObjs > 1)
222 		qsort((void *) objs, numObjs, sizeof(DumpableObject *),
223 			  DOTypeNameCompare);
224 }
225 
226 static int
DOTypeNameCompare(const void * p1,const void * p2)227 DOTypeNameCompare(const void *p1, const void *p2)
228 {
229 	DumpableObject *obj1 = *(DumpableObject *const *) p1;
230 	DumpableObject *obj2 = *(DumpableObject *const *) p2;
231 	int			cmpval;
232 
233 	/* Sort by type's priority */
234 	cmpval = dbObjectTypePriority[obj1->objType] -
235 		dbObjectTypePriority[obj2->objType];
236 
237 	if (cmpval != 0)
238 		return cmpval;
239 
240 	/*
241 	 * Sort by namespace.  Typically, all objects of the same priority would
242 	 * either have or not have a namespace link, but there are exceptions.
243 	 * Sort NULL namespace after non-NULL in such cases.
244 	 */
245 	if (obj1->namespace)
246 	{
247 		if (obj2->namespace)
248 		{
249 			cmpval = strcmp(obj1->namespace->dobj.name,
250 							obj2->namespace->dobj.name);
251 			if (cmpval != 0)
252 				return cmpval;
253 		}
254 		else
255 			return -1;
256 	}
257 	else if (obj2->namespace)
258 		return 1;
259 
260 	/* Sort by name */
261 	cmpval = strcmp(obj1->name, obj2->name);
262 	if (cmpval != 0)
263 		return cmpval;
264 
265 	/* To have a stable sort order, break ties for some object types */
266 	if (obj1->objType == DO_FUNC || obj1->objType == DO_AGG)
267 	{
268 		FuncInfo   *fobj1 = *(FuncInfo *const *) p1;
269 		FuncInfo   *fobj2 = *(FuncInfo *const *) p2;
270 		int			i;
271 
272 		/* Sort by number of arguments, then argument type names */
273 		cmpval = fobj1->nargs - fobj2->nargs;
274 		if (cmpval != 0)
275 			return cmpval;
276 		for (i = 0; i < fobj1->nargs; i++)
277 		{
278 			TypeInfo   *argtype1 = findTypeByOid(fobj1->argtypes[i]);
279 			TypeInfo   *argtype2 = findTypeByOid(fobj2->argtypes[i]);
280 
281 			if (argtype1 && argtype2)
282 			{
283 				if (argtype1->dobj.namespace && argtype2->dobj.namespace)
284 				{
285 					cmpval = strcmp(argtype1->dobj.namespace->dobj.name,
286 									argtype2->dobj.namespace->dobj.name);
287 					if (cmpval != 0)
288 						return cmpval;
289 				}
290 				cmpval = strcmp(argtype1->dobj.name, argtype2->dobj.name);
291 				if (cmpval != 0)
292 					return cmpval;
293 			}
294 		}
295 	}
296 	else if (obj1->objType == DO_OPERATOR)
297 	{
298 		OprInfo    *oobj1 = *(OprInfo *const *) p1;
299 		OprInfo    *oobj2 = *(OprInfo *const *) p2;
300 
301 		/* oprkind is 'l', 'r', or 'b'; this sorts prefix, postfix, infix */
302 		cmpval = (oobj2->oprkind - oobj1->oprkind);
303 		if (cmpval != 0)
304 			return cmpval;
305 	}
306 	else if (obj1->objType == DO_ATTRDEF)
307 	{
308 		AttrDefInfo *adobj1 = *(AttrDefInfo *const *) p1;
309 		AttrDefInfo *adobj2 = *(AttrDefInfo *const *) p2;
310 
311 		/* Sort by attribute number */
312 		cmpval = (adobj1->adnum - adobj2->adnum);
313 		if (cmpval != 0)
314 			return cmpval;
315 	}
316 	else if (obj1->objType == DO_POLICY)
317 	{
318 		PolicyInfo *pobj1 = *(PolicyInfo *const *) p1;
319 		PolicyInfo *pobj2 = *(PolicyInfo *const *) p2;
320 
321 		/* Sort by table name (table namespace was considered already) */
322 		cmpval = strcmp(pobj1->poltable->dobj.name,
323 						pobj2->poltable->dobj.name);
324 		if (cmpval != 0)
325 			return cmpval;
326 	}
327 	else if (obj1->objType == DO_TRIGGER)
328 	{
329 		TriggerInfo *tobj1 = *(TriggerInfo *const *) p1;
330 		TriggerInfo *tobj2 = *(TriggerInfo *const *) p2;
331 
332 		/* Sort by table name (table namespace was considered already) */
333 		cmpval = strcmp(tobj1->tgtable->dobj.name,
334 						tobj2->tgtable->dobj.name);
335 		if (cmpval != 0)
336 			return cmpval;
337 	}
338 
339 	/* Usually shouldn't get here, but if we do, sort by OID */
340 	return oidcmp(obj1->catId.oid, obj2->catId.oid);
341 }
342 
343 
344 /*
345  * Sort the given objects into a safe dump order using dependency
346  * information (to the extent we have it available).
347  *
348  * The DumpIds of the PRE_DATA_BOUNDARY and POST_DATA_BOUNDARY objects are
349  * passed in separately, in case we need them during dependency loop repair.
350  */
351 void
sortDumpableObjects(DumpableObject ** objs,int numObjs,DumpId preBoundaryId,DumpId postBoundaryId)352 sortDumpableObjects(DumpableObject **objs, int numObjs,
353 					DumpId preBoundaryId, DumpId postBoundaryId)
354 {
355 	DumpableObject **ordering;
356 	int			nOrdering;
357 
358 	if (numObjs <= 0)			/* can't happen anymore ... */
359 		return;
360 
361 	/*
362 	 * Saving the boundary IDs in static variables is a bit grotty, but seems
363 	 * better than adding them to parameter lists of subsidiary functions.
364 	 */
365 	preDataBoundId = preBoundaryId;
366 	postDataBoundId = postBoundaryId;
367 
368 	ordering = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
369 	while (!TopoSort(objs, numObjs, ordering, &nOrdering))
370 		findDependencyLoops(ordering, nOrdering, numObjs);
371 
372 	memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
373 
374 	free(ordering);
375 }
376 
377 /*
378  * TopoSort -- topological sort of a dump list
379  *
380  * Generate a re-ordering of the dump list that satisfies all the dependency
381  * constraints shown in the dump list.  (Each such constraint is a fact of a
382  * partial ordering.)  Minimize rearrangement of the list not needed to
383  * achieve the partial ordering.
384  *
385  * The input is the list of numObjs objects in objs[].  This list is not
386  * modified.
387  *
388  * Returns true if able to build an ordering that satisfies all the
389  * constraints, false if not (there are contradictory constraints).
390  *
391  * On success (true result), ordering[] is filled with a sorted array of
392  * DumpableObject pointers, of length equal to the input list length.
393  *
394  * On failure (false result), ordering[] is filled with an unsorted array of
395  * DumpableObject pointers of length *nOrdering, listing the objects that
396  * prevented the sort from being completed.  In general, these objects either
397  * participate directly in a dependency cycle, or are depended on by objects
398  * that are in a cycle.  (The latter objects are not actually problematic,
399  * but it takes further analysis to identify which are which.)
400  *
401  * The caller is responsible for allocating sufficient space at *ordering.
402  */
403 static bool
TopoSort(DumpableObject ** objs,int numObjs,DumpableObject ** ordering,int * nOrdering)404 TopoSort(DumpableObject **objs,
405 		 int numObjs,
406 		 DumpableObject **ordering, /* output argument */
407 		 int *nOrdering)		/* output argument */
408 {
409 	DumpId		maxDumpId = getMaxDumpId();
410 	int		   *pendingHeap;
411 	int		   *beforeConstraints;
412 	int		   *idMap;
413 	DumpableObject *obj;
414 	int			heapLength;
415 	int			i,
416 				j,
417 				k;
418 
419 	/*
420 	 * This is basically the same algorithm shown for topological sorting in
421 	 * Knuth's Volume 1.  However, we would like to minimize unnecessary
422 	 * rearrangement of the input ordering; that is, when we have a choice of
423 	 * which item to output next, we always want to take the one highest in
424 	 * the original list.  Therefore, instead of maintaining an unordered
425 	 * linked list of items-ready-to-output as Knuth does, we maintain a heap
426 	 * of their item numbers, which we can use as a priority queue.  This
427 	 * turns the algorithm from O(N) to O(N log N) because each insertion or
428 	 * removal of a heap item takes O(log N) time.  However, that's still
429 	 * plenty fast enough for this application.
430 	 */
431 
432 	*nOrdering = numObjs;		/* for success return */
433 
434 	/* Eliminate the null case */
435 	if (numObjs <= 0)
436 		return true;
437 
438 	/* Create workspace for the above-described heap */
439 	pendingHeap = (int *) pg_malloc(numObjs * sizeof(int));
440 
441 	/*
442 	 * Scan the constraints, and for each item in the input, generate a count
443 	 * of the number of constraints that say it must be before something else.
444 	 * The count for the item with dumpId j is stored in beforeConstraints[j].
445 	 * We also make a map showing the input-order index of the item with
446 	 * dumpId j.
447 	 */
448 	beforeConstraints = (int *) pg_malloc((maxDumpId + 1) * sizeof(int));
449 	memset(beforeConstraints, 0, (maxDumpId + 1) * sizeof(int));
450 	idMap = (int *) pg_malloc((maxDumpId + 1) * sizeof(int));
451 	for (i = 0; i < numObjs; i++)
452 	{
453 		obj = objs[i];
454 		j = obj->dumpId;
455 		if (j <= 0 || j > maxDumpId)
456 			exit_horribly(modulename, "invalid dumpId %d\n", j);
457 		idMap[j] = i;
458 		for (j = 0; j < obj->nDeps; j++)
459 		{
460 			k = obj->dependencies[j];
461 			if (k <= 0 || k > maxDumpId)
462 				exit_horribly(modulename, "invalid dependency %d\n", k);
463 			beforeConstraints[k]++;
464 		}
465 	}
466 
467 	/*
468 	 * Now initialize the heap of items-ready-to-output by filling it with the
469 	 * indexes of items that already have beforeConstraints[id] == 0.
470 	 *
471 	 * The essential property of a heap is heap[(j-1)/2] >= heap[j] for each j
472 	 * in the range 1..heapLength-1 (note we are using 0-based subscripts
473 	 * here, while the discussion in Knuth assumes 1-based subscripts). So, if
474 	 * we simply enter the indexes into pendingHeap[] in decreasing order, we
475 	 * a-fortiori have the heap invariant satisfied at completion of this
476 	 * loop, and don't need to do any sift-up comparisons.
477 	 */
478 	heapLength = 0;
479 	for (i = numObjs; --i >= 0;)
480 	{
481 		if (beforeConstraints[objs[i]->dumpId] == 0)
482 			pendingHeap[heapLength++] = i;
483 	}
484 
485 	/*--------------------
486 	 * Now emit objects, working backwards in the output list.  At each step,
487 	 * we use the priority heap to select the last item that has no remaining
488 	 * before-constraints.  We remove that item from the heap, output it to
489 	 * ordering[], and decrease the beforeConstraints count of each of the
490 	 * items it was constrained against.  Whenever an item's beforeConstraints
491 	 * count is thereby decreased to zero, we insert it into the priority heap
492 	 * to show that it is a candidate to output.  We are done when the heap
493 	 * becomes empty; if we have output every element then we succeeded,
494 	 * otherwise we failed.
495 	 * i = number of ordering[] entries left to output
496 	 * j = objs[] index of item we are outputting
497 	 * k = temp for scanning constraint list for item j
498 	 *--------------------
499 	 */
500 	i = numObjs;
501 	while (heapLength > 0)
502 	{
503 		/* Select object to output by removing largest heap member */
504 		j = removeHeapElement(pendingHeap, heapLength--);
505 		obj = objs[j];
506 		/* Output candidate to ordering[] */
507 		ordering[--i] = obj;
508 		/* Update beforeConstraints counts of its predecessors */
509 		for (k = 0; k < obj->nDeps; k++)
510 		{
511 			int			id = obj->dependencies[k];
512 
513 			if ((--beforeConstraints[id]) == 0)
514 				addHeapElement(idMap[id], pendingHeap, heapLength++);
515 		}
516 	}
517 
518 	/*
519 	 * If we failed, report the objects that couldn't be output; these are the
520 	 * ones with beforeConstraints[] still nonzero.
521 	 */
522 	if (i != 0)
523 	{
524 		k = 0;
525 		for (j = 1; j <= maxDumpId; j++)
526 		{
527 			if (beforeConstraints[j] != 0)
528 				ordering[k++] = objs[idMap[j]];
529 		}
530 		*nOrdering = k;
531 	}
532 
533 	/* Done */
534 	free(pendingHeap);
535 	free(beforeConstraints);
536 	free(idMap);
537 
538 	return (i == 0);
539 }
540 
541 /*
542  * Add an item to a heap (priority queue)
543  *
544  * heapLength is the current heap size; caller is responsible for increasing
545  * its value after the call.  There must be sufficient storage at *heap.
546  */
547 static void
addHeapElement(int val,int * heap,int heapLength)548 addHeapElement(int val, int *heap, int heapLength)
549 {
550 	int			j;
551 
552 	/*
553 	 * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth is
554 	 * using 1-based array indexes, not 0-based.
555 	 */
556 	j = heapLength;
557 	while (j > 0)
558 	{
559 		int			i = (j - 1) >> 1;
560 
561 		if (val <= heap[i])
562 			break;
563 		heap[j] = heap[i];
564 		j = i;
565 	}
566 	heap[j] = val;
567 }
568 
569 /*
570  * Remove the largest item present in a heap (priority queue)
571  *
572  * heapLength is the current heap size; caller is responsible for decreasing
573  * its value after the call.
574  *
575  * We remove and return heap[0], which is always the largest element of
576  * the heap, and then "sift up" to maintain the heap invariant.
577  */
578 static int
removeHeapElement(int * heap,int heapLength)579 removeHeapElement(int *heap, int heapLength)
580 {
581 	int			result = heap[0];
582 	int			val;
583 	int			i;
584 
585 	if (--heapLength <= 0)
586 		return result;
587 	val = heap[heapLength];		/* value that must be reinserted */
588 	i = 0;						/* i is where the "hole" is */
589 	for (;;)
590 	{
591 		int			j = 2 * i + 1;
592 
593 		if (j >= heapLength)
594 			break;
595 		if (j + 1 < heapLength &&
596 			heap[j] < heap[j + 1])
597 			j++;
598 		if (val >= heap[j])
599 			break;
600 		heap[i] = heap[j];
601 		i = j;
602 	}
603 	heap[i] = val;
604 	return result;
605 }
606 
607 /*
608  * findDependencyLoops - identify loops in TopoSort's failure output,
609  *		and pass each such loop to repairDependencyLoop() for action
610  *
611  * In general there may be many loops in the set of objects returned by
612  * TopoSort; for speed we should try to repair as many loops as we can
613  * before trying TopoSort again.  We can safely repair loops that are
614  * disjoint (have no members in common); if we find overlapping loops
615  * then we repair only the first one found, because the action taken to
616  * repair the first might have repaired the other as well.  (If not,
617  * we'll fix it on the next go-round.)
618  *
619  * objs[] lists the objects TopoSort couldn't sort
620  * nObjs is the number of such objects
621  * totObjs is the total number of objects in the universe
622  */
623 static void
findDependencyLoops(DumpableObject ** objs,int nObjs,int totObjs)624 findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
625 {
626 	/*
627 	 * We use three data structures here:
628 	 *
629 	 * processed[] is a bool array indexed by dump ID, marking the objects
630 	 * already processed during this invocation of findDependencyLoops().
631 	 *
632 	 * searchFailed[] is another array indexed by dump ID.  searchFailed[j] is
633 	 * set to dump ID k if we have proven that there is no dependency path
634 	 * leading from object j back to start point k.  This allows us to skip
635 	 * useless searching when there are multiple dependency paths from k to j,
636 	 * which is a common situation.  We could use a simple bool array for
637 	 * this, but then we'd need to re-zero it for each start point, resulting
638 	 * in O(N^2) zeroing work.  Using the start point's dump ID as the "true"
639 	 * value lets us skip clearing the array before we consider the next start
640 	 * point.
641 	 *
642 	 * workspace[] is an array of DumpableObject pointers, in which we try to
643 	 * build lists of objects constituting loops.  We make workspace[] large
644 	 * enough to hold all the objects in TopoSort's output, which is huge
645 	 * overkill in most cases but could theoretically be necessary if there is
646 	 * a single dependency chain linking all the objects.
647 	 */
648 	bool	   *processed;
649 	DumpId	   *searchFailed;
650 	DumpableObject **workspace;
651 	bool		fixedloop;
652 	int			i;
653 
654 	processed = (bool *) pg_malloc0((getMaxDumpId() + 1) * sizeof(bool));
655 	searchFailed = (DumpId *) pg_malloc0((getMaxDumpId() + 1) * sizeof(DumpId));
656 	workspace = (DumpableObject **) pg_malloc(totObjs * sizeof(DumpableObject *));
657 	fixedloop = false;
658 
659 	for (i = 0; i < nObjs; i++)
660 	{
661 		DumpableObject *obj = objs[i];
662 		int			looplen;
663 		int			j;
664 
665 		looplen = findLoop(obj,
666 						   obj->dumpId,
667 						   processed,
668 						   searchFailed,
669 						   workspace,
670 						   0);
671 
672 		if (looplen > 0)
673 		{
674 			/* Found a loop, repair it */
675 			repairDependencyLoop(workspace, looplen);
676 			fixedloop = true;
677 			/* Mark loop members as processed */
678 			for (j = 0; j < looplen; j++)
679 				processed[workspace[j]->dumpId] = true;
680 		}
681 		else
682 		{
683 			/*
684 			 * There's no loop starting at this object, but mark it processed
685 			 * anyway.  This is not necessary for correctness, but saves later
686 			 * invocations of findLoop() from uselessly chasing references to
687 			 * such an object.
688 			 */
689 			processed[obj->dumpId] = true;
690 		}
691 	}
692 
693 	/* We'd better have fixed at least one loop */
694 	if (!fixedloop)
695 		exit_horribly(modulename, "could not identify dependency loop\n");
696 
697 	free(workspace);
698 	free(searchFailed);
699 	free(processed);
700 }
701 
702 /*
703  * Recursively search for a circular dependency loop that doesn't include
704  * any already-processed objects.
705  *
706  *	obj: object we are examining now
707  *	startPoint: dumpId of starting object for the hoped-for circular loop
708  *	processed[]: flag array marking already-processed objects
709  *	searchFailed[]: flag array marking already-unsuccessfully-visited objects
710  *	workspace[]: work array in which we are building list of loop members
711  *	depth: number of valid entries in workspace[] at call
712  *
713  * On success, the length of the loop is returned, and workspace[] is filled
714  * with pointers to the members of the loop.  On failure, we return 0.
715  *
716  * Note: it is possible that the given starting object is a member of more
717  * than one cycle; if so, we will find an arbitrary one of the cycles.
718  */
719 static int
findLoop(DumpableObject * obj,DumpId startPoint,bool * processed,DumpId * searchFailed,DumpableObject ** workspace,int depth)720 findLoop(DumpableObject *obj,
721 		 DumpId startPoint,
722 		 bool *processed,
723 		 DumpId *searchFailed,
724 		 DumpableObject **workspace,
725 		 int depth)
726 {
727 	int			i;
728 
729 	/*
730 	 * Reject if obj is already processed.  This test prevents us from finding
731 	 * loops that overlap previously-processed loops.
732 	 */
733 	if (processed[obj->dumpId])
734 		return 0;
735 
736 	/*
737 	 * If we've already proven there is no path from this object back to the
738 	 * startPoint, forget it.
739 	 */
740 	if (searchFailed[obj->dumpId] == startPoint)
741 		return 0;
742 
743 	/*
744 	 * Reject if obj is already present in workspace.  This test prevents us
745 	 * from going into infinite recursion if we are given a startPoint object
746 	 * that links to a cycle it's not a member of, and it guarantees that we
747 	 * can't overflow the allocated size of workspace[].
748 	 */
749 	for (i = 0; i < depth; i++)
750 	{
751 		if (workspace[i] == obj)
752 			return 0;
753 	}
754 
755 	/*
756 	 * Okay, tentatively add obj to workspace
757 	 */
758 	workspace[depth++] = obj;
759 
760 	/*
761 	 * See if we've found a loop back to the desired startPoint; if so, done
762 	 */
763 	for (i = 0; i < obj->nDeps; i++)
764 	{
765 		if (obj->dependencies[i] == startPoint)
766 			return depth;
767 	}
768 
769 	/*
770 	 * Recurse down each outgoing branch
771 	 */
772 	for (i = 0; i < obj->nDeps; i++)
773 	{
774 		DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
775 		int			newDepth;
776 
777 		if (!nextobj)
778 			continue;			/* ignore dependencies on undumped objects */
779 		newDepth = findLoop(nextobj,
780 							startPoint,
781 							processed,
782 							searchFailed,
783 							workspace,
784 							depth);
785 		if (newDepth > 0)
786 			return newDepth;
787 	}
788 
789 	/*
790 	 * Remember there is no path from here back to startPoint
791 	 */
792 	searchFailed[obj->dumpId] = startPoint;
793 
794 	return 0;
795 }
796 
797 /*
798  * A user-defined datatype will have a dependency loop with each of its
799  * I/O functions (since those have the datatype as input or output).
800  * Similarly, a range type will have a loop with its canonicalize function,
801  * if any.  Break the loop by making the function depend on the associated
802  * shell type, instead.
803  */
804 static void
repairTypeFuncLoop(DumpableObject * typeobj,DumpableObject * funcobj)805 repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
806 {
807 	TypeInfo   *typeInfo = (TypeInfo *) typeobj;
808 
809 	/* remove function's dependency on type */
810 	removeObjectDependency(funcobj, typeobj->dumpId);
811 
812 	/* add function's dependency on shell type, instead */
813 	if (typeInfo->shellType)
814 	{
815 		addObjectDependency(funcobj, typeInfo->shellType->dobj.dumpId);
816 
817 		/*
818 		 * Mark shell type (always including the definition, as we need the
819 		 * shell type defined to identify the function fully) as to be dumped
820 		 * if any such function is
821 		 */
822 		if (funcobj->dump)
823 			typeInfo->shellType->dobj.dump = funcobj->dump |
824 				DUMP_COMPONENT_DEFINITION;
825 	}
826 }
827 
828 /*
829  * Because we force a view to depend on its ON SELECT rule, while there
830  * will be an implicit dependency in the other direction, we need to break
831  * the loop.  If there are no other objects in the loop then we can remove
832  * the implicit dependency and leave the ON SELECT rule non-separate.
833  * This applies to matviews, as well.
834  */
835 static void
repairViewRuleLoop(DumpableObject * viewobj,DumpableObject * ruleobj)836 repairViewRuleLoop(DumpableObject *viewobj,
837 				   DumpableObject *ruleobj)
838 {
839 	/* remove rule's dependency on view */
840 	removeObjectDependency(ruleobj, viewobj->dumpId);
841 	/* flags on the two objects are already set correctly for this case */
842 }
843 
844 /*
845  * However, if there are other objects in the loop, we must break the loop
846  * by making the ON SELECT rule a separately-dumped object.
847  *
848  * Because findLoop() finds shorter cycles before longer ones, it's likely
849  * that we will have previously fired repairViewRuleLoop() and removed the
850  * rule's dependency on the view.  Put it back to ensure the rule won't be
851  * emitted before the view.
852  *
853  * Note: this approach does *not* work for matviews, at the moment.
854  */
855 static void
repairViewRuleMultiLoop(DumpableObject * viewobj,DumpableObject * ruleobj)856 repairViewRuleMultiLoop(DumpableObject *viewobj,
857 						DumpableObject *ruleobj)
858 {
859 	TableInfo  *viewinfo = (TableInfo *) viewobj;
860 	RuleInfo   *ruleinfo = (RuleInfo *) ruleobj;
861 
862 	/* remove view's dependency on rule */
863 	removeObjectDependency(viewobj, ruleobj->dumpId);
864 	/* mark view to be printed with a dummy definition */
865 	viewinfo->dummy_view = true;
866 	/* mark rule as needing its own dump */
867 	ruleinfo->separate = true;
868 	/* put back rule's dependency on view */
869 	addObjectDependency(ruleobj, viewobj->dumpId);
870 	/* now that rule is separate, it must be post-data */
871 	addObjectDependency(ruleobj, postDataBoundId);
872 }
873 
874 /*
875  * If a matview is involved in a multi-object loop, we can't currently fix
876  * that by splitting off the rule.  As a stopgap, we try to fix it by
877  * dropping the constraint that the matview be dumped in the pre-data section.
878  * This is sufficient to handle cases where a matview depends on some unique
879  * index, as can happen if it has a GROUP BY for example.
880  *
881  * Note that the "next object" is not necessarily the matview itself;
882  * it could be the matview's rowtype, for example.  We may come through here
883  * several times while removing all the pre-data linkages.  In particular,
884  * if there are other matviews that depend on the one with the circularity
885  * problem, we'll come through here for each such matview and mark them all
886  * as postponed.  (This works because all MVs have pre-data dependencies
887  * to begin with, so each of them will get visited.)
888  */
889 static void
repairMatViewBoundaryMultiLoop(DumpableObject * boundaryobj,DumpableObject * nextobj)890 repairMatViewBoundaryMultiLoop(DumpableObject *boundaryobj,
891 							   DumpableObject *nextobj)
892 {
893 	/* remove boundary's dependency on object after it in loop */
894 	removeObjectDependency(boundaryobj, nextobj->dumpId);
895 	/* if that object is a matview, mark it as postponed into post-data */
896 	if (nextobj->objType == DO_TABLE)
897 	{
898 		TableInfo  *nextinfo = (TableInfo *) nextobj;
899 
900 		if (nextinfo->relkind == RELKIND_MATVIEW)
901 			nextinfo->postponed_def = true;
902 	}
903 }
904 
905 /*
906  * Because we make tables depend on their CHECK constraints, while there
907  * will be an automatic dependency in the other direction, we need to break
908  * the loop.  If there are no other objects in the loop then we can remove
909  * the automatic dependency and leave the CHECK constraint non-separate.
910  */
911 static void
repairTableConstraintLoop(DumpableObject * tableobj,DumpableObject * constraintobj)912 repairTableConstraintLoop(DumpableObject *tableobj,
913 						  DumpableObject *constraintobj)
914 {
915 	/* remove constraint's dependency on table */
916 	removeObjectDependency(constraintobj, tableobj->dumpId);
917 }
918 
919 /*
920  * However, if there are other objects in the loop, we must break the loop
921  * by making the CHECK constraint a separately-dumped object.
922  *
923  * Because findLoop() finds shorter cycles before longer ones, it's likely
924  * that we will have previously fired repairTableConstraintLoop() and
925  * removed the constraint's dependency on the table.  Put it back to ensure
926  * the constraint won't be emitted before the table...
927  */
928 static void
repairTableConstraintMultiLoop(DumpableObject * tableobj,DumpableObject * constraintobj)929 repairTableConstraintMultiLoop(DumpableObject *tableobj,
930 							   DumpableObject *constraintobj)
931 {
932 	/* remove table's dependency on constraint */
933 	removeObjectDependency(tableobj, constraintobj->dumpId);
934 	/* mark constraint as needing its own dump */
935 	((ConstraintInfo *) constraintobj)->separate = true;
936 	/* put back constraint's dependency on table */
937 	addObjectDependency(constraintobj, tableobj->dumpId);
938 	/* now that constraint is separate, it must be post-data */
939 	addObjectDependency(constraintobj, postDataBoundId);
940 }
941 
942 /*
943  * Attribute defaults behave exactly the same as CHECK constraints...
944  */
945 static void
repairTableAttrDefLoop(DumpableObject * tableobj,DumpableObject * attrdefobj)946 repairTableAttrDefLoop(DumpableObject *tableobj,
947 					   DumpableObject *attrdefobj)
948 {
949 	/* remove attrdef's dependency on table */
950 	removeObjectDependency(attrdefobj, tableobj->dumpId);
951 }
952 
953 static void
repairTableAttrDefMultiLoop(DumpableObject * tableobj,DumpableObject * attrdefobj)954 repairTableAttrDefMultiLoop(DumpableObject *tableobj,
955 							DumpableObject *attrdefobj)
956 {
957 	/* remove table's dependency on attrdef */
958 	removeObjectDependency(tableobj, attrdefobj->dumpId);
959 	/* mark attrdef as needing its own dump */
960 	((AttrDefInfo *) attrdefobj)->separate = true;
961 	/* put back attrdef's dependency on table */
962 	addObjectDependency(attrdefobj, tableobj->dumpId);
963 }
964 
965 /*
966  * CHECK constraints on domains work just like those on tables ...
967  */
968 static void
repairDomainConstraintLoop(DumpableObject * domainobj,DumpableObject * constraintobj)969 repairDomainConstraintLoop(DumpableObject *domainobj,
970 						   DumpableObject *constraintobj)
971 {
972 	/* remove constraint's dependency on domain */
973 	removeObjectDependency(constraintobj, domainobj->dumpId);
974 }
975 
976 static void
repairDomainConstraintMultiLoop(DumpableObject * domainobj,DumpableObject * constraintobj)977 repairDomainConstraintMultiLoop(DumpableObject *domainobj,
978 								DumpableObject *constraintobj)
979 {
980 	/* remove domain's dependency on constraint */
981 	removeObjectDependency(domainobj, constraintobj->dumpId);
982 	/* mark constraint as needing its own dump */
983 	((ConstraintInfo *) constraintobj)->separate = true;
984 	/* put back constraint's dependency on domain */
985 	addObjectDependency(constraintobj, domainobj->dumpId);
986 	/* now that constraint is separate, it must be post-data */
987 	addObjectDependency(constraintobj, postDataBoundId);
988 }
989 
990 static void
repairIndexLoop(DumpableObject * partedindex,DumpableObject * partindex)991 repairIndexLoop(DumpableObject *partedindex,
992 				DumpableObject *partindex)
993 {
994 	removeObjectDependency(partedindex, partindex->dumpId);
995 }
996 
997 /*
998  * Fix a dependency loop, or die trying ...
999  *
1000  * This routine is mainly concerned with reducing the multiple ways that
1001  * a loop might appear to common cases, which it passes off to the
1002  * "fixer" routines above.
1003  */
1004 static void
repairDependencyLoop(DumpableObject ** loop,int nLoop)1005 repairDependencyLoop(DumpableObject **loop,
1006 					 int nLoop)
1007 {
1008 	int			i,
1009 				j;
1010 
1011 	/* Datatype and one of its I/O or canonicalize functions */
1012 	if (nLoop == 2 &&
1013 		loop[0]->objType == DO_TYPE &&
1014 		loop[1]->objType == DO_FUNC)
1015 	{
1016 		repairTypeFuncLoop(loop[0], loop[1]);
1017 		return;
1018 	}
1019 	if (nLoop == 2 &&
1020 		loop[1]->objType == DO_TYPE &&
1021 		loop[0]->objType == DO_FUNC)
1022 	{
1023 		repairTypeFuncLoop(loop[1], loop[0]);
1024 		return;
1025 	}
1026 
1027 	/* View (including matview) and its ON SELECT rule */
1028 	if (nLoop == 2 &&
1029 		loop[0]->objType == DO_TABLE &&
1030 		loop[1]->objType == DO_RULE &&
1031 		(((TableInfo *) loop[0])->relkind == RELKIND_VIEW ||
1032 		 ((TableInfo *) loop[0])->relkind == RELKIND_MATVIEW) &&
1033 		((RuleInfo *) loop[1])->ev_type == '1' &&
1034 		((RuleInfo *) loop[1])->is_instead &&
1035 		((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
1036 	{
1037 		repairViewRuleLoop(loop[0], loop[1]);
1038 		return;
1039 	}
1040 	if (nLoop == 2 &&
1041 		loop[1]->objType == DO_TABLE &&
1042 		loop[0]->objType == DO_RULE &&
1043 		(((TableInfo *) loop[1])->relkind == RELKIND_VIEW ||
1044 		 ((TableInfo *) loop[1])->relkind == RELKIND_MATVIEW) &&
1045 		((RuleInfo *) loop[0])->ev_type == '1' &&
1046 		((RuleInfo *) loop[0])->is_instead &&
1047 		((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
1048 	{
1049 		repairViewRuleLoop(loop[1], loop[0]);
1050 		return;
1051 	}
1052 
1053 	/* Indirect loop involving view (but not matview) and ON SELECT rule */
1054 	if (nLoop > 2)
1055 	{
1056 		for (i = 0; i < nLoop; i++)
1057 		{
1058 			if (loop[i]->objType == DO_TABLE &&
1059 				((TableInfo *) loop[i])->relkind == RELKIND_VIEW)
1060 			{
1061 				for (j = 0; j < nLoop; j++)
1062 				{
1063 					if (loop[j]->objType == DO_RULE &&
1064 						((RuleInfo *) loop[j])->ev_type == '1' &&
1065 						((RuleInfo *) loop[j])->is_instead &&
1066 						((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
1067 					{
1068 						repairViewRuleMultiLoop(loop[i], loop[j]);
1069 						return;
1070 					}
1071 				}
1072 			}
1073 		}
1074 	}
1075 
1076 	/* Indirect loop involving matview and data boundary */
1077 	if (nLoop > 2)
1078 	{
1079 		for (i = 0; i < nLoop; i++)
1080 		{
1081 			if (loop[i]->objType == DO_TABLE &&
1082 				((TableInfo *) loop[i])->relkind == RELKIND_MATVIEW)
1083 			{
1084 				for (j = 0; j < nLoop; j++)
1085 				{
1086 					if (loop[j]->objType == DO_PRE_DATA_BOUNDARY)
1087 					{
1088 						DumpableObject *nextobj;
1089 
1090 						nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
1091 						repairMatViewBoundaryMultiLoop(loop[j], nextobj);
1092 						return;
1093 					}
1094 				}
1095 			}
1096 		}
1097 	}
1098 
1099 	/* Table and CHECK constraint */
1100 	if (nLoop == 2 &&
1101 		loop[0]->objType == DO_TABLE &&
1102 		loop[1]->objType == DO_CONSTRAINT &&
1103 		((ConstraintInfo *) loop[1])->contype == 'c' &&
1104 		((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
1105 	{
1106 		repairTableConstraintLoop(loop[0], loop[1]);
1107 		return;
1108 	}
1109 	if (nLoop == 2 &&
1110 		loop[1]->objType == DO_TABLE &&
1111 		loop[0]->objType == DO_CONSTRAINT &&
1112 		((ConstraintInfo *) loop[0])->contype == 'c' &&
1113 		((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
1114 	{
1115 		repairTableConstraintLoop(loop[1], loop[0]);
1116 		return;
1117 	}
1118 
1119 	/* Indirect loop involving table and CHECK constraint */
1120 	if (nLoop > 2)
1121 	{
1122 		for (i = 0; i < nLoop; i++)
1123 		{
1124 			if (loop[i]->objType == DO_TABLE)
1125 			{
1126 				for (j = 0; j < nLoop; j++)
1127 				{
1128 					if (loop[j]->objType == DO_CONSTRAINT &&
1129 						((ConstraintInfo *) loop[j])->contype == 'c' &&
1130 						((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
1131 					{
1132 						repairTableConstraintMultiLoop(loop[i], loop[j]);
1133 						return;
1134 					}
1135 				}
1136 			}
1137 		}
1138 	}
1139 
1140 	/* Table and attribute default */
1141 	if (nLoop == 2 &&
1142 		loop[0]->objType == DO_TABLE &&
1143 		loop[1]->objType == DO_ATTRDEF &&
1144 		((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
1145 	{
1146 		repairTableAttrDefLoop(loop[0], loop[1]);
1147 		return;
1148 	}
1149 	if (nLoop == 2 &&
1150 		loop[1]->objType == DO_TABLE &&
1151 		loop[0]->objType == DO_ATTRDEF &&
1152 		((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
1153 	{
1154 		repairTableAttrDefLoop(loop[1], loop[0]);
1155 		return;
1156 	}
1157 
1158 	/* index on partitioned table and corresponding index on partition */
1159 	if (nLoop == 2 &&
1160 		loop[0]->objType == DO_INDEX &&
1161 		loop[1]->objType == DO_INDEX)
1162 	{
1163 		if (((IndxInfo *) loop[0])->parentidx == loop[1]->catId.oid)
1164 		{
1165 			repairIndexLoop(loop[0], loop[1]);
1166 			return;
1167 		}
1168 		else if (((IndxInfo *) loop[1])->parentidx == loop[0]->catId.oid)
1169 		{
1170 			repairIndexLoop(loop[1], loop[0]);
1171 			return;
1172 		}
1173 	}
1174 
1175 	/* Indirect loop involving table and attribute default */
1176 	if (nLoop > 2)
1177 	{
1178 		for (i = 0; i < nLoop; i++)
1179 		{
1180 			if (loop[i]->objType == DO_TABLE)
1181 			{
1182 				for (j = 0; j < nLoop; j++)
1183 				{
1184 					if (loop[j]->objType == DO_ATTRDEF &&
1185 						((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
1186 					{
1187 						repairTableAttrDefMultiLoop(loop[i], loop[j]);
1188 						return;
1189 					}
1190 				}
1191 			}
1192 		}
1193 	}
1194 
1195 	/* Domain and CHECK constraint */
1196 	if (nLoop == 2 &&
1197 		loop[0]->objType == DO_TYPE &&
1198 		loop[1]->objType == DO_CONSTRAINT &&
1199 		((ConstraintInfo *) loop[1])->contype == 'c' &&
1200 		((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
1201 	{
1202 		repairDomainConstraintLoop(loop[0], loop[1]);
1203 		return;
1204 	}
1205 	if (nLoop == 2 &&
1206 		loop[1]->objType == DO_TYPE &&
1207 		loop[0]->objType == DO_CONSTRAINT &&
1208 		((ConstraintInfo *) loop[0])->contype == 'c' &&
1209 		((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
1210 	{
1211 		repairDomainConstraintLoop(loop[1], loop[0]);
1212 		return;
1213 	}
1214 
1215 	/* Indirect loop involving domain and CHECK constraint */
1216 	if (nLoop > 2)
1217 	{
1218 		for (i = 0; i < nLoop; i++)
1219 		{
1220 			if (loop[i]->objType == DO_TYPE)
1221 			{
1222 				for (j = 0; j < nLoop; j++)
1223 				{
1224 					if (loop[j]->objType == DO_CONSTRAINT &&
1225 						((ConstraintInfo *) loop[j])->contype == 'c' &&
1226 						((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
1227 					{
1228 						repairDomainConstraintMultiLoop(loop[i], loop[j]);
1229 						return;
1230 					}
1231 				}
1232 			}
1233 		}
1234 	}
1235 
1236 	/*
1237 	 * Loop of table with itself --- just ignore it.
1238 	 *
1239 	 * (Actually, what this arises from is a dependency of a table column on
1240 	 * another column, which happens with generated columns; or a dependency
1241 	 * of a table column on the whole table, which happens with partitioning.
1242 	 * But we didn't pay attention to sub-object IDs while collecting the
1243 	 * dependency data, so we can't see that here.)
1244 	 */
1245 	if (nLoop == 1)
1246 	{
1247 		if (loop[0]->objType == DO_TABLE)
1248 		{
1249 			removeObjectDependency(loop[0], loop[0]->dumpId);
1250 			return;
1251 		}
1252 	}
1253 
1254 	/*
1255 	 * If all the objects are TABLE_DATA items, what we must have is a
1256 	 * circular set of foreign key constraints (or a single self-referential
1257 	 * table).  Print an appropriate complaint and break the loop arbitrarily.
1258 	 */
1259 	for (i = 0; i < nLoop; i++)
1260 	{
1261 		if (loop[i]->objType != DO_TABLE_DATA)
1262 			break;
1263 	}
1264 	if (i >= nLoop)
1265 	{
1266 		write_msg(NULL, ngettext("NOTICE: there are circular foreign-key constraints on this table:\n",
1267 								 "NOTICE: there are circular foreign-key constraints among these tables:\n",
1268 								 nLoop));
1269 		for (i = 0; i < nLoop; i++)
1270 			write_msg(NULL, "  %s\n", loop[i]->name);
1271 		write_msg(NULL, "You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.\n");
1272 		write_msg(NULL, "Consider using a full dump instead of a --data-only dump to avoid this problem.\n");
1273 		if (nLoop > 1)
1274 			removeObjectDependency(loop[0], loop[1]->dumpId);
1275 		else					/* must be a self-dependency */
1276 			removeObjectDependency(loop[0], loop[0]->dumpId);
1277 		return;
1278 	}
1279 
1280 	/*
1281 	 * If we can't find a principled way to break the loop, complain and break
1282 	 * it in an arbitrary fashion.
1283 	 */
1284 	write_msg(modulename, "WARNING: could not resolve dependency loop among these items:\n");
1285 	for (i = 0; i < nLoop; i++)
1286 	{
1287 		char		buf[1024];
1288 
1289 		describeDumpableObject(loop[i], buf, sizeof(buf));
1290 		write_msg(modulename, "  %s\n", buf);
1291 	}
1292 
1293 	if (nLoop > 1)
1294 		removeObjectDependency(loop[0], loop[1]->dumpId);
1295 	else						/* must be a self-dependency */
1296 		removeObjectDependency(loop[0], loop[0]->dumpId);
1297 }
1298 
1299 /*
1300  * Describe a dumpable object usefully for errors
1301  *
1302  * This should probably go somewhere else...
1303  */
1304 static void
describeDumpableObject(DumpableObject * obj,char * buf,int bufsize)1305 describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
1306 {
1307 	switch (obj->objType)
1308 	{
1309 		case DO_NAMESPACE:
1310 			snprintf(buf, bufsize,
1311 					 "SCHEMA %s  (ID %d OID %u)",
1312 					 obj->name, obj->dumpId, obj->catId.oid);
1313 			return;
1314 		case DO_EXTENSION:
1315 			snprintf(buf, bufsize,
1316 					 "EXTENSION %s  (ID %d OID %u)",
1317 					 obj->name, obj->dumpId, obj->catId.oid);
1318 			return;
1319 		case DO_TYPE:
1320 			snprintf(buf, bufsize,
1321 					 "TYPE %s  (ID %d OID %u)",
1322 					 obj->name, obj->dumpId, obj->catId.oid);
1323 			return;
1324 		case DO_SHELL_TYPE:
1325 			snprintf(buf, bufsize,
1326 					 "SHELL TYPE %s  (ID %d OID %u)",
1327 					 obj->name, obj->dumpId, obj->catId.oid);
1328 			return;
1329 		case DO_FUNC:
1330 			snprintf(buf, bufsize,
1331 					 "FUNCTION %s  (ID %d OID %u)",
1332 					 obj->name, obj->dumpId, obj->catId.oid);
1333 			return;
1334 		case DO_AGG:
1335 			snprintf(buf, bufsize,
1336 					 "AGGREGATE %s  (ID %d OID %u)",
1337 					 obj->name, obj->dumpId, obj->catId.oid);
1338 			return;
1339 		case DO_OPERATOR:
1340 			snprintf(buf, bufsize,
1341 					 "OPERATOR %s  (ID %d OID %u)",
1342 					 obj->name, obj->dumpId, obj->catId.oid);
1343 			return;
1344 		case DO_ACCESS_METHOD:
1345 			snprintf(buf, bufsize,
1346 					 "ACCESS METHOD %s  (ID %d OID %u)",
1347 					 obj->name, obj->dumpId, obj->catId.oid);
1348 			return;
1349 		case DO_OPCLASS:
1350 			snprintf(buf, bufsize,
1351 					 "OPERATOR CLASS %s  (ID %d OID %u)",
1352 					 obj->name, obj->dumpId, obj->catId.oid);
1353 			return;
1354 		case DO_OPFAMILY:
1355 			snprintf(buf, bufsize,
1356 					 "OPERATOR FAMILY %s  (ID %d OID %u)",
1357 					 obj->name, obj->dumpId, obj->catId.oid);
1358 			return;
1359 		case DO_COLLATION:
1360 			snprintf(buf, bufsize,
1361 					 "COLLATION %s  (ID %d OID %u)",
1362 					 obj->name, obj->dumpId, obj->catId.oid);
1363 			return;
1364 		case DO_CONVERSION:
1365 			snprintf(buf, bufsize,
1366 					 "CONVERSION %s  (ID %d OID %u)",
1367 					 obj->name, obj->dumpId, obj->catId.oid);
1368 			return;
1369 		case DO_TABLE:
1370 			snprintf(buf, bufsize,
1371 					 "TABLE %s  (ID %d OID %u)",
1372 					 obj->name, obj->dumpId, obj->catId.oid);
1373 			return;
1374 		case DO_ATTRDEF:
1375 			snprintf(buf, bufsize,
1376 					 "ATTRDEF %s.%s  (ID %d OID %u)",
1377 					 ((AttrDefInfo *) obj)->adtable->dobj.name,
1378 					 ((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
1379 					 obj->dumpId, obj->catId.oid);
1380 			return;
1381 		case DO_INDEX:
1382 			snprintf(buf, bufsize,
1383 					 "INDEX %s  (ID %d OID %u)",
1384 					 obj->name, obj->dumpId, obj->catId.oid);
1385 			return;
1386 		case DO_INDEX_ATTACH:
1387 			snprintf(buf, bufsize,
1388 					 "INDEX ATTACH %s  (ID %d)",
1389 					 obj->name, obj->dumpId);
1390 			return;
1391 		case DO_STATSEXT:
1392 			snprintf(buf, bufsize,
1393 					 "STATISTICS %s  (ID %d OID %u)",
1394 					 obj->name, obj->dumpId, obj->catId.oid);
1395 			return;
1396 		case DO_REFRESH_MATVIEW:
1397 			snprintf(buf, bufsize,
1398 					 "REFRESH MATERIALIZED VIEW %s  (ID %d OID %u)",
1399 					 obj->name, obj->dumpId, obj->catId.oid);
1400 			return;
1401 		case DO_RULE:
1402 			snprintf(buf, bufsize,
1403 					 "RULE %s  (ID %d OID %u)",
1404 					 obj->name, obj->dumpId, obj->catId.oid);
1405 			return;
1406 		case DO_TRIGGER:
1407 			snprintf(buf, bufsize,
1408 					 "TRIGGER %s  (ID %d OID %u)",
1409 					 obj->name, obj->dumpId, obj->catId.oid);
1410 			return;
1411 		case DO_EVENT_TRIGGER:
1412 			snprintf(buf, bufsize,
1413 					 "EVENT TRIGGER %s (ID %d OID %u)",
1414 					 obj->name, obj->dumpId, obj->catId.oid);
1415 			return;
1416 		case DO_CONSTRAINT:
1417 			snprintf(buf, bufsize,
1418 					 "CONSTRAINT %s  (ID %d OID %u)",
1419 					 obj->name, obj->dumpId, obj->catId.oid);
1420 			return;
1421 		case DO_FK_CONSTRAINT:
1422 			snprintf(buf, bufsize,
1423 					 "FK CONSTRAINT %s  (ID %d OID %u)",
1424 					 obj->name, obj->dumpId, obj->catId.oid);
1425 			return;
1426 		case DO_PROCLANG:
1427 			snprintf(buf, bufsize,
1428 					 "PROCEDURAL LANGUAGE %s  (ID %d OID %u)",
1429 					 obj->name, obj->dumpId, obj->catId.oid);
1430 			return;
1431 		case DO_CAST:
1432 			snprintf(buf, bufsize,
1433 					 "CAST %u to %u  (ID %d OID %u)",
1434 					 ((CastInfo *) obj)->castsource,
1435 					 ((CastInfo *) obj)->casttarget,
1436 					 obj->dumpId, obj->catId.oid);
1437 			return;
1438 		case DO_TRANSFORM:
1439 			snprintf(buf, bufsize,
1440 					 "TRANSFORM %u lang %u  (ID %d OID %u)",
1441 					 ((TransformInfo *) obj)->trftype,
1442 					 ((TransformInfo *) obj)->trflang,
1443 					 obj->dumpId, obj->catId.oid);
1444 			return;
1445 		case DO_TABLE_DATA:
1446 			snprintf(buf, bufsize,
1447 					 "TABLE DATA %s  (ID %d OID %u)",
1448 					 obj->name, obj->dumpId, obj->catId.oid);
1449 			return;
1450 		case DO_SEQUENCE_SET:
1451 			snprintf(buf, bufsize,
1452 					 "SEQUENCE SET %s  (ID %d OID %u)",
1453 					 obj->name, obj->dumpId, obj->catId.oid);
1454 			return;
1455 		case DO_DUMMY_TYPE:
1456 			snprintf(buf, bufsize,
1457 					 "DUMMY TYPE %s  (ID %d OID %u)",
1458 					 obj->name, obj->dumpId, obj->catId.oid);
1459 			return;
1460 		case DO_TSPARSER:
1461 			snprintf(buf, bufsize,
1462 					 "TEXT SEARCH PARSER %s  (ID %d OID %u)",
1463 					 obj->name, obj->dumpId, obj->catId.oid);
1464 			return;
1465 		case DO_TSDICT:
1466 			snprintf(buf, bufsize,
1467 					 "TEXT SEARCH DICTIONARY %s  (ID %d OID %u)",
1468 					 obj->name, obj->dumpId, obj->catId.oid);
1469 			return;
1470 		case DO_TSTEMPLATE:
1471 			snprintf(buf, bufsize,
1472 					 "TEXT SEARCH TEMPLATE %s  (ID %d OID %u)",
1473 					 obj->name, obj->dumpId, obj->catId.oid);
1474 			return;
1475 		case DO_TSCONFIG:
1476 			snprintf(buf, bufsize,
1477 					 "TEXT SEARCH CONFIGURATION %s  (ID %d OID %u)",
1478 					 obj->name, obj->dumpId, obj->catId.oid);
1479 			return;
1480 		case DO_FDW:
1481 			snprintf(buf, bufsize,
1482 					 "FOREIGN DATA WRAPPER %s  (ID %d OID %u)",
1483 					 obj->name, obj->dumpId, obj->catId.oid);
1484 			return;
1485 		case DO_FOREIGN_SERVER:
1486 			snprintf(buf, bufsize,
1487 					 "FOREIGN SERVER %s  (ID %d OID %u)",
1488 					 obj->name, obj->dumpId, obj->catId.oid);
1489 			return;
1490 		case DO_DEFAULT_ACL:
1491 			snprintf(buf, bufsize,
1492 					 "DEFAULT ACL %s  (ID %d OID %u)",
1493 					 obj->name, obj->dumpId, obj->catId.oid);
1494 			return;
1495 		case DO_BLOB:
1496 			snprintf(buf, bufsize,
1497 					 "BLOB  (ID %d OID %u)",
1498 					 obj->dumpId, obj->catId.oid);
1499 			return;
1500 		case DO_BLOB_DATA:
1501 			snprintf(buf, bufsize,
1502 					 "BLOB DATA  (ID %d)",
1503 					 obj->dumpId);
1504 			return;
1505 		case DO_POLICY:
1506 			snprintf(buf, bufsize,
1507 					 "POLICY (ID %d OID %u)",
1508 					 obj->dumpId, obj->catId.oid);
1509 			return;
1510 		case DO_PUBLICATION:
1511 			snprintf(buf, bufsize,
1512 					 "PUBLICATION (ID %d OID %u)",
1513 					 obj->dumpId, obj->catId.oid);
1514 			return;
1515 		case DO_PUBLICATION_REL:
1516 			snprintf(buf, bufsize,
1517 					 "PUBLICATION TABLE (ID %d OID %u)",
1518 					 obj->dumpId, obj->catId.oid);
1519 			return;
1520 		case DO_SUBSCRIPTION:
1521 			snprintf(buf, bufsize,
1522 					 "SUBSCRIPTION (ID %d OID %u)",
1523 					 obj->dumpId, obj->catId.oid);
1524 			return;
1525 		case DO_PRE_DATA_BOUNDARY:
1526 			snprintf(buf, bufsize,
1527 					 "PRE-DATA BOUNDARY  (ID %d)",
1528 					 obj->dumpId);
1529 			return;
1530 		case DO_POST_DATA_BOUNDARY:
1531 			snprintf(buf, bufsize,
1532 					 "POST-DATA BOUNDARY  (ID %d)",
1533 					 obj->dumpId);
1534 			return;
1535 	}
1536 	/* shouldn't get here */
1537 	snprintf(buf, bufsize,
1538 			 "object type %d  (ID %d OID %u)",
1539 			 (int) obj->objType,
1540 			 obj->dumpId, obj->catId.oid);
1541 }
1542