1
/*-------------------------------------------------------------------------
4
* Sort the items of a dump into a safe order for dumping
7
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
8
* Portions Copyright (c) 1994, Regents of the University of California
12
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.8.4.1 2005-02-03 23:39:21 tgl Exp $
14
*-------------------------------------------------------------------------
17
#include "pg_backup_archiver.h"
20
static char *modulename = gettext_noop("sorter");
23
* Sort priority for object types when dumping a pre-7.3 database.
24
* Objects are sorted by priority levels, and within an equal priority level
25
* by OID. (This is a relatively crude hack to provide semi-reasonable
26
* behavior for old databases without full dependency info.)
28
static const int oldObjectTypePriority[] =
36
5, /* DO_CONVERSION */
42
11, /* DO_CONSTRAINT */
43
15, /* DO_FK_CONSTRAINT */
46
9, /* DO_TABLE_DATA */
47
7, /* DO_TABLE_TYPE */
52
* Sort priority for object types when dumping newer databases.
53
* Objects are sorted by type, and within a type by name.
55
static const int newObjectTypePriority[] =
63
9, /* DO_CONVERSION */
69
15, /* DO_CONSTRAINT */
70
19, /* DO_FK_CONSTRAINT */
73
13, /* DO_TABLE_DATA */
74
11, /* DO_TABLE_TYPE */
79
static int DOTypeNameCompare(const void *p1, const void *p2);
80
static int DOTypeOidCompare(const void *p1, const void *p2);
81
static bool TopoSort(DumpableObject **objs,
83
DumpableObject **ordering,
85
static void addHeapElement(int val, int *heap, int heapLength);
86
static int removeHeapElement(int *heap, int heapLength);
87
static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
88
static bool findLoop(DumpableObject *obj,
90
DumpableObject **workspace,
93
static void repairDependencyLoop(DumpableObject **loop,
95
static void describeDumpableObject(DumpableObject *obj,
96
char *buf, int bufsize);
100
* Sort the given objects into a type/name-based ordering
102
* Normally this is just the starting point for the dependency-based
106
sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
109
qsort((void *) objs, numObjs, sizeof(DumpableObject *),
114
DOTypeNameCompare(const void *p1, const void *p2)
116
DumpableObject *obj1 = *(DumpableObject **) p1;
117
DumpableObject *obj2 = *(DumpableObject **) p2;
121
cmpval = newObjectTypePriority[obj1->objType] -
122
newObjectTypePriority[obj2->objType];
128
* Sort by namespace. Note that all objects of the same type should
129
* either have or not have a namespace link, so we needn't be fancy
130
* about cases where one link is null and the other not.
132
if (obj1->namespace && obj2->namespace)
134
cmpval = strcmp(obj1->namespace->dobj.name,
135
obj2->namespace->dobj.name);
141
cmpval = strcmp(obj1->name, obj2->name);
145
/* Probably shouldn't get here, but if we do, sort by OID */
146
return oidcmp(obj1->catId.oid, obj2->catId.oid);
151
* Sort the given objects into a type/OID-based ordering
153
* This is used with pre-7.3 source databases as a crude substitute for the
154
* lack of dependency information.
157
sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs)
160
qsort((void *) objs, numObjs, sizeof(DumpableObject *),
165
DOTypeOidCompare(const void *p1, const void *p2)
167
DumpableObject *obj1 = *(DumpableObject **) p1;
168
DumpableObject *obj2 = *(DumpableObject **) p2;
171
cmpval = oldObjectTypePriority[obj1->objType] -
172
oldObjectTypePriority[obj2->objType];
177
return oidcmp(obj1->catId.oid, obj2->catId.oid);
182
* Sort the given objects into a safe dump order using dependency
183
* information (to the extent we have it available).
186
sortDumpableObjects(DumpableObject **objs, int numObjs)
188
DumpableObject **ordering;
194
ordering = (DumpableObject **) malloc(numObjs * sizeof(DumpableObject *));
195
if (ordering == NULL)
196
exit_horribly(NULL, modulename, "out of memory\n");
198
while (!TopoSort(objs, numObjs, ordering, &nOrdering))
199
findDependencyLoops(ordering, nOrdering, numObjs);
201
memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
207
* TopoSort -- topological sort of a dump list
209
* Generate a re-ordering of the dump list that satisfies all the dependency
210
* constraints shown in the dump list. (Each such constraint is a fact of a
211
* partial ordering.) Minimize rearrangement of the list not needed to
212
* achieve the partial ordering.
214
* The input is the list of numObjs objects in objs[]. This list is not
217
* Returns TRUE if able to build an ordering that satisfies all the
218
* constraints, FALSE if not (there are contradictory constraints).
220
* On success (TRUE result), ordering[] is filled with a sorted array of
221
* DumpableObject pointers, of length equal to the input list length.
223
* On failure (FALSE result), ordering[] is filled with an unsorted array of
224
* DumpableObject pointers of length *nOrdering, listing the objects that
225
* prevented the sort from being completed. In general, these objects either
226
* participate directly in a dependency cycle, or are depended on by objects
227
* that are in a cycle. (The latter objects are not actually problematic,
228
* but it takes further analysis to identify which are which.)
230
* The caller is responsible for allocating sufficient space at *ordering.
233
TopoSort(DumpableObject **objs,
235
DumpableObject **ordering, /* output argument */
236
int *nOrdering) /* output argument */
238
DumpId maxDumpId = getMaxDumpId();
240
int *beforeConstraints;
249
* This is basically the same algorithm shown for topological sorting
250
* in Knuth's Volume 1. However, we would like to minimize
251
* unnecessary rearrangement of the input ordering; that is, when we
252
* have a choice of which item to output next, we always want to take
253
* the one highest in the original list. Therefore, instead of
254
* maintaining an unordered linked list of items-ready-to-output as
255
* Knuth does, we maintain a heap of their item numbers, which we can
256
* use as a priority queue. This turns the algorithm from O(N) to O(N
257
* log N) because each insertion or removal of a heap item takes O(log
258
* N) time. However, that's still plenty fast enough for this
262
*nOrdering = numObjs; /* for success return */
264
/* Eliminate the null case */
268
/* Create workspace for the above-described heap */
269
pendingHeap = (int *) malloc(numObjs * sizeof(int));
270
if (pendingHeap == NULL)
271
exit_horribly(NULL, modulename, "out of memory\n");
274
* Scan the constraints, and for each item in the input, generate a
275
* count of the number of constraints that say it must be before
276
* something else. The count for the item with dumpId j is stored in
277
* beforeConstraints[j]. We also make a map showing the input-order
278
* index of the item with dumpId j.
280
beforeConstraints = (int *) malloc((maxDumpId + 1) * sizeof(int));
281
if (beforeConstraints == NULL)
282
exit_horribly(NULL, modulename, "out of memory\n");
283
memset(beforeConstraints, 0, (maxDumpId + 1) * sizeof(int));
284
idMap = (int *) malloc((maxDumpId + 1) * sizeof(int));
286
exit_horribly(NULL, modulename, "out of memory\n");
287
for (i = 0; i < numObjs; i++)
291
if (j <= 0 || j > maxDumpId)
292
exit_horribly(NULL, modulename, "invalid dumpId %d\n", j);
294
for (j = 0; j < obj->nDeps; j++)
296
k = obj->dependencies[j];
297
if (k <= 0 || k > maxDumpId)
298
exit_horribly(NULL, modulename, "invalid dependency %d\n", k);
299
beforeConstraints[k]++;
304
* Now initialize the heap of items-ready-to-output by filling it with
305
* the indexes of items that already have beforeConstraints[id] == 0.
307
* The essential property of a heap is heap[(j-1)/2] >= heap[j] for each
308
* j in the range 1..heapLength-1 (note we are using 0-based
309
* subscripts here, while the discussion in Knuth assumes 1-based
310
* subscripts). So, if we simply enter the indexes into pendingHeap[]
311
* in decreasing order, we a-fortiori have the heap invariant
312
* satisfied at completion of this loop, and don't need to do any
313
* sift-up comparisons.
316
for (i = numObjs; --i >= 0;)
318
if (beforeConstraints[objs[i]->dumpId] == 0)
319
pendingHeap[heapLength++] = i;
322
/*--------------------
323
* Now emit objects, working backwards in the output list. At each step,
324
* we use the priority heap to select the last item that has no remaining
325
* before-constraints. We remove that item from the heap, output it to
326
* ordering[], and decrease the beforeConstraints count of each of the
327
* items it was constrained against. Whenever an item's beforeConstraints
328
* count is thereby decreased to zero, we insert it into the priority heap
329
* to show that it is a candidate to output. We are done when the heap
330
* becomes empty; if we have output every element then we succeeded,
331
* otherwise we failed.
332
* i = number of ordering[] entries left to output
333
* j = objs[] index of item we are outputting
334
* k = temp for scanning constraint list for item j
335
*--------------------
338
while (heapLength > 0)
340
/* Select object to output by removing largest heap member */
341
j = removeHeapElement(pendingHeap, heapLength--);
343
/* Output candidate to ordering[] */
345
/* Update beforeConstraints counts of its predecessors */
346
for (k = 0; k < obj->nDeps; k++)
348
int id = obj->dependencies[k];
350
if ((--beforeConstraints[id]) == 0)
351
addHeapElement(idMap[id], pendingHeap, heapLength++);
356
* If we failed, report the objects that couldn't be output; these are
357
* the ones with beforeConstraints[] still nonzero.
362
for (j = 1; j <= maxDumpId; j++)
364
if (beforeConstraints[j] != 0)
365
ordering[k++] = objs[idMap[j]];
372
free(beforeConstraints);
379
* Add an item to a heap (priority queue)
381
* heapLength is the current heap size; caller is responsible for increasing
382
* its value after the call. There must be sufficient storage at *heap.
385
addHeapElement(int val, int *heap, int heapLength)
390
* Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth
391
* is using 1-based array indexes, not 0-based.
396
int i = (j - 1) >> 1;
407
* Remove the largest item present in a heap (priority queue)
409
* heapLength is the current heap size; caller is responsible for decreasing
410
* its value after the call.
412
* We remove and return heap[0], which is always the largest element of
413
* the heap, and then "sift up" to maintain the heap invariant.
416
removeHeapElement(int *heap, int heapLength)
418
int result = heap[0];
422
if (--heapLength <= 0)
424
val = heap[heapLength]; /* value that must be reinserted */
425
i = 0; /* i is where the "hole" is */
432
if (j + 1 < heapLength &&
433
heap[j] < heap[j + 1])
445
* findDependencyLoops - identify loops in TopoSort's failure output,
446
* and pass each such loop to repairDependencyLoop() for action
448
* In general there may be many loops in the set of objects returned by
449
* TopoSort; for speed we should try to repair as many loops as we can
450
* before trying TopoSort again. We can safely repair loops that are
451
* disjoint (have no members in common); if we find overlapping loops
452
* then we repair only the first one found, because the action taken to
453
* repair the first might have repaired the other as well. (If not,
454
* we'll fix it on the next go-round.)
456
* objs[] lists the objects TopoSort couldn't sort
457
* nObjs is the number of such objects
458
* totObjs is the total number of objects in the universe
461
findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
464
* We use a workspace array, the initial part of which stores objects
465
* already processed, and the rest of which is used as temporary space
466
* to try to build a loop in. This is convenient because we do not
467
* care about loops involving already-processed objects (see notes
468
* above); we can easily reject such loops in findLoop() because of
469
* this representation. After we identify and process a loop, we can
470
* add it to the initial part of the workspace just by moving the
473
* When we determine that an object is not part of any interesting loop,
474
* we also add it to the initial part of the workspace. This is not
475
* necessary for correctness, but saves later invocations of
476
* findLoop() from uselessly chasing references to such an object.
478
* We make the workspace large enough to hold all objects in the original
479
* universe. This is probably overkill, but it's provably enough
482
DumpableObject **workspace;
487
workspace = (DumpableObject **) malloc(totObjs * sizeof(DumpableObject *));
488
if (workspace == NULL)
489
exit_horribly(NULL, modulename, "out of memory\n");
493
for (i = 0; i < nObjs; i++)
495
DumpableObject *obj = objs[i];
498
workspace[initiallen] = NULL; /* see test below */
500
if (findLoop(obj, obj->dumpId, workspace, initiallen, &newlen))
502
/* Found a loop of length newlen - initiallen */
503
repairDependencyLoop(&workspace[initiallen], newlen - initiallen);
504
/* Add loop members to workspace */
511
* Didn't find a loop, but add this object to workspace
512
* anyway, unless it's already present. We piggyback on the
513
* test that findLoop() already did: it won't have tentatively
514
* added obj to workspace if it's already present.
516
if (workspace[initiallen] == obj)
521
/* We'd better have fixed at least one loop */
523
exit_horribly(NULL, modulename, "could not identify dependency loop\n");
529
* Recursively search for a circular dependency loop that doesn't include
530
* any existing workspace members.
532
* obj: object we are examining now
533
* startPoint: dumpId of starting object for the hoped-for circular loop
534
* workspace[]: work array for previously processed and current objects
535
* depth: number of valid entries in workspace[] at call
536
* newDepth: if successful, set to new number of workspace[] entries
538
* On success, *newDepth is set and workspace[] entries depth..*newDepth-1
539
* are filled with pointers to the members of the loop.
541
* Note: it is possible that the given starting object is a member of more
542
* than one cycle; if so, we will find an arbitrary one of the cycles.
545
findLoop(DumpableObject *obj,
547
DumpableObject **workspace,
554
* Reject if obj is already present in workspace. This test serves
555
* three purposes: it prevents us from finding loops that overlap
556
* previously-processed loops, it prevents us from going into infinite
557
* recursion if we are given a startPoint object that links to a cycle
558
* it's not a member of, and it guarantees that we can't overflow the
559
* allocated size of workspace[].
561
for (i = 0; i < depth; i++)
563
if (workspace[i] == obj)
568
* Okay, tentatively add obj to workspace
570
workspace[depth++] = obj;
573
* See if we've found a loop back to the desired startPoint; if so,
576
for (i = 0; i < obj->nDeps; i++)
578
if (obj->dependencies[i] == startPoint)
586
* Recurse down each outgoing branch
588
for (i = 0; i < obj->nDeps; i++)
590
DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
593
continue; /* ignore dependencies on undumped objects */
594
if (findLoop(nextobj,
606
* A user-defined datatype will have a dependency loop with each of its
607
* I/O functions (since those have the datatype as input or output).
608
* We want the dump ordering to be the input function, then any other
609
* I/O functions, then the datatype. So we break the circularity in
610
* favor of the functions, and add a dependency from any non-input
611
* function to the input function.
614
repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
616
TypeInfo *typeInfo = (TypeInfo *) typeobj;
617
FuncInfo *inputFuncInfo;
619
/* remove function's dependency on type */
620
removeObjectDependency(funcobj, typeobj->dumpId);
622
/* if this isn't the input function, make it depend on same */
623
if (funcobj->catId.oid == typeInfo->typinput)
624
return; /* it is the input function */
625
inputFuncInfo = findFuncByOid(typeInfo->typinput);
626
if (inputFuncInfo == NULL)
628
addObjectDependency(funcobj, inputFuncInfo->dobj.dumpId);
631
* Make sure the input function's dependency on type gets removed too;
632
* if it hasn't been done yet, we'd end up with loops involving the
633
* type and two or more functions, which repairDependencyLoop() is not
634
* smart enough to handle.
636
removeObjectDependency(&inputFuncInfo->dobj, typeobj->dumpId);
640
* Because we force a view to depend on its ON SELECT rule, while there
641
* will be an implicit dependency in the other direction, we need to break
642
* the loop. If there are no other objects in the loop then we can remove
643
* the implicit dependency and leave the ON SELECT rule non-separate.
646
repairViewRuleLoop(DumpableObject *viewobj,
647
DumpableObject *ruleobj)
649
/* remove rule's dependency on view */
650
removeObjectDependency(ruleobj, viewobj->dumpId);
654
* However, if there are other objects in the loop, we must break the loop
655
* by making the ON SELECT rule a separately-dumped object.
657
* Because findLoop() finds shorter cycles before longer ones, it's likely
658
* that we will have previously fired repairViewRuleLoop() and removed the
659
* rule's dependency on the view. Put it back to ensure the rule won't be
660
* emitted before the view...
663
repairViewRuleMultiLoop(DumpableObject *viewobj,
664
DumpableObject *ruleobj)
666
/* remove view's dependency on rule */
667
removeObjectDependency(viewobj, ruleobj->dumpId);
668
/* pretend view is a plain table and dump it that way */
669
((TableInfo *) viewobj)->relkind = 'r'; /* RELKIND_RELATION */
670
/* mark rule as needing its own dump */
671
((RuleInfo *) ruleobj)->separate = true;
672
/* put back rule's dependency on view */
673
addObjectDependency(ruleobj, viewobj->dumpId);
677
* Because we make tables depend on their CHECK constraints, while there
678
* will be an automatic dependency in the other direction, we need to break
679
* the loop. If there are no other objects in the loop then we can remove
680
* the automatic dependency and leave the CHECK constraint non-separate.
683
repairTableConstraintLoop(DumpableObject *tableobj,
684
DumpableObject *constraintobj)
686
/* remove constraint's dependency on table */
687
removeObjectDependency(constraintobj, tableobj->dumpId);
691
* However, if there are other objects in the loop, we must break the loop
692
* by making the CHECK constraint a separately-dumped object.
694
* Because findLoop() finds shorter cycles before longer ones, it's likely
695
* that we will have previously fired repairTableConstraintLoop() and
696
* removed the constraint's dependency on the table. Put it back to ensure
697
* the constraint won't be emitted before the table...
700
repairTableConstraintMultiLoop(DumpableObject *tableobj,
701
DumpableObject *constraintobj)
703
/* remove table's dependency on constraint */
704
removeObjectDependency(tableobj, constraintobj->dumpId);
705
/* mark constraint as needing its own dump */
706
((ConstraintInfo *) constraintobj)->separate = true;
707
/* put back constraint's dependency on table */
708
addObjectDependency(constraintobj, tableobj->dumpId);
712
* Attribute defaults behave exactly the same as CHECK constraints...
715
repairTableAttrDefLoop(DumpableObject *tableobj,
716
DumpableObject *attrdefobj)
718
/* remove attrdef's dependency on table */
719
removeObjectDependency(attrdefobj, tableobj->dumpId);
723
repairTableAttrDefMultiLoop(DumpableObject *tableobj,
724
DumpableObject *attrdefobj)
726
/* remove table's dependency on attrdef */
727
removeObjectDependency(tableobj, attrdefobj->dumpId);
728
/* mark attrdef as needing its own dump */
729
((AttrDefInfo *) attrdefobj)->separate = true;
730
/* put back attrdef's dependency on table */
731
addObjectDependency(attrdefobj, tableobj->dumpId);
735
* CHECK constraints on domains work just like those on tables ...
738
repairDomainConstraintLoop(DumpableObject *domainobj,
739
DumpableObject *constraintobj)
741
/* remove constraint's dependency on domain */
742
removeObjectDependency(constraintobj, domainobj->dumpId);
746
repairDomainConstraintMultiLoop(DumpableObject *domainobj,
747
DumpableObject *constraintobj)
749
/* remove domain's dependency on constraint */
750
removeObjectDependency(domainobj, constraintobj->dumpId);
751
/* mark constraint as needing its own dump */
752
((ConstraintInfo *) constraintobj)->separate = true;
753
/* put back constraint's dependency on domain */
754
addObjectDependency(constraintobj, domainobj->dumpId);
758
* Fix a dependency loop, or die trying ...
760
* This routine is mainly concerned with reducing the multiple ways that
761
* a loop might appear to common cases, which it passes off to the
762
* "fixer" routines above.
765
repairDependencyLoop(DumpableObject **loop,
771
/* Datatype and one of its I/O functions */
773
loop[0]->objType == DO_TYPE &&
774
loop[1]->objType == DO_FUNC)
776
repairTypeFuncLoop(loop[0], loop[1]);
780
loop[1]->objType == DO_TYPE &&
781
loop[0]->objType == DO_FUNC)
783
repairTypeFuncLoop(loop[1], loop[0]);
787
/* View and its ON SELECT rule */
789
loop[0]->objType == DO_TABLE &&
790
loop[1]->objType == DO_RULE &&
791
((RuleInfo *) loop[1])->ev_type == '1' &&
792
((RuleInfo *) loop[1])->is_instead &&
793
((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
795
repairViewRuleLoop(loop[0], loop[1]);
799
loop[1]->objType == DO_TABLE &&
800
loop[0]->objType == DO_RULE &&
801
((RuleInfo *) loop[0])->ev_type == '1' &&
802
((RuleInfo *) loop[0])->is_instead &&
803
((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
805
repairViewRuleLoop(loop[1], loop[0]);
809
/* Indirect loop involving view and ON SELECT rule */
812
for (i = 0; i < nLoop; i++)
814
if (loop[i]->objType == DO_TABLE)
816
for (j = 0; j < nLoop; j++)
818
if (loop[j]->objType == DO_RULE &&
819
((RuleInfo *) loop[j])->ev_type == '1' &&
820
((RuleInfo *) loop[j])->is_instead &&
821
((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
823
repairViewRuleMultiLoop(loop[i], loop[j]);
831
/* Table and CHECK constraint */
833
loop[0]->objType == DO_TABLE &&
834
loop[1]->objType == DO_CONSTRAINT &&
835
((ConstraintInfo *) loop[1])->contype == 'c' &&
836
((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
838
repairTableConstraintLoop(loop[0], loop[1]);
842
loop[1]->objType == DO_TABLE &&
843
loop[0]->objType == DO_CONSTRAINT &&
844
((ConstraintInfo *) loop[0])->contype == 'c' &&
845
((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
847
repairTableConstraintLoop(loop[1], loop[0]);
851
/* Indirect loop involving table and CHECK constraint */
854
for (i = 0; i < nLoop; i++)
856
if (loop[i]->objType == DO_TABLE)
858
for (j = 0; j < nLoop; j++)
860
if (loop[j]->objType == DO_CONSTRAINT &&
861
((ConstraintInfo *) loop[j])->contype == 'c' &&
862
((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
864
repairTableConstraintMultiLoop(loop[i], loop[j]);
872
/* Table and attribute default */
874
loop[0]->objType == DO_TABLE &&
875
loop[1]->objType == DO_ATTRDEF &&
876
((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
878
repairTableAttrDefLoop(loop[0], loop[1]);
882
loop[1]->objType == DO_TABLE &&
883
loop[0]->objType == DO_ATTRDEF &&
884
((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
886
repairTableAttrDefLoop(loop[1], loop[0]);
890
/* Indirect loop involving table and attribute default */
893
for (i = 0; i < nLoop; i++)
895
if (loop[i]->objType == DO_TABLE)
897
for (j = 0; j < nLoop; j++)
899
if (loop[j]->objType == DO_ATTRDEF &&
900
((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
902
repairTableAttrDefMultiLoop(loop[i], loop[j]);
910
/* Domain and CHECK constraint */
912
loop[0]->objType == DO_TYPE &&
913
loop[1]->objType == DO_CONSTRAINT &&
914
((ConstraintInfo *) loop[1])->contype == 'c' &&
915
((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
917
repairDomainConstraintLoop(loop[0], loop[1]);
921
loop[1]->objType == DO_TYPE &&
922
loop[0]->objType == DO_CONSTRAINT &&
923
((ConstraintInfo *) loop[0])->contype == 'c' &&
924
((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
926
repairDomainConstraintLoop(loop[1], loop[0]);
930
/* Indirect loop involving domain and CHECK constraint */
933
for (i = 0; i < nLoop; i++)
935
if (loop[i]->objType == DO_TYPE)
937
for (j = 0; j < nLoop; j++)
939
if (loop[j]->objType == DO_CONSTRAINT &&
940
((ConstraintInfo *) loop[j])->contype == 'c' &&
941
((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
943
repairDomainConstraintMultiLoop(loop[i], loop[j]);
952
* If we can't find a principled way to break the loop, complain and
953
* break it in an arbitrary fashion.
955
write_msg(modulename, "WARNING: could not resolve dependency loop among these items:\n");
956
for (i = 0; i < nLoop; i++)
960
describeDumpableObject(loop[i], buf, sizeof(buf));
961
write_msg(modulename, " %s\n", buf);
963
removeObjectDependency(loop[0], loop[1]->dumpId);
967
* Describe a dumpable object usefully for errors
969
* This should probably go somewhere else...
972
describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
974
switch (obj->objType)
977
snprintf(buf, bufsize,
978
"SCHEMA %s (ID %d OID %u)",
979
obj->name, obj->dumpId, obj->catId.oid);
982
snprintf(buf, bufsize,
983
"TYPE %s (ID %d OID %u)",
984
obj->name, obj->dumpId, obj->catId.oid);
987
snprintf(buf, bufsize,
988
"FUNCTION %s (ID %d OID %u)",
989
obj->name, obj->dumpId, obj->catId.oid);
992
snprintf(buf, bufsize,
993
"AGGREGATE %s (ID %d OID %u)",
994
obj->name, obj->dumpId, obj->catId.oid);
997
snprintf(buf, bufsize,
998
"OPERATOR %s (ID %d OID %u)",
999
obj->name, obj->dumpId, obj->catId.oid);
1002
snprintf(buf, bufsize,
1003
"OPERATOR CLASS %s (ID %d OID %u)",
1004
obj->name, obj->dumpId, obj->catId.oid);
1007
snprintf(buf, bufsize,
1008
"CONVERSION %s (ID %d OID %u)",
1009
obj->name, obj->dumpId, obj->catId.oid);
1012
snprintf(buf, bufsize,
1013
"TABLE %s (ID %d OID %u)",
1014
obj->name, obj->dumpId, obj->catId.oid);
1017
snprintf(buf, bufsize,
1018
"ATTRDEF %s.%s (ID %d OID %u)",
1019
((AttrDefInfo *) obj)->adtable->dobj.name,
1020
((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
1021
obj->dumpId, obj->catId.oid);
1024
snprintf(buf, bufsize,
1025
"INDEX %s (ID %d OID %u)",
1026
obj->name, obj->dumpId, obj->catId.oid);
1029
snprintf(buf, bufsize,
1030
"RULE %s (ID %d OID %u)",
1031
obj->name, obj->dumpId, obj->catId.oid);
1034
snprintf(buf, bufsize,
1035
"TRIGGER %s (ID %d OID %u)",
1036
obj->name, obj->dumpId, obj->catId.oid);
1039
snprintf(buf, bufsize,
1040
"CONSTRAINT %s (ID %d OID %u)",
1041
obj->name, obj->dumpId, obj->catId.oid);
1043
case DO_FK_CONSTRAINT:
1044
snprintf(buf, bufsize,
1045
"FK CONSTRAINT %s (ID %d OID %u)",
1046
obj->name, obj->dumpId, obj->catId.oid);
1049
snprintf(buf, bufsize,
1050
"PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1051
obj->name, obj->dumpId, obj->catId.oid);
1054
snprintf(buf, bufsize,
1055
"CAST %u to %u (ID %d OID %u)",
1056
((CastInfo *) obj)->castsource,
1057
((CastInfo *) obj)->casttarget,
1058
obj->dumpId, obj->catId.oid);
1061
snprintf(buf, bufsize,
1062
"TABLE DATA %s (ID %d OID %u)",
1063
obj->name, obj->dumpId, obj->catId.oid);
1066
snprintf(buf, bufsize,
1067
"TABLE TYPE %s (ID %d OID %u)",
1068
obj->name, obj->dumpId, obj->catId.oid);
1071
snprintf(buf, bufsize,
1076
/* shouldn't get here */
1077
snprintf(buf, bufsize,
1078
"object type %d (ID %d OID %u)",
1080
obj->dumpId, obj->catId.oid);