1/*-------------------------------------------------------------------------
2 *
3 * pg_dump_sort.c
4 * Sort the items of a dump into a safe order for dumping
5 *
6 *
7 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 *
11 * IDENTIFICATION
12 * src/bin/pg_dump/pg_dump_sort.c
13 *
14 *-------------------------------------------------------------------------
15 */
16#include "postgres_fe.h"
17
18#include "pg_backup_archiver.h"
19#include "pg_backup_utils.h"
20#include "pg_dump.h"
21
22#include "catalog/pg_class_d.h"
23
24/*
25 * Sort priority for database object types.
26 * Objects are sorted by type, and within a type by name.
27 *
28 * Because materialized views can potentially reference system views,
29 * DO_REFRESH_MATVIEW should always be the last thing on the list.
30 *
31 * NOTE: object-type priorities must match the section assignments made in
32 * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
33 * POST_DATA objects must sort after DO_POST_DATA_BOUNDARY, and DATA objects
34 * must sort between them.
35 */
36static const int dbObjectTypePriority[] =
37{
38 1, /* DO_NAMESPACE */
39 4, /* DO_EXTENSION */
40 5, /* DO_TYPE */
41 5, /* DO_SHELL_TYPE */
42 6, /* DO_FUNC */
43 7, /* DO_AGG */
44 8, /* DO_OPERATOR */
45 8, /* DO_ACCESS_METHOD */
46 9, /* DO_OPCLASS */
47 9, /* DO_OPFAMILY */
48 3, /* DO_COLLATION */
49 11, /* DO_CONVERSION */
50 18, /* DO_TABLE */
51 20, /* DO_ATTRDEF */
52 28, /* DO_INDEX */
53 29, /* DO_INDEX_ATTACH */
54 30, /* DO_STATSEXT */
55 31, /* DO_RULE */
56 32, /* DO_TRIGGER */
57 27, /* DO_CONSTRAINT */
58 33, /* DO_FK_CONSTRAINT */
59 2, /* DO_PROCLANG */
60 10, /* DO_CAST */
61 23, /* DO_TABLE_DATA */
62 24, /* DO_SEQUENCE_SET */
63 19, /* DO_DUMMY_TYPE */
64 12, /* DO_TSPARSER */
65 14, /* DO_TSDICT */
66 13, /* DO_TSTEMPLATE */
67 15, /* DO_TSCONFIG */
68 16, /* DO_FDW */
69 17, /* DO_FOREIGN_SERVER */
70 33, /* DO_DEFAULT_ACL */
71 3, /* DO_TRANSFORM */
72 21, /* DO_BLOB */
73 25, /* DO_BLOB_DATA */
74 22, /* DO_PRE_DATA_BOUNDARY */
75 26, /* DO_POST_DATA_BOUNDARY */
76 34, /* DO_EVENT_TRIGGER */
77 39, /* DO_REFRESH_MATVIEW */
78 35, /* DO_POLICY */
79 36, /* DO_PUBLICATION */
80 37, /* DO_PUBLICATION_REL */
81 38 /* DO_SUBSCRIPTION */
82};
83
84static DumpId preDataBoundId;
85static DumpId postDataBoundId;
86
87
88static int DOTypeNameCompare(const void *p1, const void *p2);
89static bool TopoSort(DumpableObject **objs,
90 int numObjs,
91 DumpableObject **ordering,
92 int *nOrdering);
93static void addHeapElement(int val, int *heap, int heapLength);
94static int removeHeapElement(int *heap, int heapLength);
95static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
96static int findLoop(DumpableObject *obj,
97 DumpId startPoint,
98 bool *processed,
99 DumpId *searchFailed,
100 DumpableObject **workspace,
101 int depth);
102static void repairDependencyLoop(DumpableObject **loop,
103 int nLoop);
104static void describeDumpableObject(DumpableObject *obj,
105 char *buf, int bufsize);
106
107
108/*
109 * Sort the given objects into a type/name-based ordering
110 *
111 * Normally this is just the starting point for the dependency-based
112 * ordering.
113 */
114void
115sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
116{
117 if (numObjs > 1)
118 qsort((void *) objs, numObjs, sizeof(DumpableObject *),
119 DOTypeNameCompare);
120}
121
122static int
123DOTypeNameCompare(const void *p1, const void *p2)
124{
125 DumpableObject *obj1 = *(DumpableObject *const *) p1;
126 DumpableObject *obj2 = *(DumpableObject *const *) p2;
127 int cmpval;
128
129 /* Sort by type's priority */
130 cmpval = dbObjectTypePriority[obj1->objType] -
131 dbObjectTypePriority[obj2->objType];
132
133 if (cmpval != 0)
134 return cmpval;
135
136 /*
137 * Sort by namespace. Typically, all objects of the same priority would
138 * either have or not have a namespace link, but there are exceptions.
139 * Sort NULL namespace after non-NULL in such cases.
140 */
141 if (obj1->namespace)
142 {
143 if (obj2->namespace)
144 {
145 cmpval = strcmp(obj1->namespace->dobj.name,
146 obj2->namespace->dobj.name);
147 if (cmpval != 0)
148 return cmpval;
149 }
150 else
151 return -1;
152 }
153 else if (obj2->namespace)
154 return 1;
155
156 /* Sort by name */
157 cmpval = strcmp(obj1->name, obj2->name);
158 if (cmpval != 0)
159 return cmpval;
160
161 /* To have a stable sort order, break ties for some object types */
162 if (obj1->objType == DO_FUNC || obj1->objType == DO_AGG)
163 {
164 FuncInfo *fobj1 = *(FuncInfo *const *) p1;
165 FuncInfo *fobj2 = *(FuncInfo *const *) p2;
166 int i;
167
168 cmpval = fobj1->nargs - fobj2->nargs;
169 if (cmpval != 0)
170 return cmpval;
171 for (i = 0; i < fobj1->nargs; i++)
172 {
173 TypeInfo *argtype1 = findTypeByOid(fobj1->argtypes[i]);
174 TypeInfo *argtype2 = findTypeByOid(fobj2->argtypes[i]);
175
176 if (argtype1 && argtype2)
177 {
178 if (argtype1->dobj.namespace && argtype2->dobj.namespace)
179 {
180 cmpval = strcmp(argtype1->dobj.namespace->dobj.name,
181 argtype2->dobj.namespace->dobj.name);
182 if (cmpval != 0)
183 return cmpval;
184 }
185 cmpval = strcmp(argtype1->dobj.name, argtype2->dobj.name);
186 if (cmpval != 0)
187 return cmpval;
188 }
189 }
190 }
191 else if (obj1->objType == DO_OPERATOR)
192 {
193 OprInfo *oobj1 = *(OprInfo *const *) p1;
194 OprInfo *oobj2 = *(OprInfo *const *) p2;
195
196 /* oprkind is 'l', 'r', or 'b'; this sorts prefix, postfix, infix */
197 cmpval = (oobj2->oprkind - oobj1->oprkind);
198 if (cmpval != 0)
199 return cmpval;
200 }
201 else if (obj1->objType == DO_ATTRDEF)
202 {
203 AttrDefInfo *adobj1 = *(AttrDefInfo *const *) p1;
204 AttrDefInfo *adobj2 = *(AttrDefInfo *const *) p2;
205
206 cmpval = (adobj1->adnum - adobj2->adnum);
207 if (cmpval != 0)
208 return cmpval;
209 }
210
211 /* Usually shouldn't get here, but if we do, sort by OID */
212 return oidcmp(obj1->catId.oid, obj2->catId.oid);
213}
214
215
216/*
217 * Sort the given objects into a safe dump order using dependency
218 * information (to the extent we have it available).
219 *
220 * The DumpIds of the PRE_DATA_BOUNDARY and POST_DATA_BOUNDARY objects are
221 * passed in separately, in case we need them during dependency loop repair.
222 */
223void
224sortDumpableObjects(DumpableObject **objs, int numObjs,
225 DumpId preBoundaryId, DumpId postBoundaryId)
226{
227 DumpableObject **ordering;
228 int nOrdering;
229
230 if (numObjs <= 0) /* can't happen anymore ... */
231 return;
232
233 /*
234 * Saving the boundary IDs in static variables is a bit grotty, but seems
235 * better than adding them to parameter lists of subsidiary functions.
236 */
237 preDataBoundId = preBoundaryId;
238 postDataBoundId = postBoundaryId;
239
240 ordering = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
241 while (!TopoSort(objs, numObjs, ordering, &nOrdering))
242 findDependencyLoops(ordering, nOrdering, numObjs);
243
244 memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
245
246 free(ordering);
247}
248
249/*
250 * TopoSort -- topological sort of a dump list
251 *
252 * Generate a re-ordering of the dump list that satisfies all the dependency
253 * constraints shown in the dump list. (Each such constraint is a fact of a
254 * partial ordering.) Minimize rearrangement of the list not needed to
255 * achieve the partial ordering.
256 *
257 * The input is the list of numObjs objects in objs[]. This list is not
258 * modified.
259 *
260 * Returns true if able to build an ordering that satisfies all the
261 * constraints, false if not (there are contradictory constraints).
262 *
263 * On success (true result), ordering[] is filled with a sorted array of
264 * DumpableObject pointers, of length equal to the input list length.
265 *
266 * On failure (false result), ordering[] is filled with an unsorted array of
267 * DumpableObject pointers of length *nOrdering, listing the objects that
268 * prevented the sort from being completed. In general, these objects either
269 * participate directly in a dependency cycle, or are depended on by objects
270 * that are in a cycle. (The latter objects are not actually problematic,
271 * but it takes further analysis to identify which are which.)
272 *
273 * The caller is responsible for allocating sufficient space at *ordering.
274 */
275static bool
276TopoSort(DumpableObject **objs,
277 int numObjs,
278 DumpableObject **ordering, /* output argument */
279 int *nOrdering) /* output argument */
280{
281 DumpId maxDumpId = getMaxDumpId();
282 int *pendingHeap;
283 int *beforeConstraints;
284 int *idMap;
285 DumpableObject *obj;
286 int heapLength;
287 int i,
288 j,
289 k;
290
291 /*
292 * This is basically the same algorithm shown for topological sorting in
293 * Knuth's Volume 1. However, we would like to minimize unnecessary
294 * rearrangement of the input ordering; that is, when we have a choice of
295 * which item to output next, we always want to take the one highest in
296 * the original list. Therefore, instead of maintaining an unordered
297 * linked list of items-ready-to-output as Knuth does, we maintain a heap
298 * of their item numbers, which we can use as a priority queue. This
299 * turns the algorithm from O(N) to O(N log N) because each insertion or
300 * removal of a heap item takes O(log N) time. However, that's still
301 * plenty fast enough for this application.
302 */
303
304 *nOrdering = numObjs; /* for success return */
305
306 /* Eliminate the null case */
307 if (numObjs <= 0)
308 return true;
309
310 /* Create workspace for the above-described heap */
311 pendingHeap = (int *) pg_malloc(numObjs * sizeof(int));
312
313 /*
314 * Scan the constraints, and for each item in the input, generate a count
315 * of the number of constraints that say it must be before something else.
316 * The count for the item with dumpId j is stored in beforeConstraints[j].
317 * We also make a map showing the input-order index of the item with
318 * dumpId j.
319 */
320 beforeConstraints = (int *) pg_malloc0((maxDumpId + 1) * sizeof(int));
321 idMap = (int *) pg_malloc((maxDumpId + 1) * sizeof(int));
322 for (i = 0; i < numObjs; i++)
323 {
324 obj = objs[i];
325 j = obj->dumpId;
326 if (j <= 0 || j > maxDumpId)
327 fatal("invalid dumpId %d", j);
328 idMap[j] = i;
329 for (j = 0; j < obj->nDeps; j++)
330 {
331 k = obj->dependencies[j];
332 if (k <= 0 || k > maxDumpId)
333 fatal("invalid dependency %d", k);
334 beforeConstraints[k]++;
335 }
336 }
337
338 /*
339 * Now initialize the heap of items-ready-to-output by filling it with the
340 * indexes of items that already have beforeConstraints[id] == 0.
341 *
342 * The essential property of a heap is heap[(j-1)/2] >= heap[j] for each j
343 * in the range 1..heapLength-1 (note we are using 0-based subscripts
344 * here, while the discussion in Knuth assumes 1-based subscripts). So, if
345 * we simply enter the indexes into pendingHeap[] in decreasing order, we
346 * a-fortiori have the heap invariant satisfied at completion of this
347 * loop, and don't need to do any sift-up comparisons.
348 */
349 heapLength = 0;
350 for (i = numObjs; --i >= 0;)
351 {
352 if (beforeConstraints[objs[i]->dumpId] == 0)
353 pendingHeap[heapLength++] = i;
354 }
355
356 /*--------------------
357 * Now emit objects, working backwards in the output list. At each step,
358 * we use the priority heap to select the last item that has no remaining
359 * before-constraints. We remove that item from the heap, output it to
360 * ordering[], and decrease the beforeConstraints count of each of the
361 * items it was constrained against. Whenever an item's beforeConstraints
362 * count is thereby decreased to zero, we insert it into the priority heap
363 * to show that it is a candidate to output. We are done when the heap
364 * becomes empty; if we have output every element then we succeeded,
365 * otherwise we failed.
366 * i = number of ordering[] entries left to output
367 * j = objs[] index of item we are outputting
368 * k = temp for scanning constraint list for item j
369 *--------------------
370 */
371 i = numObjs;
372 while (heapLength > 0)
373 {
374 /* Select object to output by removing largest heap member */
375 j = removeHeapElement(pendingHeap, heapLength--);
376 obj = objs[j];
377 /* Output candidate to ordering[] */
378 ordering[--i] = obj;
379 /* Update beforeConstraints counts of its predecessors */
380 for (k = 0; k < obj->nDeps; k++)
381 {
382 int id = obj->dependencies[k];
383
384 if ((--beforeConstraints[id]) == 0)
385 addHeapElement(idMap[id], pendingHeap, heapLength++);
386 }
387 }
388
389 /*
390 * If we failed, report the objects that couldn't be output; these are the
391 * ones with beforeConstraints[] still nonzero.
392 */
393 if (i != 0)
394 {
395 k = 0;
396 for (j = 1; j <= maxDumpId; j++)
397 {
398 if (beforeConstraints[j] != 0)
399 ordering[k++] = objs[idMap[j]];
400 }
401 *nOrdering = k;
402 }
403
404 /* Done */
405 free(pendingHeap);
406 free(beforeConstraints);
407 free(idMap);
408
409 return (i == 0);
410}
411
412/*
413 * Add an item to a heap (priority queue)
414 *
415 * heapLength is the current heap size; caller is responsible for increasing
416 * its value after the call. There must be sufficient storage at *heap.
417 */
418static void
419addHeapElement(int val, int *heap, int heapLength)
420{
421 int j;
422
423 /*
424 * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth is
425 * using 1-based array indexes, not 0-based.
426 */
427 j = heapLength;
428 while (j > 0)
429 {
430 int i = (j - 1) >> 1;
431
432 if (val <= heap[i])
433 break;
434 heap[j] = heap[i];
435 j = i;
436 }
437 heap[j] = val;
438}
439
440/*
441 * Remove the largest item present in a heap (priority queue)
442 *
443 * heapLength is the current heap size; caller is responsible for decreasing
444 * its value after the call.
445 *
446 * We remove and return heap[0], which is always the largest element of
447 * the heap, and then "sift up" to maintain the heap invariant.
448 */
449static int
450removeHeapElement(int *heap, int heapLength)
451{
452 int result = heap[0];
453 int val;
454 int i;
455
456 if (--heapLength <= 0)
457 return result;
458 val = heap[heapLength]; /* value that must be reinserted */
459 i = 0; /* i is where the "hole" is */
460 for (;;)
461 {
462 int j = 2 * i + 1;
463
464 if (j >= heapLength)
465 break;
466 if (j + 1 < heapLength &&
467 heap[j] < heap[j + 1])
468 j++;
469 if (val >= heap[j])
470 break;
471 heap[i] = heap[j];
472 i = j;
473 }
474 heap[i] = val;
475 return result;
476}
477
478/*
479 * findDependencyLoops - identify loops in TopoSort's failure output,
480 * and pass each such loop to repairDependencyLoop() for action
481 *
482 * In general there may be many loops in the set of objects returned by
483 * TopoSort; for speed we should try to repair as many loops as we can
484 * before trying TopoSort again. We can safely repair loops that are
485 * disjoint (have no members in common); if we find overlapping loops
486 * then we repair only the first one found, because the action taken to
487 * repair the first might have repaired the other as well. (If not,
488 * we'll fix it on the next go-round.)
489 *
490 * objs[] lists the objects TopoSort couldn't sort
491 * nObjs is the number of such objects
492 * totObjs is the total number of objects in the universe
493 */
494static void
495findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
496{
497 /*
498 * We use three data structures here:
499 *
500 * processed[] is a bool array indexed by dump ID, marking the objects
501 * already processed during this invocation of findDependencyLoops().
502 *
503 * searchFailed[] is another array indexed by dump ID. searchFailed[j] is
504 * set to dump ID k if we have proven that there is no dependency path
505 * leading from object j back to start point k. This allows us to skip
506 * useless searching when there are multiple dependency paths from k to j,
507 * which is a common situation. We could use a simple bool array for
508 * this, but then we'd need to re-zero it for each start point, resulting
509 * in O(N^2) zeroing work. Using the start point's dump ID as the "true"
510 * value lets us skip clearing the array before we consider the next start
511 * point.
512 *
513 * workspace[] is an array of DumpableObject pointers, in which we try to
514 * build lists of objects constituting loops. We make workspace[] large
515 * enough to hold all the objects in TopoSort's output, which is huge
516 * overkill in most cases but could theoretically be necessary if there is
517 * a single dependency chain linking all the objects.
518 */
519 bool *processed;
520 DumpId *searchFailed;
521 DumpableObject **workspace;
522 bool fixedloop;
523 int i;
524
525 processed = (bool *) pg_malloc0((getMaxDumpId() + 1) * sizeof(bool));
526 searchFailed = (DumpId *) pg_malloc0((getMaxDumpId() + 1) * sizeof(DumpId));
527 workspace = (DumpableObject **) pg_malloc(totObjs * sizeof(DumpableObject *));
528 fixedloop = false;
529
530 for (i = 0; i < nObjs; i++)
531 {
532 DumpableObject *obj = objs[i];
533 int looplen;
534 int j;
535
536 looplen = findLoop(obj,
537 obj->dumpId,
538 processed,
539 searchFailed,
540 workspace,
541 0);
542
543 if (looplen > 0)
544 {
545 /* Found a loop, repair it */
546 repairDependencyLoop(workspace, looplen);
547 fixedloop = true;
548 /* Mark loop members as processed */
549 for (j = 0; j < looplen; j++)
550 processed[workspace[j]->dumpId] = true;
551 }
552 else
553 {
554 /*
555 * There's no loop starting at this object, but mark it processed
556 * anyway. This is not necessary for correctness, but saves later
557 * invocations of findLoop() from uselessly chasing references to
558 * such an object.
559 */
560 processed[obj->dumpId] = true;
561 }
562 }
563
564 /* We'd better have fixed at least one loop */
565 if (!fixedloop)
566 fatal("could not identify dependency loop");
567
568 free(workspace);
569 free(searchFailed);
570 free(processed);
571}
572
573/*
574 * Recursively search for a circular dependency loop that doesn't include
575 * any already-processed objects.
576 *
577 * obj: object we are examining now
578 * startPoint: dumpId of starting object for the hoped-for circular loop
579 * processed[]: flag array marking already-processed objects
580 * searchFailed[]: flag array marking already-unsuccessfully-visited objects
581 * workspace[]: work array in which we are building list of loop members
582 * depth: number of valid entries in workspace[] at call
583 *
584 * On success, the length of the loop is returned, and workspace[] is filled
585 * with pointers to the members of the loop. On failure, we return 0.
586 *
587 * Note: it is possible that the given starting object is a member of more
588 * than one cycle; if so, we will find an arbitrary one of the cycles.
589 */
590static int
591findLoop(DumpableObject *obj,
592 DumpId startPoint,
593 bool *processed,
594 DumpId *searchFailed,
595 DumpableObject **workspace,
596 int depth)
597{
598 int i;
599
600 /*
601 * Reject if obj is already processed. This test prevents us from finding
602 * loops that overlap previously-processed loops.
603 */
604 if (processed[obj->dumpId])
605 return 0;
606
607 /*
608 * If we've already proven there is no path from this object back to the
609 * startPoint, forget it.
610 */
611 if (searchFailed[obj->dumpId] == startPoint)
612 return 0;
613
614 /*
615 * Reject if obj is already present in workspace. This test prevents us
616 * from going into infinite recursion if we are given a startPoint object
617 * that links to a cycle it's not a member of, and it guarantees that we
618 * can't overflow the allocated size of workspace[].
619 */
620 for (i = 0; i < depth; i++)
621 {
622 if (workspace[i] == obj)
623 return 0;
624 }
625
626 /*
627 * Okay, tentatively add obj to workspace
628 */
629 workspace[depth++] = obj;
630
631 /*
632 * See if we've found a loop back to the desired startPoint; if so, done
633 */
634 for (i = 0; i < obj->nDeps; i++)
635 {
636 if (obj->dependencies[i] == startPoint)
637 return depth;
638 }
639
640 /*
641 * Recurse down each outgoing branch
642 */
643 for (i = 0; i < obj->nDeps; i++)
644 {
645 DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
646 int newDepth;
647
648 if (!nextobj)
649 continue; /* ignore dependencies on undumped objects */
650 newDepth = findLoop(nextobj,
651 startPoint,
652 processed,
653 searchFailed,
654 workspace,
655 depth);
656 if (newDepth > 0)
657 return newDepth;
658 }
659
660 /*
661 * Remember there is no path from here back to startPoint
662 */
663 searchFailed[obj->dumpId] = startPoint;
664
665 return 0;
666}
667
668/*
669 * A user-defined datatype will have a dependency loop with each of its
670 * I/O functions (since those have the datatype as input or output).
671 * Similarly, a range type will have a loop with its canonicalize function,
672 * if any. Break the loop by making the function depend on the associated
673 * shell type, instead.
674 */
675static void
676repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
677{
678 TypeInfo *typeInfo = (TypeInfo *) typeobj;
679
680 /* remove function's dependency on type */
681 removeObjectDependency(funcobj, typeobj->dumpId);
682
683 /* add function's dependency on shell type, instead */
684 if (typeInfo->shellType)
685 {
686 addObjectDependency(funcobj, typeInfo->shellType->dobj.dumpId);
687
688 /*
689 * Mark shell type (always including the definition, as we need the
690 * shell type defined to identify the function fully) as to be dumped
691 * if any such function is
692 */
693 if (funcobj->dump)
694 typeInfo->shellType->dobj.dump = funcobj->dump |
695 DUMP_COMPONENT_DEFINITION;
696 }
697}
698
699/*
700 * Because we force a view to depend on its ON SELECT rule, while there
701 * will be an implicit dependency in the other direction, we need to break
702 * the loop. If there are no other objects in the loop then we can remove
703 * the implicit dependency and leave the ON SELECT rule non-separate.
704 * This applies to matviews, as well.
705 */
706static void
707repairViewRuleLoop(DumpableObject *viewobj,
708 DumpableObject *ruleobj)
709{
710 /* remove rule's dependency on view */
711 removeObjectDependency(ruleobj, viewobj->dumpId);
712 /* flags on the two objects are already set correctly for this case */
713}
714
715/*
716 * However, if there are other objects in the loop, we must break the loop
717 * by making the ON SELECT rule a separately-dumped object.
718 *
719 * Because findLoop() finds shorter cycles before longer ones, it's likely
720 * that we will have previously fired repairViewRuleLoop() and removed the
721 * rule's dependency on the view. Put it back to ensure the rule won't be
722 * emitted before the view.
723 *
724 * Note: this approach does *not* work for matviews, at the moment.
725 */
726static void
727repairViewRuleMultiLoop(DumpableObject *viewobj,
728 DumpableObject *ruleobj)
729{
730 TableInfo *viewinfo = (TableInfo *) viewobj;
731 RuleInfo *ruleinfo = (RuleInfo *) ruleobj;
732
733 /* remove view's dependency on rule */
734 removeObjectDependency(viewobj, ruleobj->dumpId);
735 /* mark view to be printed with a dummy definition */
736 viewinfo->dummy_view = true;
737 /* mark rule as needing its own dump */
738 ruleinfo->separate = true;
739 /* put back rule's dependency on view */
740 addObjectDependency(ruleobj, viewobj->dumpId);
741 /* now that rule is separate, it must be post-data */
742 addObjectDependency(ruleobj, postDataBoundId);
743}
744
745/*
746 * If a matview is involved in a multi-object loop, we can't currently fix
747 * that by splitting off the rule. As a stopgap, we try to fix it by
748 * dropping the constraint that the matview be dumped in the pre-data section.
749 * This is sufficient to handle cases where a matview depends on some unique
750 * index, as can happen if it has a GROUP BY for example.
751 *
752 * Note that the "next object" is not necessarily the matview itself;
753 * it could be the matview's rowtype, for example. We may come through here
754 * several times while removing all the pre-data linkages. In particular,
755 * if there are other matviews that depend on the one with the circularity
756 * problem, we'll come through here for each such matview and mark them all
757 * as postponed. (This works because all MVs have pre-data dependencies
758 * to begin with, so each of them will get visited.)
759 */
760static void
761repairMatViewBoundaryMultiLoop(DumpableObject *boundaryobj,
762 DumpableObject *nextobj)
763{
764 /* remove boundary's dependency on object after it in loop */
765 removeObjectDependency(boundaryobj, nextobj->dumpId);
766 /* if that object is a matview, mark it as postponed into post-data */
767 if (nextobj->objType == DO_TABLE)
768 {
769 TableInfo *nextinfo = (TableInfo *) nextobj;
770
771 if (nextinfo->relkind == RELKIND_MATVIEW)
772 nextinfo->postponed_def = true;
773 }
774}
775
776/*
777 * Because we make tables depend on their CHECK constraints, while there
778 * will be an automatic dependency in the other direction, we need to break
779 * the loop. If there are no other objects in the loop then we can remove
780 * the automatic dependency and leave the CHECK constraint non-separate.
781 */
782static void
783repairTableConstraintLoop(DumpableObject *tableobj,
784 DumpableObject *constraintobj)
785{
786 /* remove constraint's dependency on table */
787 removeObjectDependency(constraintobj, tableobj->dumpId);
788}
789
790/*
791 * However, if there are other objects in the loop, we must break the loop
792 * by making the CHECK constraint a separately-dumped object.
793 *
794 * Because findLoop() finds shorter cycles before longer ones, it's likely
795 * that we will have previously fired repairTableConstraintLoop() and
796 * removed the constraint's dependency on the table. Put it back to ensure
797 * the constraint won't be emitted before the table...
798 */
799static void
800repairTableConstraintMultiLoop(DumpableObject *tableobj,
801 DumpableObject *constraintobj)
802{
803 /* remove table's dependency on constraint */
804 removeObjectDependency(tableobj, constraintobj->dumpId);
805 /* mark constraint as needing its own dump */
806 ((ConstraintInfo *) constraintobj)->separate = true;
807 /* put back constraint's dependency on table */
808 addObjectDependency(constraintobj, tableobj->dumpId);
809 /* now that constraint is separate, it must be post-data */
810 addObjectDependency(constraintobj, postDataBoundId);
811}
812
813/*
814 * Attribute defaults behave exactly the same as CHECK constraints...
815 */
816static void
817repairTableAttrDefLoop(DumpableObject *tableobj,
818 DumpableObject *attrdefobj)
819{
820 /* remove attrdef's dependency on table */
821 removeObjectDependency(attrdefobj, tableobj->dumpId);
822}
823
824static void
825repairTableAttrDefMultiLoop(DumpableObject *tableobj,
826 DumpableObject *attrdefobj)
827{
828 /* remove table's dependency on attrdef */
829 removeObjectDependency(tableobj, attrdefobj->dumpId);
830 /* mark attrdef as needing its own dump */
831 ((AttrDefInfo *) attrdefobj)->separate = true;
832 /* put back attrdef's dependency on table */
833 addObjectDependency(attrdefobj, tableobj->dumpId);
834}
835
836/*
837 * CHECK constraints on domains work just like those on tables ...
838 */
839static void
840repairDomainConstraintLoop(DumpableObject *domainobj,
841 DumpableObject *constraintobj)
842{
843 /* remove constraint's dependency on domain */
844 removeObjectDependency(constraintobj, domainobj->dumpId);
845}
846
847static void
848repairDomainConstraintMultiLoop(DumpableObject *domainobj,
849 DumpableObject *constraintobj)
850{
851 /* remove domain's dependency on constraint */
852 removeObjectDependency(domainobj, constraintobj->dumpId);
853 /* mark constraint as needing its own dump */
854 ((ConstraintInfo *) constraintobj)->separate = true;
855 /* put back constraint's dependency on domain */
856 addObjectDependency(constraintobj, domainobj->dumpId);
857 /* now that constraint is separate, it must be post-data */
858 addObjectDependency(constraintobj, postDataBoundId);
859}
860
861static void
862repairIndexLoop(DumpableObject *partedindex,
863 DumpableObject *partindex)
864{
865 removeObjectDependency(partedindex, partindex->dumpId);
866}
867
868/*
869 * Fix a dependency loop, or die trying ...
870 *
871 * This routine is mainly concerned with reducing the multiple ways that
872 * a loop might appear to common cases, which it passes off to the
873 * "fixer" routines above.
874 */
875static void
876repairDependencyLoop(DumpableObject **loop,
877 int nLoop)
878{
879 int i,
880 j;
881
882 /* Datatype and one of its I/O or canonicalize functions */
883 if (nLoop == 2 &&
884 loop[0]->objType == DO_TYPE &&
885 loop[1]->objType == DO_FUNC)
886 {
887 repairTypeFuncLoop(loop[0], loop[1]);
888 return;
889 }
890 if (nLoop == 2 &&
891 loop[1]->objType == DO_TYPE &&
892 loop[0]->objType == DO_FUNC)
893 {
894 repairTypeFuncLoop(loop[1], loop[0]);
895 return;
896 }
897
898 /* View (including matview) and its ON SELECT rule */
899 if (nLoop == 2 &&
900 loop[0]->objType == DO_TABLE &&
901 loop[1]->objType == DO_RULE &&
902 (((TableInfo *) loop[0])->relkind == RELKIND_VIEW ||
903 ((TableInfo *) loop[0])->relkind == RELKIND_MATVIEW) &&
904 ((RuleInfo *) loop[1])->ev_type == '1' &&
905 ((RuleInfo *) loop[1])->is_instead &&
906 ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
907 {
908 repairViewRuleLoop(loop[0], loop[1]);
909 return;
910 }
911 if (nLoop == 2 &&
912 loop[1]->objType == DO_TABLE &&
913 loop[0]->objType == DO_RULE &&
914 (((TableInfo *) loop[1])->relkind == RELKIND_VIEW ||
915 ((TableInfo *) loop[1])->relkind == RELKIND_MATVIEW) &&
916 ((RuleInfo *) loop[0])->ev_type == '1' &&
917 ((RuleInfo *) loop[0])->is_instead &&
918 ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
919 {
920 repairViewRuleLoop(loop[1], loop[0]);
921 return;
922 }
923
924 /* Indirect loop involving view (but not matview) and ON SELECT rule */
925 if (nLoop > 2)
926 {
927 for (i = 0; i < nLoop; i++)
928 {
929 if (loop[i]->objType == DO_TABLE &&
930 ((TableInfo *) loop[i])->relkind == RELKIND_VIEW)
931 {
932 for (j = 0; j < nLoop; j++)
933 {
934 if (loop[j]->objType == DO_RULE &&
935 ((RuleInfo *) loop[j])->ev_type == '1' &&
936 ((RuleInfo *) loop[j])->is_instead &&
937 ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
938 {
939 repairViewRuleMultiLoop(loop[i], loop[j]);
940 return;
941 }
942 }
943 }
944 }
945 }
946
947 /* Indirect loop involving matview and data boundary */
948 if (nLoop > 2)
949 {
950 for (i = 0; i < nLoop; i++)
951 {
952 if (loop[i]->objType == DO_TABLE &&
953 ((TableInfo *) loop[i])->relkind == RELKIND_MATVIEW)
954 {
955 for (j = 0; j < nLoop; j++)
956 {
957 if (loop[j]->objType == DO_PRE_DATA_BOUNDARY)
958 {
959 DumpableObject *nextobj;
960
961 nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
962 repairMatViewBoundaryMultiLoop(loop[j], nextobj);
963 return;
964 }
965 }
966 }
967 }
968 }
969
970 /* Table and CHECK constraint */
971 if (nLoop == 2 &&
972 loop[0]->objType == DO_TABLE &&
973 loop[1]->objType == DO_CONSTRAINT &&
974 ((ConstraintInfo *) loop[1])->contype == 'c' &&
975 ((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
976 {
977 repairTableConstraintLoop(loop[0], loop[1]);
978 return;
979 }
980 if (nLoop == 2 &&
981 loop[1]->objType == DO_TABLE &&
982 loop[0]->objType == DO_CONSTRAINT &&
983 ((ConstraintInfo *) loop[0])->contype == 'c' &&
984 ((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
985 {
986 repairTableConstraintLoop(loop[1], loop[0]);
987 return;
988 }
989
990 /* Indirect loop involving table and CHECK constraint */
991 if (nLoop > 2)
992 {
993 for (i = 0; i < nLoop; i++)
994 {
995 if (loop[i]->objType == DO_TABLE)
996 {
997 for (j = 0; j < nLoop; j++)
998 {
999 if (loop[j]->objType == DO_CONSTRAINT &&
1000 ((ConstraintInfo *) loop[j])->contype == 'c' &&
1001 ((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
1002 {
1003 repairTableConstraintMultiLoop(loop[i], loop[j]);
1004 return;
1005 }
1006 }
1007 }
1008 }
1009 }
1010
1011 /* Table and attribute default */
1012 if (nLoop == 2 &&
1013 loop[0]->objType == DO_TABLE &&
1014 loop[1]->objType == DO_ATTRDEF &&
1015 ((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
1016 {
1017 repairTableAttrDefLoop(loop[0], loop[1]);
1018 return;
1019 }
1020 if (nLoop == 2 &&
1021 loop[1]->objType == DO_TABLE &&
1022 loop[0]->objType == DO_ATTRDEF &&
1023 ((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
1024 {
1025 repairTableAttrDefLoop(loop[1], loop[0]);
1026 return;
1027 }
1028
1029 /* index on partitioned table and corresponding index on partition */
1030 if (nLoop == 2 &&
1031 loop[0]->objType == DO_INDEX &&
1032 loop[1]->objType == DO_INDEX)
1033 {
1034 if (((IndxInfo *) loop[0])->parentidx == loop[1]->catId.oid)
1035 {
1036 repairIndexLoop(loop[0], loop[1]);
1037 return;
1038 }
1039 else if (((IndxInfo *) loop[1])->parentidx == loop[0]->catId.oid)
1040 {
1041 repairIndexLoop(loop[1], loop[0]);
1042 return;
1043 }
1044 }
1045
1046 /* Indirect loop involving table and attribute default */
1047 if (nLoop > 2)
1048 {
1049 for (i = 0; i < nLoop; i++)
1050 {
1051 if (loop[i]->objType == DO_TABLE)
1052 {
1053 for (j = 0; j < nLoop; j++)
1054 {
1055 if (loop[j]->objType == DO_ATTRDEF &&
1056 ((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
1057 {
1058 repairTableAttrDefMultiLoop(loop[i], loop[j]);
1059 return;
1060 }
1061 }
1062 }
1063 }
1064 }
1065
1066 /* Domain and CHECK constraint */
1067 if (nLoop == 2 &&
1068 loop[0]->objType == DO_TYPE &&
1069 loop[1]->objType == DO_CONSTRAINT &&
1070 ((ConstraintInfo *) loop[1])->contype == 'c' &&
1071 ((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
1072 {
1073 repairDomainConstraintLoop(loop[0], loop[1]);
1074 return;
1075 }
1076 if (nLoop == 2 &&
1077 loop[1]->objType == DO_TYPE &&
1078 loop[0]->objType == DO_CONSTRAINT &&
1079 ((ConstraintInfo *) loop[0])->contype == 'c' &&
1080 ((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
1081 {
1082 repairDomainConstraintLoop(loop[1], loop[0]);
1083 return;
1084 }
1085
1086 /* Indirect loop involving domain and CHECK constraint */
1087 if (nLoop > 2)
1088 {
1089 for (i = 0; i < nLoop; i++)
1090 {
1091 if (loop[i]->objType == DO_TYPE)
1092 {
1093 for (j = 0; j < nLoop; j++)
1094 {
1095 if (loop[j]->objType == DO_CONSTRAINT &&
1096 ((ConstraintInfo *) loop[j])->contype == 'c' &&
1097 ((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
1098 {
1099 repairDomainConstraintMultiLoop(loop[i], loop[j]);
1100 return;
1101 }
1102 }
1103 }
1104 }
1105 }
1106
1107 /*
1108 * Loop of table with itself --- just ignore it.
1109 *
1110 * (Actually, what this arises from is a dependency of a table column on
1111 * another column, which happens with generated columns; or a dependency
1112 * of a table column on the whole table, which happens with partitioning.
1113 * But we didn't pay attention to sub-object IDs while collecting the
1114 * dependency data, so we can't see that here.)
1115 */
1116 if (nLoop == 1)
1117 {
1118 if (loop[0]->objType == DO_TABLE)
1119 {
1120 removeObjectDependency(loop[0], loop[0]->dumpId);
1121 return;
1122 }
1123 }
1124
1125 /*
1126 * If all the objects are TABLE_DATA items, what we must have is a
1127 * circular set of foreign key constraints (or a single self-referential
1128 * table). Print an appropriate complaint and break the loop arbitrarily.
1129 */
1130 for (i = 0; i < nLoop; i++)
1131 {
1132 if (loop[i]->objType != DO_TABLE_DATA)
1133 break;
1134 }
1135 if (i >= nLoop)
1136 {
1137 pg_log_warning(ngettext("there are circular foreign-key constraints on this table:",
1138 "there are circular foreign-key constraints among these tables:",
1139 nLoop));
1140 for (i = 0; i < nLoop; i++)
1141 pg_log_generic(PG_LOG_INFO, " %s", loop[i]->name);
1142 pg_log_generic(PG_LOG_INFO, "You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.");
1143 pg_log_generic(PG_LOG_INFO, "Consider using a full dump instead of a --data-only dump to avoid this problem.");
1144 if (nLoop > 1)
1145 removeObjectDependency(loop[0], loop[1]->dumpId);
1146 else /* must be a self-dependency */
1147 removeObjectDependency(loop[0], loop[0]->dumpId);
1148 return;
1149 }
1150
1151 /*
1152 * If we can't find a principled way to break the loop, complain and break
1153 * it in an arbitrary fashion.
1154 */
1155 pg_log_warning("could not resolve dependency loop among these items:");
1156 for (i = 0; i < nLoop; i++)
1157 {
1158 char buf[1024];
1159
1160 describeDumpableObject(loop[i], buf, sizeof(buf));
1161 pg_log_generic(PG_LOG_INFO, " %s", buf);
1162 }
1163
1164 if (nLoop > 1)
1165 removeObjectDependency(loop[0], loop[1]->dumpId);
1166 else /* must be a self-dependency */
1167 removeObjectDependency(loop[0], loop[0]->dumpId);
1168}
1169
1170/*
1171 * Describe a dumpable object usefully for errors
1172 *
1173 * This should probably go somewhere else...
1174 */
1175static void
1176describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
1177{
1178 switch (obj->objType)
1179 {
1180 case DO_NAMESPACE:
1181 snprintf(buf, bufsize,
1182 "SCHEMA %s (ID %d OID %u)",
1183 obj->name, obj->dumpId, obj->catId.oid);
1184 return;
1185 case DO_EXTENSION:
1186 snprintf(buf, bufsize,
1187 "EXTENSION %s (ID %d OID %u)",
1188 obj->name, obj->dumpId, obj->catId.oid);
1189 return;
1190 case DO_TYPE:
1191 snprintf(buf, bufsize,
1192 "TYPE %s (ID %d OID %u)",
1193 obj->name, obj->dumpId, obj->catId.oid);
1194 return;
1195 case DO_SHELL_TYPE:
1196 snprintf(buf, bufsize,
1197 "SHELL TYPE %s (ID %d OID %u)",
1198 obj->name, obj->dumpId, obj->catId.oid);
1199 return;
1200 case DO_FUNC:
1201 snprintf(buf, bufsize,
1202 "FUNCTION %s (ID %d OID %u)",
1203 obj->name, obj->dumpId, obj->catId.oid);
1204 return;
1205 case DO_AGG:
1206 snprintf(buf, bufsize,
1207 "AGGREGATE %s (ID %d OID %u)",
1208 obj->name, obj->dumpId, obj->catId.oid);
1209 return;
1210 case DO_OPERATOR:
1211 snprintf(buf, bufsize,
1212 "OPERATOR %s (ID %d OID %u)",
1213 obj->name, obj->dumpId, obj->catId.oid);
1214 return;
1215 case DO_ACCESS_METHOD:
1216 snprintf(buf, bufsize,
1217 "ACCESS METHOD %s (ID %d OID %u)",
1218 obj->name, obj->dumpId, obj->catId.oid);
1219 return;
1220 case DO_OPCLASS:
1221 snprintf(buf, bufsize,
1222 "OPERATOR CLASS %s (ID %d OID %u)",
1223 obj->name, obj->dumpId, obj->catId.oid);
1224 return;
1225 case DO_OPFAMILY:
1226 snprintf(buf, bufsize,
1227 "OPERATOR FAMILY %s (ID %d OID %u)",
1228 obj->name, obj->dumpId, obj->catId.oid);
1229 return;
1230 case DO_COLLATION:
1231 snprintf(buf, bufsize,
1232 "COLLATION %s (ID %d OID %u)",
1233 obj->name, obj->dumpId, obj->catId.oid);
1234 return;
1235 case DO_CONVERSION:
1236 snprintf(buf, bufsize,
1237 "CONVERSION %s (ID %d OID %u)",
1238 obj->name, obj->dumpId, obj->catId.oid);
1239 return;
1240 case DO_TABLE:
1241 snprintf(buf, bufsize,
1242 "TABLE %s (ID %d OID %u)",
1243 obj->name, obj->dumpId, obj->catId.oid);
1244 return;
1245 case DO_ATTRDEF:
1246 snprintf(buf, bufsize,
1247 "ATTRDEF %s.%s (ID %d OID %u)",
1248 ((AttrDefInfo *) obj)->adtable->dobj.name,
1249 ((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
1250 obj->dumpId, obj->catId.oid);
1251 return;
1252 case DO_INDEX:
1253 snprintf(buf, bufsize,
1254 "INDEX %s (ID %d OID %u)",
1255 obj->name, obj->dumpId, obj->catId.oid);
1256 return;
1257 case DO_INDEX_ATTACH:
1258 snprintf(buf, bufsize,
1259 "INDEX ATTACH %s (ID %d)",
1260 obj->name, obj->dumpId);
1261 return;
1262 case DO_STATSEXT:
1263 snprintf(buf, bufsize,
1264 "STATISTICS %s (ID %d OID %u)",
1265 obj->name, obj->dumpId, obj->catId.oid);
1266 return;
1267 case DO_REFRESH_MATVIEW:
1268 snprintf(buf, bufsize,
1269 "REFRESH MATERIALIZED VIEW %s (ID %d OID %u)",
1270 obj->name, obj->dumpId, obj->catId.oid);
1271 return;
1272 case DO_RULE:
1273 snprintf(buf, bufsize,
1274 "RULE %s (ID %d OID %u)",
1275 obj->name, obj->dumpId, obj->catId.oid);
1276 return;
1277 case DO_TRIGGER:
1278 snprintf(buf, bufsize,
1279 "TRIGGER %s (ID %d OID %u)",
1280 obj->name, obj->dumpId, obj->catId.oid);
1281 return;
1282 case DO_EVENT_TRIGGER:
1283 snprintf(buf, bufsize,
1284 "EVENT TRIGGER %s (ID %d OID %u)",
1285 obj->name, obj->dumpId, obj->catId.oid);
1286 return;
1287 case DO_CONSTRAINT:
1288 snprintf(buf, bufsize,
1289 "CONSTRAINT %s (ID %d OID %u)",
1290 obj->name, obj->dumpId, obj->catId.oid);
1291 return;
1292 case DO_FK_CONSTRAINT:
1293 snprintf(buf, bufsize,
1294 "FK CONSTRAINT %s (ID %d OID %u)",
1295 obj->name, obj->dumpId, obj->catId.oid);
1296 return;
1297 case DO_PROCLANG:
1298 snprintf(buf, bufsize,
1299 "PROCEDURAL LANGUAGE %s (ID %d OID %u)",
1300 obj->name, obj->dumpId, obj->catId.oid);
1301 return;
1302 case DO_CAST:
1303 snprintf(buf, bufsize,
1304 "CAST %u to %u (ID %d OID %u)",
1305 ((CastInfo *) obj)->castsource,
1306 ((CastInfo *) obj)->casttarget,
1307 obj->dumpId, obj->catId.oid);
1308 return;
1309 case DO_TRANSFORM:
1310 snprintf(buf, bufsize,
1311 "TRANSFORM %u lang %u (ID %d OID %u)",
1312 ((TransformInfo *) obj)->trftype,
1313 ((TransformInfo *) obj)->trflang,
1314 obj->dumpId, obj->catId.oid);
1315 return;
1316 case DO_TABLE_DATA:
1317 snprintf(buf, bufsize,
1318 "TABLE DATA %s (ID %d OID %u)",
1319 obj->name, obj->dumpId, obj->catId.oid);
1320 return;
1321 case DO_SEQUENCE_SET:
1322 snprintf(buf, bufsize,
1323 "SEQUENCE SET %s (ID %d OID %u)",
1324 obj->name, obj->dumpId, obj->catId.oid);
1325 return;
1326 case DO_DUMMY_TYPE:
1327 snprintf(buf, bufsize,
1328 "DUMMY TYPE %s (ID %d OID %u)",
1329 obj->name, obj->dumpId, obj->catId.oid);
1330 return;
1331 case DO_TSPARSER:
1332 snprintf(buf, bufsize,
1333 "TEXT SEARCH PARSER %s (ID %d OID %u)",
1334 obj->name, obj->dumpId, obj->catId.oid);
1335 return;
1336 case DO_TSDICT:
1337 snprintf(buf, bufsize,
1338 "TEXT SEARCH DICTIONARY %s (ID %d OID %u)",
1339 obj->name, obj->dumpId, obj->catId.oid);
1340 return;
1341 case DO_TSTEMPLATE:
1342 snprintf(buf, bufsize,
1343 "TEXT SEARCH TEMPLATE %s (ID %d OID %u)",
1344 obj->name, obj->dumpId, obj->catId.oid);
1345 return;
1346 case DO_TSCONFIG:
1347 snprintf(buf, bufsize,
1348 "TEXT SEARCH CONFIGURATION %s (ID %d OID %u)",
1349 obj->name, obj->dumpId, obj->catId.oid);
1350 return;
1351 case DO_FDW:
1352 snprintf(buf, bufsize,
1353 "FOREIGN DATA WRAPPER %s (ID %d OID %u)",
1354 obj->name, obj->dumpId, obj->catId.oid);
1355 return;
1356 case DO_FOREIGN_SERVER:
1357 snprintf(buf, bufsize,
1358 "FOREIGN SERVER %s (ID %d OID %u)",
1359 obj->name, obj->dumpId, obj->catId.oid);
1360 return;
1361 case DO_DEFAULT_ACL:
1362 snprintf(buf, bufsize,
1363 "DEFAULT ACL %s (ID %d OID %u)",
1364 obj->name, obj->dumpId, obj->catId.oid);
1365 return;
1366 case DO_BLOB:
1367 snprintf(buf, bufsize,
1368 "BLOB (ID %d OID %u)",
1369 obj->dumpId, obj->catId.oid);
1370 return;
1371 case DO_BLOB_DATA:
1372 snprintf(buf, bufsize,
1373 "BLOB DATA (ID %d)",
1374 obj->dumpId);
1375 return;
1376 case DO_POLICY:
1377 snprintf(buf, bufsize,
1378 "POLICY (ID %d OID %u)",
1379 obj->dumpId, obj->catId.oid);
1380 return;
1381 case DO_PUBLICATION:
1382 snprintf(buf, bufsize,
1383 "PUBLICATION (ID %d OID %u)",
1384 obj->dumpId, obj->catId.oid);
1385 return;
1386 case DO_PUBLICATION_REL:
1387 snprintf(buf, bufsize,
1388 "PUBLICATION TABLE (ID %d OID %u)",
1389 obj->dumpId, obj->catId.oid);
1390 return;
1391 case DO_SUBSCRIPTION:
1392 snprintf(buf, bufsize,
1393 "SUBSCRIPTION (ID %d OID %u)",
1394 obj->dumpId, obj->catId.oid);
1395 return;
1396 case DO_PRE_DATA_BOUNDARY:
1397 snprintf(buf, bufsize,
1398 "PRE-DATA BOUNDARY (ID %d)",
1399 obj->dumpId);
1400 return;
1401 case DO_POST_DATA_BOUNDARY:
1402 snprintf(buf, bufsize,
1403 "POST-DATA BOUNDARY (ID %d)",
1404 obj->dumpId);
1405 return;
1406 }
1407 /* shouldn't get here */
1408 snprintf(buf, bufsize,
1409 "object type %d (ID %d OID %u)",
1410 (int) obj->objType,
1411 obj->dumpId, obj->catId.oid);
1412}
1413