1 /*-------------------------------------------------------------------------
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
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
[] =
31 2, /* DO_SHELL_TYPE */
37 5, /* DO_CONVERSION */
43 12, /* DO_CONSTRAINT */
44 16, /* DO_FK_CONSTRAINT */
47 9, /* DO_TABLE_DATA */
48 7, /* DO_TABLE_TYPE */
51 3, /* DO_TSTEMPLATE */
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
[] =
65 3, /* DO_SHELL_TYPE */
71 9, /* DO_CONVERSION */
77 16, /* DO_CONSTRAINT */
78 20, /* DO_FK_CONSTRAINT */
81 13, /* DO_TABLE_DATA */
82 11, /* DO_TABLE_TYPE */
85 5, /* DO_TSTEMPLATE */
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
,
96 DumpableObject
**ordering
,
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
,
103 DumpableObject
**workspace
,
106 static void repairDependencyLoop(DumpableObject
**loop
,
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
119 sortDumpableObjectsByTypeName(DumpableObject
**objs
, int numObjs
)
122 qsort((void *) objs
, numObjs
, sizeof(DumpableObject
*),
127 DOTypeNameCompare(const void *p1
, const void *p2
)
129 DumpableObject
*obj1
= *(DumpableObject
**) p1
;
130 DumpableObject
*obj2
= *(DumpableObject
**) p2
;
134 cmpval
= newObjectTypePriority
[obj1
->objType
] -
135 newObjectTypePriority
[obj2
->objType
];
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
);
154 cmpval
= strcmp(obj1
->name
, obj2
->name
);
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.
170 sortDumpableObjectsByTypeOid(DumpableObject
**objs
, int numObjs
)
173 qsort((void *) objs
, numObjs
, sizeof(DumpableObject
*),
178 DOTypeOidCompare(const void *p1
, const void *p2
)
180 DumpableObject
*obj1
= *(DumpableObject
**) p1
;
181 DumpableObject
*obj2
= *(DumpableObject
**) p2
;
184 cmpval
= oldObjectTypePriority
[obj1
->objType
] -
185 oldObjectTypePriority
[obj2
->objType
];
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).
199 sortDumpableObjects(DumpableObject
**objs
, int numObjs
)
201 DumpableObject
**ordering
;
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
*));
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
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.
246 TopoSort(DumpableObject
**objs
,
248 DumpableObject
**ordering
, /* output argument */
249 int *nOrdering
) /* output argument */
251 DumpId maxDumpId
= getMaxDumpId();
253 int *beforeConstraints
;
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 */
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
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));
298 exit_horribly(NULL
, modulename
, "out of memory\n");
299 for (i
= 0; i
< numObjs
; i
++)
303 if (j
<= 0 || j
> maxDumpId
)
304 exit_horribly(NULL
, modulename
, "invalid dumpId %d\n", j
);
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.
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 *--------------------
349 while (heapLength
> 0)
351 /* Select object to output by removing largest heap member */
352 j
= removeHeapElement(pendingHeap
, heapLength
--);
354 /* Output candidate to ordering[] */
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.
373 for (j
= 1; j
<= maxDumpId
; j
++)
375 if (beforeConstraints
[j
] != 0)
376 ordering
[k
++] = objs
[idMap
[j
]];
383 free(beforeConstraints
);
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.
396 addHeapElement(int val
, int *heap
, int heapLength
)
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.
407 int i
= (j
- 1) >> 1;
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.
427 removeHeapElement(int *heap
, int heapLength
)
429 int result
= heap
[0];
433 if (--heapLength
<= 0)
435 val
= heap
[heapLength
]; /* value that must be reinserted */
436 i
= 0; /* i is where the "hole" is */
443 if (j
+ 1 < heapLength
&&
444 heap
[j
] < heap
[j
+ 1])
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
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
;
496 workspace
= (DumpableObject
**) malloc(totObjs
* sizeof(DumpableObject
*));
497 if (workspace
== NULL
)
498 exit_horribly(NULL
, modulename
, "out of memory\n");
502 for (i
= 0; i
< nObjs
; i
++)
504 DumpableObject
*obj
= objs
[i
];
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 */
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
)
530 /* We'd better have fixed at least one loop */
532 exit_horribly(NULL
, modulename
, "could not identify dependency loop\n");
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.
554 findLoop(DumpableObject
*obj
,
556 DumpableObject
**workspace
,
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
)
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
)
594 * Recurse down each outgoing branch
596 for (i
= 0; i
< obj
->nDeps
; i
++)
598 DumpableObject
*nextobj
= findObjectByDumpId(obj
->dependencies
[i
]);
601 continue; /* ignore dependencies on undumped objects */
602 if (findLoop(nextobj
,
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.
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 */
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.
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...
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.
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...
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...
713 repairTableAttrDefLoop(DumpableObject
*tableobj
,
714 DumpableObject
*attrdefobj
)
716 /* remove attrdef's dependency on table */
717 removeObjectDependency(attrdefobj
, tableobj
->dumpId
);
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 ...
736 repairDomainConstraintLoop(DumpableObject
*domainobj
,
737 DumpableObject
*constraintobj
)
739 /* remove constraint's dependency on domain */
740 removeObjectDependency(constraintobj
, domainobj
->dumpId
);
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.
763 repairDependencyLoop(DumpableObject
**loop
,
769 /* Datatype and one of its I/O functions */
771 loop
[0]->objType
== DO_TYPE
&&
772 loop
[1]->objType
== DO_FUNC
)
774 repairTypeFuncLoop(loop
[0], loop
[1]);
778 loop
[1]->objType
== DO_TYPE
&&
779 loop
[0]->objType
== DO_FUNC
)
781 repairTypeFuncLoop(loop
[1], loop
[0]);
785 /* View and its ON SELECT rule */
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]);
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]);
807 /* Indirect loop involving view and ON SELECT rule */
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
]);
829 /* Table and CHECK constraint */
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]);
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]);
849 /* Indirect loop involving table and CHECK constraint */
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
]);
870 /* Table and attribute default */
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]);
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]);
888 /* Indirect loop involving table and attribute default */
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
]);
908 /* Domain and CHECK constraint */
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]);
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]);
928 /* Indirect loop involving domain and CHECK constraint */
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
]);
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
)
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");
967 removeObjectDependency(loop
[0], loop
[1]->dumpId
);
968 else /* must be a self-dependency */
969 removeObjectDependency(loop
[0], loop
[0]->dumpId
);
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
++)
982 describeDumpableObject(loop
[i
], buf
, sizeof(buf
));
983 write_msg(modulename
, " %s\n", buf
);
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...
998 describeDumpableObject(DumpableObject
*obj
, char *buf
, int bufsize
)
1000 switch (obj
->objType
)
1003 snprintf(buf
, bufsize
,
1004 "SCHEMA %s (ID %d OID %u)",
1005 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1008 snprintf(buf
, bufsize
,
1009 "TYPE %s (ID %d OID %u)",
1010 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1013 snprintf(buf
, bufsize
,
1014 "SHELL TYPE %s (ID %d OID %u)",
1015 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1018 snprintf(buf
, bufsize
,
1019 "FUNCTION %s (ID %d OID %u)",
1020 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1023 snprintf(buf
, bufsize
,
1024 "AGGREGATE %s (ID %d OID %u)",
1025 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1028 snprintf(buf
, bufsize
,
1029 "OPERATOR %s (ID %d OID %u)",
1030 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1033 snprintf(buf
, bufsize
,
1034 "OPERATOR CLASS %s (ID %d OID %u)",
1035 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1038 snprintf(buf
, bufsize
,
1039 "OPERATOR FAMILY %s (ID %d OID %u)",
1040 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1043 snprintf(buf
, bufsize
,
1044 "CONVERSION %s (ID %d OID %u)",
1045 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1048 snprintf(buf
, bufsize
,
1049 "TABLE %s (ID %d OID %u)",
1050 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
1060 snprintf(buf
, bufsize
,
1061 "INDEX %s (ID %d OID %u)",
1062 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1065 snprintf(buf
, bufsize
,
1066 "RULE %s (ID %d OID %u)",
1067 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1070 snprintf(buf
, bufsize
,
1071 "TRIGGER %s (ID %d OID %u)",
1072 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1075 snprintf(buf
, bufsize
,
1076 "CONSTRAINT %s (ID %d OID %u)",
1077 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
1085 snprintf(buf
, bufsize
,
1086 "PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1087 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
1097 snprintf(buf
, bufsize
,
1098 "TABLE DATA %s (ID %d OID %u)",
1099 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1102 snprintf(buf
, bufsize
,
1103 "TABLE TYPE %s (ID %d OID %u)",
1104 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1107 snprintf(buf
, bufsize
,
1108 "TEXT SEARCH PARSER %s (ID %d OID %u)",
1109 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1112 snprintf(buf
, bufsize
,
1113 "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
1114 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1117 snprintf(buf
, bufsize
,
1118 "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
1119 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1122 snprintf(buf
, bufsize
,
1123 "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
1124 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1127 snprintf(buf
, bufsize
,
1131 case DO_BLOB_COMMENTS
:
1132 snprintf(buf
, bufsize
,
1133 "BLOB COMMENTS (ID %d)",
1137 /* shouldn't get here */
1138 snprintf(buf
, bufsize
,
1139 "object type %d (ID %d OID %u)",
1141 obj
->dumpId
, obj
->catId
.oid
);