Force a checkpoint in CREATE DATABASE before starting to copy the files,
[PostgreSQL.git] / src / bin / pg_dump / pg_dump_sort.c
blobcf391f49bd3c3d703d9fee2af01c6f92283ed209
1 /*-------------------------------------------------------------------------
3 * pg_dump_sort.c
4 * Sort the items of a dump into a safe order for dumping
7 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * IDENTIFICATION
12 * $PostgreSQL$
14 *-------------------------------------------------------------------------
16 #include "pg_backup_archiver.h"
19 static const char *modulename = gettext_noop("sorter");
22 * Sort priority for object types when dumping a pre-7.3 database.
23 * Objects are sorted by priority levels, and within an equal priority level
24 * by OID. (This is a relatively crude hack to provide semi-reasonable
25 * behavior for old databases without full dependency info.)
27 static const int oldObjectTypePriority[] =
29 1, /* DO_NAMESPACE */
30 2, /* DO_TYPE */
31 2, /* DO_SHELL_TYPE */
32 2, /* DO_FUNC */
33 3, /* DO_AGG */
34 3, /* DO_OPERATOR */
35 4, /* DO_OPCLASS */
36 4, /* DO_OPFAMILY */
37 5, /* DO_CONVERSION */
38 6, /* DO_TABLE */
39 8, /* DO_ATTRDEF */
40 13, /* DO_INDEX */
41 14, /* DO_RULE */
42 15, /* DO_TRIGGER */
43 12, /* DO_CONSTRAINT */
44 16, /* DO_FK_CONSTRAINT */
45 2, /* DO_PROCLANG */
46 2, /* DO_CAST */
47 9, /* DO_TABLE_DATA */
48 7, /* DO_TABLE_TYPE */
49 3, /* DO_TSPARSER */
50 4, /* DO_TSDICT */
51 3, /* DO_TSTEMPLATE */
52 5, /* DO_TSCONFIG */
53 10, /* DO_BLOBS */
54 11 /* DO_BLOB_COMMENTS */
58 * Sort priority for object types when dumping newer databases.
59 * Objects are sorted by type, and within a type by name.
61 static const int newObjectTypePriority[] =
63 1, /* DO_NAMESPACE */
64 3, /* DO_TYPE */
65 3, /* DO_SHELL_TYPE */
66 4, /* DO_FUNC */
67 5, /* DO_AGG */
68 6, /* DO_OPERATOR */
69 7, /* DO_OPCLASS */
70 7, /* DO_OPFAMILY */
71 9, /* DO_CONVERSION */
72 10, /* DO_TABLE */
73 12, /* DO_ATTRDEF */
74 17, /* DO_INDEX */
75 18, /* DO_RULE */
76 19, /* DO_TRIGGER */
77 16, /* DO_CONSTRAINT */
78 20, /* DO_FK_CONSTRAINT */
79 2, /* DO_PROCLANG */
80 8, /* DO_CAST */
81 13, /* DO_TABLE_DATA */
82 11, /* DO_TABLE_TYPE */
83 5, /* DO_TSPARSER */
84 6, /* DO_TSDICT */
85 5, /* DO_TSTEMPLATE */
86 7, /* DO_TSCONFIG */
87 14, /* DO_BLOBS */
88 15 /* DO_BLOB_COMMENTS */
92 static int DOTypeNameCompare(const void *p1, const void *p2);
93 static int DOTypeOidCompare(const void *p1, const void *p2);
94 static bool TopoSort(DumpableObject **objs,
95 int numObjs,
96 DumpableObject **ordering,
97 int *nOrdering);
98 static void addHeapElement(int val, int *heap, int heapLength);
99 static int removeHeapElement(int *heap, int heapLength);
100 static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
101 static bool findLoop(DumpableObject *obj,
102 DumpId startPoint,
103 DumpableObject **workspace,
104 int depth,
105 int *newDepth);
106 static void repairDependencyLoop(DumpableObject **loop,
107 int nLoop);
108 static void describeDumpableObject(DumpableObject *obj,
109 char *buf, int bufsize);
113 * Sort the given objects into a type/name-based ordering
115 * Normally this is just the starting point for the dependency-based
116 * ordering.
118 void
119 sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
121 if (numObjs > 1)
122 qsort((void *) objs, numObjs, sizeof(DumpableObject *),
123 DOTypeNameCompare);
126 static int
127 DOTypeNameCompare(const void *p1, const void *p2)
129 DumpableObject *obj1 = *(DumpableObject **) p1;
130 DumpableObject *obj2 = *(DumpableObject **) p2;
131 int cmpval;
133 /* Sort by type */
134 cmpval = newObjectTypePriority[obj1->objType] -
135 newObjectTypePriority[obj2->objType];
137 if (cmpval != 0)
138 return cmpval;
141 * Sort by namespace. Note that all objects of the same type should
142 * either have or not have a namespace link, so we needn't be fancy about
143 * cases where one link is null and the other not.
145 if (obj1->namespace && obj2->namespace)
147 cmpval = strcmp(obj1->namespace->dobj.name,
148 obj2->namespace->dobj.name);
149 if (cmpval != 0)
150 return cmpval;
153 /* Sort by name */
154 cmpval = strcmp(obj1->name, obj2->name);
155 if (cmpval != 0)
156 return cmpval;
158 /* Probably shouldn't get here, but if we do, sort by OID */
159 return oidcmp(obj1->catId.oid, obj2->catId.oid);
164 * Sort the given objects into a type/OID-based ordering
166 * This is used with pre-7.3 source databases as a crude substitute for the
167 * lack of dependency information.
169 void
170 sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs)
172 if (numObjs > 1)
173 qsort((void *) objs, numObjs, sizeof(DumpableObject *),
174 DOTypeOidCompare);
177 static int
178 DOTypeOidCompare(const void *p1, const void *p2)
180 DumpableObject *obj1 = *(DumpableObject **) p1;
181 DumpableObject *obj2 = *(DumpableObject **) p2;
182 int cmpval;
184 cmpval = oldObjectTypePriority[obj1->objType] -
185 oldObjectTypePriority[obj2->objType];
187 if (cmpval != 0)
188 return cmpval;
190 return oidcmp(obj1->catId.oid, obj2->catId.oid);
195 * Sort the given objects into a safe dump order using dependency
196 * information (to the extent we have it available).
198 void
199 sortDumpableObjects(DumpableObject **objs, int numObjs)
201 DumpableObject **ordering;
202 int nOrdering;
204 if (numObjs <= 0)
205 return;
207 ordering = (DumpableObject **) malloc(numObjs * sizeof(DumpableObject *));
208 if (ordering == NULL)
209 exit_horribly(NULL, modulename, "out of memory\n");
211 while (!TopoSort(objs, numObjs, ordering, &nOrdering))
212 findDependencyLoops(ordering, nOrdering, numObjs);
214 memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
216 free(ordering);
220 * TopoSort -- topological sort of a dump list
222 * Generate a re-ordering of the dump list that satisfies all the dependency
223 * constraints shown in the dump list. (Each such constraint is a fact of a
224 * partial ordering.) Minimize rearrangement of the list not needed to
225 * achieve the partial ordering.
227 * The input is the list of numObjs objects in objs[]. This list is not
228 * modified.
230 * Returns TRUE if able to build an ordering that satisfies all the
231 * constraints, FALSE if not (there are contradictory constraints).
233 * On success (TRUE result), ordering[] is filled with a sorted array of
234 * DumpableObject pointers, of length equal to the input list length.
236 * On failure (FALSE result), ordering[] is filled with an unsorted array of
237 * DumpableObject pointers of length *nOrdering, listing the objects that
238 * prevented the sort from being completed. In general, these objects either
239 * participate directly in a dependency cycle, or are depended on by objects
240 * that are in a cycle. (The latter objects are not actually problematic,
241 * but it takes further analysis to identify which are which.)
243 * The caller is responsible for allocating sufficient space at *ordering.
245 static bool
246 TopoSort(DumpableObject **objs,
247 int numObjs,
248 DumpableObject **ordering, /* output argument */
249 int *nOrdering) /* output argument */
251 DumpId maxDumpId = getMaxDumpId();
252 int *pendingHeap;
253 int *beforeConstraints;
254 int *idMap;
255 DumpableObject *obj;
256 int heapLength;
257 int i,
262 * This is basically the same algorithm shown for topological sorting in
263 * Knuth's Volume 1. However, we would like to minimize unnecessary
264 * rearrangement of the input ordering; that is, when we have a choice of
265 * which item to output next, we always want to take the one highest in
266 * the original list. Therefore, instead of maintaining an unordered
267 * linked list of items-ready-to-output as Knuth does, we maintain a heap
268 * of their item numbers, which we can use as a priority queue. This
269 * turns the algorithm from O(N) to O(N log N) because each insertion or
270 * removal of a heap item takes O(log N) time. However, that's still
271 * plenty fast enough for this application.
274 *nOrdering = numObjs; /* for success return */
276 /* Eliminate the null case */
277 if (numObjs <= 0)
278 return true;
280 /* Create workspace for the above-described heap */
281 pendingHeap = (int *) malloc(numObjs * sizeof(int));
282 if (pendingHeap == NULL)
283 exit_horribly(NULL, modulename, "out of memory\n");
286 * Scan the constraints, and for each item in the input, generate a count
287 * of the number of constraints that say it must be before something else.
288 * The count for the item with dumpId j is stored in beforeConstraints[j].
289 * We also make a map showing the input-order index of the item with
290 * dumpId j.
292 beforeConstraints = (int *) malloc((maxDumpId + 1) * sizeof(int));
293 if (beforeConstraints == NULL)
294 exit_horribly(NULL, modulename, "out of memory\n");
295 memset(beforeConstraints, 0, (maxDumpId + 1) * sizeof(int));
296 idMap = (int *) malloc((maxDumpId + 1) * sizeof(int));
297 if (idMap == NULL)
298 exit_horribly(NULL, modulename, "out of memory\n");
299 for (i = 0; i < numObjs; i++)
301 obj = objs[i];
302 j = obj->dumpId;
303 if (j <= 0 || j > maxDumpId)
304 exit_horribly(NULL, modulename, "invalid dumpId %d\n", j);
305 idMap[j] = i;
306 for (j = 0; j < obj->nDeps; j++)
308 k = obj->dependencies[j];
309 if (k <= 0 || k > maxDumpId)
310 exit_horribly(NULL, modulename, "invalid dependency %d\n", k);
311 beforeConstraints[k]++;
316 * Now initialize the heap of items-ready-to-output by filling it with the
317 * indexes of items that already have beforeConstraints[id] == 0.
319 * The essential property of a heap is heap[(j-1)/2] >= heap[j] for each j
320 * in the range 1..heapLength-1 (note we are using 0-based subscripts
321 * here, while the discussion in Knuth assumes 1-based subscripts). So, if
322 * we simply enter the indexes into pendingHeap[] in decreasing order, we
323 * a-fortiori have the heap invariant satisfied at completion of this
324 * loop, and don't need to do any sift-up comparisons.
326 heapLength = 0;
327 for (i = numObjs; --i >= 0;)
329 if (beforeConstraints[objs[i]->dumpId] == 0)
330 pendingHeap[heapLength++] = i;
333 /*--------------------
334 * Now emit objects, working backwards in the output list. At each step,
335 * we use the priority heap to select the last item that has no remaining
336 * before-constraints. We remove that item from the heap, output it to
337 * ordering[], and decrease the beforeConstraints count of each of the
338 * items it was constrained against. Whenever an item's beforeConstraints
339 * count is thereby decreased to zero, we insert it into the priority heap
340 * to show that it is a candidate to output. We are done when the heap
341 * becomes empty; if we have output every element then we succeeded,
342 * otherwise we failed.
343 * i = number of ordering[] entries left to output
344 * j = objs[] index of item we are outputting
345 * k = temp for scanning constraint list for item j
346 *--------------------
348 i = numObjs;
349 while (heapLength > 0)
351 /* Select object to output by removing largest heap member */
352 j = removeHeapElement(pendingHeap, heapLength--);
353 obj = objs[j];
354 /* Output candidate to ordering[] */
355 ordering[--i] = obj;
356 /* Update beforeConstraints counts of its predecessors */
357 for (k = 0; k < obj->nDeps; k++)
359 int id = obj->dependencies[k];
361 if ((--beforeConstraints[id]) == 0)
362 addHeapElement(idMap[id], pendingHeap, heapLength++);
367 * If we failed, report the objects that couldn't be output; these are the
368 * ones with beforeConstraints[] still nonzero.
370 if (i != 0)
372 k = 0;
373 for (j = 1; j <= maxDumpId; j++)
375 if (beforeConstraints[j] != 0)
376 ordering[k++] = objs[idMap[j]];
378 *nOrdering = k;
381 /* Done */
382 free(pendingHeap);
383 free(beforeConstraints);
384 free(idMap);
386 return (i == 0);
390 * Add an item to a heap (priority queue)
392 * heapLength is the current heap size; caller is responsible for increasing
393 * its value after the call. There must be sufficient storage at *heap.
395 static void
396 addHeapElement(int val, int *heap, int heapLength)
398 int j;
401 * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth is
402 * using 1-based array indexes, not 0-based.
404 j = heapLength;
405 while (j > 0)
407 int i = (j - 1) >> 1;
409 if (val <= heap[i])
410 break;
411 heap[j] = heap[i];
412 j = i;
414 heap[j] = val;
418 * Remove the largest item present in a heap (priority queue)
420 * heapLength is the current heap size; caller is responsible for decreasing
421 * its value after the call.
423 * We remove and return heap[0], which is always the largest element of
424 * the heap, and then "sift up" to maintain the heap invariant.
426 static int
427 removeHeapElement(int *heap, int heapLength)
429 int result = heap[0];
430 int val;
431 int i;
433 if (--heapLength <= 0)
434 return result;
435 val = heap[heapLength]; /* value that must be reinserted */
436 i = 0; /* i is where the "hole" is */
437 for (;;)
439 int j = 2 * i + 1;
441 if (j >= heapLength)
442 break;
443 if (j + 1 < heapLength &&
444 heap[j] < heap[j + 1])
445 j++;
446 if (val >= heap[j])
447 break;
448 heap[i] = heap[j];
449 i = j;
451 heap[i] = val;
452 return result;
456 * findDependencyLoops - identify loops in TopoSort's failure output,
457 * and pass each such loop to repairDependencyLoop() for action
459 * In general there may be many loops in the set of objects returned by
460 * TopoSort; for speed we should try to repair as many loops as we can
461 * before trying TopoSort again. We can safely repair loops that are
462 * disjoint (have no members in common); if we find overlapping loops
463 * then we repair only the first one found, because the action taken to
464 * repair the first might have repaired the other as well. (If not,
465 * we'll fix it on the next go-round.)
467 * objs[] lists the objects TopoSort couldn't sort
468 * nObjs is the number of such objects
469 * totObjs is the total number of objects in the universe
471 static void
472 findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
475 * We use a workspace array, the initial part of which stores objects
476 * already processed, and the rest of which is used as temporary space to
477 * try to build a loop in. This is convenient because we do not care
478 * about loops involving already-processed objects (see notes above); we
479 * can easily reject such loops in findLoop() because of this
480 * representation. After we identify and process a loop, we can add it to
481 * the initial part of the workspace just by moving the boundary pointer.
483 * When we determine that an object is not part of any interesting loop,
484 * we also add it to the initial part of the workspace. This is not
485 * necessary for correctness, but saves later invocations of findLoop()
486 * from uselessly chasing references to such an object.
488 * We make the workspace large enough to hold all objects in the original
489 * universe. This is probably overkill, but it's provably enough space...
491 DumpableObject **workspace;
492 int initiallen;
493 bool fixedloop;
494 int i;
496 workspace = (DumpableObject **) malloc(totObjs * sizeof(DumpableObject *));
497 if (workspace == NULL)
498 exit_horribly(NULL, modulename, "out of memory\n");
499 initiallen = 0;
500 fixedloop = false;
502 for (i = 0; i < nObjs; i++)
504 DumpableObject *obj = objs[i];
505 int newlen;
507 workspace[initiallen] = NULL; /* see test below */
509 if (findLoop(obj, obj->dumpId, workspace, initiallen, &newlen))
511 /* Found a loop of length newlen - initiallen */
512 repairDependencyLoop(&workspace[initiallen], newlen - initiallen);
513 /* Add loop members to workspace */
514 initiallen = newlen;
515 fixedloop = true;
517 else
520 * Didn't find a loop, but add this object to workspace anyway,
521 * unless it's already present. We piggyback on the test that
522 * findLoop() already did: it won't have tentatively added obj to
523 * workspace if it's already present.
525 if (workspace[initiallen] == obj)
526 initiallen++;
530 /* We'd better have fixed at least one loop */
531 if (!fixedloop)
532 exit_horribly(NULL, modulename, "could not identify dependency loop\n");
534 free(workspace);
538 * Recursively search for a circular dependency loop that doesn't include
539 * any existing workspace members.
541 * obj: object we are examining now
542 * startPoint: dumpId of starting object for the hoped-for circular loop
543 * workspace[]: work array for previously processed and current objects
544 * depth: number of valid entries in workspace[] at call
545 * newDepth: if successful, set to new number of workspace[] entries
547 * On success, *newDepth is set and workspace[] entries depth..*newDepth-1
548 * are filled with pointers to the members of the loop.
550 * Note: it is possible that the given starting object is a member of more
551 * than one cycle; if so, we will find an arbitrary one of the cycles.
553 static bool
554 findLoop(DumpableObject *obj,
555 DumpId startPoint,
556 DumpableObject **workspace,
557 int depth,
558 int *newDepth)
560 int i;
563 * Reject if obj is already present in workspace. This test serves three
564 * purposes: it prevents us from finding loops that overlap
565 * previously-processed loops, it prevents us from going into infinite
566 * recursion if we are given a startPoint object that links to a cycle
567 * it's not a member of, and it guarantees that we can't overflow the
568 * allocated size of workspace[].
570 for (i = 0; i < depth; i++)
572 if (workspace[i] == obj)
573 return false;
577 * Okay, tentatively add obj to workspace
579 workspace[depth++] = obj;
582 * See if we've found a loop back to the desired startPoint; if so, done
584 for (i = 0; i < obj->nDeps; i++)
586 if (obj->dependencies[i] == startPoint)
588 *newDepth = depth;
589 return true;
594 * Recurse down each outgoing branch
596 for (i = 0; i < obj->nDeps; i++)
598 DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
600 if (!nextobj)
601 continue; /* ignore dependencies on undumped objects */
602 if (findLoop(nextobj,
603 startPoint,
604 workspace,
605 depth,
606 newDepth))
607 return true;
610 return false;
614 * A user-defined datatype will have a dependency loop with each of its
615 * I/O functions (since those have the datatype as input or output).
616 * Break the loop and make the I/O function depend on the associated
617 * shell type, instead.
619 static void
620 repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
622 TypeInfo *typeInfo = (TypeInfo *) typeobj;
624 /* remove function's dependency on type */
625 removeObjectDependency(funcobj, typeobj->dumpId);
627 /* add function's dependency on shell type, instead */
628 if (typeInfo->shellType)
630 addObjectDependency(funcobj, typeInfo->shellType->dobj.dumpId);
631 /* Mark shell type as to be dumped if any I/O function is */
632 if (funcobj->dump)
633 typeInfo->shellType->dobj.dump = true;
638 * Because we force a view to depend on its ON SELECT rule, while there
639 * will be an implicit dependency in the other direction, we need to break
640 * the loop. If there are no other objects in the loop then we can remove
641 * the implicit dependency and leave the ON SELECT rule non-separate.
643 static void
644 repairViewRuleLoop(DumpableObject *viewobj,
645 DumpableObject *ruleobj)
647 /* remove rule's dependency on view */
648 removeObjectDependency(ruleobj, viewobj->dumpId);
652 * However, if there are other objects in the loop, we must break the loop
653 * by making the ON SELECT rule a separately-dumped object.
655 * Because findLoop() finds shorter cycles before longer ones, it's likely
656 * that we will have previously fired repairViewRuleLoop() and removed the
657 * rule's dependency on the view. Put it back to ensure the rule won't be
658 * emitted before the view...
660 static void
661 repairViewRuleMultiLoop(DumpableObject *viewobj,
662 DumpableObject *ruleobj)
664 /* remove view's dependency on rule */
665 removeObjectDependency(viewobj, ruleobj->dumpId);
666 /* pretend view is a plain table and dump it that way */
667 ((TableInfo *) viewobj)->relkind = 'r'; /* RELKIND_RELATION */
668 /* mark rule as needing its own dump */
669 ((RuleInfo *) ruleobj)->separate = true;
670 /* put back rule's dependency on view */
671 addObjectDependency(ruleobj, viewobj->dumpId);
675 * Because we make tables depend on their CHECK constraints, while there
676 * will be an automatic dependency in the other direction, we need to break
677 * the loop. If there are no other objects in the loop then we can remove
678 * the automatic dependency and leave the CHECK constraint non-separate.
680 static void
681 repairTableConstraintLoop(DumpableObject *tableobj,
682 DumpableObject *constraintobj)
684 /* remove constraint's dependency on table */
685 removeObjectDependency(constraintobj, tableobj->dumpId);
689 * However, if there are other objects in the loop, we must break the loop
690 * by making the CHECK constraint a separately-dumped object.
692 * Because findLoop() finds shorter cycles before longer ones, it's likely
693 * that we will have previously fired repairTableConstraintLoop() and
694 * removed the constraint's dependency on the table. Put it back to ensure
695 * the constraint won't be emitted before the table...
697 static void
698 repairTableConstraintMultiLoop(DumpableObject *tableobj,
699 DumpableObject *constraintobj)
701 /* remove table's dependency on constraint */
702 removeObjectDependency(tableobj, constraintobj->dumpId);
703 /* mark constraint as needing its own dump */
704 ((ConstraintInfo *) constraintobj)->separate = true;
705 /* put back constraint's dependency on table */
706 addObjectDependency(constraintobj, tableobj->dumpId);
710 * Attribute defaults behave exactly the same as CHECK constraints...
712 static void
713 repairTableAttrDefLoop(DumpableObject *tableobj,
714 DumpableObject *attrdefobj)
716 /* remove attrdef's dependency on table */
717 removeObjectDependency(attrdefobj, tableobj->dumpId);
720 static void
721 repairTableAttrDefMultiLoop(DumpableObject *tableobj,
722 DumpableObject *attrdefobj)
724 /* remove table's dependency on attrdef */
725 removeObjectDependency(tableobj, attrdefobj->dumpId);
726 /* mark attrdef as needing its own dump */
727 ((AttrDefInfo *) attrdefobj)->separate = true;
728 /* put back attrdef's dependency on table */
729 addObjectDependency(attrdefobj, tableobj->dumpId);
733 * CHECK constraints on domains work just like those on tables ...
735 static void
736 repairDomainConstraintLoop(DumpableObject *domainobj,
737 DumpableObject *constraintobj)
739 /* remove constraint's dependency on domain */
740 removeObjectDependency(constraintobj, domainobj->dumpId);
743 static void
744 repairDomainConstraintMultiLoop(DumpableObject *domainobj,
745 DumpableObject *constraintobj)
747 /* remove domain's dependency on constraint */
748 removeObjectDependency(domainobj, constraintobj->dumpId);
749 /* mark constraint as needing its own dump */
750 ((ConstraintInfo *) constraintobj)->separate = true;
751 /* put back constraint's dependency on domain */
752 addObjectDependency(constraintobj, domainobj->dumpId);
756 * Fix a dependency loop, or die trying ...
758 * This routine is mainly concerned with reducing the multiple ways that
759 * a loop might appear to common cases, which it passes off to the
760 * "fixer" routines above.
762 static void
763 repairDependencyLoop(DumpableObject **loop,
764 int nLoop)
766 int i,
769 /* Datatype and one of its I/O functions */
770 if (nLoop == 2 &&
771 loop[0]->objType == DO_TYPE &&
772 loop[1]->objType == DO_FUNC)
774 repairTypeFuncLoop(loop[0], loop[1]);
775 return;
777 if (nLoop == 2 &&
778 loop[1]->objType == DO_TYPE &&
779 loop[0]->objType == DO_FUNC)
781 repairTypeFuncLoop(loop[1], loop[0]);
782 return;
785 /* View and its ON SELECT rule */
786 if (nLoop == 2 &&
787 loop[0]->objType == DO_TABLE &&
788 loop[1]->objType == DO_RULE &&
789 ((RuleInfo *) loop[1])->ev_type == '1' &&
790 ((RuleInfo *) loop[1])->is_instead &&
791 ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
793 repairViewRuleLoop(loop[0], loop[1]);
794 return;
796 if (nLoop == 2 &&
797 loop[1]->objType == DO_TABLE &&
798 loop[0]->objType == DO_RULE &&
799 ((RuleInfo *) loop[0])->ev_type == '1' &&
800 ((RuleInfo *) loop[0])->is_instead &&
801 ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
803 repairViewRuleLoop(loop[1], loop[0]);
804 return;
807 /* Indirect loop involving view and ON SELECT rule */
808 if (nLoop > 2)
810 for (i = 0; i < nLoop; i++)
812 if (loop[i]->objType == DO_TABLE)
814 for (j = 0; j < nLoop; j++)
816 if (loop[j]->objType == DO_RULE &&
817 ((RuleInfo *) loop[j])->ev_type == '1' &&
818 ((RuleInfo *) loop[j])->is_instead &&
819 ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
821 repairViewRuleMultiLoop(loop[i], loop[j]);
822 return;
829 /* Table and CHECK constraint */
830 if (nLoop == 2 &&
831 loop[0]->objType == DO_TABLE &&
832 loop[1]->objType == DO_CONSTRAINT &&
833 ((ConstraintInfo *) loop[1])->contype == 'c' &&
834 ((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
836 repairTableConstraintLoop(loop[0], loop[1]);
837 return;
839 if (nLoop == 2 &&
840 loop[1]->objType == DO_TABLE &&
841 loop[0]->objType == DO_CONSTRAINT &&
842 ((ConstraintInfo *) loop[0])->contype == 'c' &&
843 ((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
845 repairTableConstraintLoop(loop[1], loop[0]);
846 return;
849 /* Indirect loop involving table and CHECK constraint */
850 if (nLoop > 2)
852 for (i = 0; i < nLoop; i++)
854 if (loop[i]->objType == DO_TABLE)
856 for (j = 0; j < nLoop; j++)
858 if (loop[j]->objType == DO_CONSTRAINT &&
859 ((ConstraintInfo *) loop[j])->contype == 'c' &&
860 ((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
862 repairTableConstraintMultiLoop(loop[i], loop[j]);
863 return;
870 /* Table and attribute default */
871 if (nLoop == 2 &&
872 loop[0]->objType == DO_TABLE &&
873 loop[1]->objType == DO_ATTRDEF &&
874 ((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
876 repairTableAttrDefLoop(loop[0], loop[1]);
877 return;
879 if (nLoop == 2 &&
880 loop[1]->objType == DO_TABLE &&
881 loop[0]->objType == DO_ATTRDEF &&
882 ((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
884 repairTableAttrDefLoop(loop[1], loop[0]);
885 return;
888 /* Indirect loop involving table and attribute default */
889 if (nLoop > 2)
891 for (i = 0; i < nLoop; i++)
893 if (loop[i]->objType == DO_TABLE)
895 for (j = 0; j < nLoop; j++)
897 if (loop[j]->objType == DO_ATTRDEF &&
898 ((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
900 repairTableAttrDefMultiLoop(loop[i], loop[j]);
901 return;
908 /* Domain and CHECK constraint */
909 if (nLoop == 2 &&
910 loop[0]->objType == DO_TYPE &&
911 loop[1]->objType == DO_CONSTRAINT &&
912 ((ConstraintInfo *) loop[1])->contype == 'c' &&
913 ((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
915 repairDomainConstraintLoop(loop[0], loop[1]);
916 return;
918 if (nLoop == 2 &&
919 loop[1]->objType == DO_TYPE &&
920 loop[0]->objType == DO_CONSTRAINT &&
921 ((ConstraintInfo *) loop[0])->contype == 'c' &&
922 ((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
924 repairDomainConstraintLoop(loop[1], loop[0]);
925 return;
928 /* Indirect loop involving domain and CHECK constraint */
929 if (nLoop > 2)
931 for (i = 0; i < nLoop; i++)
933 if (loop[i]->objType == DO_TYPE)
935 for (j = 0; j < nLoop; j++)
937 if (loop[j]->objType == DO_CONSTRAINT &&
938 ((ConstraintInfo *) loop[j])->contype == 'c' &&
939 ((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
941 repairDomainConstraintMultiLoop(loop[i], loop[j]);
942 return;
950 * If all the objects are TABLE_DATA items, what we must have is a
951 * circular set of foreign key constraints (or a single self-referential
952 * table). Print an appropriate complaint and break the loop arbitrarily.
954 for (i = 0; i < nLoop; i++)
956 if (loop[i]->objType != DO_TABLE_DATA)
957 break;
959 if (i >= nLoop)
961 write_msg(NULL, "NOTICE: there are circular foreign-key constraints among these table(s):\n");
962 for (i = 0; i < nLoop; i++)
963 write_msg(NULL, " %s\n", loop[i]->name);
964 write_msg(NULL, "You may not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.\n");
965 write_msg(NULL, "Consider using a full dump instead of a --data-only dump to avoid this problem.\n");
966 if (nLoop > 1)
967 removeObjectDependency(loop[0], loop[1]->dumpId);
968 else /* must be a self-dependency */
969 removeObjectDependency(loop[0], loop[0]->dumpId);
970 return;
974 * If we can't find a principled way to break the loop, complain and break
975 * it in an arbitrary fashion.
977 write_msg(modulename, "WARNING: could not resolve dependency loop among these items:\n");
978 for (i = 0; i < nLoop; i++)
980 char buf[1024];
982 describeDumpableObject(loop[i], buf, sizeof(buf));
983 write_msg(modulename, " %s\n", buf);
986 if (nLoop > 1)
987 removeObjectDependency(loop[0], loop[1]->dumpId);
988 else /* must be a self-dependency */
989 removeObjectDependency(loop[0], loop[0]->dumpId);
993 * Describe a dumpable object usefully for errors
995 * This should probably go somewhere else...
997 static void
998 describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
1000 switch (obj->objType)
1002 case DO_NAMESPACE:
1003 snprintf(buf, bufsize,
1004 "SCHEMA %s (ID %d OID %u)",
1005 obj->name, obj->dumpId, obj->catId.oid);
1006 return;
1007 case DO_TYPE:
1008 snprintf(buf, bufsize,
1009 "TYPE %s (ID %d OID %u)",
1010 obj->name, obj->dumpId, obj->catId.oid);
1011 return;
1012 case DO_SHELL_TYPE:
1013 snprintf(buf, bufsize,
1014 "SHELL TYPE %s (ID %d OID %u)",
1015 obj->name, obj->dumpId, obj->catId.oid);
1016 return;
1017 case DO_FUNC:
1018 snprintf(buf, bufsize,
1019 "FUNCTION %s (ID %d OID %u)",
1020 obj->name, obj->dumpId, obj->catId.oid);
1021 return;
1022 case DO_AGG:
1023 snprintf(buf, bufsize,
1024 "AGGREGATE %s (ID %d OID %u)",
1025 obj->name, obj->dumpId, obj->catId.oid);
1026 return;
1027 case DO_OPERATOR:
1028 snprintf(buf, bufsize,
1029 "OPERATOR %s (ID %d OID %u)",
1030 obj->name, obj->dumpId, obj->catId.oid);
1031 return;
1032 case DO_OPCLASS:
1033 snprintf(buf, bufsize,
1034 "OPERATOR CLASS %s (ID %d OID %u)",
1035 obj->name, obj->dumpId, obj->catId.oid);
1036 return;
1037 case DO_OPFAMILY:
1038 snprintf(buf, bufsize,
1039 "OPERATOR FAMILY %s (ID %d OID %u)",
1040 obj->name, obj->dumpId, obj->catId.oid);
1041 return;
1042 case DO_CONVERSION:
1043 snprintf(buf, bufsize,
1044 "CONVERSION %s (ID %d OID %u)",
1045 obj->name, obj->dumpId, obj->catId.oid);
1046 return;
1047 case DO_TABLE:
1048 snprintf(buf, bufsize,
1049 "TABLE %s (ID %d OID %u)",
1050 obj->name, obj->dumpId, obj->catId.oid);
1051 return;
1052 case DO_ATTRDEF:
1053 snprintf(buf, bufsize,
1054 "ATTRDEF %s.%s (ID %d OID %u)",
1055 ((AttrDefInfo *) obj)->adtable->dobj.name,
1056 ((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
1057 obj->dumpId, obj->catId.oid);
1058 return;
1059 case DO_INDEX:
1060 snprintf(buf, bufsize,
1061 "INDEX %s (ID %d OID %u)",
1062 obj->name, obj->dumpId, obj->catId.oid);
1063 return;
1064 case DO_RULE:
1065 snprintf(buf, bufsize,
1066 "RULE %s (ID %d OID %u)",
1067 obj->name, obj->dumpId, obj->catId.oid);
1068 return;
1069 case DO_TRIGGER:
1070 snprintf(buf, bufsize,
1071 "TRIGGER %s (ID %d OID %u)",
1072 obj->name, obj->dumpId, obj->catId.oid);
1073 return;
1074 case DO_CONSTRAINT:
1075 snprintf(buf, bufsize,
1076 "CONSTRAINT %s (ID %d OID %u)",
1077 obj->name, obj->dumpId, obj->catId.oid);
1078 return;
1079 case DO_FK_CONSTRAINT:
1080 snprintf(buf, bufsize,
1081 "FK CONSTRAINT %s (ID %d OID %u)",
1082 obj->name, obj->dumpId, obj->catId.oid);
1083 return;
1084 case DO_PROCLANG:
1085 snprintf(buf, bufsize,
1086 "PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1087 obj->name, obj->dumpId, obj->catId.oid);
1088 return;
1089 case DO_CAST:
1090 snprintf(buf, bufsize,
1091 "CAST %u to %u (ID %d OID %u)",
1092 ((CastInfo *) obj)->castsource,
1093 ((CastInfo *) obj)->casttarget,
1094 obj->dumpId, obj->catId.oid);
1095 return;
1096 case DO_TABLE_DATA:
1097 snprintf(buf, bufsize,
1098 "TABLE DATA %s (ID %d OID %u)",
1099 obj->name, obj->dumpId, obj->catId.oid);
1100 return;
1101 case DO_TABLE_TYPE:
1102 snprintf(buf, bufsize,
1103 "TABLE TYPE %s (ID %d OID %u)",
1104 obj->name, obj->dumpId, obj->catId.oid);
1105 return;
1106 case DO_TSPARSER:
1107 snprintf(buf, bufsize,
1108 "TEXT SEARCH PARSER %s (ID %d OID %u)",
1109 obj->name, obj->dumpId, obj->catId.oid);
1110 return;
1111 case DO_TSDICT:
1112 snprintf(buf, bufsize,
1113 "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
1114 obj->name, obj->dumpId, obj->catId.oid);
1115 return;
1116 case DO_TSTEMPLATE:
1117 snprintf(buf, bufsize,
1118 "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
1119 obj->name, obj->dumpId, obj->catId.oid);
1120 return;
1121 case DO_TSCONFIG:
1122 snprintf(buf, bufsize,
1123 "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
1124 obj->name, obj->dumpId, obj->catId.oid);
1125 return;
1126 case DO_BLOBS:
1127 snprintf(buf, bufsize,
1128 "BLOBS (ID %d)",
1129 obj->dumpId);
1130 return;
1131 case DO_BLOB_COMMENTS:
1132 snprintf(buf, bufsize,
1133 "BLOB COMMENTS (ID %d)",
1134 obj->dumpId);
1135 return;
1137 /* shouldn't get here */
1138 snprintf(buf, bufsize,
1139 "object type %d (ID %d OID %u)",
1140 (int) obj->objType,
1141 obj->dumpId, obj->catId.oid);