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