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