Refine nbtree = redundancy preprocessing comment.
[pgsql.git] / src / bin / pg_dump / pg_dump_sort.c
blob3c8f2eb808dcfc451629e577ad44cf114c23006d
1 /*-------------------------------------------------------------------------
3 * pg_dump_sort.c
4 * Sort the items of a dump into a safe order for dumping
7 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * IDENTIFICATION
12 * src/bin/pg_dump/pg_dump_sort.c
14 *-------------------------------------------------------------------------
16 #include "postgres_fe.h"
18 #include "catalog/pg_class_d.h"
19 #include "common/int.h"
20 #include "lib/binaryheap.h"
21 #include "pg_backup_utils.h"
22 #include "pg_dump.h"
25 * Sort priority for database object types.
26 * Objects are sorted by type, and within a type by name.
28 * Triggers, event triggers, and materialized views are intentionally sorted
29 * late. Triggers must be restored after all data modifications, so that
30 * they don't interfere with loading data. Event triggers are restored
31 * next-to-last so that they don't interfere with object creations of any
32 * kind. Matview refreshes are last because they should execute in the
33 * database's normal state (e.g., they must come after all ACLs are restored;
34 * also, if they choose to look at system catalogs, they should see the final
35 * restore state). If you think to change this, see also the RestorePass
36 * mechanism in pg_backup_archiver.c.
38 * On the other hand, casts are intentionally sorted earlier than you might
39 * expect; logically they should come after functions, since they usually
40 * depend on those. This works around the backend's habit of recording
41 * views that use casts as dependent on the cast's underlying function.
42 * We initially sort casts first, and then any functions used by casts
43 * will be hoisted above the casts, and in turn views that those functions
44 * depend on will be hoisted above the functions. But views not used that
45 * way won't be hoisted.
47 * NOTE: object-type priorities must match the section assignments made in
48 * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
49 * POST_DATA objects must sort after DO_POST_DATA_BOUNDARY, and DATA objects
50 * must sort between them.
53 /* This enum lists the priority levels in order */
54 enum dbObjectTypePriorities
56 PRIO_NAMESPACE = 1,
57 PRIO_PROCLANG,
58 PRIO_COLLATION,
59 PRIO_TRANSFORM,
60 PRIO_EXTENSION,
61 PRIO_TYPE, /* used for DO_TYPE and DO_SHELL_TYPE */
62 PRIO_CAST,
63 PRIO_FUNC,
64 PRIO_AGG,
65 PRIO_ACCESS_METHOD,
66 PRIO_OPERATOR,
67 PRIO_OPFAMILY, /* used for DO_OPFAMILY and DO_OPCLASS */
68 PRIO_CONVERSION,
69 PRIO_TSPARSER,
70 PRIO_TSTEMPLATE,
71 PRIO_TSDICT,
72 PRIO_TSCONFIG,
73 PRIO_FDW,
74 PRIO_FOREIGN_SERVER,
75 PRIO_TABLE,
76 PRIO_TABLE_ATTACH,
77 PRIO_DUMMY_TYPE,
78 PRIO_ATTRDEF,
79 PRIO_LARGE_OBJECT,
80 PRIO_PRE_DATA_BOUNDARY, /* boundary! */
81 PRIO_TABLE_DATA,
82 PRIO_SEQUENCE_SET,
83 PRIO_LARGE_OBJECT_DATA,
84 PRIO_POST_DATA_BOUNDARY, /* boundary! */
85 PRIO_CONSTRAINT,
86 PRIO_INDEX,
87 PRIO_INDEX_ATTACH,
88 PRIO_STATSEXT,
89 PRIO_RULE,
90 PRIO_TRIGGER,
91 PRIO_FK_CONSTRAINT,
92 PRIO_POLICY,
93 PRIO_PUBLICATION,
94 PRIO_PUBLICATION_REL,
95 PRIO_PUBLICATION_TABLE_IN_SCHEMA,
96 PRIO_SUBSCRIPTION,
97 PRIO_SUBSCRIPTION_REL,
98 PRIO_DEFAULT_ACL, /* done in ACL pass */
99 PRIO_EVENT_TRIGGER, /* must be next to last! */
100 PRIO_REFRESH_MATVIEW /* must be last! */
103 /* This table is indexed by enum DumpableObjectType */
104 static const int dbObjectTypePriority[] =
106 [DO_NAMESPACE] = PRIO_NAMESPACE,
107 [DO_EXTENSION] = PRIO_EXTENSION,
108 [DO_TYPE] = PRIO_TYPE,
109 [DO_SHELL_TYPE] = PRIO_TYPE,
110 [DO_FUNC] = PRIO_FUNC,
111 [DO_AGG] = PRIO_AGG,
112 [DO_OPERATOR] = PRIO_OPERATOR,
113 [DO_ACCESS_METHOD] = PRIO_ACCESS_METHOD,
114 [DO_OPCLASS] = PRIO_OPFAMILY,
115 [DO_OPFAMILY] = PRIO_OPFAMILY,
116 [DO_COLLATION] = PRIO_COLLATION,
117 [DO_CONVERSION] = PRIO_CONVERSION,
118 [DO_TABLE] = PRIO_TABLE,
119 [DO_TABLE_ATTACH] = PRIO_TABLE_ATTACH,
120 [DO_ATTRDEF] = PRIO_ATTRDEF,
121 [DO_INDEX] = PRIO_INDEX,
122 [DO_INDEX_ATTACH] = PRIO_INDEX_ATTACH,
123 [DO_STATSEXT] = PRIO_STATSEXT,
124 [DO_RULE] = PRIO_RULE,
125 [DO_TRIGGER] = PRIO_TRIGGER,
126 [DO_CONSTRAINT] = PRIO_CONSTRAINT,
127 [DO_FK_CONSTRAINT] = PRIO_FK_CONSTRAINT,
128 [DO_PROCLANG] = PRIO_PROCLANG,
129 [DO_CAST] = PRIO_CAST,
130 [DO_TABLE_DATA] = PRIO_TABLE_DATA,
131 [DO_SEQUENCE_SET] = PRIO_SEQUENCE_SET,
132 [DO_DUMMY_TYPE] = PRIO_DUMMY_TYPE,
133 [DO_TSPARSER] = PRIO_TSPARSER,
134 [DO_TSDICT] = PRIO_TSDICT,
135 [DO_TSTEMPLATE] = PRIO_TSTEMPLATE,
136 [DO_TSCONFIG] = PRIO_TSCONFIG,
137 [DO_FDW] = PRIO_FDW,
138 [DO_FOREIGN_SERVER] = PRIO_FOREIGN_SERVER,
139 [DO_DEFAULT_ACL] = PRIO_DEFAULT_ACL,
140 [DO_TRANSFORM] = PRIO_TRANSFORM,
141 [DO_LARGE_OBJECT] = PRIO_LARGE_OBJECT,
142 [DO_LARGE_OBJECT_DATA] = PRIO_LARGE_OBJECT_DATA,
143 [DO_PRE_DATA_BOUNDARY] = PRIO_PRE_DATA_BOUNDARY,
144 [DO_POST_DATA_BOUNDARY] = PRIO_POST_DATA_BOUNDARY,
145 [DO_EVENT_TRIGGER] = PRIO_EVENT_TRIGGER,
146 [DO_REFRESH_MATVIEW] = PRIO_REFRESH_MATVIEW,
147 [DO_POLICY] = PRIO_POLICY,
148 [DO_PUBLICATION] = PRIO_PUBLICATION,
149 [DO_PUBLICATION_REL] = PRIO_PUBLICATION_REL,
150 [DO_PUBLICATION_TABLE_IN_SCHEMA] = PRIO_PUBLICATION_TABLE_IN_SCHEMA,
151 [DO_SUBSCRIPTION] = PRIO_SUBSCRIPTION,
152 [DO_SUBSCRIPTION_REL] = PRIO_SUBSCRIPTION_REL,
155 StaticAssertDecl(lengthof(dbObjectTypePriority) == (DO_SUBSCRIPTION_REL + 1),
156 "array length mismatch");
158 static DumpId preDataBoundId;
159 static DumpId postDataBoundId;
162 static int DOTypeNameCompare(const void *p1, const void *p2);
163 static bool TopoSort(DumpableObject **objs,
164 int numObjs,
165 DumpableObject **ordering,
166 int *nOrdering);
167 static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
168 static int findLoop(DumpableObject *obj,
169 DumpId startPoint,
170 bool *processed,
171 DumpId *searchFailed,
172 DumpableObject **workspace,
173 int depth);
174 static void repairDependencyLoop(DumpableObject **loop,
175 int nLoop);
176 static void describeDumpableObject(DumpableObject *obj,
177 char *buf, int bufsize);
178 static int int_cmp(void *a, void *b, void *arg);
182 * Sort the given objects into a type/name-based ordering
184 * Normally this is just the starting point for the dependency-based
185 * ordering.
187 void
188 sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
190 if (numObjs > 1)
191 qsort(objs, numObjs, sizeof(DumpableObject *),
192 DOTypeNameCompare);
195 static int
196 DOTypeNameCompare(const void *p1, const void *p2)
198 DumpableObject *obj1 = *(DumpableObject *const *) p1;
199 DumpableObject *obj2 = *(DumpableObject *const *) p2;
200 int cmpval;
202 /* Sort by type's priority */
203 cmpval = dbObjectTypePriority[obj1->objType] -
204 dbObjectTypePriority[obj2->objType];
206 if (cmpval != 0)
207 return cmpval;
210 * Sort by namespace. Typically, all objects of the same priority would
211 * either have or not have a namespace link, but there are exceptions.
212 * Sort NULL namespace after non-NULL in such cases.
214 if (obj1->namespace)
216 if (obj2->namespace)
218 cmpval = strcmp(obj1->namespace->dobj.name,
219 obj2->namespace->dobj.name);
220 if (cmpval != 0)
221 return cmpval;
223 else
224 return -1;
226 else if (obj2->namespace)
227 return 1;
229 /* Sort by name */
230 cmpval = strcmp(obj1->name, obj2->name);
231 if (cmpval != 0)
232 return cmpval;
234 /* To have a stable sort order, break ties for some object types */
235 if (obj1->objType == DO_FUNC || obj1->objType == DO_AGG)
237 FuncInfo *fobj1 = *(FuncInfo *const *) p1;
238 FuncInfo *fobj2 = *(FuncInfo *const *) p2;
239 int i;
241 /* Sort by number of arguments, then argument type names */
242 cmpval = fobj1->nargs - fobj2->nargs;
243 if (cmpval != 0)
244 return cmpval;
245 for (i = 0; i < fobj1->nargs; i++)
247 TypeInfo *argtype1 = findTypeByOid(fobj1->argtypes[i]);
248 TypeInfo *argtype2 = findTypeByOid(fobj2->argtypes[i]);
250 if (argtype1 && argtype2)
252 if (argtype1->dobj.namespace && argtype2->dobj.namespace)
254 cmpval = strcmp(argtype1->dobj.namespace->dobj.name,
255 argtype2->dobj.namespace->dobj.name);
256 if (cmpval != 0)
257 return cmpval;
259 cmpval = strcmp(argtype1->dobj.name, argtype2->dobj.name);
260 if (cmpval != 0)
261 return cmpval;
265 else if (obj1->objType == DO_OPERATOR)
267 OprInfo *oobj1 = *(OprInfo *const *) p1;
268 OprInfo *oobj2 = *(OprInfo *const *) p2;
270 /* oprkind is 'l', 'r', or 'b'; this sorts prefix, postfix, infix */
271 cmpval = (oobj2->oprkind - oobj1->oprkind);
272 if (cmpval != 0)
273 return cmpval;
275 else if (obj1->objType == DO_ATTRDEF)
277 AttrDefInfo *adobj1 = *(AttrDefInfo *const *) p1;
278 AttrDefInfo *adobj2 = *(AttrDefInfo *const *) p2;
280 /* Sort by attribute number */
281 cmpval = (adobj1->adnum - adobj2->adnum);
282 if (cmpval != 0)
283 return cmpval;
285 else if (obj1->objType == DO_POLICY)
287 PolicyInfo *pobj1 = *(PolicyInfo *const *) p1;
288 PolicyInfo *pobj2 = *(PolicyInfo *const *) p2;
290 /* Sort by table name (table namespace was considered already) */
291 cmpval = strcmp(pobj1->poltable->dobj.name,
292 pobj2->poltable->dobj.name);
293 if (cmpval != 0)
294 return cmpval;
296 else if (obj1->objType == DO_RULE)
298 RuleInfo *robj1 = *(RuleInfo *const *) p1;
299 RuleInfo *robj2 = *(RuleInfo *const *) p2;
301 /* Sort by table name (table namespace was considered already) */
302 cmpval = strcmp(robj1->ruletable->dobj.name,
303 robj2->ruletable->dobj.name);
304 if (cmpval != 0)
305 return cmpval;
307 else if (obj1->objType == DO_TRIGGER)
309 TriggerInfo *tobj1 = *(TriggerInfo *const *) p1;
310 TriggerInfo *tobj2 = *(TriggerInfo *const *) p2;
312 /* Sort by table name (table namespace was considered already) */
313 cmpval = strcmp(tobj1->tgtable->dobj.name,
314 tobj2->tgtable->dobj.name);
315 if (cmpval != 0)
316 return cmpval;
319 /* Usually shouldn't get here, but if we do, sort by OID */
320 return oidcmp(obj1->catId.oid, obj2->catId.oid);
325 * Sort the given objects into a safe dump order using dependency
326 * information (to the extent we have it available).
328 * The DumpIds of the PRE_DATA_BOUNDARY and POST_DATA_BOUNDARY objects are
329 * passed in separately, in case we need them during dependency loop repair.
331 void
332 sortDumpableObjects(DumpableObject **objs, int numObjs,
333 DumpId preBoundaryId, DumpId postBoundaryId)
335 DumpableObject **ordering;
336 int nOrdering;
338 if (numObjs <= 0) /* can't happen anymore ... */
339 return;
342 * Saving the boundary IDs in static variables is a bit grotty, but seems
343 * better than adding them to parameter lists of subsidiary functions.
345 preDataBoundId = preBoundaryId;
346 postDataBoundId = postBoundaryId;
348 ordering = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
349 while (!TopoSort(objs, numObjs, ordering, &nOrdering))
350 findDependencyLoops(ordering, nOrdering, numObjs);
352 memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
354 free(ordering);
358 * TopoSort -- topological sort of a dump list
360 * Generate a re-ordering of the dump list that satisfies all the dependency
361 * constraints shown in the dump list. (Each such constraint is a fact of a
362 * partial ordering.) Minimize rearrangement of the list not needed to
363 * achieve the partial ordering.
365 * The input is the list of numObjs objects in objs[]. This list is not
366 * modified.
368 * Returns true if able to build an ordering that satisfies all the
369 * constraints, false if not (there are contradictory constraints).
371 * On success (true result), ordering[] is filled with a sorted array of
372 * DumpableObject pointers, of length equal to the input list length.
374 * On failure (false result), ordering[] is filled with an unsorted array of
375 * DumpableObject pointers of length *nOrdering, listing the objects that
376 * prevented the sort from being completed. In general, these objects either
377 * participate directly in a dependency cycle, or are depended on by objects
378 * that are in a cycle. (The latter objects are not actually problematic,
379 * but it takes further analysis to identify which are which.)
381 * The caller is responsible for allocating sufficient space at *ordering.
383 static bool
384 TopoSort(DumpableObject **objs,
385 int numObjs,
386 DumpableObject **ordering, /* output argument */
387 int *nOrdering) /* output argument */
389 DumpId maxDumpId = getMaxDumpId();
390 binaryheap *pendingHeap;
391 int *beforeConstraints;
392 int *idMap;
393 DumpableObject *obj;
394 int i,
399 * This is basically the same algorithm shown for topological sorting in
400 * Knuth's Volume 1. However, we would like to minimize unnecessary
401 * rearrangement of the input ordering; that is, when we have a choice of
402 * which item to output next, we always want to take the one highest in
403 * the original list. Therefore, instead of maintaining an unordered
404 * linked list of items-ready-to-output as Knuth does, we maintain a heap
405 * of their item numbers, which we can use as a priority queue. This
406 * turns the algorithm from O(N) to O(N log N) because each insertion or
407 * removal of a heap item takes O(log N) time. However, that's still
408 * plenty fast enough for this application.
411 *nOrdering = numObjs; /* for success return */
413 /* Eliminate the null case */
414 if (numObjs <= 0)
415 return true;
417 /* Create workspace for the above-described heap */
418 pendingHeap = binaryheap_allocate(numObjs, int_cmp, NULL);
421 * Scan the constraints, and for each item in the input, generate a count
422 * of the number of constraints that say it must be before something else.
423 * The count for the item with dumpId j is stored in beforeConstraints[j].
424 * We also make a map showing the input-order index of the item with
425 * dumpId j.
427 beforeConstraints = (int *) pg_malloc0((maxDumpId + 1) * sizeof(int));
428 idMap = (int *) pg_malloc((maxDumpId + 1) * sizeof(int));
429 for (i = 0; i < numObjs; i++)
431 obj = objs[i];
432 j = obj->dumpId;
433 if (j <= 0 || j > maxDumpId)
434 pg_fatal("invalid dumpId %d", j);
435 idMap[j] = i;
436 for (j = 0; j < obj->nDeps; j++)
438 k = obj->dependencies[j];
439 if (k <= 0 || k > maxDumpId)
440 pg_fatal("invalid dependency %d", k);
441 beforeConstraints[k]++;
446 * Now initialize the heap of items-ready-to-output by filling it with the
447 * indexes of items that already have beforeConstraints[id] == 0.
449 * We enter the indexes into pendingHeap in decreasing order so that the
450 * heap invariant is satisfied at the completion of this loop. This
451 * reduces the amount of work that binaryheap_build() must do.
453 for (i = numObjs; --i >= 0;)
455 if (beforeConstraints[objs[i]->dumpId] == 0)
456 binaryheap_add_unordered(pendingHeap, (void *) (intptr_t) i);
458 binaryheap_build(pendingHeap);
460 /*--------------------
461 * Now emit objects, working backwards in the output list. At each step,
462 * we use the priority heap to select the last item that has no remaining
463 * before-constraints. We remove that item from the heap, output it to
464 * ordering[], and decrease the beforeConstraints count of each of the
465 * items it was constrained against. Whenever an item's beforeConstraints
466 * count is thereby decreased to zero, we insert it into the priority heap
467 * to show that it is a candidate to output. We are done when the heap
468 * becomes empty; if we have output every element then we succeeded,
469 * otherwise we failed.
470 * i = number of ordering[] entries left to output
471 * j = objs[] index of item we are outputting
472 * k = temp for scanning constraint list for item j
473 *--------------------
475 i = numObjs;
476 while (!binaryheap_empty(pendingHeap))
478 /* Select object to output by removing largest heap member */
479 j = (int) (intptr_t) binaryheap_remove_first(pendingHeap);
480 obj = objs[j];
481 /* Output candidate to ordering[] */
482 ordering[--i] = obj;
483 /* Update beforeConstraints counts of its predecessors */
484 for (k = 0; k < obj->nDeps; k++)
486 int id = obj->dependencies[k];
488 if ((--beforeConstraints[id]) == 0)
489 binaryheap_add(pendingHeap, (void *) (intptr_t) idMap[id]);
494 * If we failed, report the objects that couldn't be output; these are the
495 * ones with beforeConstraints[] still nonzero.
497 if (i != 0)
499 k = 0;
500 for (j = 1; j <= maxDumpId; j++)
502 if (beforeConstraints[j] != 0)
503 ordering[k++] = objs[idMap[j]];
505 *nOrdering = k;
508 /* Done */
509 binaryheap_free(pendingHeap);
510 free(beforeConstraints);
511 free(idMap);
513 return (i == 0);
517 * findDependencyLoops - identify loops in TopoSort's failure output,
518 * and pass each such loop to repairDependencyLoop() for action
520 * In general there may be many loops in the set of objects returned by
521 * TopoSort; for speed we should try to repair as many loops as we can
522 * before trying TopoSort again. We can safely repair loops that are
523 * disjoint (have no members in common); if we find overlapping loops
524 * then we repair only the first one found, because the action taken to
525 * repair the first might have repaired the other as well. (If not,
526 * we'll fix it on the next go-round.)
528 * objs[] lists the objects TopoSort couldn't sort
529 * nObjs is the number of such objects
530 * totObjs is the total number of objects in the universe
532 static void
533 findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
536 * We use three data structures here:
538 * processed[] is a bool array indexed by dump ID, marking the objects
539 * already processed during this invocation of findDependencyLoops().
541 * searchFailed[] is another array indexed by dump ID. searchFailed[j] is
542 * set to dump ID k if we have proven that there is no dependency path
543 * leading from object j back to start point k. This allows us to skip
544 * useless searching when there are multiple dependency paths from k to j,
545 * which is a common situation. We could use a simple bool array for
546 * this, but then we'd need to re-zero it for each start point, resulting
547 * in O(N^2) zeroing work. Using the start point's dump ID as the "true"
548 * value lets us skip clearing the array before we consider the next start
549 * point.
551 * workspace[] is an array of DumpableObject pointers, in which we try to
552 * build lists of objects constituting loops. We make workspace[] large
553 * enough to hold all the objects in TopoSort's output, which is huge
554 * overkill in most cases but could theoretically be necessary if there is
555 * a single dependency chain linking all the objects.
557 bool *processed;
558 DumpId *searchFailed;
559 DumpableObject **workspace;
560 bool fixedloop;
561 int i;
563 processed = (bool *) pg_malloc0((getMaxDumpId() + 1) * sizeof(bool));
564 searchFailed = (DumpId *) pg_malloc0((getMaxDumpId() + 1) * sizeof(DumpId));
565 workspace = (DumpableObject **) pg_malloc(totObjs * sizeof(DumpableObject *));
566 fixedloop = false;
568 for (i = 0; i < nObjs; i++)
570 DumpableObject *obj = objs[i];
571 int looplen;
572 int j;
574 looplen = findLoop(obj,
575 obj->dumpId,
576 processed,
577 searchFailed,
578 workspace,
581 if (looplen > 0)
583 /* Found a loop, repair it */
584 repairDependencyLoop(workspace, looplen);
585 fixedloop = true;
586 /* Mark loop members as processed */
587 for (j = 0; j < looplen; j++)
588 processed[workspace[j]->dumpId] = true;
590 else
593 * There's no loop starting at this object, but mark it processed
594 * anyway. This is not necessary for correctness, but saves later
595 * invocations of findLoop() from uselessly chasing references to
596 * such an object.
598 processed[obj->dumpId] = true;
602 /* We'd better have fixed at least one loop */
603 if (!fixedloop)
604 pg_fatal("could not identify dependency loop");
606 free(workspace);
607 free(searchFailed);
608 free(processed);
612 * Recursively search for a circular dependency loop that doesn't include
613 * any already-processed objects.
615 * obj: object we are examining now
616 * startPoint: dumpId of starting object for the hoped-for circular loop
617 * processed[]: flag array marking already-processed objects
618 * searchFailed[]: flag array marking already-unsuccessfully-visited objects
619 * workspace[]: work array in which we are building list of loop members
620 * depth: number of valid entries in workspace[] at call
622 * On success, the length of the loop is returned, and workspace[] is filled
623 * with pointers to the members of the loop. On failure, we return 0.
625 * Note: it is possible that the given starting object is a member of more
626 * than one cycle; if so, we will find an arbitrary one of the cycles.
628 static int
629 findLoop(DumpableObject *obj,
630 DumpId startPoint,
631 bool *processed,
632 DumpId *searchFailed,
633 DumpableObject **workspace,
634 int depth)
636 int i;
639 * Reject if obj is already processed. This test prevents us from finding
640 * loops that overlap previously-processed loops.
642 if (processed[obj->dumpId])
643 return 0;
646 * If we've already proven there is no path from this object back to the
647 * startPoint, forget it.
649 if (searchFailed[obj->dumpId] == startPoint)
650 return 0;
653 * Reject if obj is already present in workspace. This test prevents us
654 * from going into infinite recursion if we are given a startPoint object
655 * that links to a cycle it's not a member of, and it guarantees that we
656 * can't overflow the allocated size of workspace[].
658 for (i = 0; i < depth; i++)
660 if (workspace[i] == obj)
661 return 0;
665 * Okay, tentatively add obj to workspace
667 workspace[depth++] = obj;
670 * See if we've found a loop back to the desired startPoint; if so, done
672 for (i = 0; i < obj->nDeps; i++)
674 if (obj->dependencies[i] == startPoint)
675 return depth;
679 * Recurse down each outgoing branch
681 for (i = 0; i < obj->nDeps; i++)
683 DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
684 int newDepth;
686 if (!nextobj)
687 continue; /* ignore dependencies on undumped objects */
688 newDepth = findLoop(nextobj,
689 startPoint,
690 processed,
691 searchFailed,
692 workspace,
693 depth);
694 if (newDepth > 0)
695 return newDepth;
699 * Remember there is no path from here back to startPoint
701 searchFailed[obj->dumpId] = startPoint;
703 return 0;
707 * A user-defined datatype will have a dependency loop with each of its
708 * I/O functions (since those have the datatype as input or output).
709 * Similarly, a range type will have a loop with its canonicalize function,
710 * if any. Break the loop by making the function depend on the associated
711 * shell type, instead.
713 static void
714 repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
716 TypeInfo *typeInfo = (TypeInfo *) typeobj;
718 /* remove function's dependency on type */
719 removeObjectDependency(funcobj, typeobj->dumpId);
721 /* add function's dependency on shell type, instead */
722 if (typeInfo->shellType)
724 addObjectDependency(funcobj, typeInfo->shellType->dobj.dumpId);
727 * Mark shell type (always including the definition, as we need the
728 * shell type defined to identify the function fully) as to be dumped
729 * if any such function is
731 if (funcobj->dump)
732 typeInfo->shellType->dobj.dump = funcobj->dump |
733 DUMP_COMPONENT_DEFINITION;
738 * Because we force a view to depend on its ON SELECT rule, while there
739 * will be an implicit dependency in the other direction, we need to break
740 * the loop. If there are no other objects in the loop then we can remove
741 * the implicit dependency and leave the ON SELECT rule non-separate.
742 * This applies to matviews, as well.
744 static void
745 repairViewRuleLoop(DumpableObject *viewobj,
746 DumpableObject *ruleobj)
748 /* remove rule's dependency on view */
749 removeObjectDependency(ruleobj, viewobj->dumpId);
750 /* flags on the two objects are already set correctly for this case */
754 * However, if there are other objects in the loop, we must break the loop
755 * by making the ON SELECT rule a separately-dumped object.
757 * Because findLoop() finds shorter cycles before longer ones, it's likely
758 * that we will have previously fired repairViewRuleLoop() and removed the
759 * rule's dependency on the view. Put it back to ensure the rule won't be
760 * emitted before the view.
762 * Note: this approach does *not* work for matviews, at the moment.
764 static void
765 repairViewRuleMultiLoop(DumpableObject *viewobj,
766 DumpableObject *ruleobj)
768 TableInfo *viewinfo = (TableInfo *) viewobj;
769 RuleInfo *ruleinfo = (RuleInfo *) ruleobj;
771 /* remove view's dependency on rule */
772 removeObjectDependency(viewobj, ruleobj->dumpId);
773 /* mark view to be printed with a dummy definition */
774 viewinfo->dummy_view = true;
775 /* mark rule as needing its own dump */
776 ruleinfo->separate = true;
777 /* put back rule's dependency on view */
778 addObjectDependency(ruleobj, viewobj->dumpId);
779 /* now that rule is separate, it must be post-data */
780 addObjectDependency(ruleobj, postDataBoundId);
784 * If a matview is involved in a multi-object loop, we can't currently fix
785 * that by splitting off the rule. As a stopgap, we try to fix it by
786 * dropping the constraint that the matview be dumped in the pre-data section.
787 * This is sufficient to handle cases where a matview depends on some unique
788 * index, as can happen if it has a GROUP BY for example.
790 * Note that the "next object" is not necessarily the matview itself;
791 * it could be the matview's rowtype, for example. We may come through here
792 * several times while removing all the pre-data linkages. In particular,
793 * if there are other matviews that depend on the one with the circularity
794 * problem, we'll come through here for each such matview and mark them all
795 * as postponed. (This works because all MVs have pre-data dependencies
796 * to begin with, so each of them will get visited.)
798 static void
799 repairMatViewBoundaryMultiLoop(DumpableObject *boundaryobj,
800 DumpableObject *nextobj)
802 /* remove boundary's dependency on object after it in loop */
803 removeObjectDependency(boundaryobj, nextobj->dumpId);
804 /* if that object is a matview, mark it as postponed into post-data */
805 if (nextobj->objType == DO_TABLE)
807 TableInfo *nextinfo = (TableInfo *) nextobj;
809 if (nextinfo->relkind == RELKIND_MATVIEW)
810 nextinfo->postponed_def = true;
815 * If a function is involved in a multi-object loop, we can't currently fix
816 * that by splitting it into two DumpableObjects. As a stopgap, we try to fix
817 * it by dropping the constraint that the function be dumped in the pre-data
818 * section. This is sufficient to handle cases where a function depends on
819 * some unique index, as can happen if it has a GROUP BY for example.
821 static void
822 repairFunctionBoundaryMultiLoop(DumpableObject *boundaryobj,
823 DumpableObject *nextobj)
825 /* remove boundary's dependency on object after it in loop */
826 removeObjectDependency(boundaryobj, nextobj->dumpId);
827 /* if that object is a function, mark it as postponed into post-data */
828 if (nextobj->objType == DO_FUNC)
830 FuncInfo *nextinfo = (FuncInfo *) nextobj;
832 nextinfo->postponed_def = true;
837 * Because we make tables depend on their CHECK constraints, while there
838 * will be an automatic dependency in the other direction, we need to break
839 * the loop. If there are no other objects in the loop then we can remove
840 * the automatic dependency and leave the CHECK constraint non-separate.
842 static void
843 repairTableConstraintLoop(DumpableObject *tableobj,
844 DumpableObject *constraintobj)
846 /* remove constraint's dependency on table */
847 removeObjectDependency(constraintobj, tableobj->dumpId);
851 * However, if there are other objects in the loop, we must break the loop
852 * by making the CHECK constraint a separately-dumped object.
854 * Because findLoop() finds shorter cycles before longer ones, it's likely
855 * that we will have previously fired repairTableConstraintLoop() and
856 * removed the constraint's dependency on the table. Put it back to ensure
857 * the constraint won't be emitted before the table...
859 static void
860 repairTableConstraintMultiLoop(DumpableObject *tableobj,
861 DumpableObject *constraintobj)
863 /* remove table's dependency on constraint */
864 removeObjectDependency(tableobj, constraintobj->dumpId);
865 /* mark constraint as needing its own dump */
866 ((ConstraintInfo *) constraintobj)->separate = true;
867 /* put back constraint's dependency on table */
868 addObjectDependency(constraintobj, tableobj->dumpId);
869 /* now that constraint is separate, it must be post-data */
870 addObjectDependency(constraintobj, postDataBoundId);
874 * Attribute defaults behave exactly the same as CHECK constraints...
876 static void
877 repairTableAttrDefLoop(DumpableObject *tableobj,
878 DumpableObject *attrdefobj)
880 /* remove attrdef's dependency on table */
881 removeObjectDependency(attrdefobj, tableobj->dumpId);
884 static void
885 repairTableAttrDefMultiLoop(DumpableObject *tableobj,
886 DumpableObject *attrdefobj)
888 /* remove table's dependency on attrdef */
889 removeObjectDependency(tableobj, attrdefobj->dumpId);
890 /* mark attrdef as needing its own dump */
891 ((AttrDefInfo *) attrdefobj)->separate = true;
892 /* put back attrdef's dependency on table */
893 addObjectDependency(attrdefobj, tableobj->dumpId);
897 * CHECK constraints on domains work just like those on tables ...
899 static void
900 repairDomainConstraintLoop(DumpableObject *domainobj,
901 DumpableObject *constraintobj)
903 /* remove constraint's dependency on domain */
904 removeObjectDependency(constraintobj, domainobj->dumpId);
907 static void
908 repairDomainConstraintMultiLoop(DumpableObject *domainobj,
909 DumpableObject *constraintobj)
911 /* remove domain's dependency on constraint */
912 removeObjectDependency(domainobj, constraintobj->dumpId);
913 /* mark constraint as needing its own dump */
914 ((ConstraintInfo *) constraintobj)->separate = true;
915 /* put back constraint's dependency on domain */
916 addObjectDependency(constraintobj, domainobj->dumpId);
917 /* now that constraint is separate, it must be post-data */
918 addObjectDependency(constraintobj, postDataBoundId);
921 static void
922 repairIndexLoop(DumpableObject *partedindex,
923 DumpableObject *partindex)
925 removeObjectDependency(partedindex, partindex->dumpId);
929 * Fix a dependency loop, or die trying ...
931 * This routine is mainly concerned with reducing the multiple ways that
932 * a loop might appear to common cases, which it passes off to the
933 * "fixer" routines above.
935 static void
936 repairDependencyLoop(DumpableObject **loop,
937 int nLoop)
939 int i,
942 /* Datatype and one of its I/O or canonicalize functions */
943 if (nLoop == 2 &&
944 loop[0]->objType == DO_TYPE &&
945 loop[1]->objType == DO_FUNC)
947 repairTypeFuncLoop(loop[0], loop[1]);
948 return;
950 if (nLoop == 2 &&
951 loop[1]->objType == DO_TYPE &&
952 loop[0]->objType == DO_FUNC)
954 repairTypeFuncLoop(loop[1], loop[0]);
955 return;
958 /* View (including matview) and its ON SELECT rule */
959 if (nLoop == 2 &&
960 loop[0]->objType == DO_TABLE &&
961 loop[1]->objType == DO_RULE &&
962 (((TableInfo *) loop[0])->relkind == RELKIND_VIEW ||
963 ((TableInfo *) loop[0])->relkind == RELKIND_MATVIEW) &&
964 ((RuleInfo *) loop[1])->ev_type == '1' &&
965 ((RuleInfo *) loop[1])->is_instead &&
966 ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
968 repairViewRuleLoop(loop[0], loop[1]);
969 return;
971 if (nLoop == 2 &&
972 loop[1]->objType == DO_TABLE &&
973 loop[0]->objType == DO_RULE &&
974 (((TableInfo *) loop[1])->relkind == RELKIND_VIEW ||
975 ((TableInfo *) loop[1])->relkind == RELKIND_MATVIEW) &&
976 ((RuleInfo *) loop[0])->ev_type == '1' &&
977 ((RuleInfo *) loop[0])->is_instead &&
978 ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
980 repairViewRuleLoop(loop[1], loop[0]);
981 return;
984 /* Indirect loop involving view (but not matview) and ON SELECT rule */
985 if (nLoop > 2)
987 for (i = 0; i < nLoop; i++)
989 if (loop[i]->objType == DO_TABLE &&
990 ((TableInfo *) loop[i])->relkind == RELKIND_VIEW)
992 for (j = 0; j < nLoop; j++)
994 if (loop[j]->objType == DO_RULE &&
995 ((RuleInfo *) loop[j])->ev_type == '1' &&
996 ((RuleInfo *) loop[j])->is_instead &&
997 ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
999 repairViewRuleMultiLoop(loop[i], loop[j]);
1000 return;
1007 /* Indirect loop involving matview and data boundary */
1008 if (nLoop > 2)
1010 for (i = 0; i < nLoop; i++)
1012 if (loop[i]->objType == DO_TABLE &&
1013 ((TableInfo *) loop[i])->relkind == RELKIND_MATVIEW)
1015 for (j = 0; j < nLoop; j++)
1017 if (loop[j]->objType == DO_PRE_DATA_BOUNDARY)
1019 DumpableObject *nextobj;
1021 nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
1022 repairMatViewBoundaryMultiLoop(loop[j], nextobj);
1023 return;
1030 /* Indirect loop involving function and data boundary */
1031 if (nLoop > 2)
1033 for (i = 0; i < nLoop; i++)
1035 if (loop[i]->objType == DO_FUNC)
1037 for (j = 0; j < nLoop; j++)
1039 if (loop[j]->objType == DO_PRE_DATA_BOUNDARY)
1041 DumpableObject *nextobj;
1043 nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
1044 repairFunctionBoundaryMultiLoop(loop[j], nextobj);
1045 return;
1052 /* Table and CHECK constraint */
1053 if (nLoop == 2 &&
1054 loop[0]->objType == DO_TABLE &&
1055 loop[1]->objType == DO_CONSTRAINT &&
1056 ((ConstraintInfo *) loop[1])->contype == 'c' &&
1057 ((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
1059 repairTableConstraintLoop(loop[0], loop[1]);
1060 return;
1062 if (nLoop == 2 &&
1063 loop[1]->objType == DO_TABLE &&
1064 loop[0]->objType == DO_CONSTRAINT &&
1065 ((ConstraintInfo *) loop[0])->contype == 'c' &&
1066 ((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
1068 repairTableConstraintLoop(loop[1], loop[0]);
1069 return;
1072 /* Indirect loop involving table and CHECK constraint */
1073 if (nLoop > 2)
1075 for (i = 0; i < nLoop; i++)
1077 if (loop[i]->objType == DO_TABLE)
1079 for (j = 0; j < nLoop; j++)
1081 if (loop[j]->objType == DO_CONSTRAINT &&
1082 ((ConstraintInfo *) loop[j])->contype == 'c' &&
1083 ((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
1085 repairTableConstraintMultiLoop(loop[i], loop[j]);
1086 return;
1093 /* Table and attribute default */
1094 if (nLoop == 2 &&
1095 loop[0]->objType == DO_TABLE &&
1096 loop[1]->objType == DO_ATTRDEF &&
1097 ((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
1099 repairTableAttrDefLoop(loop[0], loop[1]);
1100 return;
1102 if (nLoop == 2 &&
1103 loop[1]->objType == DO_TABLE &&
1104 loop[0]->objType == DO_ATTRDEF &&
1105 ((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
1107 repairTableAttrDefLoop(loop[1], loop[0]);
1108 return;
1111 /* index on partitioned table and corresponding index on partition */
1112 if (nLoop == 2 &&
1113 loop[0]->objType == DO_INDEX &&
1114 loop[1]->objType == DO_INDEX)
1116 if (((IndxInfo *) loop[0])->parentidx == loop[1]->catId.oid)
1118 repairIndexLoop(loop[0], loop[1]);
1119 return;
1121 else if (((IndxInfo *) loop[1])->parentidx == loop[0]->catId.oid)
1123 repairIndexLoop(loop[1], loop[0]);
1124 return;
1128 /* Indirect loop involving table and attribute default */
1129 if (nLoop > 2)
1131 for (i = 0; i < nLoop; i++)
1133 if (loop[i]->objType == DO_TABLE)
1135 for (j = 0; j < nLoop; j++)
1137 if (loop[j]->objType == DO_ATTRDEF &&
1138 ((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
1140 repairTableAttrDefMultiLoop(loop[i], loop[j]);
1141 return;
1148 /* Domain and CHECK constraint */
1149 if (nLoop == 2 &&
1150 loop[0]->objType == DO_TYPE &&
1151 loop[1]->objType == DO_CONSTRAINT &&
1152 ((ConstraintInfo *) loop[1])->contype == 'c' &&
1153 ((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
1155 repairDomainConstraintLoop(loop[0], loop[1]);
1156 return;
1158 if (nLoop == 2 &&
1159 loop[1]->objType == DO_TYPE &&
1160 loop[0]->objType == DO_CONSTRAINT &&
1161 ((ConstraintInfo *) loop[0])->contype == 'c' &&
1162 ((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
1164 repairDomainConstraintLoop(loop[1], loop[0]);
1165 return;
1168 /* Indirect loop involving domain and CHECK constraint */
1169 if (nLoop > 2)
1171 for (i = 0; i < nLoop; i++)
1173 if (loop[i]->objType == DO_TYPE)
1175 for (j = 0; j < nLoop; j++)
1177 if (loop[j]->objType == DO_CONSTRAINT &&
1178 ((ConstraintInfo *) loop[j])->contype == 'c' &&
1179 ((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
1181 repairDomainConstraintMultiLoop(loop[i], loop[j]);
1182 return;
1190 * Loop of table with itself --- just ignore it.
1192 * (Actually, what this arises from is a dependency of a table column on
1193 * another column, which happened with generated columns before v15; or a
1194 * dependency of a table column on the whole table, which happens with
1195 * partitioning. But we didn't pay attention to sub-object IDs while
1196 * collecting the dependency data, so we can't see that here.)
1198 if (nLoop == 1)
1200 if (loop[0]->objType == DO_TABLE)
1202 removeObjectDependency(loop[0], loop[0]->dumpId);
1203 return;
1208 * If all the objects are TABLE_DATA items, what we must have is a
1209 * circular set of foreign key constraints (or a single self-referential
1210 * table). Print an appropriate complaint and break the loop arbitrarily.
1212 for (i = 0; i < nLoop; i++)
1214 if (loop[i]->objType != DO_TABLE_DATA)
1215 break;
1217 if (i >= nLoop)
1219 pg_log_warning(ngettext("there are circular foreign-key constraints on this table:",
1220 "there are circular foreign-key constraints among these tables:",
1221 nLoop));
1222 for (i = 0; i < nLoop; i++)
1223 pg_log_warning_detail("%s", loop[i]->name);
1224 pg_log_warning_hint("You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.");
1225 pg_log_warning_hint("Consider using a full dump instead of a --data-only dump to avoid this problem.");
1226 if (nLoop > 1)
1227 removeObjectDependency(loop[0], loop[1]->dumpId);
1228 else /* must be a self-dependency */
1229 removeObjectDependency(loop[0], loop[0]->dumpId);
1230 return;
1234 * If we can't find a principled way to break the loop, complain and break
1235 * it in an arbitrary fashion.
1237 pg_log_warning("could not resolve dependency loop among these items:");
1238 for (i = 0; i < nLoop; i++)
1240 char buf[1024];
1242 describeDumpableObject(loop[i], buf, sizeof(buf));
1243 pg_log_warning_detail("%s", buf);
1246 if (nLoop > 1)
1247 removeObjectDependency(loop[0], loop[1]->dumpId);
1248 else /* must be a self-dependency */
1249 removeObjectDependency(loop[0], loop[0]->dumpId);
1253 * Describe a dumpable object usefully for errors
1255 * This should probably go somewhere else...
1257 static void
1258 describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
1260 switch (obj->objType)
1262 case DO_NAMESPACE:
1263 snprintf(buf, bufsize,
1264 "SCHEMA %s (ID %d OID %u)",
1265 obj->name, obj->dumpId, obj->catId.oid);
1266 return;
1267 case DO_EXTENSION:
1268 snprintf(buf, bufsize,
1269 "EXTENSION %s (ID %d OID %u)",
1270 obj->name, obj->dumpId, obj->catId.oid);
1271 return;
1272 case DO_TYPE:
1273 snprintf(buf, bufsize,
1274 "TYPE %s (ID %d OID %u)",
1275 obj->name, obj->dumpId, obj->catId.oid);
1276 return;
1277 case DO_SHELL_TYPE:
1278 snprintf(buf, bufsize,
1279 "SHELL TYPE %s (ID %d OID %u)",
1280 obj->name, obj->dumpId, obj->catId.oid);
1281 return;
1282 case DO_FUNC:
1283 snprintf(buf, bufsize,
1284 "FUNCTION %s (ID %d OID %u)",
1285 obj->name, obj->dumpId, obj->catId.oid);
1286 return;
1287 case DO_AGG:
1288 snprintf(buf, bufsize,
1289 "AGGREGATE %s (ID %d OID %u)",
1290 obj->name, obj->dumpId, obj->catId.oid);
1291 return;
1292 case DO_OPERATOR:
1293 snprintf(buf, bufsize,
1294 "OPERATOR %s (ID %d OID %u)",
1295 obj->name, obj->dumpId, obj->catId.oid);
1296 return;
1297 case DO_ACCESS_METHOD:
1298 snprintf(buf, bufsize,
1299 "ACCESS METHOD %s (ID %d OID %u)",
1300 obj->name, obj->dumpId, obj->catId.oid);
1301 return;
1302 case DO_OPCLASS:
1303 snprintf(buf, bufsize,
1304 "OPERATOR CLASS %s (ID %d OID %u)",
1305 obj->name, obj->dumpId, obj->catId.oid);
1306 return;
1307 case DO_OPFAMILY:
1308 snprintf(buf, bufsize,
1309 "OPERATOR FAMILY %s (ID %d OID %u)",
1310 obj->name, obj->dumpId, obj->catId.oid);
1311 return;
1312 case DO_COLLATION:
1313 snprintf(buf, bufsize,
1314 "COLLATION %s (ID %d OID %u)",
1315 obj->name, obj->dumpId, obj->catId.oid);
1316 return;
1317 case DO_CONVERSION:
1318 snprintf(buf, bufsize,
1319 "CONVERSION %s (ID %d OID %u)",
1320 obj->name, obj->dumpId, obj->catId.oid);
1321 return;
1322 case DO_TABLE:
1323 snprintf(buf, bufsize,
1324 "TABLE %s (ID %d OID %u)",
1325 obj->name, obj->dumpId, obj->catId.oid);
1326 return;
1327 case DO_TABLE_ATTACH:
1328 snprintf(buf, bufsize,
1329 "TABLE ATTACH %s (ID %d)",
1330 obj->name, obj->dumpId);
1331 return;
1332 case DO_ATTRDEF:
1333 snprintf(buf, bufsize,
1334 "ATTRDEF %s.%s (ID %d OID %u)",
1335 ((AttrDefInfo *) obj)->adtable->dobj.name,
1336 ((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
1337 obj->dumpId, obj->catId.oid);
1338 return;
1339 case DO_INDEX:
1340 snprintf(buf, bufsize,
1341 "INDEX %s (ID %d OID %u)",
1342 obj->name, obj->dumpId, obj->catId.oid);
1343 return;
1344 case DO_INDEX_ATTACH:
1345 snprintf(buf, bufsize,
1346 "INDEX ATTACH %s (ID %d)",
1347 obj->name, obj->dumpId);
1348 return;
1349 case DO_STATSEXT:
1350 snprintf(buf, bufsize,
1351 "STATISTICS %s (ID %d OID %u)",
1352 obj->name, obj->dumpId, obj->catId.oid);
1353 return;
1354 case DO_REFRESH_MATVIEW:
1355 snprintf(buf, bufsize,
1356 "REFRESH MATERIALIZED VIEW %s (ID %d OID %u)",
1357 obj->name, obj->dumpId, obj->catId.oid);
1358 return;
1359 case DO_RULE:
1360 snprintf(buf, bufsize,
1361 "RULE %s (ID %d OID %u)",
1362 obj->name, obj->dumpId, obj->catId.oid);
1363 return;
1364 case DO_TRIGGER:
1365 snprintf(buf, bufsize,
1366 "TRIGGER %s (ID %d OID %u)",
1367 obj->name, obj->dumpId, obj->catId.oid);
1368 return;
1369 case DO_EVENT_TRIGGER:
1370 snprintf(buf, bufsize,
1371 "EVENT TRIGGER %s (ID %d OID %u)",
1372 obj->name, obj->dumpId, obj->catId.oid);
1373 return;
1374 case DO_CONSTRAINT:
1375 snprintf(buf, bufsize,
1376 "CONSTRAINT %s (ID %d OID %u)",
1377 obj->name, obj->dumpId, obj->catId.oid);
1378 return;
1379 case DO_FK_CONSTRAINT:
1380 snprintf(buf, bufsize,
1381 "FK CONSTRAINT %s (ID %d OID %u)",
1382 obj->name, obj->dumpId, obj->catId.oid);
1383 return;
1384 case DO_PROCLANG:
1385 snprintf(buf, bufsize,
1386 "PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1387 obj->name, obj->dumpId, obj->catId.oid);
1388 return;
1389 case DO_CAST:
1390 snprintf(buf, bufsize,
1391 "CAST %u to %u (ID %d OID %u)",
1392 ((CastInfo *) obj)->castsource,
1393 ((CastInfo *) obj)->casttarget,
1394 obj->dumpId, obj->catId.oid);
1395 return;
1396 case DO_TRANSFORM:
1397 snprintf(buf, bufsize,
1398 "TRANSFORM %u lang %u (ID %d OID %u)",
1399 ((TransformInfo *) obj)->trftype,
1400 ((TransformInfo *) obj)->trflang,
1401 obj->dumpId, obj->catId.oid);
1402 return;
1403 case DO_TABLE_DATA:
1404 snprintf(buf, bufsize,
1405 "TABLE DATA %s (ID %d OID %u)",
1406 obj->name, obj->dumpId, obj->catId.oid);
1407 return;
1408 case DO_SEQUENCE_SET:
1409 snprintf(buf, bufsize,
1410 "SEQUENCE SET %s (ID %d OID %u)",
1411 obj->name, obj->dumpId, obj->catId.oid);
1412 return;
1413 case DO_DUMMY_TYPE:
1414 snprintf(buf, bufsize,
1415 "DUMMY TYPE %s (ID %d OID %u)",
1416 obj->name, obj->dumpId, obj->catId.oid);
1417 return;
1418 case DO_TSPARSER:
1419 snprintf(buf, bufsize,
1420 "TEXT SEARCH PARSER %s (ID %d OID %u)",
1421 obj->name, obj->dumpId, obj->catId.oid);
1422 return;
1423 case DO_TSDICT:
1424 snprintf(buf, bufsize,
1425 "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
1426 obj->name, obj->dumpId, obj->catId.oid);
1427 return;
1428 case DO_TSTEMPLATE:
1429 snprintf(buf, bufsize,
1430 "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
1431 obj->name, obj->dumpId, obj->catId.oid);
1432 return;
1433 case DO_TSCONFIG:
1434 snprintf(buf, bufsize,
1435 "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
1436 obj->name, obj->dumpId, obj->catId.oid);
1437 return;
1438 case DO_FDW:
1439 snprintf(buf, bufsize,
1440 "FOREIGN DATA WRAPPER %s (ID %d OID %u)",
1441 obj->name, obj->dumpId, obj->catId.oid);
1442 return;
1443 case DO_FOREIGN_SERVER:
1444 snprintf(buf, bufsize,
1445 "FOREIGN SERVER %s (ID %d OID %u)",
1446 obj->name, obj->dumpId, obj->catId.oid);
1447 return;
1448 case DO_DEFAULT_ACL:
1449 snprintf(buf, bufsize,
1450 "DEFAULT ACL %s (ID %d OID %u)",
1451 obj->name, obj->dumpId, obj->catId.oid);
1452 return;
1453 case DO_LARGE_OBJECT:
1454 snprintf(buf, bufsize,
1455 "LARGE OBJECT (ID %d OID %u)",
1456 obj->dumpId, obj->catId.oid);
1457 return;
1458 case DO_LARGE_OBJECT_DATA:
1459 snprintf(buf, bufsize,
1460 "LARGE OBJECT DATA (ID %d)",
1461 obj->dumpId);
1462 return;
1463 case DO_POLICY:
1464 snprintf(buf, bufsize,
1465 "POLICY (ID %d OID %u)",
1466 obj->dumpId, obj->catId.oid);
1467 return;
1468 case DO_PUBLICATION:
1469 snprintf(buf, bufsize,
1470 "PUBLICATION (ID %d OID %u)",
1471 obj->dumpId, obj->catId.oid);
1472 return;
1473 case DO_PUBLICATION_REL:
1474 snprintf(buf, bufsize,
1475 "PUBLICATION TABLE (ID %d OID %u)",
1476 obj->dumpId, obj->catId.oid);
1477 return;
1478 case DO_PUBLICATION_TABLE_IN_SCHEMA:
1479 snprintf(buf, bufsize,
1480 "PUBLICATION TABLES IN SCHEMA (ID %d OID %u)",
1481 obj->dumpId, obj->catId.oid);
1482 return;
1483 case DO_SUBSCRIPTION:
1484 snprintf(buf, bufsize,
1485 "SUBSCRIPTION (ID %d OID %u)",
1486 obj->dumpId, obj->catId.oid);
1487 return;
1488 case DO_SUBSCRIPTION_REL:
1489 snprintf(buf, bufsize,
1490 "SUBSCRIPTION TABLE (ID %d OID %u)",
1491 obj->dumpId, obj->catId.oid);
1492 return;
1493 case DO_PRE_DATA_BOUNDARY:
1494 snprintf(buf, bufsize,
1495 "PRE-DATA BOUNDARY (ID %d)",
1496 obj->dumpId);
1497 return;
1498 case DO_POST_DATA_BOUNDARY:
1499 snprintf(buf, bufsize,
1500 "POST-DATA BOUNDARY (ID %d)",
1501 obj->dumpId);
1502 return;
1504 /* shouldn't get here */
1505 snprintf(buf, bufsize,
1506 "object type %d (ID %d OID %u)",
1507 (int) obj->objType,
1508 obj->dumpId, obj->catId.oid);
1511 /* binaryheap comparator that compares "a" and "b" as integers */
1512 static int
1513 int_cmp(void *a, void *b, void *arg)
1515 int ai = (int) (intptr_t) a;
1516 int bi = (int) (intptr_t) b;
1518 return pg_cmp_s32(ai, bi);