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