Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / bin / pg_dump / pg_dump_sort.c
blobbafabe9fa47737539cdd8ff3cf765533d4f19f1c
1 /*-------------------------------------------------------------------------
3 * pg_dump_sort.c
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
11 * IDENTIFICATION
12 * $PostgreSQL$
14 *-------------------------------------------------------------------------
16 #include "pg_backup_archiver.h"
19 static const char *modulename = gettext_noop("sorter");
22 * Sort priority for object types when dumping a pre-7.3 database.
23 * Objects are sorted by priority levels, and within an equal priority level
24 * by OID. (This is a relatively crude hack to provide semi-reasonable
25 * behavior for old databases without full dependency info.) 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[] =
31 1, /* DO_NAMESPACE */
32 2, /* DO_TYPE */
33 2, /* DO_SHELL_TYPE */
34 2, /* DO_FUNC */
35 3, /* DO_AGG */
36 3, /* DO_OPERATOR */
37 4, /* DO_OPCLASS */
38 4, /* DO_OPFAMILY */
39 5, /* DO_CONVERSION */
40 6, /* DO_TABLE */
41 8, /* DO_ATTRDEF */
42 13, /* DO_INDEX */
43 14, /* DO_RULE */
44 15, /* DO_TRIGGER */
45 12, /* DO_CONSTRAINT */
46 16, /* DO_FK_CONSTRAINT */
47 2, /* DO_PROCLANG */
48 2, /* DO_CAST */
49 9, /* DO_TABLE_DATA */
50 7, /* DO_DUMMY_TYPE */
51 3, /* DO_TSPARSER */
52 4, /* DO_TSDICT */
53 3, /* DO_TSTEMPLATE */
54 5, /* DO_TSCONFIG */
55 3, /* DO_FDW */
56 4, /* DO_FOREIGN_SERVER */
57 10, /* DO_BLOBS */
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[] =
67 1, /* DO_NAMESPACE */
68 3, /* DO_TYPE */
69 3, /* DO_SHELL_TYPE */
70 4, /* DO_FUNC */
71 5, /* DO_AGG */
72 6, /* DO_OPERATOR */
73 7, /* DO_OPCLASS */
74 7, /* DO_OPFAMILY */
75 9, /* DO_CONVERSION */
76 16, /* DO_TABLE */
77 18, /* DO_ATTRDEF */
78 23, /* DO_INDEX */
79 24, /* DO_RULE */
80 25, /* DO_TRIGGER */
81 22, /* DO_CONSTRAINT */
82 26, /* DO_FK_CONSTRAINT */
83 2, /* DO_PROCLANG */
84 8, /* DO_CAST */
85 19, /* DO_TABLE_DATA */
86 17, /* DO_DUMMY_TYPE */
87 10, /* DO_TSPARSER */
88 12, /* DO_TSDICT */
89 11, /* DO_TSTEMPLATE */
90 13, /* DO_TSCONFIG */
91 14, /* DO_FDW */
92 15, /* DO_FOREIGN_SERVER */
93 20, /* DO_BLOBS */
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,
101 int numObjs,
102 DumpableObject **ordering,
103 int *nOrdering);
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,
108 DumpId startPoint,
109 DumpableObject **workspace,
110 int depth,
111 int *newDepth);
112 static void repairDependencyLoop(DumpableObject **loop,
113 int nLoop);
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
122 * ordering.
124 void
125 sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
127 if (numObjs > 1)
128 qsort((void *) objs, numObjs, sizeof(DumpableObject *),
129 DOTypeNameCompare);
132 static int
133 DOTypeNameCompare(const void *p1, const void *p2)
135 DumpableObject *obj1 = *(DumpableObject **) p1;
136 DumpableObject *obj2 = *(DumpableObject **) p2;
137 int cmpval;
139 /* Sort by type */
140 cmpval = newObjectTypePriority[obj1->objType] -
141 newObjectTypePriority[obj2->objType];
143 if (cmpval != 0)
144 return cmpval;
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);
155 if (cmpval != 0)
156 return cmpval;
159 /* Sort by name */
160 cmpval = strcmp(obj1->name, obj2->name);
161 if (cmpval != 0)
162 return cmpval;
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.
175 void
176 sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs)
178 if (numObjs > 1)
179 qsort((void *) objs, numObjs, sizeof(DumpableObject *),
180 DOTypeOidCompare);
183 static int
184 DOTypeOidCompare(const void *p1, const void *p2)
186 DumpableObject *obj1 = *(DumpableObject **) p1;
187 DumpableObject *obj2 = *(DumpableObject **) p2;
188 int cmpval;
190 cmpval = oldObjectTypePriority[obj1->objType] -
191 oldObjectTypePriority[obj2->objType];
193 if (cmpval != 0)
194 return cmpval;
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).
204 void
205 sortDumpableObjects(DumpableObject **objs, int numObjs)
207 DumpableObject **ordering;
208 int nOrdering;
210 if (numObjs <= 0)
211 return;
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 *));
222 free(ordering);
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
234 * modified.
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.
251 static bool
252 TopoSort(DumpableObject **objs,
253 int numObjs,
254 DumpableObject **ordering, /* output argument */
255 int *nOrdering) /* output argument */
257 DumpId maxDumpId = getMaxDumpId();
258 int *pendingHeap;
259 int *beforeConstraints;
260 int *idMap;
261 DumpableObject *obj;
262 int heapLength;
263 int i,
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 */
283 if (numObjs <= 0)
284 return true;
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
296 * dumpId j.
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));
303 if (idMap == NULL)
304 exit_horribly(NULL, modulename, "out of memory\n");
305 for (i = 0; i < numObjs; i++)
307 obj = objs[i];
308 j = obj->dumpId;
309 if (j <= 0 || j > maxDumpId)
310 exit_horribly(NULL, modulename, "invalid dumpId %d\n", j);
311 idMap[j] = i;
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.
332 heapLength = 0;
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 *--------------------
354 i = numObjs;
355 while (heapLength > 0)
357 /* Select object to output by removing largest heap member */
358 j = removeHeapElement(pendingHeap, heapLength--);
359 obj = objs[j];
360 /* Output candidate to ordering[] */
361 ordering[--i] = obj;
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.
376 if (i != 0)
378 k = 0;
379 for (j = 1; j <= maxDumpId; j++)
381 if (beforeConstraints[j] != 0)
382 ordering[k++] = objs[idMap[j]];
384 *nOrdering = k;
387 /* Done */
388 free(pendingHeap);
389 free(beforeConstraints);
390 free(idMap);
392 return (i == 0);
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.
401 static void
402 addHeapElement(int val, int *heap, int heapLength)
404 int j;
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.
410 j = heapLength;
411 while (j > 0)
413 int i = (j - 1) >> 1;
415 if (val <= heap[i])
416 break;
417 heap[j] = heap[i];
418 j = i;
420 heap[j] = val;
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.
432 static int
433 removeHeapElement(int *heap, int heapLength)
435 int result = heap[0];
436 int val;
437 int i;
439 if (--heapLength <= 0)
440 return result;
441 val = heap[heapLength]; /* value that must be reinserted */
442 i = 0; /* i is where the "hole" is */
443 for (;;)
445 int j = 2 * i + 1;
447 if (j >= heapLength)
448 break;
449 if (j + 1 < heapLength &&
450 heap[j] < heap[j + 1])
451 j++;
452 if (val >= heap[j])
453 break;
454 heap[i] = heap[j];
455 i = j;
457 heap[i] = val;
458 return result;
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
477 static void
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;
498 int initiallen;
499 bool fixedloop;
500 int i;
502 workspace = (DumpableObject **) malloc(totObjs * sizeof(DumpableObject *));
503 if (workspace == NULL)
504 exit_horribly(NULL, modulename, "out of memory\n");
505 initiallen = 0;
506 fixedloop = false;
508 for (i = 0; i < nObjs; i++)
510 DumpableObject *obj = objs[i];
511 int newlen;
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 */
520 initiallen = newlen;
521 fixedloop = true;
523 else
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)
532 initiallen++;
536 /* We'd better have fixed at least one loop */
537 if (!fixedloop)
538 exit_horribly(NULL, modulename, "could not identify dependency loop\n");
540 free(workspace);
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.
559 static bool
560 findLoop(DumpableObject *obj,
561 DumpId startPoint,
562 DumpableObject **workspace,
563 int depth,
564 int *newDepth)
566 int i;
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)
579 return false;
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)
594 *newDepth = depth;
595 return true;
600 * Recurse down each outgoing branch
602 for (i = 0; i < obj->nDeps; i++)
604 DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
606 if (!nextobj)
607 continue; /* ignore dependencies on undumped objects */
608 if (findLoop(nextobj,
609 startPoint,
610 workspace,
611 depth,
612 newDepth))
613 return true;
616 return false;
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.
625 static void
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 */
638 if (funcobj->dump)
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.
649 static void
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...
666 static void
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.
686 static void
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...
703 static void
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...
718 static void
719 repairTableAttrDefLoop(DumpableObject *tableobj,
720 DumpableObject *attrdefobj)
722 /* remove attrdef's dependency on table */
723 removeObjectDependency(attrdefobj, tableobj->dumpId);
726 static void
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 ...
741 static void
742 repairDomainConstraintLoop(DumpableObject *domainobj,
743 DumpableObject *constraintobj)
745 /* remove constraint's dependency on domain */
746 removeObjectDependency(constraintobj, domainobj->dumpId);
749 static void
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.
768 static void
769 repairDependencyLoop(DumpableObject **loop,
770 int nLoop)
772 int i,
775 /* Datatype and one of its I/O functions */
776 if (nLoop == 2 &&
777 loop[0]->objType == DO_TYPE &&
778 loop[1]->objType == DO_FUNC)
780 repairTypeFuncLoop(loop[0], loop[1]);
781 return;
783 if (nLoop == 2 &&
784 loop[1]->objType == DO_TYPE &&
785 loop[0]->objType == DO_FUNC)
787 repairTypeFuncLoop(loop[1], loop[0]);
788 return;
791 /* View and its ON SELECT rule */
792 if (nLoop == 2 &&
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]);
800 return;
802 if (nLoop == 2 &&
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]);
810 return;
813 /* Indirect loop involving view and ON SELECT rule */
814 if (nLoop > 2)
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]);
828 return;
835 /* Table and CHECK constraint */
836 if (nLoop == 2 &&
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]);
843 return;
845 if (nLoop == 2 &&
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]);
852 return;
855 /* Indirect loop involving table and CHECK constraint */
856 if (nLoop > 2)
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]);
869 return;
876 /* Table and attribute default */
877 if (nLoop == 2 &&
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]);
883 return;
885 if (nLoop == 2 &&
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]);
891 return;
894 /* Indirect loop involving table and attribute default */
895 if (nLoop > 2)
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]);
907 return;
914 /* Domain and CHECK constraint */
915 if (nLoop == 2 &&
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]);
922 return;
924 if (nLoop == 2 &&
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]);
931 return;
934 /* Indirect loop involving domain and CHECK constraint */
935 if (nLoop > 2)
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]);
948 return;
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)
963 break;
965 if (i >= nLoop)
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");
972 if (nLoop > 1)
973 removeObjectDependency(loop[0], loop[1]->dumpId);
974 else /* must be a self-dependency */
975 removeObjectDependency(loop[0], loop[0]->dumpId);
976 return;
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++)
986 char buf[1024];
988 describeDumpableObject(loop[i], buf, sizeof(buf));
989 write_msg(modulename, " %s\n", buf);
992 if (nLoop > 1)
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...
1003 static void
1004 describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
1006 switch (obj->objType)
1008 case DO_NAMESPACE:
1009 snprintf(buf, bufsize,
1010 "SCHEMA %s (ID %d OID %u)",
1011 obj->name, obj->dumpId, obj->catId.oid);
1012 return;
1013 case DO_TYPE:
1014 snprintf(buf, bufsize,
1015 "TYPE %s (ID %d OID %u)",
1016 obj->name, obj->dumpId, obj->catId.oid);
1017 return;
1018 case DO_SHELL_TYPE:
1019 snprintf(buf, bufsize,
1020 "SHELL TYPE %s (ID %d OID %u)",
1021 obj->name, obj->dumpId, obj->catId.oid);
1022 return;
1023 case DO_FUNC:
1024 snprintf(buf, bufsize,
1025 "FUNCTION %s (ID %d OID %u)",
1026 obj->name, obj->dumpId, obj->catId.oid);
1027 return;
1028 case DO_AGG:
1029 snprintf(buf, bufsize,
1030 "AGGREGATE %s (ID %d OID %u)",
1031 obj->name, obj->dumpId, obj->catId.oid);
1032 return;
1033 case DO_OPERATOR:
1034 snprintf(buf, bufsize,
1035 "OPERATOR %s (ID %d OID %u)",
1036 obj->name, obj->dumpId, obj->catId.oid);
1037 return;
1038 case DO_OPCLASS:
1039 snprintf(buf, bufsize,
1040 "OPERATOR CLASS %s (ID %d OID %u)",
1041 obj->name, obj->dumpId, obj->catId.oid);
1042 return;
1043 case DO_OPFAMILY:
1044 snprintf(buf, bufsize,
1045 "OPERATOR FAMILY %s (ID %d OID %u)",
1046 obj->name, obj->dumpId, obj->catId.oid);
1047 return;
1048 case DO_CONVERSION:
1049 snprintf(buf, bufsize,
1050 "CONVERSION %s (ID %d OID %u)",
1051 obj->name, obj->dumpId, obj->catId.oid);
1052 return;
1053 case DO_TABLE:
1054 snprintf(buf, bufsize,
1055 "TABLE %s (ID %d OID %u)",
1056 obj->name, obj->dumpId, obj->catId.oid);
1057 return;
1058 case DO_ATTRDEF:
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);
1064 return;
1065 case DO_INDEX:
1066 snprintf(buf, bufsize,
1067 "INDEX %s (ID %d OID %u)",
1068 obj->name, obj->dumpId, obj->catId.oid);
1069 return;
1070 case DO_RULE:
1071 snprintf(buf, bufsize,
1072 "RULE %s (ID %d OID %u)",
1073 obj->name, obj->dumpId, obj->catId.oid);
1074 return;
1075 case DO_TRIGGER:
1076 snprintf(buf, bufsize,
1077 "TRIGGER %s (ID %d OID %u)",
1078 obj->name, obj->dumpId, obj->catId.oid);
1079 return;
1080 case DO_CONSTRAINT:
1081 snprintf(buf, bufsize,
1082 "CONSTRAINT %s (ID %d OID %u)",
1083 obj->name, obj->dumpId, obj->catId.oid);
1084 return;
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);
1089 return;
1090 case DO_PROCLANG:
1091 snprintf(buf, bufsize,
1092 "PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1093 obj->name, obj->dumpId, obj->catId.oid);
1094 return;
1095 case DO_CAST:
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);
1101 return;
1102 case DO_TABLE_DATA:
1103 snprintf(buf, bufsize,
1104 "TABLE DATA %s (ID %d OID %u)",
1105 obj->name, obj->dumpId, obj->catId.oid);
1106 return;
1107 case DO_DUMMY_TYPE:
1108 snprintf(buf, bufsize,
1109 "DUMMY TYPE %s (ID %d OID %u)",
1110 obj->name, obj->dumpId, obj->catId.oid);
1111 return;
1112 case DO_TSPARSER:
1113 snprintf(buf, bufsize,
1114 "TEXT SEARCH PARSER %s (ID %d OID %u)",
1115 obj->name, obj->dumpId, obj->catId.oid);
1116 return;
1117 case DO_TSDICT:
1118 snprintf(buf, bufsize,
1119 "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
1120 obj->name, obj->dumpId, obj->catId.oid);
1121 return;
1122 case DO_TSTEMPLATE:
1123 snprintf(buf, bufsize,
1124 "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
1125 obj->name, obj->dumpId, obj->catId.oid);
1126 return;
1127 case DO_TSCONFIG:
1128 snprintf(buf, bufsize,
1129 "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
1130 obj->name, obj->dumpId, obj->catId.oid);
1131 return;
1132 case DO_FDW:
1133 snprintf(buf, bufsize,
1134 "FOREIGN DATA WRAPPER %s (ID %d OID %u)",
1135 obj->name, obj->dumpId, obj->catId.oid);
1136 return;
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);
1141 return;
1142 case DO_BLOBS:
1143 snprintf(buf, bufsize,
1144 "BLOBS (ID %d)",
1145 obj->dumpId);
1146 return;
1147 case DO_BLOB_COMMENTS:
1148 snprintf(buf, bufsize,
1149 "BLOB COMMENTS (ID %d)",
1150 obj->dumpId);
1151 return;
1153 /* shouldn't get here */
1154 snprintf(buf, bufsize,
1155 "object type %d (ID %d OID %u)",
1156 (int) obj->objType,
1157 obj->dumpId, obj->catId.oid);