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