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