1 /*-------------------------------------------------------------------------
4 * Sort the items of a dump into a safe order for dumping
7 * Portions Copyright (c) 1996-2009, 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.) Note: text
26 * search and foreign-data objects can't really happen here, so the rather
27 * bogus priorities for them don't matter.
29 static const int oldObjectTypePriority
[] =
33 2, /* DO_SHELL_TYPE */
39 5, /* DO_CONVERSION */
45 12, /* DO_CONSTRAINT */
46 16, /* DO_FK_CONSTRAINT */
49 9, /* DO_TABLE_DATA */
50 7, /* DO_DUMMY_TYPE */
53 3, /* DO_TSTEMPLATE */
56 4, /* DO_FOREIGN_SERVER */
58 11 /* DO_BLOB_COMMENTS */
62 * Sort priority for object types when dumping newer databases.
63 * Objects are sorted by type, and within a type by name.
65 static const int newObjectTypePriority
[] =
69 3, /* DO_SHELL_TYPE */
75 9, /* DO_CONVERSION */
81 22, /* DO_CONSTRAINT */
82 26, /* DO_FK_CONSTRAINT */
85 19, /* DO_TABLE_DATA */
86 17, /* DO_DUMMY_TYPE */
89 11, /* DO_TSTEMPLATE */
92 15, /* DO_FOREIGN_SERVER */
94 21 /* DO_BLOB_COMMENTS */
98 static int DOTypeNameCompare(const void *p1
, const void *p2
);
99 static int DOTypeOidCompare(const void *p1
, const void *p2
);
100 static bool TopoSort(DumpableObject
**objs
,
102 DumpableObject
**ordering
,
104 static void addHeapElement(int val
, int *heap
, int heapLength
);
105 static int removeHeapElement(int *heap
, int heapLength
);
106 static void findDependencyLoops(DumpableObject
**objs
, int nObjs
, int totObjs
);
107 static bool findLoop(DumpableObject
*obj
,
109 DumpableObject
**workspace
,
112 static void repairDependencyLoop(DumpableObject
**loop
,
114 static void describeDumpableObject(DumpableObject
*obj
,
115 char *buf
, int bufsize
);
119 * Sort the given objects into a type/name-based ordering
121 * Normally this is just the starting point for the dependency-based
125 sortDumpableObjectsByTypeName(DumpableObject
**objs
, int numObjs
)
128 qsort((void *) objs
, numObjs
, sizeof(DumpableObject
*),
133 DOTypeNameCompare(const void *p1
, const void *p2
)
135 DumpableObject
*obj1
= *(DumpableObject
**) p1
;
136 DumpableObject
*obj2
= *(DumpableObject
**) p2
;
140 cmpval
= newObjectTypePriority
[obj1
->objType
] -
141 newObjectTypePriority
[obj2
->objType
];
147 * Sort by namespace. Note that all objects of the same type should
148 * either have or not have a namespace link, so we needn't be fancy about
149 * cases where one link is null and the other not.
151 if (obj1
->namespace && obj2
->namespace)
153 cmpval
= strcmp(obj1
->namespace->dobj
.name
,
154 obj2
->namespace->dobj
.name
);
160 cmpval
= strcmp(obj1
->name
, obj2
->name
);
164 /* Probably shouldn't get here, but if we do, sort by OID */
165 return oidcmp(obj1
->catId
.oid
, obj2
->catId
.oid
);
170 * Sort the given objects into a type/OID-based ordering
172 * This is used with pre-7.3 source databases as a crude substitute for the
173 * lack of dependency information.
176 sortDumpableObjectsByTypeOid(DumpableObject
**objs
, int numObjs
)
179 qsort((void *) objs
, numObjs
, sizeof(DumpableObject
*),
184 DOTypeOidCompare(const void *p1
, const void *p2
)
186 DumpableObject
*obj1
= *(DumpableObject
**) p1
;
187 DumpableObject
*obj2
= *(DumpableObject
**) p2
;
190 cmpval
= oldObjectTypePriority
[obj1
->objType
] -
191 oldObjectTypePriority
[obj2
->objType
];
196 return oidcmp(obj1
->catId
.oid
, obj2
->catId
.oid
);
201 * Sort the given objects into a safe dump order using dependency
202 * information (to the extent we have it available).
205 sortDumpableObjects(DumpableObject
**objs
, int numObjs
)
207 DumpableObject
**ordering
;
213 ordering
= (DumpableObject
**) malloc(numObjs
* sizeof(DumpableObject
*));
214 if (ordering
== NULL
)
215 exit_horribly(NULL
, modulename
, "out of memory\n");
217 while (!TopoSort(objs
, numObjs
, ordering
, &nOrdering
))
218 findDependencyLoops(ordering
, nOrdering
, numObjs
);
220 memcpy(objs
, ordering
, numObjs
* sizeof(DumpableObject
*));
226 * TopoSort -- topological sort of a dump list
228 * Generate a re-ordering of the dump list that satisfies all the dependency
229 * constraints shown in the dump list. (Each such constraint is a fact of a
230 * partial ordering.) Minimize rearrangement of the list not needed to
231 * achieve the partial ordering.
233 * The input is the list of numObjs objects in objs[]. This list is not
236 * Returns TRUE if able to build an ordering that satisfies all the
237 * constraints, FALSE if not (there are contradictory constraints).
239 * On success (TRUE result), ordering[] is filled with a sorted array of
240 * DumpableObject pointers, of length equal to the input list length.
242 * On failure (FALSE result), ordering[] is filled with an unsorted array of
243 * DumpableObject pointers of length *nOrdering, listing the objects that
244 * prevented the sort from being completed. In general, these objects either
245 * participate directly in a dependency cycle, or are depended on by objects
246 * that are in a cycle. (The latter objects are not actually problematic,
247 * but it takes further analysis to identify which are which.)
249 * The caller is responsible for allocating sufficient space at *ordering.
252 TopoSort(DumpableObject
**objs
,
254 DumpableObject
**ordering
, /* output argument */
255 int *nOrdering
) /* output argument */
257 DumpId maxDumpId
= getMaxDumpId();
259 int *beforeConstraints
;
268 * This is basically the same algorithm shown for topological sorting in
269 * Knuth's Volume 1. However, we would like to minimize unnecessary
270 * rearrangement of the input ordering; that is, when we have a choice of
271 * which item to output next, we always want to take the one highest in
272 * the original list. Therefore, instead of maintaining an unordered
273 * linked list of items-ready-to-output as Knuth does, we maintain a heap
274 * of their item numbers, which we can use as a priority queue. This
275 * turns the algorithm from O(N) to O(N log N) because each insertion or
276 * removal of a heap item takes O(log N) time. However, that's still
277 * plenty fast enough for this application.
280 *nOrdering
= numObjs
; /* for success return */
282 /* Eliminate the null case */
286 /* Create workspace for the above-described heap */
287 pendingHeap
= (int *) malloc(numObjs
* sizeof(int));
288 if (pendingHeap
== NULL
)
289 exit_horribly(NULL
, modulename
, "out of memory\n");
292 * Scan the constraints, and for each item in the input, generate a count
293 * of the number of constraints that say it must be before something else.
294 * The count for the item with dumpId j is stored in beforeConstraints[j].
295 * We also make a map showing the input-order index of the item with
298 beforeConstraints
= (int *) malloc((maxDumpId
+ 1) * sizeof(int));
299 if (beforeConstraints
== NULL
)
300 exit_horribly(NULL
, modulename
, "out of memory\n");
301 memset(beforeConstraints
, 0, (maxDumpId
+ 1) * sizeof(int));
302 idMap
= (int *) malloc((maxDumpId
+ 1) * sizeof(int));
304 exit_horribly(NULL
, modulename
, "out of memory\n");
305 for (i
= 0; i
< numObjs
; i
++)
309 if (j
<= 0 || j
> maxDumpId
)
310 exit_horribly(NULL
, modulename
, "invalid dumpId %d\n", j
);
312 for (j
= 0; j
< obj
->nDeps
; j
++)
314 k
= obj
->dependencies
[j
];
315 if (k
<= 0 || k
> maxDumpId
)
316 exit_horribly(NULL
, modulename
, "invalid dependency %d\n", k
);
317 beforeConstraints
[k
]++;
322 * Now initialize the heap of items-ready-to-output by filling it with the
323 * indexes of items that already have beforeConstraints[id] == 0.
325 * The essential property of a heap is heap[(j-1)/2] >= heap[j] for each j
326 * in the range 1..heapLength-1 (note we are using 0-based subscripts
327 * here, while the discussion in Knuth assumes 1-based subscripts). So, if
328 * we simply enter the indexes into pendingHeap[] in decreasing order, we
329 * a-fortiori have the heap invariant satisfied at completion of this
330 * loop, and don't need to do any sift-up comparisons.
333 for (i
= numObjs
; --i
>= 0;)
335 if (beforeConstraints
[objs
[i
]->dumpId
] == 0)
336 pendingHeap
[heapLength
++] = i
;
339 /*--------------------
340 * Now emit objects, working backwards in the output list. At each step,
341 * we use the priority heap to select the last item that has no remaining
342 * before-constraints. We remove that item from the heap, output it to
343 * ordering[], and decrease the beforeConstraints count of each of the
344 * items it was constrained against. Whenever an item's beforeConstraints
345 * count is thereby decreased to zero, we insert it into the priority heap
346 * to show that it is a candidate to output. We are done when the heap
347 * becomes empty; if we have output every element then we succeeded,
348 * otherwise we failed.
349 * i = number of ordering[] entries left to output
350 * j = objs[] index of item we are outputting
351 * k = temp for scanning constraint list for item j
352 *--------------------
355 while (heapLength
> 0)
357 /* Select object to output by removing largest heap member */
358 j
= removeHeapElement(pendingHeap
, heapLength
--);
360 /* Output candidate to ordering[] */
362 /* Update beforeConstraints counts of its predecessors */
363 for (k
= 0; k
< obj
->nDeps
; k
++)
365 int id
= obj
->dependencies
[k
];
367 if ((--beforeConstraints
[id
]) == 0)
368 addHeapElement(idMap
[id
], pendingHeap
, heapLength
++);
373 * If we failed, report the objects that couldn't be output; these are the
374 * ones with beforeConstraints[] still nonzero.
379 for (j
= 1; j
<= maxDumpId
; j
++)
381 if (beforeConstraints
[j
] != 0)
382 ordering
[k
++] = objs
[idMap
[j
]];
389 free(beforeConstraints
);
396 * Add an item to a heap (priority queue)
398 * heapLength is the current heap size; caller is responsible for increasing
399 * its value after the call. There must be sufficient storage at *heap.
402 addHeapElement(int val
, int *heap
, int heapLength
)
407 * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth is
408 * using 1-based array indexes, not 0-based.
413 int i
= (j
- 1) >> 1;
424 * Remove the largest item present in a heap (priority queue)
426 * heapLength is the current heap size; caller is responsible for decreasing
427 * its value after the call.
429 * We remove and return heap[0], which is always the largest element of
430 * the heap, and then "sift up" to maintain the heap invariant.
433 removeHeapElement(int *heap
, int heapLength
)
435 int result
= heap
[0];
439 if (--heapLength
<= 0)
441 val
= heap
[heapLength
]; /* value that must be reinserted */
442 i
= 0; /* i is where the "hole" is */
449 if (j
+ 1 < heapLength
&&
450 heap
[j
] < heap
[j
+ 1])
462 * findDependencyLoops - identify loops in TopoSort's failure output,
463 * and pass each such loop to repairDependencyLoop() for action
465 * In general there may be many loops in the set of objects returned by
466 * TopoSort; for speed we should try to repair as many loops as we can
467 * before trying TopoSort again. We can safely repair loops that are
468 * disjoint (have no members in common); if we find overlapping loops
469 * then we repair only the first one found, because the action taken to
470 * repair the first might have repaired the other as well. (If not,
471 * we'll fix it on the next go-round.)
473 * objs[] lists the objects TopoSort couldn't sort
474 * nObjs is the number of such objects
475 * totObjs is the total number of objects in the universe
478 findDependencyLoops(DumpableObject
**objs
, int nObjs
, int totObjs
)
481 * We use a workspace array, the initial part of which stores objects
482 * already processed, and the rest of which is used as temporary space to
483 * try to build a loop in. This is convenient because we do not care
484 * about loops involving already-processed objects (see notes above); we
485 * can easily reject such loops in findLoop() because of this
486 * representation. After we identify and process a loop, we can add it to
487 * the initial part of the workspace just by moving the boundary pointer.
489 * When we determine that an object is not part of any interesting loop,
490 * we also add it to the initial part of the workspace. This is not
491 * necessary for correctness, but saves later invocations of findLoop()
492 * from uselessly chasing references to such an object.
494 * We make the workspace large enough to hold all objects in the original
495 * universe. This is probably overkill, but it's provably enough space...
497 DumpableObject
**workspace
;
502 workspace
= (DumpableObject
**) malloc(totObjs
* sizeof(DumpableObject
*));
503 if (workspace
== NULL
)
504 exit_horribly(NULL
, modulename
, "out of memory\n");
508 for (i
= 0; i
< nObjs
; i
++)
510 DumpableObject
*obj
= objs
[i
];
513 workspace
[initiallen
] = NULL
; /* see test below */
515 if (findLoop(obj
, obj
->dumpId
, workspace
, initiallen
, &newlen
))
517 /* Found a loop of length newlen - initiallen */
518 repairDependencyLoop(&workspace
[initiallen
], newlen
- initiallen
);
519 /* Add loop members to workspace */
526 * Didn't find a loop, but add this object to workspace anyway,
527 * unless it's already present. We piggyback on the test that
528 * findLoop() already did: it won't have tentatively added obj to
529 * workspace if it's already present.
531 if (workspace
[initiallen
] == obj
)
536 /* We'd better have fixed at least one loop */
538 exit_horribly(NULL
, modulename
, "could not identify dependency loop\n");
544 * Recursively search for a circular dependency loop that doesn't include
545 * any existing workspace members.
547 * obj: object we are examining now
548 * startPoint: dumpId of starting object for the hoped-for circular loop
549 * workspace[]: work array for previously processed and current objects
550 * depth: number of valid entries in workspace[] at call
551 * newDepth: if successful, set to new number of workspace[] entries
553 * On success, *newDepth is set and workspace[] entries depth..*newDepth-1
554 * are filled with pointers to the members of the loop.
556 * Note: it is possible that the given starting object is a member of more
557 * than one cycle; if so, we will find an arbitrary one of the cycles.
560 findLoop(DumpableObject
*obj
,
562 DumpableObject
**workspace
,
569 * Reject if obj is already present in workspace. This test serves three
570 * purposes: it prevents us from finding loops that overlap
571 * previously-processed loops, it prevents us from going into infinite
572 * recursion if we are given a startPoint object that links to a cycle
573 * it's not a member of, and it guarantees that we can't overflow the
574 * allocated size of workspace[].
576 for (i
= 0; i
< depth
; i
++)
578 if (workspace
[i
] == obj
)
583 * Okay, tentatively add obj to workspace
585 workspace
[depth
++] = obj
;
588 * See if we've found a loop back to the desired startPoint; if so, done
590 for (i
= 0; i
< obj
->nDeps
; i
++)
592 if (obj
->dependencies
[i
] == startPoint
)
600 * Recurse down each outgoing branch
602 for (i
= 0; i
< obj
->nDeps
; i
++)
604 DumpableObject
*nextobj
= findObjectByDumpId(obj
->dependencies
[i
]);
607 continue; /* ignore dependencies on undumped objects */
608 if (findLoop(nextobj
,
620 * A user-defined datatype will have a dependency loop with each of its
621 * I/O functions (since those have the datatype as input or output).
622 * Break the loop and make the I/O function depend on the associated
623 * shell type, instead.
626 repairTypeFuncLoop(DumpableObject
*typeobj
, DumpableObject
*funcobj
)
628 TypeInfo
*typeInfo
= (TypeInfo
*) typeobj
;
630 /* remove function's dependency on type */
631 removeObjectDependency(funcobj
, typeobj
->dumpId
);
633 /* add function's dependency on shell type, instead */
634 if (typeInfo
->shellType
)
636 addObjectDependency(funcobj
, typeInfo
->shellType
->dobj
.dumpId
);
637 /* Mark shell type as to be dumped if any I/O function is */
639 typeInfo
->shellType
->dobj
.dump
= true;
644 * Because we force a view to depend on its ON SELECT rule, while there
645 * will be an implicit dependency in the other direction, we need to break
646 * the loop. If there are no other objects in the loop then we can remove
647 * the implicit dependency and leave the ON SELECT rule non-separate.
650 repairViewRuleLoop(DumpableObject
*viewobj
,
651 DumpableObject
*ruleobj
)
653 /* remove rule's dependency on view */
654 removeObjectDependency(ruleobj
, viewobj
->dumpId
);
658 * However, if there are other objects in the loop, we must break the loop
659 * by making the ON SELECT rule a separately-dumped object.
661 * Because findLoop() finds shorter cycles before longer ones, it's likely
662 * that we will have previously fired repairViewRuleLoop() and removed the
663 * rule's dependency on the view. Put it back to ensure the rule won't be
664 * emitted before the view...
667 repairViewRuleMultiLoop(DumpableObject
*viewobj
,
668 DumpableObject
*ruleobj
)
670 /* remove view's dependency on rule */
671 removeObjectDependency(viewobj
, ruleobj
->dumpId
);
672 /* pretend view is a plain table and dump it that way */
673 ((TableInfo
*) viewobj
)->relkind
= 'r'; /* RELKIND_RELATION */
674 /* mark rule as needing its own dump */
675 ((RuleInfo
*) ruleobj
)->separate
= true;
676 /* put back rule's dependency on view */
677 addObjectDependency(ruleobj
, viewobj
->dumpId
);
681 * Because we make tables depend on their CHECK constraints, while there
682 * will be an automatic dependency in the other direction, we need to break
683 * the loop. If there are no other objects in the loop then we can remove
684 * the automatic dependency and leave the CHECK constraint non-separate.
687 repairTableConstraintLoop(DumpableObject
*tableobj
,
688 DumpableObject
*constraintobj
)
690 /* remove constraint's dependency on table */
691 removeObjectDependency(constraintobj
, tableobj
->dumpId
);
695 * However, if there are other objects in the loop, we must break the loop
696 * by making the CHECK constraint a separately-dumped object.
698 * Because findLoop() finds shorter cycles before longer ones, it's likely
699 * that we will have previously fired repairTableConstraintLoop() and
700 * removed the constraint's dependency on the table. Put it back to ensure
701 * the constraint won't be emitted before the table...
704 repairTableConstraintMultiLoop(DumpableObject
*tableobj
,
705 DumpableObject
*constraintobj
)
707 /* remove table's dependency on constraint */
708 removeObjectDependency(tableobj
, constraintobj
->dumpId
);
709 /* mark constraint as needing its own dump */
710 ((ConstraintInfo
*) constraintobj
)->separate
= true;
711 /* put back constraint's dependency on table */
712 addObjectDependency(constraintobj
, tableobj
->dumpId
);
716 * Attribute defaults behave exactly the same as CHECK constraints...
719 repairTableAttrDefLoop(DumpableObject
*tableobj
,
720 DumpableObject
*attrdefobj
)
722 /* remove attrdef's dependency on table */
723 removeObjectDependency(attrdefobj
, tableobj
->dumpId
);
727 repairTableAttrDefMultiLoop(DumpableObject
*tableobj
,
728 DumpableObject
*attrdefobj
)
730 /* remove table's dependency on attrdef */
731 removeObjectDependency(tableobj
, attrdefobj
->dumpId
);
732 /* mark attrdef as needing its own dump */
733 ((AttrDefInfo
*) attrdefobj
)->separate
= true;
734 /* put back attrdef's dependency on table */
735 addObjectDependency(attrdefobj
, tableobj
->dumpId
);
739 * CHECK constraints on domains work just like those on tables ...
742 repairDomainConstraintLoop(DumpableObject
*domainobj
,
743 DumpableObject
*constraintobj
)
745 /* remove constraint's dependency on domain */
746 removeObjectDependency(constraintobj
, domainobj
->dumpId
);
750 repairDomainConstraintMultiLoop(DumpableObject
*domainobj
,
751 DumpableObject
*constraintobj
)
753 /* remove domain's dependency on constraint */
754 removeObjectDependency(domainobj
, constraintobj
->dumpId
);
755 /* mark constraint as needing its own dump */
756 ((ConstraintInfo
*) constraintobj
)->separate
= true;
757 /* put back constraint's dependency on domain */
758 addObjectDependency(constraintobj
, domainobj
->dumpId
);
762 * Fix a dependency loop, or die trying ...
764 * This routine is mainly concerned with reducing the multiple ways that
765 * a loop might appear to common cases, which it passes off to the
766 * "fixer" routines above.
769 repairDependencyLoop(DumpableObject
**loop
,
775 /* Datatype and one of its I/O functions */
777 loop
[0]->objType
== DO_TYPE
&&
778 loop
[1]->objType
== DO_FUNC
)
780 repairTypeFuncLoop(loop
[0], loop
[1]);
784 loop
[1]->objType
== DO_TYPE
&&
785 loop
[0]->objType
== DO_FUNC
)
787 repairTypeFuncLoop(loop
[1], loop
[0]);
791 /* View and its ON SELECT rule */
793 loop
[0]->objType
== DO_TABLE
&&
794 loop
[1]->objType
== DO_RULE
&&
795 ((RuleInfo
*) loop
[1])->ev_type
== '1' &&
796 ((RuleInfo
*) loop
[1])->is_instead
&&
797 ((RuleInfo
*) loop
[1])->ruletable
== (TableInfo
*) loop
[0])
799 repairViewRuleLoop(loop
[0], loop
[1]);
803 loop
[1]->objType
== DO_TABLE
&&
804 loop
[0]->objType
== DO_RULE
&&
805 ((RuleInfo
*) loop
[0])->ev_type
== '1' &&
806 ((RuleInfo
*) loop
[0])->is_instead
&&
807 ((RuleInfo
*) loop
[0])->ruletable
== (TableInfo
*) loop
[1])
809 repairViewRuleLoop(loop
[1], loop
[0]);
813 /* Indirect loop involving view and ON SELECT rule */
816 for (i
= 0; i
< nLoop
; i
++)
818 if (loop
[i
]->objType
== DO_TABLE
)
820 for (j
= 0; j
< nLoop
; j
++)
822 if (loop
[j
]->objType
== DO_RULE
&&
823 ((RuleInfo
*) loop
[j
])->ev_type
== '1' &&
824 ((RuleInfo
*) loop
[j
])->is_instead
&&
825 ((RuleInfo
*) loop
[j
])->ruletable
== (TableInfo
*) loop
[i
])
827 repairViewRuleMultiLoop(loop
[i
], loop
[j
]);
835 /* Table and CHECK constraint */
837 loop
[0]->objType
== DO_TABLE
&&
838 loop
[1]->objType
== DO_CONSTRAINT
&&
839 ((ConstraintInfo
*) loop
[1])->contype
== 'c' &&
840 ((ConstraintInfo
*) loop
[1])->contable
== (TableInfo
*) loop
[0])
842 repairTableConstraintLoop(loop
[0], loop
[1]);
846 loop
[1]->objType
== DO_TABLE
&&
847 loop
[0]->objType
== DO_CONSTRAINT
&&
848 ((ConstraintInfo
*) loop
[0])->contype
== 'c' &&
849 ((ConstraintInfo
*) loop
[0])->contable
== (TableInfo
*) loop
[1])
851 repairTableConstraintLoop(loop
[1], loop
[0]);
855 /* Indirect loop involving table and CHECK constraint */
858 for (i
= 0; i
< nLoop
; i
++)
860 if (loop
[i
]->objType
== DO_TABLE
)
862 for (j
= 0; j
< nLoop
; j
++)
864 if (loop
[j
]->objType
== DO_CONSTRAINT
&&
865 ((ConstraintInfo
*) loop
[j
])->contype
== 'c' &&
866 ((ConstraintInfo
*) loop
[j
])->contable
== (TableInfo
*) loop
[i
])
868 repairTableConstraintMultiLoop(loop
[i
], loop
[j
]);
876 /* Table and attribute default */
878 loop
[0]->objType
== DO_TABLE
&&
879 loop
[1]->objType
== DO_ATTRDEF
&&
880 ((AttrDefInfo
*) loop
[1])->adtable
== (TableInfo
*) loop
[0])
882 repairTableAttrDefLoop(loop
[0], loop
[1]);
886 loop
[1]->objType
== DO_TABLE
&&
887 loop
[0]->objType
== DO_ATTRDEF
&&
888 ((AttrDefInfo
*) loop
[0])->adtable
== (TableInfo
*) loop
[1])
890 repairTableAttrDefLoop(loop
[1], loop
[0]);
894 /* Indirect loop involving table and attribute default */
897 for (i
= 0; i
< nLoop
; i
++)
899 if (loop
[i
]->objType
== DO_TABLE
)
901 for (j
= 0; j
< nLoop
; j
++)
903 if (loop
[j
]->objType
== DO_ATTRDEF
&&
904 ((AttrDefInfo
*) loop
[j
])->adtable
== (TableInfo
*) loop
[i
])
906 repairTableAttrDefMultiLoop(loop
[i
], loop
[j
]);
914 /* Domain and CHECK constraint */
916 loop
[0]->objType
== DO_TYPE
&&
917 loop
[1]->objType
== DO_CONSTRAINT
&&
918 ((ConstraintInfo
*) loop
[1])->contype
== 'c' &&
919 ((ConstraintInfo
*) loop
[1])->condomain
== (TypeInfo
*) loop
[0])
921 repairDomainConstraintLoop(loop
[0], loop
[1]);
925 loop
[1]->objType
== DO_TYPE
&&
926 loop
[0]->objType
== DO_CONSTRAINT
&&
927 ((ConstraintInfo
*) loop
[0])->contype
== 'c' &&
928 ((ConstraintInfo
*) loop
[0])->condomain
== (TypeInfo
*) loop
[1])
930 repairDomainConstraintLoop(loop
[1], loop
[0]);
934 /* Indirect loop involving domain and CHECK constraint */
937 for (i
= 0; i
< nLoop
; i
++)
939 if (loop
[i
]->objType
== DO_TYPE
)
941 for (j
= 0; j
< nLoop
; j
++)
943 if (loop
[j
]->objType
== DO_CONSTRAINT
&&
944 ((ConstraintInfo
*) loop
[j
])->contype
== 'c' &&
945 ((ConstraintInfo
*) loop
[j
])->condomain
== (TypeInfo
*) loop
[i
])
947 repairDomainConstraintMultiLoop(loop
[i
], loop
[j
]);
956 * If all the objects are TABLE_DATA items, what we must have is a
957 * circular set of foreign key constraints (or a single self-referential
958 * table). Print an appropriate complaint and break the loop arbitrarily.
960 for (i
= 0; i
< nLoop
; i
++)
962 if (loop
[i
]->objType
!= DO_TABLE_DATA
)
967 write_msg(NULL
, "NOTICE: there are circular foreign-key constraints among these table(s):\n");
968 for (i
= 0; i
< nLoop
; i
++)
969 write_msg(NULL
, " %s\n", loop
[i
]->name
);
970 write_msg(NULL
, "You may not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.\n");
971 write_msg(NULL
, "Consider using a full dump instead of a --data-only dump to avoid this problem.\n");
973 removeObjectDependency(loop
[0], loop
[1]->dumpId
);
974 else /* must be a self-dependency */
975 removeObjectDependency(loop
[0], loop
[0]->dumpId
);
980 * If we can't find a principled way to break the loop, complain and break
981 * it in an arbitrary fashion.
983 write_msg(modulename
, "WARNING: could not resolve dependency loop among these items:\n");
984 for (i
= 0; i
< nLoop
; i
++)
988 describeDumpableObject(loop
[i
], buf
, sizeof(buf
));
989 write_msg(modulename
, " %s\n", buf
);
993 removeObjectDependency(loop
[0], loop
[1]->dumpId
);
994 else /* must be a self-dependency */
995 removeObjectDependency(loop
[0], loop
[0]->dumpId
);
999 * Describe a dumpable object usefully for errors
1001 * This should probably go somewhere else...
1004 describeDumpableObject(DumpableObject
*obj
, char *buf
, int bufsize
)
1006 switch (obj
->objType
)
1009 snprintf(buf
, bufsize
,
1010 "SCHEMA %s (ID %d OID %u)",
1011 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1014 snprintf(buf
, bufsize
,
1015 "TYPE %s (ID %d OID %u)",
1016 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1019 snprintf(buf
, bufsize
,
1020 "SHELL TYPE %s (ID %d OID %u)",
1021 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1024 snprintf(buf
, bufsize
,
1025 "FUNCTION %s (ID %d OID %u)",
1026 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1029 snprintf(buf
, bufsize
,
1030 "AGGREGATE %s (ID %d OID %u)",
1031 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1034 snprintf(buf
, bufsize
,
1035 "OPERATOR %s (ID %d OID %u)",
1036 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1039 snprintf(buf
, bufsize
,
1040 "OPERATOR CLASS %s (ID %d OID %u)",
1041 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1044 snprintf(buf
, bufsize
,
1045 "OPERATOR FAMILY %s (ID %d OID %u)",
1046 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1049 snprintf(buf
, bufsize
,
1050 "CONVERSION %s (ID %d OID %u)",
1051 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1054 snprintf(buf
, bufsize
,
1055 "TABLE %s (ID %d OID %u)",
1056 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1059 snprintf(buf
, bufsize
,
1060 "ATTRDEF %s.%s (ID %d OID %u)",
1061 ((AttrDefInfo
*) obj
)->adtable
->dobj
.name
,
1062 ((AttrDefInfo
*) obj
)->adtable
->attnames
[((AttrDefInfo
*) obj
)->adnum
- 1],
1063 obj
->dumpId
, obj
->catId
.oid
);
1066 snprintf(buf
, bufsize
,
1067 "INDEX %s (ID %d OID %u)",
1068 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1071 snprintf(buf
, bufsize
,
1072 "RULE %s (ID %d OID %u)",
1073 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1076 snprintf(buf
, bufsize
,
1077 "TRIGGER %s (ID %d OID %u)",
1078 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1081 snprintf(buf
, bufsize
,
1082 "CONSTRAINT %s (ID %d OID %u)",
1083 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1085 case DO_FK_CONSTRAINT
:
1086 snprintf(buf
, bufsize
,
1087 "FK CONSTRAINT %s (ID %d OID %u)",
1088 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1091 snprintf(buf
, bufsize
,
1092 "PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1093 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1096 snprintf(buf
, bufsize
,
1097 "CAST %u to %u (ID %d OID %u)",
1098 ((CastInfo
*) obj
)->castsource
,
1099 ((CastInfo
*) obj
)->casttarget
,
1100 obj
->dumpId
, obj
->catId
.oid
);
1103 snprintf(buf
, bufsize
,
1104 "TABLE DATA %s (ID %d OID %u)",
1105 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1108 snprintf(buf
, bufsize
,
1109 "DUMMY TYPE %s (ID %d OID %u)",
1110 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1113 snprintf(buf
, bufsize
,
1114 "TEXT SEARCH PARSER %s (ID %d OID %u)",
1115 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1118 snprintf(buf
, bufsize
,
1119 "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
1120 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1123 snprintf(buf
, bufsize
,
1124 "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
1125 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1128 snprintf(buf
, bufsize
,
1129 "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
1130 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1133 snprintf(buf
, bufsize
,
1134 "FOREIGN DATA WRAPPER %s (ID %d OID %u)",
1135 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1137 case DO_FOREIGN_SERVER
:
1138 snprintf(buf
, bufsize
,
1139 "FOREIGN SERVER %s (ID %d OID %u)",
1140 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1143 snprintf(buf
, bufsize
,
1147 case DO_BLOB_COMMENTS
:
1148 snprintf(buf
, bufsize
,
1149 "BLOB COMMENTS (ID %d)",
1153 /* shouldn't get here */
1154 snprintf(buf
, bufsize
,
1155 "object type %d (ID %d OID %u)",
1157 obj
->dumpId
, obj
->catId
.oid
);