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