Header And Logo

PostgreSQL
| The world's most advanced open source database.

pg_dump_sort.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * pg_dump_sort.c
00004  *    Sort the items of a dump into a safe order for dumping
00005  *
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  *
00011  * IDENTIFICATION
00012  *    src/bin/pg_dump/pg_dump_sort.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 #include "pg_backup_archiver.h"
00017 #include "pg_backup_utils.h"
00018 #include "parallel.h"
00019 
00020 /* translator: this is a module name */
00021 static const char *modulename = gettext_noop("sorter");
00022 
00023 /*
00024  * Sort priority for object types when dumping a pre-7.3 database.
00025  * Objects are sorted by priority levels, and within an equal priority level
00026  * by OID.  (This is a relatively crude hack to provide semi-reasonable
00027  * behavior for old databases without full dependency info.)  Note: collations,
00028  * extensions, text search, foreign-data, materialized view, event trigger,
00029  * and default ACL objects can't really happen here, so the rather bogus
00030  * priorities for them don't matter.
00031  *
00032  * NOTE: object-type priorities must match the section assignments made in
00033  * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
00034  * POST_DATA objects must sort after DO_POST_DATA_BOUNDARY, and DATA objects
00035  * must sort between them.
00036  */
00037 static const int oldObjectTypePriority[] =
00038 {
00039     1,                          /* DO_NAMESPACE */
00040     1,                          /* DO_EXTENSION */
00041     2,                          /* DO_TYPE */
00042     2,                          /* DO_SHELL_TYPE */
00043     2,                          /* DO_FUNC */
00044     3,                          /* DO_AGG */
00045     3,                          /* DO_OPERATOR */
00046     4,                          /* DO_OPCLASS */
00047     4,                          /* DO_OPFAMILY */
00048     4,                          /* DO_COLLATION */
00049     5,                          /* DO_CONVERSION */
00050     6,                          /* DO_TABLE */
00051     8,                          /* DO_ATTRDEF */
00052     15,                         /* DO_INDEX */
00053     16,                         /* DO_RULE */
00054     17,                         /* DO_TRIGGER */
00055     14,                         /* DO_CONSTRAINT */
00056     18,                         /* DO_FK_CONSTRAINT */
00057     2,                          /* DO_PROCLANG */
00058     2,                          /* DO_CAST */
00059     11,                         /* DO_TABLE_DATA */
00060     7,                          /* DO_DUMMY_TYPE */
00061     4,                          /* DO_TSPARSER */
00062     4,                          /* DO_TSDICT */
00063     4,                          /* DO_TSTEMPLATE */
00064     4,                          /* DO_TSCONFIG */
00065     4,                          /* DO_FDW */
00066     4,                          /* DO_FOREIGN_SERVER */
00067     19,                         /* DO_DEFAULT_ACL */
00068     9,                          /* DO_BLOB */
00069     12,                         /* DO_BLOB_DATA */
00070     10,                         /* DO_PRE_DATA_BOUNDARY */
00071     13,                         /* DO_POST_DATA_BOUNDARY */
00072     20,                         /* DO_EVENT_TRIGGER */
00073     15                          /* DO_REFRESH_MATVIEW */
00074 };
00075 
00076 /*
00077  * Sort priority for object types when dumping newer databases.
00078  * Objects are sorted by type, and within a type by name.
00079  *
00080  * NOTE: object-type priorities must match the section assignments made in
00081  * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
00082  * POST_DATA objects must sort after DO_POST_DATA_BOUNDARY, and DATA objects
00083  * must sort between them.
00084  */
00085 static const int newObjectTypePriority[] =
00086 {
00087     1,                          /* DO_NAMESPACE */
00088     4,                          /* DO_EXTENSION */
00089     5,                          /* DO_TYPE */
00090     5,                          /* DO_SHELL_TYPE */
00091     6,                          /* DO_FUNC */
00092     7,                          /* DO_AGG */
00093     8,                          /* DO_OPERATOR */
00094     9,                          /* DO_OPCLASS */
00095     9,                          /* DO_OPFAMILY */
00096     3,                          /* DO_COLLATION */
00097     11,                         /* DO_CONVERSION */
00098     18,                         /* DO_TABLE */
00099     20,                         /* DO_ATTRDEF */
00100     27,                         /* DO_INDEX */
00101     28,                         /* DO_RULE */
00102     29,                         /* DO_TRIGGER */
00103     26,                         /* DO_CONSTRAINT */
00104     30,                         /* DO_FK_CONSTRAINT */
00105     2,                          /* DO_PROCLANG */
00106     10,                         /* DO_CAST */
00107     23,                         /* DO_TABLE_DATA */
00108     19,                         /* DO_DUMMY_TYPE */
00109     12,                         /* DO_TSPARSER */
00110     14,                         /* DO_TSDICT */
00111     13,                         /* DO_TSTEMPLATE */
00112     15,                         /* DO_TSCONFIG */
00113     16,                         /* DO_FDW */
00114     17,                         /* DO_FOREIGN_SERVER */
00115     31,                         /* DO_DEFAULT_ACL */
00116     21,                         /* DO_BLOB */
00117     24,                         /* DO_BLOB_DATA */
00118     22,                         /* DO_PRE_DATA_BOUNDARY */
00119     25,                         /* DO_POST_DATA_BOUNDARY */
00120     32,                         /* DO_EVENT_TRIGGER */
00121     33                          /* DO_REFRESH_MATVIEW */
00122 };
00123 
00124 static DumpId preDataBoundId;
00125 static DumpId postDataBoundId;
00126 
00127 
00128 static int  DOTypeNameCompare(const void *p1, const void *p2);
00129 static int  DOTypeOidCompare(const void *p1, const void *p2);
00130 static bool TopoSort(DumpableObject **objs,
00131          int numObjs,
00132          DumpableObject **ordering,
00133          int *nOrdering);
00134 static void addHeapElement(int val, int *heap, int heapLength);
00135 static int  removeHeapElement(int *heap, int heapLength);
00136 static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
00137 static int findLoop(DumpableObject *obj,
00138          DumpId startPoint,
00139          bool *processed,
00140          DumpableObject **workspace,
00141          int depth);
00142 static void repairDependencyLoop(DumpableObject **loop,
00143                      int nLoop);
00144 static void describeDumpableObject(DumpableObject *obj,
00145                        char *buf, int bufsize);
00146 
00147 static int  DOSizeCompare(const void *p1, const void *p2);
00148 
00149 static int
00150 findFirstEqualType(DumpableObjectType type, DumpableObject **objs, int numObjs)
00151 {
00152     int         i;
00153 
00154     for (i = 0; i < numObjs; i++)
00155         if (objs[i]->objType == type)
00156             return i;
00157     return -1;
00158 }
00159 
00160 static int
00161 findFirstDifferentType(DumpableObjectType type, DumpableObject **objs, int numObjs, int start)
00162 {
00163     int         i;
00164 
00165     for (i = start; i < numObjs; i++)
00166         if (objs[i]->objType != type)
00167             return i;
00168     return numObjs - 1;
00169 }
00170 
00171 /*
00172  * When we do a parallel dump, we want to start with the largest items first.
00173  *
00174  * Say we have the objects in this order:
00175  * ....DDDDD....III....
00176  *
00177  * with D = Table data, I = Index, . = other object
00178  *
00179  * This sorting function now takes each of the D or I blocks and sorts them
00180  * according to their size.
00181  */
00182 void
00183 sortDataAndIndexObjectsBySize(DumpableObject **objs, int numObjs)
00184 {
00185     int         startIdx,
00186                 endIdx;
00187     void       *startPtr;
00188 
00189     if (numObjs <= 1)
00190         return;
00191 
00192     startIdx = findFirstEqualType(DO_TABLE_DATA, objs, numObjs);
00193     if (startIdx >= 0)
00194     {
00195         endIdx = findFirstDifferentType(DO_TABLE_DATA, objs, numObjs, startIdx);
00196         startPtr = objs + startIdx;
00197         qsort(startPtr, endIdx - startIdx, sizeof(DumpableObject *),
00198               DOSizeCompare);
00199     }
00200 
00201     startIdx = findFirstEqualType(DO_INDEX, objs, numObjs);
00202     if (startIdx >= 0)
00203     {
00204         endIdx = findFirstDifferentType(DO_INDEX, objs, numObjs, startIdx);
00205         startPtr = objs + startIdx;
00206         qsort(startPtr, endIdx - startIdx, sizeof(DumpableObject *),
00207               DOSizeCompare);
00208     }
00209 }
00210 
00211 static int
00212 DOSizeCompare(const void *p1, const void *p2)
00213 {
00214     DumpableObject *obj1 = *(DumpableObject **) p1;
00215     DumpableObject *obj2 = *(DumpableObject **) p2;
00216     int         obj1_size = 0;
00217     int         obj2_size = 0;
00218 
00219     if (obj1->objType == DO_TABLE_DATA)
00220         obj1_size = ((TableDataInfo *) obj1)->tdtable->relpages;
00221     if (obj1->objType == DO_INDEX)
00222         obj1_size = ((IndxInfo *) obj1)->relpages;
00223 
00224     if (obj2->objType == DO_TABLE_DATA)
00225         obj2_size = ((TableDataInfo *) obj2)->tdtable->relpages;
00226     if (obj2->objType == DO_INDEX)
00227         obj2_size = ((IndxInfo *) obj2)->relpages;
00228 
00229     /* we want to see the biggest item go first */
00230     if (obj1_size > obj2_size)
00231         return -1;
00232     if (obj2_size > obj1_size)
00233         return 1;
00234 
00235     return 0;
00236 }
00237 
00238 /*
00239  * Sort the given objects into a type/name-based ordering
00240  *
00241  * Normally this is just the starting point for the dependency-based
00242  * ordering.
00243  */
00244 void
00245 sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
00246 {
00247     if (numObjs > 1)
00248         qsort((void *) objs, numObjs, sizeof(DumpableObject *),
00249               DOTypeNameCompare);
00250 }
00251 
00252 static int
00253 DOTypeNameCompare(const void *p1, const void *p2)
00254 {
00255     DumpableObject *obj1 = *(DumpableObject *const *) p1;
00256     DumpableObject *obj2 = *(DumpableObject *const *) p2;
00257     int         cmpval;
00258 
00259     /* Sort by type */
00260     cmpval = newObjectTypePriority[obj1->objType] -
00261         newObjectTypePriority[obj2->objType];
00262 
00263     if (cmpval != 0)
00264         return cmpval;
00265 
00266     /*
00267      * Sort by namespace.  Note that all objects of the same type should
00268      * either have or not have a namespace link, so we needn't be fancy about
00269      * cases where one link is null and the other not.
00270      */
00271     if (obj1->namespace && obj2->namespace)
00272     {
00273         cmpval = strcmp(obj1->namespace->dobj.name,
00274                         obj2->namespace->dobj.name);
00275         if (cmpval != 0)
00276             return cmpval;
00277     }
00278 
00279     /* Sort by name */
00280     cmpval = strcmp(obj1->name, obj2->name);
00281     if (cmpval != 0)
00282         return cmpval;
00283 
00284     /* To have a stable sort order, break ties for some object types */
00285     if (obj1->objType == DO_FUNC || obj1->objType == DO_AGG)
00286     {
00287         FuncInfo   *fobj1 = *(FuncInfo *const *) p1;
00288         FuncInfo   *fobj2 = *(FuncInfo *const *) p2;
00289 
00290         cmpval = fobj1->nargs - fobj2->nargs;
00291         if (cmpval != 0)
00292             return cmpval;
00293         cmpval = strcmp(fobj1->proiargs, fobj2->proiargs);
00294         if (cmpval != 0)
00295             return cmpval;
00296     }
00297     else if (obj1->objType == DO_OPERATOR)
00298     {
00299         OprInfo    *oobj1 = *(OprInfo *const *) p1;
00300         OprInfo    *oobj2 = *(OprInfo *const *) p2;
00301 
00302         /* oprkind is 'l', 'r', or 'b'; this sorts prefix, postfix, infix */
00303         cmpval = (oobj2->oprkind - oobj1->oprkind);
00304         if (cmpval != 0)
00305             return cmpval;
00306     }
00307     else if (obj1->objType == DO_ATTRDEF)
00308     {
00309         AttrDefInfo *adobj1 = *(AttrDefInfo *const *) p1;
00310         AttrDefInfo *adobj2 = *(AttrDefInfo *const *) p2;
00311 
00312         cmpval = (adobj1->adnum - adobj2->adnum);
00313         if (cmpval != 0)
00314             return cmpval;
00315     }
00316 
00317     /* Usually shouldn't get here, but if we do, sort by OID */
00318     return oidcmp(obj1->catId.oid, obj2->catId.oid);
00319 }
00320 
00321 
00322 /*
00323  * Sort the given objects into a type/OID-based ordering
00324  *
00325  * This is used with pre-7.3 source databases as a crude substitute for the
00326  * lack of dependency information.
00327  */
00328 void
00329 sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs)
00330 {
00331     if (numObjs > 1)
00332         qsort((void *) objs, numObjs, sizeof(DumpableObject *),
00333               DOTypeOidCompare);
00334 }
00335 
00336 static int
00337 DOTypeOidCompare(const void *p1, const void *p2)
00338 {
00339     DumpableObject *obj1 = *(DumpableObject *const *) p1;
00340     DumpableObject *obj2 = *(DumpableObject *const *) p2;
00341     int         cmpval;
00342 
00343     cmpval = oldObjectTypePriority[obj1->objType] -
00344         oldObjectTypePriority[obj2->objType];
00345 
00346     if (cmpval != 0)
00347         return cmpval;
00348 
00349     return oidcmp(obj1->catId.oid, obj2->catId.oid);
00350 }
00351 
00352 
00353 /*
00354  * Sort the given objects into a safe dump order using dependency
00355  * information (to the extent we have it available).
00356  *
00357  * The DumpIds of the PRE_DATA_BOUNDARY and POST_DATA_BOUNDARY objects are
00358  * passed in separately, in case we need them during dependency loop repair.
00359  */
00360 void
00361 sortDumpableObjects(DumpableObject **objs, int numObjs,
00362                     DumpId preBoundaryId, DumpId postBoundaryId)
00363 {
00364     DumpableObject **ordering;
00365     int         nOrdering;
00366 
00367     if (numObjs <= 0)           /* can't happen anymore ... */
00368         return;
00369 
00370     /*
00371      * Saving the boundary IDs in static variables is a bit grotty, but seems
00372      * better than adding them to parameter lists of subsidiary functions.
00373      */
00374     preDataBoundId = preBoundaryId;
00375     postDataBoundId = postBoundaryId;
00376 
00377     ordering = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
00378     while (!TopoSort(objs, numObjs, ordering, &nOrdering))
00379         findDependencyLoops(ordering, nOrdering, numObjs);
00380 
00381     memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
00382 
00383     free(ordering);
00384 }
00385 
00386 /*
00387  * TopoSort -- topological sort of a dump list
00388  *
00389  * Generate a re-ordering of the dump list that satisfies all the dependency
00390  * constraints shown in the dump list.  (Each such constraint is a fact of a
00391  * partial ordering.)  Minimize rearrangement of the list not needed to
00392  * achieve the partial ordering.
00393  *
00394  * The input is the list of numObjs objects in objs[].  This list is not
00395  * modified.
00396  *
00397  * Returns TRUE if able to build an ordering that satisfies all the
00398  * constraints, FALSE if not (there are contradictory constraints).
00399  *
00400  * On success (TRUE result), ordering[] is filled with a sorted array of
00401  * DumpableObject pointers, of length equal to the input list length.
00402  *
00403  * On failure (FALSE result), ordering[] is filled with an unsorted array of
00404  * DumpableObject pointers of length *nOrdering, listing the objects that
00405  * prevented the sort from being completed.  In general, these objects either
00406  * participate directly in a dependency cycle, or are depended on by objects
00407  * that are in a cycle.  (The latter objects are not actually problematic,
00408  * but it takes further analysis to identify which are which.)
00409  *
00410  * The caller is responsible for allocating sufficient space at *ordering.
00411  */
00412 static bool
00413 TopoSort(DumpableObject **objs,
00414          int numObjs,
00415          DumpableObject **ordering,     /* output argument */
00416          int *nOrdering)        /* output argument */
00417 {
00418     DumpId      maxDumpId = getMaxDumpId();
00419     int        *pendingHeap;
00420     int        *beforeConstraints;
00421     int        *idMap;
00422     DumpableObject *obj;
00423     int         heapLength;
00424     int         i,
00425                 j,
00426                 k;
00427 
00428     /*
00429      * This is basically the same algorithm shown for topological sorting in
00430      * Knuth's Volume 1.  However, we would like to minimize unnecessary
00431      * rearrangement of the input ordering; that is, when we have a choice of
00432      * which item to output next, we always want to take the one highest in
00433      * the original list.  Therefore, instead of maintaining an unordered
00434      * linked list of items-ready-to-output as Knuth does, we maintain a heap
00435      * of their item numbers, which we can use as a priority queue.  This
00436      * turns the algorithm from O(N) to O(N log N) because each insertion or
00437      * removal of a heap item takes O(log N) time.  However, that's still
00438      * plenty fast enough for this application.
00439      */
00440 
00441     *nOrdering = numObjs;       /* for success return */
00442 
00443     /* Eliminate the null case */
00444     if (numObjs <= 0)
00445         return true;
00446 
00447     /* Create workspace for the above-described heap */
00448     pendingHeap = (int *) pg_malloc(numObjs * sizeof(int));
00449 
00450     /*
00451      * Scan the constraints, and for each item in the input, generate a count
00452      * of the number of constraints that say it must be before something else.
00453      * The count for the item with dumpId j is stored in beforeConstraints[j].
00454      * We also make a map showing the input-order index of the item with
00455      * dumpId j.
00456      */
00457     beforeConstraints = (int *) pg_malloc((maxDumpId + 1) * sizeof(int));
00458     memset(beforeConstraints, 0, (maxDumpId + 1) * sizeof(int));
00459     idMap = (int *) pg_malloc((maxDumpId + 1) * sizeof(int));
00460     for (i = 0; i < numObjs; i++)
00461     {
00462         obj = objs[i];
00463         j = obj->dumpId;
00464         if (j <= 0 || j > maxDumpId)
00465             exit_horribly(modulename, "invalid dumpId %d\n", j);
00466         idMap[j] = i;
00467         for (j = 0; j < obj->nDeps; j++)
00468         {
00469             k = obj->dependencies[j];
00470             if (k <= 0 || k > maxDumpId)
00471                 exit_horribly(modulename, "invalid dependency %d\n", k);
00472             beforeConstraints[k]++;
00473         }
00474     }
00475 
00476     /*
00477      * Now initialize the heap of items-ready-to-output by filling it with the
00478      * indexes of items that already have beforeConstraints[id] == 0.
00479      *
00480      * The essential property of a heap is heap[(j-1)/2] >= heap[j] for each j
00481      * in the range 1..heapLength-1 (note we are using 0-based subscripts
00482      * here, while the discussion in Knuth assumes 1-based subscripts). So, if
00483      * we simply enter the indexes into pendingHeap[] in decreasing order, we
00484      * a-fortiori have the heap invariant satisfied at completion of this
00485      * loop, and don't need to do any sift-up comparisons.
00486      */
00487     heapLength = 0;
00488     for (i = numObjs; --i >= 0;)
00489     {
00490         if (beforeConstraints[objs[i]->dumpId] == 0)
00491             pendingHeap[heapLength++] = i;
00492     }
00493 
00494     /*--------------------
00495      * Now emit objects, working backwards in the output list.  At each step,
00496      * we use the priority heap to select the last item that has no remaining
00497      * before-constraints.  We remove that item from the heap, output it to
00498      * ordering[], and decrease the beforeConstraints count of each of the
00499      * items it was constrained against.  Whenever an item's beforeConstraints
00500      * count is thereby decreased to zero, we insert it into the priority heap
00501      * to show that it is a candidate to output.  We are done when the heap
00502      * becomes empty; if we have output every element then we succeeded,
00503      * otherwise we failed.
00504      * i = number of ordering[] entries left to output
00505      * j = objs[] index of item we are outputting
00506      * k = temp for scanning constraint list for item j
00507      *--------------------
00508      */
00509     i = numObjs;
00510     while (heapLength > 0)
00511     {
00512         /* Select object to output by removing largest heap member */
00513         j = removeHeapElement(pendingHeap, heapLength--);
00514         obj = objs[j];
00515         /* Output candidate to ordering[] */
00516         ordering[--i] = obj;
00517         /* Update beforeConstraints counts of its predecessors */
00518         for (k = 0; k < obj->nDeps; k++)
00519         {
00520             int         id = obj->dependencies[k];
00521 
00522             if ((--beforeConstraints[id]) == 0)
00523                 addHeapElement(idMap[id], pendingHeap, heapLength++);
00524         }
00525     }
00526 
00527     /*
00528      * If we failed, report the objects that couldn't be output; these are the
00529      * ones with beforeConstraints[] still nonzero.
00530      */
00531     if (i != 0)
00532     {
00533         k = 0;
00534         for (j = 1; j <= maxDumpId; j++)
00535         {
00536             if (beforeConstraints[j] != 0)
00537                 ordering[k++] = objs[idMap[j]];
00538         }
00539         *nOrdering = k;
00540     }
00541 
00542     /* Done */
00543     free(pendingHeap);
00544     free(beforeConstraints);
00545     free(idMap);
00546 
00547     return (i == 0);
00548 }
00549 
00550 /*
00551  * Add an item to a heap (priority queue)
00552  *
00553  * heapLength is the current heap size; caller is responsible for increasing
00554  * its value after the call.  There must be sufficient storage at *heap.
00555  */
00556 static void
00557 addHeapElement(int val, int *heap, int heapLength)
00558 {
00559     int         j;
00560 
00561     /*
00562      * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth is
00563      * using 1-based array indexes, not 0-based.
00564      */
00565     j = heapLength;
00566     while (j > 0)
00567     {
00568         int         i = (j - 1) >> 1;
00569 
00570         if (val <= heap[i])
00571             break;
00572         heap[j] = heap[i];
00573         j = i;
00574     }
00575     heap[j] = val;
00576 }
00577 
00578 /*
00579  * Remove the largest item present in a heap (priority queue)
00580  *
00581  * heapLength is the current heap size; caller is responsible for decreasing
00582  * its value after the call.
00583  *
00584  * We remove and return heap[0], which is always the largest element of
00585  * the heap, and then "sift up" to maintain the heap invariant.
00586  */
00587 static int
00588 removeHeapElement(int *heap, int heapLength)
00589 {
00590     int         result = heap[0];
00591     int         val;
00592     int         i;
00593 
00594     if (--heapLength <= 0)
00595         return result;
00596     val = heap[heapLength];     /* value that must be reinserted */
00597     i = 0;                      /* i is where the "hole" is */
00598     for (;;)
00599     {
00600         int         j = 2 * i + 1;
00601 
00602         if (j >= heapLength)
00603             break;
00604         if (j + 1 < heapLength &&
00605             heap[j] < heap[j + 1])
00606             j++;
00607         if (val >= heap[j])
00608             break;
00609         heap[i] = heap[j];
00610         i = j;
00611     }
00612     heap[i] = val;
00613     return result;
00614 }
00615 
00616 /*
00617  * findDependencyLoops - identify loops in TopoSort's failure output,
00618  *      and pass each such loop to repairDependencyLoop() for action
00619  *
00620  * In general there may be many loops in the set of objects returned by
00621  * TopoSort; for speed we should try to repair as many loops as we can
00622  * before trying TopoSort again.  We can safely repair loops that are
00623  * disjoint (have no members in common); if we find overlapping loops
00624  * then we repair only the first one found, because the action taken to
00625  * repair the first might have repaired the other as well.  (If not,
00626  * we'll fix it on the next go-round.)
00627  *
00628  * objs[] lists the objects TopoSort couldn't sort
00629  * nObjs is the number of such objects
00630  * totObjs is the total number of objects in the universe
00631  */
00632 static void
00633 findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
00634 {
00635     /*
00636      * We use two data structures here.  One is a bool array processed[],
00637      * which is indexed by dump ID and marks the objects already processed
00638      * during this invocation of findDependencyLoops().  The other is a
00639      * workspace[] array of DumpableObject pointers, in which we try to build
00640      * lists of objects constituting loops.  We make workspace[] large enough
00641      * to hold all the objects, which is huge overkill in most cases but could
00642      * theoretically be necessary if there is a single dependency chain
00643      * linking all the objects.
00644      */
00645     bool       *processed;
00646     DumpableObject **workspace;
00647     bool        fixedloop;
00648     int         i;
00649 
00650     processed = (bool *) pg_malloc0((getMaxDumpId() + 1) * sizeof(bool));
00651     workspace = (DumpableObject **) pg_malloc(totObjs * sizeof(DumpableObject *));
00652     fixedloop = false;
00653 
00654     for (i = 0; i < nObjs; i++)
00655     {
00656         DumpableObject *obj = objs[i];
00657         int         looplen;
00658         int         j;
00659 
00660         looplen = findLoop(obj, obj->dumpId, processed, workspace, 0);
00661 
00662         if (looplen > 0)
00663         {
00664             /* Found a loop, repair it */
00665             repairDependencyLoop(workspace, looplen);
00666             fixedloop = true;
00667             /* Mark loop members as processed */
00668             for (j = 0; j < looplen; j++)
00669                 processed[workspace[j]->dumpId] = true;
00670         }
00671         else
00672         {
00673             /*
00674              * There's no loop starting at this object, but mark it processed
00675              * anyway.  This is not necessary for correctness, but saves later
00676              * invocations of findLoop() from uselessly chasing references to
00677              * such an object.
00678              */
00679             processed[obj->dumpId] = true;
00680         }
00681     }
00682 
00683     /* We'd better have fixed at least one loop */
00684     if (!fixedloop)
00685         exit_horribly(modulename, "could not identify dependency loop\n");
00686 
00687     free(workspace);
00688     free(processed);
00689 }
00690 
00691 /*
00692  * Recursively search for a circular dependency loop that doesn't include
00693  * any already-processed objects.
00694  *
00695  *  obj: object we are examining now
00696  *  startPoint: dumpId of starting object for the hoped-for circular loop
00697  *  processed[]: flag array marking already-processed objects
00698  *  workspace[]: work array in which we are building list of loop members
00699  *  depth: number of valid entries in workspace[] at call
00700  *
00701  * On success, the length of the loop is returned, and workspace[] is filled
00702  * with pointers to the members of the loop.  On failure, we return 0.
00703  *
00704  * Note: it is possible that the given starting object is a member of more
00705  * than one cycle; if so, we will find an arbitrary one of the cycles.
00706  */
00707 static int
00708 findLoop(DumpableObject *obj,
00709          DumpId startPoint,
00710          bool *processed,
00711          DumpableObject **workspace,
00712          int depth)
00713 {
00714     int         i;
00715 
00716     /*
00717      * Reject if obj is already processed.  This test prevents us from finding
00718      * loops that overlap previously-processed loops.
00719      */
00720     if (processed[obj->dumpId])
00721         return 0;
00722 
00723     /*
00724      * Reject if obj is already present in workspace.  This test prevents us
00725      * from going into infinite recursion if we are given a startPoint object
00726      * that links to a cycle it's not a member of, and it guarantees that we
00727      * can't overflow the allocated size of workspace[].
00728      */
00729     for (i = 0; i < depth; i++)
00730     {
00731         if (workspace[i] == obj)
00732             return 0;
00733     }
00734 
00735     /*
00736      * Okay, tentatively add obj to workspace
00737      */
00738     workspace[depth++] = obj;
00739 
00740     /*
00741      * See if we've found a loop back to the desired startPoint; if so, done
00742      */
00743     for (i = 0; i < obj->nDeps; i++)
00744     {
00745         if (obj->dependencies[i] == startPoint)
00746             return depth;
00747     }
00748 
00749     /*
00750      * Recurse down each outgoing branch
00751      */
00752     for (i = 0; i < obj->nDeps; i++)
00753     {
00754         DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
00755         int         newDepth;
00756 
00757         if (!nextobj)
00758             continue;           /* ignore dependencies on undumped objects */
00759         newDepth = findLoop(nextobj,
00760                             startPoint,
00761                             processed,
00762                             workspace,
00763                             depth);
00764         if (newDepth > 0)
00765             return newDepth;
00766     }
00767 
00768     return 0;
00769 }
00770 
00771 /*
00772  * A user-defined datatype will have a dependency loop with each of its
00773  * I/O functions (since those have the datatype as input or output).
00774  * Similarly, a range type will have a loop with its canonicalize function,
00775  * if any.  Break the loop by making the function depend on the associated
00776  * shell type, instead.
00777  */
00778 static void
00779 repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
00780 {
00781     TypeInfo   *typeInfo = (TypeInfo *) typeobj;
00782 
00783     /* remove function's dependency on type */
00784     removeObjectDependency(funcobj, typeobj->dumpId);
00785 
00786     /* add function's dependency on shell type, instead */
00787     if (typeInfo->shellType)
00788     {
00789         addObjectDependency(funcobj, typeInfo->shellType->dobj.dumpId);
00790         /* Mark shell type as to be dumped if any such function is */
00791         if (funcobj->dump)
00792             typeInfo->shellType->dobj.dump = true;
00793     }
00794 }
00795 
00796 /*
00797  * Because we force a view to depend on its ON SELECT rule, while there
00798  * will be an implicit dependency in the other direction, we need to break
00799  * the loop.  If there are no other objects in the loop then we can remove
00800  * the implicit dependency and leave the ON SELECT rule non-separate.
00801  */
00802 static void
00803 repairViewRuleLoop(DumpableObject *viewobj,
00804                    DumpableObject *ruleobj)
00805 {
00806     /* remove rule's dependency on view */
00807     removeObjectDependency(ruleobj, viewobj->dumpId);
00808 }
00809 
00810 /*
00811  * However, if there are other objects in the loop, we must break the loop
00812  * by making the ON SELECT rule a separately-dumped object.
00813  *
00814  * Because findLoop() finds shorter cycles before longer ones, it's likely
00815  * that we will have previously fired repairViewRuleLoop() and removed the
00816  * rule's dependency on the view.  Put it back to ensure the rule won't be
00817  * emitted before the view...
00818  */
00819 static void
00820 repairViewRuleMultiLoop(DumpableObject *viewobj,
00821                         DumpableObject *ruleobj)
00822 {
00823     TableInfo  *viewinfo = (TableInfo *) viewobj;
00824     RuleInfo   *ruleinfo = (RuleInfo *) ruleobj;
00825 
00826     /* remove view's dependency on rule */
00827     removeObjectDependency(viewobj, ruleobj->dumpId);
00828     /* pretend view is a plain table and dump it that way */
00829     viewinfo->relkind = 'r';    /* RELKIND_RELATION */
00830     /* mark rule as needing its own dump */
00831     ruleinfo->separate = true;
00832     /* move any reloptions from view to rule */
00833     if (viewinfo->reloptions)
00834     {
00835         ruleinfo->reloptions = viewinfo->reloptions;
00836         viewinfo->reloptions = NULL;
00837     }
00838     /* put back rule's dependency on view */
00839     addObjectDependency(ruleobj, viewobj->dumpId);
00840     /* now that rule is separate, it must be post-data */
00841     addObjectDependency(ruleobj, postDataBoundId);
00842 }
00843 
00844 /*
00845  * Because we make tables depend on their CHECK constraints, while there
00846  * will be an automatic dependency in the other direction, we need to break
00847  * the loop.  If there are no other objects in the loop then we can remove
00848  * the automatic dependency and leave the CHECK constraint non-separate.
00849  */
00850 static void
00851 repairTableConstraintLoop(DumpableObject *tableobj,
00852                           DumpableObject *constraintobj)
00853 {
00854     /* remove constraint's dependency on table */
00855     removeObjectDependency(constraintobj, tableobj->dumpId);
00856 }
00857 
00858 /*
00859  * However, if there are other objects in the loop, we must break the loop
00860  * by making the CHECK constraint a separately-dumped object.
00861  *
00862  * Because findLoop() finds shorter cycles before longer ones, it's likely
00863  * that we will have previously fired repairTableConstraintLoop() and
00864  * removed the constraint's dependency on the table.  Put it back to ensure
00865  * the constraint won't be emitted before the table...
00866  */
00867 static void
00868 repairTableConstraintMultiLoop(DumpableObject *tableobj,
00869                                DumpableObject *constraintobj)
00870 {
00871     /* remove table's dependency on constraint */
00872     removeObjectDependency(tableobj, constraintobj->dumpId);
00873     /* mark constraint as needing its own dump */
00874     ((ConstraintInfo *) constraintobj)->separate = true;
00875     /* put back constraint's dependency on table */
00876     addObjectDependency(constraintobj, tableobj->dumpId);
00877     /* now that constraint is separate, it must be post-data */
00878     addObjectDependency(constraintobj, postDataBoundId);
00879 }
00880 
00881 /*
00882  * Attribute defaults behave exactly the same as CHECK constraints...
00883  */
00884 static void
00885 repairTableAttrDefLoop(DumpableObject *tableobj,
00886                        DumpableObject *attrdefobj)
00887 {
00888     /* remove attrdef's dependency on table */
00889     removeObjectDependency(attrdefobj, tableobj->dumpId);
00890 }
00891 
00892 static void
00893 repairTableAttrDefMultiLoop(DumpableObject *tableobj,
00894                             DumpableObject *attrdefobj)
00895 {
00896     /* remove table's dependency on attrdef */
00897     removeObjectDependency(tableobj, attrdefobj->dumpId);
00898     /* mark attrdef as needing its own dump */
00899     ((AttrDefInfo *) attrdefobj)->separate = true;
00900     /* put back attrdef's dependency on table */
00901     addObjectDependency(attrdefobj, tableobj->dumpId);
00902 }
00903 
00904 /*
00905  * CHECK constraints on domains work just like those on tables ...
00906  */
00907 static void
00908 repairDomainConstraintLoop(DumpableObject *domainobj,
00909                            DumpableObject *constraintobj)
00910 {
00911     /* remove constraint's dependency on domain */
00912     removeObjectDependency(constraintobj, domainobj->dumpId);
00913 }
00914 
00915 static void
00916 repairDomainConstraintMultiLoop(DumpableObject *domainobj,
00917                                 DumpableObject *constraintobj)
00918 {
00919     /* remove domain's dependency on constraint */
00920     removeObjectDependency(domainobj, constraintobj->dumpId);
00921     /* mark constraint as needing its own dump */
00922     ((ConstraintInfo *) constraintobj)->separate = true;
00923     /* put back constraint's dependency on domain */
00924     addObjectDependency(constraintobj, domainobj->dumpId);
00925     /* now that constraint is separate, it must be post-data */
00926     addObjectDependency(constraintobj, postDataBoundId);
00927 }
00928 
00929 /*
00930  * Fix a dependency loop, or die trying ...
00931  *
00932  * This routine is mainly concerned with reducing the multiple ways that
00933  * a loop might appear to common cases, which it passes off to the
00934  * "fixer" routines above.
00935  */
00936 static void
00937 repairDependencyLoop(DumpableObject **loop,
00938                      int nLoop)
00939 {
00940     int         i,
00941                 j;
00942 
00943     /* Datatype and one of its I/O or canonicalize functions */
00944     if (nLoop == 2 &&
00945         loop[0]->objType == DO_TYPE &&
00946         loop[1]->objType == DO_FUNC)
00947     {
00948         repairTypeFuncLoop(loop[0], loop[1]);
00949         return;
00950     }
00951     if (nLoop == 2 &&
00952         loop[1]->objType == DO_TYPE &&
00953         loop[0]->objType == DO_FUNC)
00954     {
00955         repairTypeFuncLoop(loop[1], loop[0]);
00956         return;
00957     }
00958 
00959     /* View and its ON SELECT rule */
00960     if (nLoop == 2 &&
00961         loop[0]->objType == DO_TABLE &&
00962         loop[1]->objType == DO_RULE &&
00963         ((RuleInfo *) loop[1])->ev_type == '1' &&
00964         ((RuleInfo *) loop[1])->is_instead &&
00965         ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
00966     {
00967         repairViewRuleLoop(loop[0], loop[1]);
00968         return;
00969     }
00970     if (nLoop == 2 &&
00971         loop[1]->objType == DO_TABLE &&
00972         loop[0]->objType == DO_RULE &&
00973         ((RuleInfo *) loop[0])->ev_type == '1' &&
00974         ((RuleInfo *) loop[0])->is_instead &&
00975         ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
00976     {
00977         repairViewRuleLoop(loop[1], loop[0]);
00978         return;
00979     }
00980 
00981     /* Indirect loop involving view and ON SELECT rule */
00982     if (nLoop > 2)
00983     {
00984         for (i = 0; i < nLoop; i++)
00985         {
00986             if (loop[i]->objType == DO_TABLE)
00987             {
00988                 for (j = 0; j < nLoop; j++)
00989                 {
00990                     if (loop[j]->objType == DO_RULE &&
00991                         ((RuleInfo *) loop[j])->ev_type == '1' &&
00992                         ((RuleInfo *) loop[j])->is_instead &&
00993                         ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
00994                     {
00995                         repairViewRuleMultiLoop(loop[i], loop[j]);
00996                         return;
00997                     }
00998                 }
00999             }
01000         }
01001     }
01002 
01003     /* Table and CHECK constraint */
01004     if (nLoop == 2 &&
01005         loop[0]->objType == DO_TABLE &&
01006         loop[1]->objType == DO_CONSTRAINT &&
01007         ((ConstraintInfo *) loop[1])->contype == 'c' &&
01008         ((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
01009     {
01010         repairTableConstraintLoop(loop[0], loop[1]);
01011         return;
01012     }
01013     if (nLoop == 2 &&
01014         loop[1]->objType == DO_TABLE &&
01015         loop[0]->objType == DO_CONSTRAINT &&
01016         ((ConstraintInfo *) loop[0])->contype == 'c' &&
01017         ((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
01018     {
01019         repairTableConstraintLoop(loop[1], loop[0]);
01020         return;
01021     }
01022 
01023     /* Indirect loop involving table and CHECK constraint */
01024     if (nLoop > 2)
01025     {
01026         for (i = 0; i < nLoop; i++)
01027         {
01028             if (loop[i]->objType == DO_TABLE)
01029             {
01030                 for (j = 0; j < nLoop; j++)
01031                 {
01032                     if (loop[j]->objType == DO_CONSTRAINT &&
01033                         ((ConstraintInfo *) loop[j])->contype == 'c' &&
01034                         ((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
01035                     {
01036                         repairTableConstraintMultiLoop(loop[i], loop[j]);
01037                         return;
01038                     }
01039                 }
01040             }
01041         }
01042     }
01043 
01044     /* Table and attribute default */
01045     if (nLoop == 2 &&
01046         loop[0]->objType == DO_TABLE &&
01047         loop[1]->objType == DO_ATTRDEF &&
01048         ((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
01049     {
01050         repairTableAttrDefLoop(loop[0], loop[1]);
01051         return;
01052     }
01053     if (nLoop == 2 &&
01054         loop[1]->objType == DO_TABLE &&
01055         loop[0]->objType == DO_ATTRDEF &&
01056         ((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
01057     {
01058         repairTableAttrDefLoop(loop[1], loop[0]);
01059         return;
01060     }
01061 
01062     /* Indirect loop involving table and attribute default */
01063     if (nLoop > 2)
01064     {
01065         for (i = 0; i < nLoop; i++)
01066         {
01067             if (loop[i]->objType == DO_TABLE)
01068             {
01069                 for (j = 0; j < nLoop; j++)
01070                 {
01071                     if (loop[j]->objType == DO_ATTRDEF &&
01072                         ((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
01073                     {
01074                         repairTableAttrDefMultiLoop(loop[i], loop[j]);
01075                         return;
01076                     }
01077                 }
01078             }
01079         }
01080     }
01081 
01082     /* Domain and CHECK constraint */
01083     if (nLoop == 2 &&
01084         loop[0]->objType == DO_TYPE &&
01085         loop[1]->objType == DO_CONSTRAINT &&
01086         ((ConstraintInfo *) loop[1])->contype == 'c' &&
01087         ((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
01088     {
01089         repairDomainConstraintLoop(loop[0], loop[1]);
01090         return;
01091     }
01092     if (nLoop == 2 &&
01093         loop[1]->objType == DO_TYPE &&
01094         loop[0]->objType == DO_CONSTRAINT &&
01095         ((ConstraintInfo *) loop[0])->contype == 'c' &&
01096         ((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
01097     {
01098         repairDomainConstraintLoop(loop[1], loop[0]);
01099         return;
01100     }
01101 
01102     /* Indirect loop involving domain and CHECK constraint */
01103     if (nLoop > 2)
01104     {
01105         for (i = 0; i < nLoop; i++)
01106         {
01107             if (loop[i]->objType == DO_TYPE)
01108             {
01109                 for (j = 0; j < nLoop; j++)
01110                 {
01111                     if (loop[j]->objType == DO_CONSTRAINT &&
01112                         ((ConstraintInfo *) loop[j])->contype == 'c' &&
01113                         ((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
01114                     {
01115                         repairDomainConstraintMultiLoop(loop[i], loop[j]);
01116                         return;
01117                     }
01118                 }
01119             }
01120         }
01121     }
01122 
01123     /*
01124      * If all the objects are TABLE_DATA items, what we must have is a
01125      * circular set of foreign key constraints (or a single self-referential
01126      * table).  Print an appropriate complaint and break the loop arbitrarily.
01127      */
01128     for (i = 0; i < nLoop; i++)
01129     {
01130         if (loop[i]->objType != DO_TABLE_DATA)
01131             break;
01132     }
01133     if (i >= nLoop)
01134     {
01135         write_msg(NULL, "NOTICE: there are circular foreign-key constraints among these table(s):\n");
01136         for (i = 0; i < nLoop; i++)
01137             write_msg(NULL, "  %s\n", loop[i]->name);
01138         write_msg(NULL, "You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.\n");
01139         write_msg(NULL, "Consider using a full dump instead of a --data-only dump to avoid this problem.\n");
01140         if (nLoop > 1)
01141             removeObjectDependency(loop[0], loop[1]->dumpId);
01142         else    /* must be a self-dependency */
01143             removeObjectDependency(loop[0], loop[0]->dumpId);
01144         return;
01145     }
01146 
01147     /*
01148      * If we can't find a principled way to break the loop, complain and break
01149      * it in an arbitrary fashion.
01150      */
01151     write_msg(modulename, "WARNING: could not resolve dependency loop among these items:\n");
01152     for (i = 0; i < nLoop; i++)
01153     {
01154         char        buf[1024];
01155 
01156         describeDumpableObject(loop[i], buf, sizeof(buf));
01157         write_msg(modulename, "  %s\n", buf);
01158     }
01159 
01160     if (nLoop > 1)
01161         removeObjectDependency(loop[0], loop[1]->dumpId);
01162     else    /* must be a self-dependency */
01163         removeObjectDependency(loop[0], loop[0]->dumpId);
01164 }
01165 
01166 /*
01167  * Describe a dumpable object usefully for errors
01168  *
01169  * This should probably go somewhere else...
01170  */
01171 static void
01172 describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
01173 {
01174     switch (obj->objType)
01175     {
01176         case DO_NAMESPACE:
01177             snprintf(buf, bufsize,
01178                      "SCHEMA %s  (ID %d OID %u)",
01179                      obj->name, obj->dumpId, obj->catId.oid);
01180             return;
01181         case DO_EXTENSION:
01182             snprintf(buf, bufsize,
01183                      "EXTENSION %s  (ID %d OID %u)",
01184                      obj->name, obj->dumpId, obj->catId.oid);
01185             return;
01186         case DO_TYPE:
01187             snprintf(buf, bufsize,
01188                      "TYPE %s  (ID %d OID %u)",
01189                      obj->name, obj->dumpId, obj->catId.oid);
01190             return;
01191         case DO_SHELL_TYPE:
01192             snprintf(buf, bufsize,
01193                      "SHELL TYPE %s  (ID %d OID %u)",
01194                      obj->name, obj->dumpId, obj->catId.oid);
01195             return;
01196         case DO_FUNC:
01197             snprintf(buf, bufsize,
01198                      "FUNCTION %s  (ID %d OID %u)",
01199                      obj->name, obj->dumpId, obj->catId.oid);
01200             return;
01201         case DO_AGG:
01202             snprintf(buf, bufsize,
01203                      "AGGREGATE %s  (ID %d OID %u)",
01204                      obj->name, obj->dumpId, obj->catId.oid);
01205             return;
01206         case DO_OPERATOR:
01207             snprintf(buf, bufsize,
01208                      "OPERATOR %s  (ID %d OID %u)",
01209                      obj->name, obj->dumpId, obj->catId.oid);
01210             return;
01211         case DO_OPCLASS:
01212             snprintf(buf, bufsize,
01213                      "OPERATOR CLASS %s  (ID %d OID %u)",
01214                      obj->name, obj->dumpId, obj->catId.oid);
01215             return;
01216         case DO_OPFAMILY:
01217             snprintf(buf, bufsize,
01218                      "OPERATOR FAMILY %s  (ID %d OID %u)",
01219                      obj->name, obj->dumpId, obj->catId.oid);
01220             return;
01221         case DO_COLLATION:
01222             snprintf(buf, bufsize,
01223                      "COLLATION %s  (ID %d OID %u)",
01224                      obj->name, obj->dumpId, obj->catId.oid);
01225             return;
01226         case DO_CONVERSION:
01227             snprintf(buf, bufsize,
01228                      "CONVERSION %s  (ID %d OID %u)",
01229                      obj->name, obj->dumpId, obj->catId.oid);
01230             return;
01231         case DO_TABLE:
01232             snprintf(buf, bufsize,
01233                      "TABLE %s  (ID %d OID %u)",
01234                      obj->name, obj->dumpId, obj->catId.oid);
01235             return;
01236         case DO_ATTRDEF:
01237             snprintf(buf, bufsize,
01238                      "ATTRDEF %s.%s  (ID %d OID %u)",
01239                      ((AttrDefInfo *) obj)->adtable->dobj.name,
01240                      ((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
01241                      obj->dumpId, obj->catId.oid);
01242             return;
01243         case DO_INDEX:
01244             snprintf(buf, bufsize,
01245                      "INDEX %s  (ID %d OID %u)",
01246                      obj->name, obj->dumpId, obj->catId.oid);
01247             return;
01248         case DO_REFRESH_MATVIEW:
01249             snprintf(buf, bufsize,
01250                      "REFRESH MATERIALIZED VIEW %s  (ID %d OID %u)",
01251                      obj->name, obj->dumpId, obj->catId.oid);
01252             return;
01253         case DO_RULE:
01254             snprintf(buf, bufsize,
01255                      "RULE %s  (ID %d OID %u)",
01256                      obj->name, obj->dumpId, obj->catId.oid);
01257             return;
01258         case DO_TRIGGER:
01259             snprintf(buf, bufsize,
01260                      "TRIGGER %s  (ID %d OID %u)",
01261                      obj->name, obj->dumpId, obj->catId.oid);
01262             return;
01263         case DO_EVENT_TRIGGER:
01264             snprintf(buf, bufsize,
01265                      "EVENT TRIGGER %s (ID %d OID %u)",
01266                      obj->name, obj->dumpId, obj->catId.oid);
01267             return;
01268         case DO_CONSTRAINT:
01269             snprintf(buf, bufsize,
01270                      "CONSTRAINT %s  (ID %d OID %u)",
01271                      obj->name, obj->dumpId, obj->catId.oid);
01272             return;
01273         case DO_FK_CONSTRAINT:
01274             snprintf(buf, bufsize,
01275                      "FK CONSTRAINT %s  (ID %d OID %u)",
01276                      obj->name, obj->dumpId, obj->catId.oid);
01277             return;
01278         case DO_PROCLANG:
01279             snprintf(buf, bufsize,
01280                      "PROCEDURAL LANGUAGE %s  (ID %d OID %u)",
01281                      obj->name, obj->dumpId, obj->catId.oid);
01282             return;
01283         case DO_CAST:
01284             snprintf(buf, bufsize,
01285                      "CAST %u to %u  (ID %d OID %u)",
01286                      ((CastInfo *) obj)->castsource,
01287                      ((CastInfo *) obj)->casttarget,
01288                      obj->dumpId, obj->catId.oid);
01289             return;
01290         case DO_TABLE_DATA:
01291             snprintf(buf, bufsize,
01292                      "TABLE DATA %s  (ID %d OID %u)",
01293                      obj->name, obj->dumpId, obj->catId.oid);
01294             return;
01295         case DO_DUMMY_TYPE:
01296             snprintf(buf, bufsize,
01297                      "DUMMY TYPE %s  (ID %d OID %u)",
01298                      obj->name, obj->dumpId, obj->catId.oid);
01299             return;
01300         case DO_TSPARSER:
01301             snprintf(buf, bufsize,
01302                      "TEXT SEARCH PARSER %s  (ID %d OID %u)",
01303                      obj->name, obj->dumpId, obj->catId.oid);
01304             return;
01305         case DO_TSDICT:
01306             snprintf(buf, bufsize,
01307                      "TEXT SEARCH DICTIONARY %s  (ID %d OID %u)",
01308                      obj->name, obj->dumpId, obj->catId.oid);
01309             return;
01310         case DO_TSTEMPLATE:
01311             snprintf(buf, bufsize,
01312                      "TEXT SEARCH TEMPLATE %s  (ID %d OID %u)",
01313                      obj->name, obj->dumpId, obj->catId.oid);
01314             return;
01315         case DO_TSCONFIG:
01316             snprintf(buf, bufsize,
01317                      "TEXT SEARCH CONFIGURATION %s  (ID %d OID %u)",
01318                      obj->name, obj->dumpId, obj->catId.oid);
01319             return;
01320         case DO_FDW:
01321             snprintf(buf, bufsize,
01322                      "FOREIGN DATA WRAPPER %s  (ID %d OID %u)",
01323                      obj->name, obj->dumpId, obj->catId.oid);
01324             return;
01325         case DO_FOREIGN_SERVER:
01326             snprintf(buf, bufsize,
01327                      "FOREIGN SERVER %s  (ID %d OID %u)",
01328                      obj->name, obj->dumpId, obj->catId.oid);
01329             return;
01330         case DO_DEFAULT_ACL:
01331             snprintf(buf, bufsize,
01332                      "DEFAULT ACL %s  (ID %d OID %u)",
01333                      obj->name, obj->dumpId, obj->catId.oid);
01334             return;
01335         case DO_BLOB:
01336             snprintf(buf, bufsize,
01337                      "BLOB  (ID %d OID %u)",
01338                      obj->dumpId, obj->catId.oid);
01339             return;
01340         case DO_BLOB_DATA:
01341             snprintf(buf, bufsize,
01342                      "BLOB DATA  (ID %d)",
01343                      obj->dumpId);
01344             return;
01345         case DO_PRE_DATA_BOUNDARY:
01346             snprintf(buf, bufsize,
01347                      "PRE-DATA BOUNDARY  (ID %d)",
01348                      obj->dumpId);
01349             return;
01350         case DO_POST_DATA_BOUNDARY:
01351             snprintf(buf, bufsize,
01352                      "POST-DATA BOUNDARY  (ID %d)",
01353                      obj->dumpId);
01354             return;
01355     }
01356     /* shouldn't get here */
01357     snprintf(buf, bufsize,
01358              "object type %d  (ID %d OID %u)",
01359              (int) obj->objType,
01360              obj->dumpId, obj->catId.oid);
01361 }