1 /*-------------------------------------------------------------------------
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
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"
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
61 PRIO_TYPE
, /* used for DO_TYPE and DO_SHELL_TYPE */
67 PRIO_OPFAMILY
, /* used for DO_OPFAMILY and DO_OPCLASS */
80 PRIO_PRE_DATA_BOUNDARY
, /* boundary! */
83 PRIO_LARGE_OBJECT_DATA
,
84 PRIO_POST_DATA_BOUNDARY
, /* boundary! */
95 PRIO_PUBLICATION_TABLE_IN_SCHEMA
,
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
,
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
,
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
,
165 DumpableObject
**ordering
,
167 static void findDependencyLoops(DumpableObject
**objs
, int nObjs
, int totObjs
);
168 static int findLoop(DumpableObject
*obj
,
171 DumpId
*searchFailed
,
172 DumpableObject
**workspace
,
174 static void repairDependencyLoop(DumpableObject
**loop
,
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
188 sortDumpableObjectsByTypeName(DumpableObject
**objs
, int numObjs
)
191 qsort(objs
, numObjs
, sizeof(DumpableObject
*),
196 DOTypeNameCompare(const void *p1
, const void *p2
)
198 DumpableObject
*obj1
= *(DumpableObject
*const *) p1
;
199 DumpableObject
*obj2
= *(DumpableObject
*const *) p2
;
202 /* Sort by type's priority */
203 cmpval
= dbObjectTypePriority
[obj1
->objType
] -
204 dbObjectTypePriority
[obj2
->objType
];
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.
218 cmpval
= strcmp(obj1
->namespace->dobj
.name
,
219 obj2
->namespace->dobj
.name
);
226 else if (obj2
->namespace)
230 cmpval
= strcmp(obj1
->name
, obj2
->name
);
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
;
241 /* Sort by number of arguments, then argument type names */
242 cmpval
= fobj1
->nargs
- fobj2
->nargs
;
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
);
259 cmpval
= strcmp(argtype1
->dobj
.name
, argtype2
->dobj
.name
);
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
);
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
);
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
);
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
);
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
);
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.
332 sortDumpableObjects(DumpableObject
**objs
, int numObjs
,
333 DumpId preBoundaryId
, DumpId postBoundaryId
)
335 DumpableObject
**ordering
;
338 if (numObjs
<= 0) /* can't happen anymore ... */
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
*));
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
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.
384 TopoSort(DumpableObject
**objs
,
386 DumpableObject
**ordering
, /* output argument */
387 int *nOrdering
) /* output argument */
389 DumpId maxDumpId
= getMaxDumpId();
390 binaryheap
*pendingHeap
;
391 int *beforeConstraints
;
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 */
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
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
++)
433 if (j
<= 0 || j
> maxDumpId
)
434 pg_fatal("invalid dumpId %d", j
);
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 *--------------------
476 while (!binaryheap_empty(pendingHeap
))
478 /* Select object to output by removing largest heap member */
479 j
= (int) (intptr_t) binaryheap_remove_first(pendingHeap
);
481 /* Output candidate to ordering[] */
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.
500 for (j
= 1; j
<= maxDumpId
; j
++)
502 if (beforeConstraints
[j
] != 0)
503 ordering
[k
++] = objs
[idMap
[j
]];
509 binaryheap_free(pendingHeap
);
510 free(beforeConstraints
);
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
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
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.
558 DumpId
*searchFailed
;
559 DumpableObject
**workspace
;
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
*));
568 for (i
= 0; i
< nObjs
; i
++)
570 DumpableObject
*obj
= objs
[i
];
574 looplen
= findLoop(obj
,
583 /* Found a loop, repair it */
584 repairDependencyLoop(workspace
, looplen
);
586 /* Mark loop members as processed */
587 for (j
= 0; j
< looplen
; j
++)
588 processed
[workspace
[j
]->dumpId
] = true;
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
598 processed
[obj
->dumpId
] = true;
602 /* We'd better have fixed at least one loop */
604 pg_fatal("could not identify dependency loop");
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.
629 findLoop(DumpableObject
*obj
,
632 DumpId
*searchFailed
,
633 DumpableObject
**workspace
,
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
])
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
)
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
)
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
)
679 * Recurse down each outgoing branch
681 for (i
= 0; i
< obj
->nDeps
; i
++)
683 DumpableObject
*nextobj
= findObjectByDumpId(obj
->dependencies
[i
]);
687 continue; /* ignore dependencies on undumped objects */
688 newDepth
= findLoop(nextobj
,
699 * Remember there is no path from here back to startPoint
701 searchFailed
[obj
->dumpId
] = startPoint
;
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.
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
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.
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.
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.)
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.
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.
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...
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...
877 repairTableAttrDefLoop(DumpableObject
*tableobj
,
878 DumpableObject
*attrdefobj
)
880 /* remove attrdef's dependency on table */
881 removeObjectDependency(attrdefobj
, tableobj
->dumpId
);
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 ...
900 repairDomainConstraintLoop(DumpableObject
*domainobj
,
901 DumpableObject
*constraintobj
)
903 /* remove constraint's dependency on domain */
904 removeObjectDependency(constraintobj
, domainobj
->dumpId
);
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
);
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.
936 repairDependencyLoop(DumpableObject
**loop
,
942 /* Datatype and one of its I/O or canonicalize functions */
944 loop
[0]->objType
== DO_TYPE
&&
945 loop
[1]->objType
== DO_FUNC
)
947 repairTypeFuncLoop(loop
[0], loop
[1]);
951 loop
[1]->objType
== DO_TYPE
&&
952 loop
[0]->objType
== DO_FUNC
)
954 repairTypeFuncLoop(loop
[1], loop
[0]);
958 /* View (including matview) and its ON SELECT rule */
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]);
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]);
984 /* Indirect loop involving view (but not matview) and ON SELECT rule */
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
]);
1007 /* Indirect loop involving matview and data boundary */
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
);
1030 /* Indirect loop involving function and data boundary */
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
);
1052 /* Table and CHECK constraint */
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]);
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]);
1072 /* Indirect loop involving table and CHECK constraint */
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
]);
1093 /* Table and attribute default */
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]);
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]);
1111 /* index on partitioned table and corresponding index on partition */
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]);
1121 else if (((IndxInfo
*) loop
[1])->parentidx
== loop
[0]->catId
.oid
)
1123 repairIndexLoop(loop
[1], loop
[0]);
1128 /* Indirect loop involving table and attribute default */
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
]);
1148 /* Domain and CHECK constraint */
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]);
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]);
1168 /* Indirect loop involving domain and CHECK constraint */
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
]);
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.)
1200 if (loop
[0]->objType
== DO_TABLE
)
1202 removeObjectDependency(loop
[0], loop
[0]->dumpId
);
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
)
1219 pg_log_warning(ngettext("there are circular foreign-key constraints on this table:",
1220 "there are circular foreign-key constraints among these tables:",
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.");
1227 removeObjectDependency(loop
[0], loop
[1]->dumpId
);
1228 else /* must be a self-dependency */
1229 removeObjectDependency(loop
[0], loop
[0]->dumpId
);
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
++)
1242 describeDumpableObject(loop
[i
], buf
, sizeof(buf
));
1243 pg_log_warning_detail("%s", buf
);
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...
1258 describeDumpableObject(DumpableObject
*obj
, char *buf
, int bufsize
)
1260 switch (obj
->objType
)
1263 snprintf(buf
, bufsize
,
1264 "SCHEMA %s (ID %d OID %u)",
1265 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1268 snprintf(buf
, bufsize
,
1269 "EXTENSION %s (ID %d OID %u)",
1270 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1273 snprintf(buf
, bufsize
,
1274 "TYPE %s (ID %d OID %u)",
1275 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1278 snprintf(buf
, bufsize
,
1279 "SHELL TYPE %s (ID %d OID %u)",
1280 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1283 snprintf(buf
, bufsize
,
1284 "FUNCTION %s (ID %d OID %u)",
1285 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1288 snprintf(buf
, bufsize
,
1289 "AGGREGATE %s (ID %d OID %u)",
1290 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1293 snprintf(buf
, bufsize
,
1294 "OPERATOR %s (ID %d OID %u)",
1295 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
1303 snprintf(buf
, bufsize
,
1304 "OPERATOR CLASS %s (ID %d OID %u)",
1305 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1308 snprintf(buf
, bufsize
,
1309 "OPERATOR FAMILY %s (ID %d OID %u)",
1310 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1313 snprintf(buf
, bufsize
,
1314 "COLLATION %s (ID %d OID %u)",
1315 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1318 snprintf(buf
, bufsize
,
1319 "CONVERSION %s (ID %d OID %u)",
1320 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1323 snprintf(buf
, bufsize
,
1324 "TABLE %s (ID %d OID %u)",
1325 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1327 case DO_TABLE_ATTACH
:
1328 snprintf(buf
, bufsize
,
1329 "TABLE ATTACH %s (ID %d)",
1330 obj
->name
, obj
->dumpId
);
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
);
1340 snprintf(buf
, bufsize
,
1341 "INDEX %s (ID %d OID %u)",
1342 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1344 case DO_INDEX_ATTACH
:
1345 snprintf(buf
, bufsize
,
1346 "INDEX ATTACH %s (ID %d)",
1347 obj
->name
, obj
->dumpId
);
1350 snprintf(buf
, bufsize
,
1351 "STATISTICS %s (ID %d OID %u)",
1352 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
1360 snprintf(buf
, bufsize
,
1361 "RULE %s (ID %d OID %u)",
1362 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1365 snprintf(buf
, bufsize
,
1366 "TRIGGER %s (ID %d OID %u)",
1367 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
1375 snprintf(buf
, bufsize
,
1376 "CONSTRAINT %s (ID %d OID %u)",
1377 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
1385 snprintf(buf
, bufsize
,
1386 "PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1387 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
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
);
1404 snprintf(buf
, bufsize
,
1405 "TABLE DATA %s (ID %d OID %u)",
1406 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
1414 snprintf(buf
, bufsize
,
1415 "DUMMY TYPE %s (ID %d OID %u)",
1416 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1419 snprintf(buf
, bufsize
,
1420 "TEXT SEARCH PARSER %s (ID %d OID %u)",
1421 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1424 snprintf(buf
, bufsize
,
1425 "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
1426 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1429 snprintf(buf
, bufsize
,
1430 "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
1431 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1434 snprintf(buf
, bufsize
,
1435 "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
1436 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
1439 snprintf(buf
, bufsize
,
1440 "FOREIGN DATA WRAPPER %s (ID %d OID %u)",
1441 obj
->name
, obj
->dumpId
, obj
->catId
.oid
);
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
);
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
);
1453 case DO_LARGE_OBJECT
:
1454 snprintf(buf
, bufsize
,
1455 "LARGE OBJECT (ID %d OID %u)",
1456 obj
->dumpId
, obj
->catId
.oid
);
1458 case DO_LARGE_OBJECT_DATA
:
1459 snprintf(buf
, bufsize
,
1460 "LARGE OBJECT DATA (ID %d)",
1464 snprintf(buf
, bufsize
,
1465 "POLICY (ID %d OID %u)",
1466 obj
->dumpId
, obj
->catId
.oid
);
1468 case DO_PUBLICATION
:
1469 snprintf(buf
, bufsize
,
1470 "PUBLICATION (ID %d OID %u)",
1471 obj
->dumpId
, obj
->catId
.oid
);
1473 case DO_PUBLICATION_REL
:
1474 snprintf(buf
, bufsize
,
1475 "PUBLICATION TABLE (ID %d OID %u)",
1476 obj
->dumpId
, obj
->catId
.oid
);
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
);
1483 case DO_SUBSCRIPTION
:
1484 snprintf(buf
, bufsize
,
1485 "SUBSCRIPTION (ID %d OID %u)",
1486 obj
->dumpId
, obj
->catId
.oid
);
1488 case DO_SUBSCRIPTION_REL
:
1489 snprintf(buf
, bufsize
,
1490 "SUBSCRIPTION TABLE (ID %d OID %u)",
1491 obj
->dumpId
, obj
->catId
.oid
);
1493 case DO_PRE_DATA_BOUNDARY
:
1494 snprintf(buf
, bufsize
,
1495 "PRE-DATA BOUNDARY (ID %d)",
1498 case DO_POST_DATA_BOUNDARY
:
1499 snprintf(buf
, bufsize
,
1500 "POST-DATA BOUNDARY (ID %d)",
1504 /* shouldn't get here */
1505 snprintf(buf
, bufsize
,
1506 "object type %d (ID %d OID %u)",
1508 obj
->dumpId
, obj
->catId
.oid
);
1511 /* binaryheap comparator that compares "a" and "b" as integers */
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
);