1 /*-------------------------------------------------------------------------
4 * Sort the items of a dump into a safe order for dumping
7 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/bin/pg_dump/pg_dump_sort.c
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.) Note: extensions,
26 * text search, foreign-data, and default ACL objects can't really happen here,
27 * so the rather bogus priorities for them don't matter.
29 static const int oldObjectTypePriority[] =
34 2, /* DO_SHELL_TYPE */
40 5, /* DO_CONVERSION */
46 12, /* DO_CONSTRAINT */
47 16, /* DO_FK_CONSTRAINT */
50 10, /* DO_TABLE_DATA */
51 7, /* DO_DUMMY_TYPE */
54 3, /* DO_TSTEMPLATE */
57 4, /* DO_FOREIGN_SERVER */
58 17, /* DO_DEFAULT_ACL */
64 * Sort priority for object types when dumping newer databases.
65 * Objects are sorted by type, and within a type by name.
67 static const int newObjectTypePriority[] =
72 4, /* DO_SHELL_TYPE */
78 10, /* DO_CONVERSION */
84 23, /* DO_CONSTRAINT */
85 27, /* DO_FK_CONSTRAINT */
88 21, /* DO_TABLE_DATA */
89 18, /* DO_DUMMY_TYPE */
92 12, /* DO_TSTEMPLATE */
95 16, /* DO_FOREIGN_SERVER */
96 28, /* DO_DEFAULT_ACL */
102 static int DOTypeNameCompare(const void *p1, const void *p2);
103 static int DOTypeOidCompare(const void *p1, const void *p2);
104 static bool TopoSort(DumpableObject **objs,
106 DumpableObject **ordering,
108 static void addHeapElement(int val, int *heap, int heapLength);
109 static int removeHeapElement(int *heap, int heapLength);
110 static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
111 static bool findLoop(DumpableObject *obj,
113 DumpableObject **workspace,
116 static void repairDependencyLoop(DumpableObject **loop,
118 static void describeDumpableObject(DumpableObject *obj,
119 char *buf, int bufsize);
123 * Sort the given objects into a type/name-based ordering
125 * Normally this is just the starting point for the dependency-based
129 sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
132 qsort((void *) objs, numObjs, sizeof(DumpableObject *),
137 DOTypeNameCompare(const void *p1, const void *p2)
139 DumpableObject *obj1 = *(DumpableObject **) p1;
140 DumpableObject *obj2 = *(DumpableObject **) p2;
144 cmpval = newObjectTypePriority[obj1->objType] -
145 newObjectTypePriority[obj2->objType];
151 * Sort by namespace. Note that all objects of the same type should
152 * either have or not have a namespace link, so we needn't be fancy about
153 * cases where one link is null and the other not.
155 if (obj1->namespace && obj2->namespace)
157 cmpval = strcmp(obj1->namespace->dobj.name,
158 obj2->namespace->dobj.name);
164 cmpval = strcmp(obj1->name, obj2->name);
168 /* To have a stable sort order, break ties for some object types */
169 if (obj1->objType == DO_FUNC || obj1->objType == DO_AGG)
171 FuncInfo *fobj1 = *(FuncInfo **) p1;
172 FuncInfo *fobj2 = *(FuncInfo **) p2;
174 cmpval = fobj1->nargs - fobj2->nargs;
179 /* Usually shouldn't get here, but if we do, sort by OID */
180 return oidcmp(obj1->catId.oid, obj2->catId.oid);
185 * Sort the given objects into a type/OID-based ordering
187 * This is used with pre-7.3 source databases as a crude substitute for the
188 * lack of dependency information.
191 sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs)
194 qsort((void *) objs, numObjs, sizeof(DumpableObject *),
199 DOTypeOidCompare(const void *p1, const void *p2)
201 DumpableObject *obj1 = *(DumpableObject **) p1;
202 DumpableObject *obj2 = *(DumpableObject **) p2;
205 cmpval = oldObjectTypePriority[obj1->objType] -
206 oldObjectTypePriority[obj2->objType];
211 return oidcmp(obj1->catId.oid, obj2->catId.oid);
216 * Sort the given objects into a safe dump order using dependency
217 * information (to the extent we have it available).
220 sortDumpableObjects(DumpableObject **objs, int numObjs)
222 DumpableObject **ordering;
228 ordering = (DumpableObject **) malloc(numObjs * sizeof(DumpableObject *));
229 if (ordering == NULL)
230 exit_horribly(NULL, modulename, "out of memory\n");
232 while (!TopoSort(objs, numObjs, ordering, &nOrdering))
233 findDependencyLoops(ordering, nOrdering, numObjs);
235 memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
241 * TopoSort -- topological sort of a dump list
243 * Generate a re-ordering of the dump list that satisfies all the dependency
244 * constraints shown in the dump list. (Each such constraint is a fact of a
245 * partial ordering.) Minimize rearrangement of the list not needed to
246 * achieve the partial ordering.
248 * The input is the list of numObjs objects in objs[]. This list is not
251 * Returns TRUE if able to build an ordering that satisfies all the
252 * constraints, FALSE if not (there are contradictory constraints).
254 * On success (TRUE result), ordering[] is filled with a sorted array of
255 * DumpableObject pointers, of length equal to the input list length.
257 * On failure (FALSE result), ordering[] is filled with an unsorted array of
258 * DumpableObject pointers of length *nOrdering, listing the objects that
259 * prevented the sort from being completed. In general, these objects either
260 * participate directly in a dependency cycle, or are depended on by objects
261 * that are in a cycle. (The latter objects are not actually problematic,
262 * but it takes further analysis to identify which are which.)
264 * The caller is responsible for allocating sufficient space at *ordering.
267 TopoSort(DumpableObject **objs,
269 DumpableObject **ordering, /* output argument */
270 int *nOrdering) /* output argument */
272 DumpId maxDumpId = getMaxDumpId();
274 int *beforeConstraints;
283 * This is basically the same algorithm shown for topological sorting in
284 * Knuth's Volume 1. However, we would like to minimize unnecessary
285 * rearrangement of the input ordering; that is, when we have a choice of
286 * which item to output next, we always want to take the one highest in
287 * the original list. Therefore, instead of maintaining an unordered
288 * linked list of items-ready-to-output as Knuth does, we maintain a heap
289 * of their item numbers, which we can use as a priority queue. This
290 * turns the algorithm from O(N) to O(N log N) because each insertion or
291 * removal of a heap item takes O(log N) time. However, that's still
292 * plenty fast enough for this application.
295 *nOrdering = numObjs; /* for success return */
297 /* Eliminate the null case */
301 /* Create workspace for the above-described heap */
302 pendingHeap = (int *) malloc(numObjs * sizeof(int));
303 if (pendingHeap == NULL)
304 exit_horribly(NULL, modulename, "out of memory\n");
307 * Scan the constraints, and for each item in the input, generate a count
308 * of the number of constraints that say it must be before something else.
309 * The count for the item with dumpId j is stored in beforeConstraints[j].
310 * We also make a map showing the input-order index of the item with
313 beforeConstraints = (int *) malloc((maxDumpId + 1) * sizeof(int));
314 if (beforeConstraints == NULL)
315 exit_horribly(NULL, modulename, "out of memory\n");
316 memset(beforeConstraints, 0, (maxDumpId + 1) * sizeof(int));
317 idMap = (int *) malloc((maxDumpId + 1) * sizeof(int));
319 exit_horribly(NULL, modulename, "out of memory\n");
320 for (i = 0; i < numObjs; i++)
324 if (j <= 0 || j > maxDumpId)
325 exit_horribly(NULL, modulename, "invalid dumpId %d\n", j);
327 for (j = 0; j < obj->nDeps; j++)
329 k = obj->dependencies[j];
330 if (k <= 0 || k > maxDumpId)
331 exit_horribly(NULL, modulename, "invalid dependency %d\n", k);
332 beforeConstraints[k]++;
337 * Now initialize the heap of items-ready-to-output by filling it with the
338 * indexes of items that already have beforeConstraints[id] == 0.
340 * The essential property of a heap is heap[(j-1)/2] >= heap[j] for each j
341 * in the range 1..heapLength-1 (note we are using 0-based subscripts
342 * here, while the discussion in Knuth assumes 1-based subscripts). So, if
343 * we simply enter the indexes into pendingHeap[] in decreasing order, we
344 * a-fortiori have the heap invariant satisfied at completion of this
345 * loop, and don't need to do any sift-up comparisons.
348 for (i = numObjs; --i >= 0;)
350 if (beforeConstraints[objs[i]->dumpId] == 0)
351 pendingHeap[heapLength++] = i;
354 /*--------------------
355 * Now emit objects, working backwards in the output list. At each step,
356 * we use the priority heap to select the last item that has no remaining
357 * before-constraints. We remove that item from the heap, output it to
358 * ordering[], and decrease the beforeConstraints count of each of the
359 * items it was constrained against. Whenever an item's beforeConstraints
360 * count is thereby decreased to zero, we insert it into the priority heap
361 * to show that it is a candidate to output. We are done when the heap
362 * becomes empty; if we have output every element then we succeeded,
363 * otherwise we failed.
364 * i = number of ordering[] entries left to output
365 * j = objs[] index of item we are outputting
366 * k = temp for scanning constraint list for item j
367 *--------------------
370 while (heapLength > 0)
372 /* Select object to output by removing largest heap member */
373 j = removeHeapElement(pendingHeap, heapLength--);
375 /* Output candidate to ordering[] */
377 /* Update beforeConstraints counts of its predecessors */
378 for (k = 0; k < obj->nDeps; k++)
380 int id = obj->dependencies[k];
382 if ((--beforeConstraints[id]) == 0)
383 addHeapElement(idMap[id], pendingHeap, heapLength++);
388 * If we failed, report the objects that couldn't be output; these are the
389 * ones with beforeConstraints[] still nonzero.
394 for (j = 1; j <= maxDumpId; j++)
396 if (beforeConstraints[j] != 0)
397 ordering[k++] = objs[idMap[j]];
404 free(beforeConstraints);
411 * Add an item to a heap (priority queue)
413 * heapLength is the current heap size; caller is responsible for increasing
414 * its value after the call. There must be sufficient storage at *heap.
417 addHeapElement(int val, int *heap, int heapLength)
422 * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth is
423 * using 1-based array indexes, not 0-based.
428 int i = (j - 1) >> 1;
439 * Remove the largest item present in a heap (priority queue)
441 * heapLength is the current heap size; caller is responsible for decreasing
442 * its value after the call.
444 * We remove and return heap[0], which is always the largest element of
445 * the heap, and then "sift up" to maintain the heap invariant.
448 removeHeapElement(int *heap, int heapLength)
450 int result = heap[0];
454 if (--heapLength <= 0)
456 val = heap[heapLength]; /* value that must be reinserted */
457 i = 0; /* i is where the "hole" is */
464 if (j + 1 < heapLength &&
465 heap[j] < heap[j + 1])
477 * findDependencyLoops - identify loops in TopoSort's failure output,
478 * and pass each such loop to repairDependencyLoop() for action
480 * In general there may be many loops in the set of objects returned by
481 * TopoSort; for speed we should try to repair as many loops as we can
482 * before trying TopoSort again. We can safely repair loops that are
483 * disjoint (have no members in common); if we find overlapping loops
484 * then we repair only the first one found, because the action taken to
485 * repair the first might have repaired the other as well. (If not,
486 * we'll fix it on the next go-round.)
488 * objs[] lists the objects TopoSort couldn't sort
489 * nObjs is the number of such objects
490 * totObjs is the total number of objects in the universe
493 findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
496 * We use a workspace array, the initial part of which stores objects
497 * already processed, and the rest of which is used as temporary space to
498 * try to build a loop in. This is convenient because we do not care
499 * about loops involving already-processed objects (see notes above); we
500 * can easily reject such loops in findLoop() because of this
501 * representation. After we identify and process a loop, we can add it to
502 * the initial part of the workspace just by moving the boundary pointer.
504 * When we determine that an object is not part of any interesting loop,
505 * we also add it to the initial part of the workspace. This is not
506 * necessary for correctness, but saves later invocations of findLoop()
507 * from uselessly chasing references to such an object.
509 * We make the workspace large enough to hold all objects in the original
510 * universe. This is probably overkill, but it's provably enough space...
512 DumpableObject **workspace;
517 workspace = (DumpableObject **) malloc(totObjs * sizeof(DumpableObject *));
518 if (workspace == NULL)
519 exit_horribly(NULL, modulename, "out of memory\n");
523 for (i = 0; i < nObjs; i++)
525 DumpableObject *obj = objs[i];
528 workspace[initiallen] = NULL; /* see test below */
530 if (findLoop(obj, obj->dumpId, workspace, initiallen, &newlen))
532 /* Found a loop of length newlen - initiallen */
533 repairDependencyLoop(&workspace[initiallen], newlen - initiallen);
534 /* Add loop members to workspace */
541 * Didn't find a loop, but add this object to workspace anyway,
542 * unless it's already present. We piggyback on the test that
543 * findLoop() already did: it won't have tentatively added obj to
544 * workspace if it's already present.
546 if (workspace[initiallen] == obj)
551 /* We'd better have fixed at least one loop */
553 exit_horribly(NULL, modulename, "could not identify dependency loop\n");
559 * Recursively search for a circular dependency loop that doesn't include
560 * any existing workspace members.
562 * obj: object we are examining now
563 * startPoint: dumpId of starting object for the hoped-for circular loop
564 * workspace[]: work array for previously processed and current objects
565 * depth: number of valid entries in workspace[] at call
566 * newDepth: if successful, set to new number of workspace[] entries
568 * On success, *newDepth is set and workspace[] entries depth..*newDepth-1
569 * are filled with pointers to the members of the loop.
571 * Note: it is possible that the given starting object is a member of more
572 * than one cycle; if so, we will find an arbitrary one of the cycles.
575 findLoop(DumpableObject *obj,
577 DumpableObject **workspace,
584 * Reject if obj is already present in workspace. This test serves three
585 * purposes: it prevents us from finding loops that overlap
586 * previously-processed loops, it prevents us from going into infinite
587 * recursion if we are given a startPoint object that links to a cycle
588 * it's not a member of, and it guarantees that we can't overflow the
589 * allocated size of workspace[].
591 for (i = 0; i < depth; i++)
593 if (workspace[i] == obj)
598 * Okay, tentatively add obj to workspace
600 workspace[depth++] = obj;
603 * See if we've found a loop back to the desired startPoint; if so, done
605 for (i = 0; i < obj->nDeps; i++)
607 if (obj->dependencies[i] == startPoint)
615 * Recurse down each outgoing branch
617 for (i = 0; i < obj->nDeps; i++)
619 DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
622 continue; /* ignore dependencies on undumped objects */
623 if (findLoop(nextobj,
635 * A user-defined datatype will have a dependency loop with each of its
636 * I/O functions (since those have the datatype as input or output).
637 * Break the loop and make the I/O function depend on the associated
638 * shell type, instead.
641 repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
643 TypeInfo *typeInfo = (TypeInfo *) typeobj;
645 /* remove function's dependency on type */
646 removeObjectDependency(funcobj, typeobj->dumpId);
648 /* add function's dependency on shell type, instead */
649 if (typeInfo->shellType)
651 addObjectDependency(funcobj, typeInfo->shellType->dobj.dumpId);
652 /* Mark shell type as to be dumped if any I/O function is */
654 typeInfo->shellType->dobj.dump = true;
659 * Because we force a view to depend on its ON SELECT rule, while there
660 * will be an implicit dependency in the other direction, we need to break
661 * the loop. If there are no other objects in the loop then we can remove
662 * the implicit dependency and leave the ON SELECT rule non-separate.
665 repairViewRuleLoop(DumpableObject *viewobj,
666 DumpableObject *ruleobj)
668 /* remove rule's dependency on view */
669 removeObjectDependency(ruleobj, viewobj->dumpId);
673 * However, if there are other objects in the loop, we must break the loop
674 * by making the ON SELECT rule a separately-dumped object.
676 * Because findLoop() finds shorter cycles before longer ones, it's likely
677 * that we will have previously fired repairViewRuleLoop() and removed the
678 * rule's dependency on the view. Put it back to ensure the rule won't be
679 * emitted before the view...
682 repairViewRuleMultiLoop(DumpableObject *viewobj,
683 DumpableObject *ruleobj)
685 /* remove view's dependency on rule */
686 removeObjectDependency(viewobj, ruleobj->dumpId);
687 /* pretend view is a plain table and dump it that way */
688 ((TableInfo *) viewobj)->relkind = 'r'; /* RELKIND_RELATION */
689 /* mark rule as needing its own dump */
690 ((RuleInfo *) ruleobj)->separate = true;
691 /* put back rule's dependency on view */
692 addObjectDependency(ruleobj, viewobj->dumpId);
696 * Because we make tables depend on their CHECK constraints, while there
697 * will be an automatic dependency in the other direction, we need to break
698 * the loop. If there are no other objects in the loop then we can remove
699 * the automatic dependency and leave the CHECK constraint non-separate.
702 repairTableConstraintLoop(DumpableObject *tableobj,
703 DumpableObject *constraintobj)
705 /* remove constraint's dependency on table */
706 removeObjectDependency(constraintobj, tableobj->dumpId);
710 * However, if there are other objects in the loop, we must break the loop
711 * by making the CHECK constraint a separately-dumped object.
713 * Because findLoop() finds shorter cycles before longer ones, it's likely
714 * that we will have previously fired repairTableConstraintLoop() and
715 * removed the constraint's dependency on the table. Put it back to ensure
716 * the constraint won't be emitted before the table...
719 repairTableConstraintMultiLoop(DumpableObject *tableobj,
720 DumpableObject *constraintobj)
722 /* remove table's dependency on constraint */
723 removeObjectDependency(tableobj, constraintobj->dumpId);
724 /* mark constraint as needing its own dump */
725 ((ConstraintInfo *) constraintobj)->separate = true;
726 /* put back constraint's dependency on table */
727 addObjectDependency(constraintobj, tableobj->dumpId);
731 * Attribute defaults behave exactly the same as CHECK constraints...
734 repairTableAttrDefLoop(DumpableObject *tableobj,
735 DumpableObject *attrdefobj)
737 /* remove attrdef's dependency on table */
738 removeObjectDependency(attrdefobj, tableobj->dumpId);
742 repairTableAttrDefMultiLoop(DumpableObject *tableobj,
743 DumpableObject *attrdefobj)
745 /* remove table's dependency on attrdef */
746 removeObjectDependency(tableobj, attrdefobj->dumpId);
747 /* mark attrdef as needing its own dump */
748 ((AttrDefInfo *) attrdefobj)->separate = true;
749 /* put back attrdef's dependency on table */
750 addObjectDependency(attrdefobj, tableobj->dumpId);
754 * CHECK constraints on domains work just like those on tables ...
757 repairDomainConstraintLoop(DumpableObject *domainobj,
758 DumpableObject *constraintobj)
760 /* remove constraint's dependency on domain */
761 removeObjectDependency(constraintobj, domainobj->dumpId);
765 repairDomainConstraintMultiLoop(DumpableObject *domainobj,
766 DumpableObject *constraintobj)
768 /* remove domain's dependency on constraint */
769 removeObjectDependency(domainobj, constraintobj->dumpId);
770 /* mark constraint as needing its own dump */
771 ((ConstraintInfo *) constraintobj)->separate = true;
772 /* put back constraint's dependency on domain */
773 addObjectDependency(constraintobj, domainobj->dumpId);
777 * Fix a dependency loop, or die trying ...
779 * This routine is mainly concerned with reducing the multiple ways that
780 * a loop might appear to common cases, which it passes off to the
781 * "fixer" routines above.
784 repairDependencyLoop(DumpableObject **loop,
790 /* Datatype and one of its I/O functions */
792 loop[0]->objType == DO_TYPE &&
793 loop[1]->objType == DO_FUNC)
795 repairTypeFuncLoop(loop[0], loop[1]);
799 loop[1]->objType == DO_TYPE &&
800 loop[0]->objType == DO_FUNC)
802 repairTypeFuncLoop(loop[1], loop[0]);
806 /* View and its ON SELECT rule */
808 loop[0]->objType == DO_TABLE &&
809 loop[1]->objType == DO_RULE &&
810 ((RuleInfo *) loop[1])->ev_type == '1' &&
811 ((RuleInfo *) loop[1])->is_instead &&
812 ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
814 repairViewRuleLoop(loop[0], loop[1]);
818 loop[1]->objType == DO_TABLE &&
819 loop[0]->objType == DO_RULE &&
820 ((RuleInfo *) loop[0])->ev_type == '1' &&
821 ((RuleInfo *) loop[0])->is_instead &&
822 ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
824 repairViewRuleLoop(loop[1], loop[0]);
828 /* Indirect loop involving view and ON SELECT rule */
831 for (i = 0; i < nLoop; i++)
833 if (loop[i]->objType == DO_TABLE)
835 for (j = 0; j < nLoop; j++)
837 if (loop[j]->objType == DO_RULE &&
838 ((RuleInfo *) loop[j])->ev_type == '1' &&
839 ((RuleInfo *) loop[j])->is_instead &&
840 ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
842 repairViewRuleMultiLoop(loop[i], loop[j]);
850 /* Table and CHECK constraint */
852 loop[0]->objType == DO_TABLE &&
853 loop[1]->objType == DO_CONSTRAINT &&
854 ((ConstraintInfo *) loop[1])->contype == 'c' &&
855 ((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
857 repairTableConstraintLoop(loop[0], loop[1]);
861 loop[1]->objType == DO_TABLE &&
862 loop[0]->objType == DO_CONSTRAINT &&
863 ((ConstraintInfo *) loop[0])->contype == 'c' &&
864 ((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
866 repairTableConstraintLoop(loop[1], loop[0]);
870 /* Indirect loop involving table and CHECK constraint */
873 for (i = 0; i < nLoop; i++)
875 if (loop[i]->objType == DO_TABLE)
877 for (j = 0; j < nLoop; j++)
879 if (loop[j]->objType == DO_CONSTRAINT &&
880 ((ConstraintInfo *) loop[j])->contype == 'c' &&
881 ((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
883 repairTableConstraintMultiLoop(loop[i], loop[j]);
891 /* Table and attribute default */
893 loop[0]->objType == DO_TABLE &&
894 loop[1]->objType == DO_ATTRDEF &&
895 ((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
897 repairTableAttrDefLoop(loop[0], loop[1]);
901 loop[1]->objType == DO_TABLE &&
902 loop[0]->objType == DO_ATTRDEF &&
903 ((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
905 repairTableAttrDefLoop(loop[1], loop[0]);
909 /* Indirect loop involving table and attribute default */
912 for (i = 0; i < nLoop; i++)
914 if (loop[i]->objType == DO_TABLE)
916 for (j = 0; j < nLoop; j++)
918 if (loop[j]->objType == DO_ATTRDEF &&
919 ((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
921 repairTableAttrDefMultiLoop(loop[i], loop[j]);
929 /* Domain and CHECK constraint */
931 loop[0]->objType == DO_TYPE &&
932 loop[1]->objType == DO_CONSTRAINT &&
933 ((ConstraintInfo *) loop[1])->contype == 'c' &&
934 ((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
936 repairDomainConstraintLoop(loop[0], loop[1]);
940 loop[1]->objType == DO_TYPE &&
941 loop[0]->objType == DO_CONSTRAINT &&
942 ((ConstraintInfo *) loop[0])->contype == 'c' &&
943 ((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
945 repairDomainConstraintLoop(loop[1], loop[0]);
949 /* Indirect loop involving domain and CHECK constraint */
952 for (i = 0; i < nLoop; i++)
954 if (loop[i]->objType == DO_TYPE)
956 for (j = 0; j < nLoop; j++)
958 if (loop[j]->objType == DO_CONSTRAINT &&
959 ((ConstraintInfo *) loop[j])->contype == 'c' &&
960 ((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
962 repairDomainConstraintMultiLoop(loop[i], loop[j]);
971 * If all the objects are TABLE_DATA items, what we must have is a
972 * circular set of foreign key constraints (or a single self-referential
973 * table). Print an appropriate complaint and break the loop arbitrarily.
975 for (i = 0; i < nLoop; i++)
977 if (loop[i]->objType != DO_TABLE_DATA)
982 write_msg(NULL, "NOTICE: there are circular foreign-key constraints among these table(s):\n");
983 for (i = 0; i < nLoop; i++)
984 write_msg(NULL, " %s\n", loop[i]->name);
985 write_msg(NULL, "You may not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.\n");
986 write_msg(NULL, "Consider using a full dump instead of a --data-only dump to avoid this problem.\n");
988 removeObjectDependency(loop[0], loop[1]->dumpId);
989 else /* must be a self-dependency */
990 removeObjectDependency(loop[0], loop[0]->dumpId);
995 * If we can't find a principled way to break the loop, complain and break
996 * it in an arbitrary fashion.
998 write_msg(modulename, "WARNING: could not resolve dependency loop among these items:\n");
999 for (i = 0; i < nLoop; i++)
1003 describeDumpableObject(loop[i], buf, sizeof(buf));
1004 write_msg(modulename, " %s\n", buf);
1008 removeObjectDependency(loop[0], loop[1]->dumpId);
1009 else /* must be a self-dependency */
1010 removeObjectDependency(loop[0], loop[0]->dumpId);
1014 * Describe a dumpable object usefully for errors
1016 * This should probably go somewhere else...
1019 describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
1021 switch (obj->objType)
1024 snprintf(buf, bufsize,
1025 "SCHEMA %s (ID %d OID %u)",
1026 obj->name, obj->dumpId, obj->catId.oid);
1029 snprintf(buf, bufsize,
1030 "EXTENSION %s (ID %d OID %u)",
1031 obj->name, obj->dumpId, obj->catId.oid);
1034 snprintf(buf, bufsize,
1035 "TYPE %s (ID %d OID %u)",
1036 obj->name, obj->dumpId, obj->catId.oid);
1039 snprintf(buf, bufsize,
1040 "SHELL TYPE %s (ID %d OID %u)",
1041 obj->name, obj->dumpId, obj->catId.oid);
1044 snprintf(buf, bufsize,
1045 "FUNCTION %s (ID %d OID %u)",
1046 obj->name, obj->dumpId, obj->catId.oid);
1049 snprintf(buf, bufsize,
1050 "AGGREGATE %s (ID %d OID %u)",
1051 obj->name, obj->dumpId, obj->catId.oid);
1054 snprintf(buf, bufsize,
1055 "OPERATOR %s (ID %d OID %u)",
1056 obj->name, obj->dumpId, obj->catId.oid);
1059 snprintf(buf, bufsize,
1060 "OPERATOR CLASS %s (ID %d OID %u)",
1061 obj->name, obj->dumpId, obj->catId.oid);
1064 snprintf(buf, bufsize,
1065 "OPERATOR FAMILY %s (ID %d OID %u)",
1066 obj->name, obj->dumpId, obj->catId.oid);
1069 snprintf(buf, bufsize,
1070 "CONVERSION %s (ID %d OID %u)",
1071 obj->name, obj->dumpId, obj->catId.oid);
1074 snprintf(buf, bufsize,
1075 "TABLE %s (ID %d OID %u)",
1076 obj->name, obj->dumpId, obj->catId.oid);
1079 snprintf(buf, bufsize,
1080 "ATTRDEF %s.%s (ID %d OID %u)",
1081 ((AttrDefInfo *) obj)->adtable->dobj.name,
1082 ((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
1083 obj->dumpId, obj->catId.oid);
1086 snprintf(buf, bufsize,
1087 "INDEX %s (ID %d OID %u)",
1088 obj->name, obj->dumpId, obj->catId.oid);
1091 snprintf(buf, bufsize,
1092 "RULE %s (ID %d OID %u)",
1093 obj->name, obj->dumpId, obj->catId.oid);
1096 snprintf(buf, bufsize,
1097 "TRIGGER %s (ID %d OID %u)",
1098 obj->name, obj->dumpId, obj->catId.oid);
1101 snprintf(buf, bufsize,
1102 "CONSTRAINT %s (ID %d OID %u)",
1103 obj->name, obj->dumpId, obj->catId.oid);
1105 case DO_FK_CONSTRAINT:
1106 snprintf(buf, bufsize,
1107 "FK CONSTRAINT %s (ID %d OID %u)",
1108 obj->name, obj->dumpId, obj->catId.oid);
1111 snprintf(buf, bufsize,
1112 "PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1113 obj->name, obj->dumpId, obj->catId.oid);
1116 snprintf(buf, bufsize,
1117 "CAST %u to %u (ID %d OID %u)",
1118 ((CastInfo *) obj)->castsource,
1119 ((CastInfo *) obj)->casttarget,
1120 obj->dumpId, obj->catId.oid);
1123 snprintf(buf, bufsize,
1124 "TABLE DATA %s (ID %d OID %u)",
1125 obj->name, obj->dumpId, obj->catId.oid);
1128 snprintf(buf, bufsize,
1129 "DUMMY TYPE %s (ID %d OID %u)",
1130 obj->name, obj->dumpId, obj->catId.oid);
1133 snprintf(buf, bufsize,
1134 "TEXT SEARCH PARSER %s (ID %d OID %u)",
1135 obj->name, obj->dumpId, obj->catId.oid);
1138 snprintf(buf, bufsize,
1139 "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
1140 obj->name, obj->dumpId, obj->catId.oid);
1143 snprintf(buf, bufsize,
1144 "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
1145 obj->name, obj->dumpId, obj->catId.oid);
1148 snprintf(buf, bufsize,
1149 "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
1150 obj->name, obj->dumpId, obj->catId.oid);
1153 snprintf(buf, bufsize,
1154 "FOREIGN DATA WRAPPER %s (ID %d OID %u)",
1155 obj->name, obj->dumpId, obj->catId.oid);
1157 case DO_FOREIGN_SERVER:
1158 snprintf(buf, bufsize,
1159 "FOREIGN SERVER %s (ID %d OID %u)",
1160 obj->name, obj->dumpId, obj->catId.oid);
1162 case DO_DEFAULT_ACL:
1163 snprintf(buf, bufsize,
1164 "DEFAULT ACL %s (ID %d OID %u)",
1165 obj->name, obj->dumpId, obj->catId.oid);
1168 snprintf(buf, bufsize,
1169 "BLOB (ID %d OID %u)",
1170 obj->dumpId, obj->catId.oid);
1173 snprintf(buf, bufsize,
1174 "BLOB DATA (ID %d)",
1178 /* shouldn't get here */
1179 snprintf(buf, bufsize,
1180 "object type %d (ID %d OID %u)",
1182 obj->dumpId, obj->catId.oid);