~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/bin/pg_dump/pg_dump_sort.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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-2005, PostgreSQL Global Development Group
 
8
 * Portions Copyright (c) 1994, Regents of the University of California
 
9
 *
 
10
 *
 
11
 * IDENTIFICATION
 
12
 *        $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.8.4.1 2005-02-03 23:39:21 tgl Exp $
 
13
 *
 
14
 *-------------------------------------------------------------------------
 
15
 */
 
16
#include "pg_dump.h"
 
17
#include "pg_backup_archiver.h"
 
18
 
 
19
 
 
20
static char *modulename = gettext_noop("sorter");
 
21
 
 
22
/*
 
23
 * Sort priority for object types when dumping a pre-7.3 database.
 
24
 * Objects are sorted by priority levels, and within an equal priority level
 
25
 * by OID.      (This is a relatively crude hack to provide semi-reasonable
 
26
 * behavior for old databases without full dependency info.)
 
27
 */
 
28
static const int oldObjectTypePriority[] =
 
29
{
 
30
        1,                                                      /* DO_NAMESPACE */
 
31
        2,                                                      /* DO_TYPE */
 
32
        2,                                                      /* DO_FUNC */
 
33
        3,                                                      /* DO_AGG */
 
34
        3,                                                      /* DO_OPERATOR */
 
35
        4,                                                      /* DO_OPCLASS */
 
36
        5,                                                      /* DO_CONVERSION */
 
37
        6,                                                      /* DO_TABLE */
 
38
        8,                                                      /* DO_ATTRDEF */
 
39
        12,                                                     /* DO_INDEX */
 
40
        13,                                                     /* DO_RULE */
 
41
        14,                                                     /* DO_TRIGGER */
 
42
        11,                                                     /* DO_CONSTRAINT */
 
43
        15,                                                     /* DO_FK_CONSTRAINT */
 
44
        2,                                                      /* DO_PROCLANG */
 
45
        2,                                                      /* DO_CAST */
 
46
        9,                                                      /* DO_TABLE_DATA */
 
47
        7,                                                      /* DO_TABLE_TYPE */
 
48
        10                                                      /* DO_BLOBS */
 
49
};
 
50
 
 
51
/*
 
52
 * Sort priority for object types when dumping newer databases.
 
53
 * Objects are sorted by type, and within a type by name.
 
54
 */
 
55
static const int newObjectTypePriority[] =
 
56
{
 
57
        1,                                                      /* DO_NAMESPACE */
 
58
        3,                                                      /* DO_TYPE */
 
59
        4,                                                      /* DO_FUNC */
 
60
        5,                                                      /* DO_AGG */
 
61
        6,                                                      /* DO_OPERATOR */
 
62
        7,                                                      /* DO_OPCLASS */
 
63
        9,                                                      /* DO_CONVERSION */
 
64
        10,                                                     /* DO_TABLE */
 
65
        12,                                                     /* DO_ATTRDEF */
 
66
        16,                                                     /* DO_INDEX */
 
67
        17,                                                     /* DO_RULE */
 
68
        18,                                                     /* DO_TRIGGER */
 
69
        15,                                                     /* DO_CONSTRAINT */
 
70
        19,                                                     /* DO_FK_CONSTRAINT */
 
71
        2,                                                      /* DO_PROCLANG */
 
72
        8,                                                      /* DO_CAST */
 
73
        13,                                                     /* DO_TABLE_DATA */
 
74
        11,                                                     /* DO_TABLE_TYPE */
 
75
        14                                                      /* DO_BLOBS */
 
76
};
 
77
 
 
78
 
 
79
static int      DOTypeNameCompare(const void *p1, const void *p2);
 
80
static int      DOTypeOidCompare(const void *p1, const void *p2);
 
81
static bool TopoSort(DumpableObject **objs,
 
82
                 int numObjs,
 
83
                 DumpableObject **ordering,
 
84
                 int *nOrdering);
 
85
static void addHeapElement(int val, int *heap, int heapLength);
 
86
static int      removeHeapElement(int *heap, int heapLength);
 
87
static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
 
88
static bool findLoop(DumpableObject *obj,
 
89
                 DumpId startPoint,
 
90
                 DumpableObject **workspace,
 
91
                 int depth,
 
92
                 int *newDepth);
 
93
static void repairDependencyLoop(DumpableObject **loop,
 
94
                                         int nLoop);
 
95
static void describeDumpableObject(DumpableObject *obj,
 
96
                                           char *buf, int bufsize);
 
97
 
 
98
 
 
99
/*
 
100
 * Sort the given objects into a type/name-based ordering
 
101
 *
 
102
 * Normally this is just the starting point for the dependency-based
 
103
 * ordering.
 
104
 */
 
105
void
 
106
sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
 
107
{
 
108
        if (numObjs > 1)
 
109
                qsort((void *) objs, numObjs, sizeof(DumpableObject *),
 
110
                          DOTypeNameCompare);
 
111
}
 
112
 
 
113
static int
 
114
DOTypeNameCompare(const void *p1, const void *p2)
 
115
{
 
116
        DumpableObject *obj1 = *(DumpableObject **) p1;
 
117
        DumpableObject *obj2 = *(DumpableObject **) p2;
 
118
        int                     cmpval;
 
119
 
 
120
        /* Sort by type */
 
121
        cmpval = newObjectTypePriority[obj1->objType] -
 
122
                newObjectTypePriority[obj2->objType];
 
123
 
 
124
        if (cmpval != 0)
 
125
                return cmpval;
 
126
 
 
127
        /*
 
128
         * Sort by namespace.  Note that all objects of the same type should
 
129
         * either have or not have a namespace link, so we needn't be fancy
 
130
         * about cases where one link is null and the other not.
 
131
         */
 
132
        if (obj1->namespace && obj2->namespace)
 
133
        {
 
134
                cmpval = strcmp(obj1->namespace->dobj.name,
 
135
                                                obj2->namespace->dobj.name);
 
136
                if (cmpval != 0)
 
137
                        return cmpval;
 
138
        }
 
139
 
 
140
        /* Sort by name */
 
141
        cmpval = strcmp(obj1->name, obj2->name);
 
142
        if (cmpval != 0)
 
143
                return cmpval;
 
144
 
 
145
        /* Probably shouldn't get here, but if we do, sort by OID */
 
146
        return oidcmp(obj1->catId.oid, obj2->catId.oid);
 
147
}
 
148
 
 
149
 
 
150
/*
 
151
 * Sort the given objects into a type/OID-based ordering
 
152
 *
 
153
 * This is used with pre-7.3 source databases as a crude substitute for the
 
154
 * lack of dependency information.
 
155
 */
 
156
void
 
157
sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs)
 
158
{
 
159
        if (numObjs > 1)
 
160
                qsort((void *) objs, numObjs, sizeof(DumpableObject *),
 
161
                          DOTypeOidCompare);
 
162
}
 
163
 
 
164
static int
 
165
DOTypeOidCompare(const void *p1, const void *p2)
 
166
{
 
167
        DumpableObject *obj1 = *(DumpableObject **) p1;
 
168
        DumpableObject *obj2 = *(DumpableObject **) p2;
 
169
        int                     cmpval;
 
170
 
 
171
        cmpval = oldObjectTypePriority[obj1->objType] -
 
172
                oldObjectTypePriority[obj2->objType];
 
173
 
 
174
        if (cmpval != 0)
 
175
                return cmpval;
 
176
 
 
177
        return oidcmp(obj1->catId.oid, obj2->catId.oid);
 
178
}
 
179
 
 
180
 
 
181
/*
 
182
 * Sort the given objects into a safe dump order using dependency
 
183
 * information (to the extent we have it available).
 
184
 */
 
185
void
 
186
sortDumpableObjects(DumpableObject **objs, int numObjs)
 
187
{
 
188
        DumpableObject **ordering;
 
189
        int                     nOrdering;
 
190
 
 
191
        if (numObjs <= 0)
 
192
                return;
 
193
 
 
194
        ordering = (DumpableObject **) malloc(numObjs * sizeof(DumpableObject *));
 
195
        if (ordering == NULL)
 
196
                exit_horribly(NULL, modulename, "out of memory\n");
 
197
 
 
198
        while (!TopoSort(objs, numObjs, ordering, &nOrdering))
 
199
                findDependencyLoops(ordering, nOrdering, numObjs);
 
200
 
 
201
        memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
 
202
 
 
203
        free(ordering);
 
204
}
 
205
 
 
206
/*
 
207
 * TopoSort -- topological sort of a dump list
 
208
 *
 
209
 * Generate a re-ordering of the dump list that satisfies all the dependency
 
210
 * constraints shown in the dump list.  (Each such constraint is a fact of a
 
211
 * partial ordering.)  Minimize rearrangement of the list not needed to
 
212
 * achieve the partial ordering.
 
213
 *
 
214
 * The input is the list of numObjs objects in objs[].  This list is not
 
215
 * modified.
 
216
 *
 
217
 * Returns TRUE if able to build an ordering that satisfies all the
 
218
 * constraints, FALSE if not (there are contradictory constraints).
 
219
 *
 
220
 * On success (TRUE result), ordering[] is filled with a sorted array of
 
221
 * DumpableObject pointers, of length equal to the input list length.
 
222
 *
 
223
 * On failure (FALSE result), ordering[] is filled with an unsorted array of
 
224
 * DumpableObject pointers of length *nOrdering, listing the objects that
 
225
 * prevented the sort from being completed.  In general, these objects either
 
226
 * participate directly in a dependency cycle, or are depended on by objects
 
227
 * that are in a cycle.  (The latter objects are not actually problematic,
 
228
 * but it takes further analysis to identify which are which.)
 
229
 *
 
230
 * The caller is responsible for allocating sufficient space at *ordering.
 
231
 */
 
232
static bool
 
233
TopoSort(DumpableObject **objs,
 
234
                 int numObjs,
 
235
                 DumpableObject **ordering,             /* output argument */
 
236
                 int *nOrdering)                /* output argument */
 
237
{
 
238
        DumpId          maxDumpId = getMaxDumpId();
 
239
        int                *pendingHeap;
 
240
        int                *beforeConstraints;
 
241
        int                *idMap;
 
242
        DumpableObject *obj;
 
243
        int                     heapLength;
 
244
        int                     i,
 
245
                                j,
 
246
                                k;
 
247
 
 
248
        /*
 
249
         * This is basically the same algorithm shown for topological sorting
 
250
         * in Knuth's Volume 1.  However, we would like to minimize
 
251
         * unnecessary rearrangement of the input ordering; that is, when we
 
252
         * have a choice of which item to output next, we always want to take
 
253
         * the one highest in the original list.  Therefore, instead of
 
254
         * maintaining an unordered linked list of items-ready-to-output as
 
255
         * Knuth does, we maintain a heap of their item numbers, which we can
 
256
         * use as a priority queue.  This turns the algorithm from O(N) to O(N
 
257
         * log N) because each insertion or removal of a heap item takes O(log
 
258
         * N) time.  However, that's still plenty fast enough for this
 
259
         * application.
 
260
         */
 
261
 
 
262
        *nOrdering = numObjs;           /* for success return */
 
263
 
 
264
        /* Eliminate the null case */
 
265
        if (numObjs <= 0)
 
266
                return true;
 
267
 
 
268
        /* Create workspace for the above-described heap */
 
269
        pendingHeap = (int *) malloc(numObjs * sizeof(int));
 
270
        if (pendingHeap == NULL)
 
271
                exit_horribly(NULL, modulename, "out of memory\n");
 
272
 
 
273
        /*
 
274
         * Scan the constraints, and for each item in the input, generate a
 
275
         * count of the number of constraints that say it must be before
 
276
         * something else. The count for the item with dumpId j is stored in
 
277
         * beforeConstraints[j].  We also make a map showing the input-order
 
278
         * index of the item with dumpId j.
 
279
         */
 
280
        beforeConstraints = (int *) malloc((maxDumpId + 1) * sizeof(int));
 
281
        if (beforeConstraints == NULL)
 
282
                exit_horribly(NULL, modulename, "out of memory\n");
 
283
        memset(beforeConstraints, 0, (maxDumpId + 1) * sizeof(int));
 
284
        idMap = (int *) malloc((maxDumpId + 1) * sizeof(int));
 
285
        if (idMap == NULL)
 
286
                exit_horribly(NULL, modulename, "out of memory\n");
 
287
        for (i = 0; i < numObjs; i++)
 
288
        {
 
289
                obj = objs[i];
 
290
                j = obj->dumpId;
 
291
                if (j <= 0 || j > maxDumpId)
 
292
                        exit_horribly(NULL, modulename, "invalid dumpId %d\n", j);
 
293
                idMap[j] = i;
 
294
                for (j = 0; j < obj->nDeps; j++)
 
295
                {
 
296
                        k = obj->dependencies[j];
 
297
                        if (k <= 0 || k > maxDumpId)
 
298
                                exit_horribly(NULL, modulename, "invalid dependency %d\n", k);
 
299
                        beforeConstraints[k]++;
 
300
                }
 
301
        }
 
302
 
 
303
        /*
 
304
         * Now initialize the heap of items-ready-to-output by filling it with
 
305
         * the indexes of items that already have beforeConstraints[id] == 0.
 
306
         *
 
307
         * The essential property of a heap is heap[(j-1)/2] >= heap[j] for each
 
308
         * j in the range 1..heapLength-1 (note we are using 0-based
 
309
         * subscripts here, while the discussion in Knuth assumes 1-based
 
310
         * subscripts). So, if we simply enter the indexes into pendingHeap[]
 
311
         * in decreasing order, we a-fortiori have the heap invariant
 
312
         * satisfied at completion of this loop, and don't need to do any
 
313
         * sift-up comparisons.
 
314
         */
 
315
        heapLength = 0;
 
316
        for (i = numObjs; --i >= 0;)
 
317
        {
 
318
                if (beforeConstraints[objs[i]->dumpId] == 0)
 
319
                        pendingHeap[heapLength++] = i;
 
320
        }
 
321
 
 
322
        /*--------------------
 
323
         * Now emit objects, working backwards in the output list.      At each step,
 
324
         * we use the priority heap to select the last item that has no remaining
 
325
         * before-constraints.  We remove that item from the heap, output it to
 
326
         * ordering[], and decrease the beforeConstraints count of each of the
 
327
         * items it was constrained against.  Whenever an item's beforeConstraints
 
328
         * count is thereby decreased to zero, we insert it into the priority heap
 
329
         * to show that it is a candidate to output.  We are done when the heap
 
330
         * becomes empty; if we have output every element then we succeeded,
 
331
         * otherwise we failed.
 
332
         * i = number of ordering[] entries left to output
 
333
         * j = objs[] index of item we are outputting
 
334
         * k = temp for scanning constraint list for item j
 
335
         *--------------------
 
336
         */
 
337
        i = numObjs;
 
338
        while (heapLength > 0)
 
339
        {
 
340
                /* Select object to output by removing largest heap member */
 
341
                j = removeHeapElement(pendingHeap, heapLength--);
 
342
                obj = objs[j];
 
343
                /* Output candidate to ordering[] */
 
344
                ordering[--i] = obj;
 
345
                /* Update beforeConstraints counts of its predecessors */
 
346
                for (k = 0; k < obj->nDeps; k++)
 
347
                {
 
348
                        int                     id = obj->dependencies[k];
 
349
 
 
350
                        if ((--beforeConstraints[id]) == 0)
 
351
                                addHeapElement(idMap[id], pendingHeap, heapLength++);
 
352
                }
 
353
        }
 
354
 
 
355
        /*
 
356
         * If we failed, report the objects that couldn't be output; these are
 
357
         * the ones with beforeConstraints[] still nonzero.
 
358
         */
 
359
        if (i != 0)
 
360
        {
 
361
                k = 0;
 
362
                for (j = 1; j <= maxDumpId; j++)
 
363
                {
 
364
                        if (beforeConstraints[j] != 0)
 
365
                                ordering[k++] = objs[idMap[j]];
 
366
                }
 
367
                *nOrdering = k;
 
368
        }
 
369
 
 
370
        /* Done */
 
371
        free(pendingHeap);
 
372
        free(beforeConstraints);
 
373
        free(idMap);
 
374
 
 
375
        return (i == 0);
 
376
}
 
377
 
 
378
/*
 
379
 * Add an item to a heap (priority queue)
 
380
 *
 
381
 * heapLength is the current heap size; caller is responsible for increasing
 
382
 * its value after the call.  There must be sufficient storage at *heap.
 
383
 */
 
384
static void
 
385
addHeapElement(int val, int *heap, int heapLength)
 
386
{
 
387
        int                     j;
 
388
 
 
389
        /*
 
390
         * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth
 
391
         * is using 1-based array indexes, not 0-based.
 
392
         */
 
393
        j = heapLength;
 
394
        while (j > 0)
 
395
        {
 
396
                int                     i = (j - 1) >> 1;
 
397
 
 
398
                if (val <= heap[i])
 
399
                        break;
 
400
                heap[j] = heap[i];
 
401
                j = i;
 
402
        }
 
403
        heap[j] = val;
 
404
}
 
405
 
 
406
/*
 
407
 * Remove the largest item present in a heap (priority queue)
 
408
 *
 
409
 * heapLength is the current heap size; caller is responsible for decreasing
 
410
 * its value after the call.
 
411
 *
 
412
 * We remove and return heap[0], which is always the largest element of
 
413
 * the heap, and then "sift up" to maintain the heap invariant.
 
414
 */
 
415
static int
 
416
removeHeapElement(int *heap, int heapLength)
 
417
{
 
418
        int                     result = heap[0];
 
419
        int                     val;
 
420
        int                     i;
 
421
 
 
422
        if (--heapLength <= 0)
 
423
                return result;
 
424
        val = heap[heapLength];         /* value that must be reinserted */
 
425
        i = 0;                                          /* i is where the "hole" is */
 
426
        for (;;)
 
427
        {
 
428
                int                     j = 2 * i + 1;
 
429
 
 
430
                if (j >= heapLength)
 
431
                        break;
 
432
                if (j + 1 < heapLength &&
 
433
                        heap[j] < heap[j + 1])
 
434
                        j++;
 
435
                if (val >= heap[j])
 
436
                        break;
 
437
                heap[i] = heap[j];
 
438
                i = j;
 
439
        }
 
440
        heap[i] = val;
 
441
        return result;
 
442
}
 
443
 
 
444
/*
 
445
 * findDependencyLoops - identify loops in TopoSort's failure output,
 
446
 *              and pass each such loop to repairDependencyLoop() for action
 
447
 *
 
448
 * In general there may be many loops in the set of objects returned by
 
449
 * TopoSort; for speed we should try to repair as many loops as we can
 
450
 * before trying TopoSort again.  We can safely repair loops that are
 
451
 * disjoint (have no members in common); if we find overlapping loops
 
452
 * then we repair only the first one found, because the action taken to
 
453
 * repair the first might have repaired the other as well.      (If not,
 
454
 * we'll fix it on the next go-round.)
 
455
 *
 
456
 * objs[] lists the objects TopoSort couldn't sort
 
457
 * nObjs is the number of such objects
 
458
 * totObjs is the total number of objects in the universe
 
459
 */
 
460
static void
 
461
findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
 
462
{
 
463
        /*
 
464
         * We use a workspace array, the initial part of which stores objects
 
465
         * already processed, and the rest of which is used as temporary space
 
466
         * to try to build a loop in.  This is convenient because we do not
 
467
         * care about loops involving already-processed objects (see notes
 
468
         * above); we can easily reject such loops in findLoop() because of
 
469
         * this representation.  After we identify and process a loop, we can
 
470
         * add it to the initial part of the workspace just by moving the
 
471
         * boundary pointer.
 
472
         *
 
473
         * When we determine that an object is not part of any interesting loop,
 
474
         * we also add it to the initial part of the workspace.  This is not
 
475
         * necessary for correctness, but saves later invocations of
 
476
         * findLoop() from uselessly chasing references to such an object.
 
477
         *
 
478
         * We make the workspace large enough to hold all objects in the original
 
479
         * universe.  This is probably overkill, but it's provably enough
 
480
         * space...
 
481
         */
 
482
        DumpableObject **workspace;
 
483
        int                     initiallen;
 
484
        bool            fixedloop;
 
485
        int                     i;
 
486
 
 
487
        workspace = (DumpableObject **) malloc(totObjs * sizeof(DumpableObject *));
 
488
        if (workspace == NULL)
 
489
                exit_horribly(NULL, modulename, "out of memory\n");
 
490
        initiallen = 0;
 
491
        fixedloop = false;
 
492
 
 
493
        for (i = 0; i < nObjs; i++)
 
494
        {
 
495
                DumpableObject *obj = objs[i];
 
496
                int                     newlen;
 
497
 
 
498
                workspace[initiallen] = NULL;   /* see test below */
 
499
 
 
500
                if (findLoop(obj, obj->dumpId, workspace, initiallen, &newlen))
 
501
                {
 
502
                        /* Found a loop of length newlen - initiallen */
 
503
                        repairDependencyLoop(&workspace[initiallen], newlen - initiallen);
 
504
                        /* Add loop members to workspace */
 
505
                        initiallen = newlen;
 
506
                        fixedloop = true;
 
507
                }
 
508
                else
 
509
                {
 
510
                        /*
 
511
                         * Didn't find a loop, but add this object to workspace
 
512
                         * anyway, unless it's already present.  We piggyback on the
 
513
                         * test that findLoop() already did: it won't have tentatively
 
514
                         * added obj to workspace if it's already present.
 
515
                         */
 
516
                        if (workspace[initiallen] == obj)
 
517
                                initiallen++;
 
518
                }
 
519
        }
 
520
 
 
521
        /* We'd better have fixed at least one loop */
 
522
        if (!fixedloop)
 
523
                exit_horribly(NULL, modulename, "could not identify dependency loop\n");
 
524
 
 
525
        free(workspace);
 
526
}
 
527
 
 
528
/*
 
529
 * Recursively search for a circular dependency loop that doesn't include
 
530
 * any existing workspace members.
 
531
 *
 
532
 *      obj: object we are examining now
 
533
 *      startPoint: dumpId of starting object for the hoped-for circular loop
 
534
 *      workspace[]: work array for previously processed and current objects
 
535
 *      depth: number of valid entries in workspace[] at call
 
536
 *      newDepth: if successful, set to new number of workspace[] entries
 
537
 *
 
538
 * On success, *newDepth is set and workspace[] entries depth..*newDepth-1
 
539
 * are filled with pointers to the members of the loop.
 
540
 *
 
541
 * Note: it is possible that the given starting object is a member of more
 
542
 * than one cycle; if so, we will find an arbitrary one of the cycles.
 
543
 */
 
544
static bool
 
545
findLoop(DumpableObject *obj,
 
546
                 DumpId startPoint,
 
547
                 DumpableObject **workspace,
 
548
                 int depth,
 
549
                 int *newDepth)
 
550
{
 
551
        int                     i;
 
552
 
 
553
        /*
 
554
         * Reject if obj is already present in workspace.  This test serves
 
555
         * three purposes: it prevents us from finding loops that overlap
 
556
         * previously-processed loops, it prevents us from going into infinite
 
557
         * recursion if we are given a startPoint object that links to a cycle
 
558
         * it's not a member of, and it guarantees that we can't overflow the
 
559
         * allocated size of workspace[].
 
560
         */
 
561
        for (i = 0; i < depth; i++)
 
562
        {
 
563
                if (workspace[i] == obj)
 
564
                        return false;
 
565
        }
 
566
 
 
567
        /*
 
568
         * Okay, tentatively add obj to workspace
 
569
         */
 
570
        workspace[depth++] = obj;
 
571
 
 
572
        /*
 
573
         * See if we've found a loop back to the desired startPoint; if so,
 
574
         * done
 
575
         */
 
576
        for (i = 0; i < obj->nDeps; i++)
 
577
        {
 
578
                if (obj->dependencies[i] == startPoint)
 
579
                {
 
580
                        *newDepth = depth;
 
581
                        return true;
 
582
                }
 
583
        }
 
584
 
 
585
        /*
 
586
         * Recurse down each outgoing branch
 
587
         */
 
588
        for (i = 0; i < obj->nDeps; i++)
 
589
        {
 
590
                DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
 
591
 
 
592
                if (!nextobj)
 
593
                        continue;                       /* ignore dependencies on undumped objects */
 
594
                if (findLoop(nextobj,
 
595
                                         startPoint,
 
596
                                         workspace,
 
597
                                         depth,
 
598
                                         newDepth))
 
599
                        return true;
 
600
        }
 
601
 
 
602
        return false;
 
603
}
 
604
 
 
605
/*
 
606
 * A user-defined datatype will have a dependency loop with each of its
 
607
 * I/O functions (since those have the datatype as input or output).
 
608
 * We want the dump ordering to be the input function, then any other
 
609
 * I/O functions, then the datatype.  So we break the circularity in
 
610
 * favor of the functions, and add a dependency from any non-input
 
611
 * function to the input function.
 
612
 */
 
613
static void
 
614
repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
 
615
{
 
616
        TypeInfo   *typeInfo = (TypeInfo *) typeobj;
 
617
        FuncInfo   *inputFuncInfo;
 
618
 
 
619
        /* remove function's dependency on type */
 
620
        removeObjectDependency(funcobj, typeobj->dumpId);
 
621
 
 
622
        /* if this isn't the input function, make it depend on same */
 
623
        if (funcobj->catId.oid == typeInfo->typinput)
 
624
                return;                                 /* it is the input function */
 
625
        inputFuncInfo = findFuncByOid(typeInfo->typinput);
 
626
        if (inputFuncInfo == NULL)
 
627
                return;
 
628
        addObjectDependency(funcobj, inputFuncInfo->dobj.dumpId);
 
629
 
 
630
        /*
 
631
         * Make sure the input function's dependency on type gets removed too;
 
632
         * if it hasn't been done yet, we'd end up with loops involving the
 
633
         * type and two or more functions, which repairDependencyLoop() is not
 
634
         * smart enough to handle.
 
635
         */
 
636
        removeObjectDependency(&inputFuncInfo->dobj, typeobj->dumpId);
 
637
}
 
638
 
 
639
/*
 
640
 * Because we force a view to depend on its ON SELECT rule, while there
 
641
 * will be an implicit dependency in the other direction, we need to break
 
642
 * the loop.  If there are no other objects in the loop then we can remove
 
643
 * the implicit dependency and leave the ON SELECT rule non-separate.
 
644
 */
 
645
static void
 
646
repairViewRuleLoop(DumpableObject *viewobj,
 
647
                                   DumpableObject *ruleobj)
 
648
{
 
649
        /* remove rule's dependency on view */
 
650
        removeObjectDependency(ruleobj, viewobj->dumpId);
 
651
}
 
652
 
 
653
/*
 
654
 * However, if there are other objects in the loop, we must break the loop
 
655
 * by making the ON SELECT rule a separately-dumped object.
 
656
 *
 
657
 * Because findLoop() finds shorter cycles before longer ones, it's likely
 
658
 * that we will have previously fired repairViewRuleLoop() and removed the
 
659
 * rule's dependency on the view.  Put it back to ensure the rule won't be
 
660
 * emitted before the view...
 
661
 */
 
662
static void
 
663
repairViewRuleMultiLoop(DumpableObject *viewobj,
 
664
                                                DumpableObject *ruleobj)
 
665
{
 
666
        /* remove view's dependency on rule */
 
667
        removeObjectDependency(viewobj, ruleobj->dumpId);
 
668
        /* pretend view is a plain table and dump it that way */
 
669
        ((TableInfo *) viewobj)->relkind = 'r';         /* RELKIND_RELATION */
 
670
        /* mark rule as needing its own dump */
 
671
        ((RuleInfo *) ruleobj)->separate = true;
 
672
        /* put back rule's dependency on view */
 
673
        addObjectDependency(ruleobj, viewobj->dumpId);
 
674
}
 
675
 
 
676
/*
 
677
 * Because we make tables depend on their CHECK constraints, while there
 
678
 * will be an automatic dependency in the other direction, we need to break
 
679
 * the loop.  If there are no other objects in the loop then we can remove
 
680
 * the automatic dependency and leave the CHECK constraint non-separate.
 
681
 */
 
682
static void
 
683
repairTableConstraintLoop(DumpableObject *tableobj,
 
684
                                                  DumpableObject *constraintobj)
 
685
{
 
686
        /* remove constraint's dependency on table */
 
687
        removeObjectDependency(constraintobj, tableobj->dumpId);
 
688
}
 
689
 
 
690
/*
 
691
 * However, if there are other objects in the loop, we must break the loop
 
692
 * by making the CHECK constraint a separately-dumped object.
 
693
 *
 
694
 * Because findLoop() finds shorter cycles before longer ones, it's likely
 
695
 * that we will have previously fired repairTableConstraintLoop() and
 
696
 * removed the constraint's dependency on the table.  Put it back to ensure
 
697
 * the constraint won't be emitted before the table...
 
698
 */
 
699
static void
 
700
repairTableConstraintMultiLoop(DumpableObject *tableobj,
 
701
                                                           DumpableObject *constraintobj)
 
702
{
 
703
        /* remove table's dependency on constraint */
 
704
        removeObjectDependency(tableobj, constraintobj->dumpId);
 
705
        /* mark constraint as needing its own dump */
 
706
        ((ConstraintInfo *) constraintobj)->separate = true;
 
707
        /* put back constraint's dependency on table */
 
708
        addObjectDependency(constraintobj, tableobj->dumpId);
 
709
}
 
710
 
 
711
/*
 
712
 * Attribute defaults behave exactly the same as CHECK constraints...
 
713
 */
 
714
static void
 
715
repairTableAttrDefLoop(DumpableObject *tableobj,
 
716
                                           DumpableObject *attrdefobj)
 
717
{
 
718
        /* remove attrdef's dependency on table */
 
719
        removeObjectDependency(attrdefobj, tableobj->dumpId);
 
720
}
 
721
 
 
722
static void
 
723
repairTableAttrDefMultiLoop(DumpableObject *tableobj,
 
724
                                                        DumpableObject *attrdefobj)
 
725
{
 
726
        /* remove table's dependency on attrdef */
 
727
        removeObjectDependency(tableobj, attrdefobj->dumpId);
 
728
        /* mark attrdef as needing its own dump */
 
729
        ((AttrDefInfo *) attrdefobj)->separate = true;
 
730
        /* put back attrdef's dependency on table */
 
731
        addObjectDependency(attrdefobj, tableobj->dumpId);
 
732
}
 
733
 
 
734
/*
 
735
 * CHECK constraints on domains work just like those on tables ...
 
736
 */
 
737
static void
 
738
repairDomainConstraintLoop(DumpableObject *domainobj,
 
739
                                                   DumpableObject *constraintobj)
 
740
{
 
741
        /* remove constraint's dependency on domain */
 
742
        removeObjectDependency(constraintobj, domainobj->dumpId);
 
743
}
 
744
 
 
745
static void
 
746
repairDomainConstraintMultiLoop(DumpableObject *domainobj,
 
747
                                                                DumpableObject *constraintobj)
 
748
{
 
749
        /* remove domain's dependency on constraint */
 
750
        removeObjectDependency(domainobj, constraintobj->dumpId);
 
751
        /* mark constraint as needing its own dump */
 
752
        ((ConstraintInfo *) constraintobj)->separate = true;
 
753
        /* put back constraint's dependency on domain */
 
754
        addObjectDependency(constraintobj, domainobj->dumpId);
 
755
}
 
756
 
 
757
/*
 
758
 * Fix a dependency loop, or die trying ...
 
759
 *
 
760
 * This routine is mainly concerned with reducing the multiple ways that
 
761
 * a loop might appear to common cases, which it passes off to the
 
762
 * "fixer" routines above.
 
763
 */
 
764
static void
 
765
repairDependencyLoop(DumpableObject **loop,
 
766
                                         int nLoop)
 
767
{
 
768
        int                     i,
 
769
                                j;
 
770
 
 
771
        /* Datatype and one of its I/O functions */
 
772
        if (nLoop == 2 &&
 
773
                loop[0]->objType == DO_TYPE &&
 
774
                loop[1]->objType == DO_FUNC)
 
775
        {
 
776
                repairTypeFuncLoop(loop[0], loop[1]);
 
777
                return;
 
778
        }
 
779
        if (nLoop == 2 &&
 
780
                loop[1]->objType == DO_TYPE &&
 
781
                loop[0]->objType == DO_FUNC)
 
782
        {
 
783
                repairTypeFuncLoop(loop[1], loop[0]);
 
784
                return;
 
785
        }
 
786
 
 
787
        /* View and its ON SELECT rule */
 
788
        if (nLoop == 2 &&
 
789
                loop[0]->objType == DO_TABLE &&
 
790
                loop[1]->objType == DO_RULE &&
 
791
                ((RuleInfo *) loop[1])->ev_type == '1' &&
 
792
                ((RuleInfo *) loop[1])->is_instead &&
 
793
                ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
 
794
        {
 
795
                repairViewRuleLoop(loop[0], loop[1]);
 
796
                return;
 
797
        }
 
798
        if (nLoop == 2 &&
 
799
                loop[1]->objType == DO_TABLE &&
 
800
                loop[0]->objType == DO_RULE &&
 
801
                ((RuleInfo *) loop[0])->ev_type == '1' &&
 
802
                ((RuleInfo *) loop[0])->is_instead &&
 
803
                ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
 
804
        {
 
805
                repairViewRuleLoop(loop[1], loop[0]);
 
806
                return;
 
807
        }
 
808
 
 
809
        /* Indirect loop involving view and ON SELECT rule */
 
810
        if (nLoop > 2)
 
811
        {
 
812
                for (i = 0; i < nLoop; i++)
 
813
                {
 
814
                        if (loop[i]->objType == DO_TABLE)
 
815
                        {
 
816
                                for (j = 0; j < nLoop; j++)
 
817
                                {
 
818
                                        if (loop[j]->objType == DO_RULE &&
 
819
                                                ((RuleInfo *) loop[j])->ev_type == '1' &&
 
820
                                                ((RuleInfo *) loop[j])->is_instead &&
 
821
                                                ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
 
822
                                        {
 
823
                                                repairViewRuleMultiLoop(loop[i], loop[j]);
 
824
                                                return;
 
825
                                        }
 
826
                                }
 
827
                        }
 
828
                }
 
829
        }
 
830
 
 
831
        /* Table and CHECK constraint */
 
832
        if (nLoop == 2 &&
 
833
                loop[0]->objType == DO_TABLE &&
 
834
                loop[1]->objType == DO_CONSTRAINT &&
 
835
                ((ConstraintInfo *) loop[1])->contype == 'c' &&
 
836
                ((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
 
837
        {
 
838
                repairTableConstraintLoop(loop[0], loop[1]);
 
839
                return;
 
840
        }
 
841
        if (nLoop == 2 &&
 
842
                loop[1]->objType == DO_TABLE &&
 
843
                loop[0]->objType == DO_CONSTRAINT &&
 
844
                ((ConstraintInfo *) loop[0])->contype == 'c' &&
 
845
                ((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
 
846
        {
 
847
                repairTableConstraintLoop(loop[1], loop[0]);
 
848
                return;
 
849
        }
 
850
 
 
851
        /* Indirect loop involving table and CHECK constraint */
 
852
        if (nLoop > 2)
 
853
        {
 
854
                for (i = 0; i < nLoop; i++)
 
855
                {
 
856
                        if (loop[i]->objType == DO_TABLE)
 
857
                        {
 
858
                                for (j = 0; j < nLoop; j++)
 
859
                                {
 
860
                                        if (loop[j]->objType == DO_CONSTRAINT &&
 
861
                                                ((ConstraintInfo *) loop[j])->contype == 'c' &&
 
862
                                                ((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
 
863
                                        {
 
864
                                                repairTableConstraintMultiLoop(loop[i], loop[j]);
 
865
                                                return;
 
866
                                        }
 
867
                                }
 
868
                        }
 
869
                }
 
870
        }
 
871
 
 
872
        /* Table and attribute default */
 
873
        if (nLoop == 2 &&
 
874
                loop[0]->objType == DO_TABLE &&
 
875
                loop[1]->objType == DO_ATTRDEF &&
 
876
                ((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
 
877
        {
 
878
                repairTableAttrDefLoop(loop[0], loop[1]);
 
879
                return;
 
880
        }
 
881
        if (nLoop == 2 &&
 
882
                loop[1]->objType == DO_TABLE &&
 
883
                loop[0]->objType == DO_ATTRDEF &&
 
884
                ((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
 
885
        {
 
886
                repairTableAttrDefLoop(loop[1], loop[0]);
 
887
                return;
 
888
        }
 
889
 
 
890
        /* Indirect loop involving table and attribute default */
 
891
        if (nLoop > 2)
 
892
        {
 
893
                for (i = 0; i < nLoop; i++)
 
894
                {
 
895
                        if (loop[i]->objType == DO_TABLE)
 
896
                        {
 
897
                                for (j = 0; j < nLoop; j++)
 
898
                                {
 
899
                                        if (loop[j]->objType == DO_ATTRDEF &&
 
900
                                                ((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
 
901
                                        {
 
902
                                                repairTableAttrDefMultiLoop(loop[i], loop[j]);
 
903
                                                return;
 
904
                                        }
 
905
                                }
 
906
                        }
 
907
                }
 
908
        }
 
909
 
 
910
        /* Domain and CHECK constraint */
 
911
        if (nLoop == 2 &&
 
912
                loop[0]->objType == DO_TYPE &&
 
913
                loop[1]->objType == DO_CONSTRAINT &&
 
914
                ((ConstraintInfo *) loop[1])->contype == 'c' &&
 
915
                ((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
 
916
        {
 
917
                repairDomainConstraintLoop(loop[0], loop[1]);
 
918
                return;
 
919
        }
 
920
        if (nLoop == 2 &&
 
921
                loop[1]->objType == DO_TYPE &&
 
922
                loop[0]->objType == DO_CONSTRAINT &&
 
923
                ((ConstraintInfo *) loop[0])->contype == 'c' &&
 
924
                ((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
 
925
        {
 
926
                repairDomainConstraintLoop(loop[1], loop[0]);
 
927
                return;
 
928
        }
 
929
 
 
930
        /* Indirect loop involving domain and CHECK constraint */
 
931
        if (nLoop > 2)
 
932
        {
 
933
                for (i = 0; i < nLoop; i++)
 
934
                {
 
935
                        if (loop[i]->objType == DO_TYPE)
 
936
                        {
 
937
                                for (j = 0; j < nLoop; j++)
 
938
                                {
 
939
                                        if (loop[j]->objType == DO_CONSTRAINT &&
 
940
                                                ((ConstraintInfo *) loop[j])->contype == 'c' &&
 
941
                                                ((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
 
942
                                        {
 
943
                                                repairDomainConstraintMultiLoop(loop[i], loop[j]);
 
944
                                                return;
 
945
                                        }
 
946
                                }
 
947
                        }
 
948
                }
 
949
        }
 
950
 
 
951
        /*
 
952
         * If we can't find a principled way to break the loop, complain and
 
953
         * break it in an arbitrary fashion.
 
954
         */
 
955
        write_msg(modulename, "WARNING: could not resolve dependency loop among these items:\n");
 
956
        for (i = 0; i < nLoop; i++)
 
957
        {
 
958
                char            buf[1024];
 
959
 
 
960
                describeDumpableObject(loop[i], buf, sizeof(buf));
 
961
                write_msg(modulename, "  %s\n", buf);
 
962
        }
 
963
        removeObjectDependency(loop[0], loop[1]->dumpId);
 
964
}
 
965
 
 
966
/*
 
967
 * Describe a dumpable object usefully for errors
 
968
 *
 
969
 * This should probably go somewhere else...
 
970
 */
 
971
static void
 
972
describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
 
973
{
 
974
        switch (obj->objType)
 
975
        {
 
976
                case DO_NAMESPACE:
 
977
                        snprintf(buf, bufsize,
 
978
                                         "SCHEMA %s  (ID %d OID %u)",
 
979
                                         obj->name, obj->dumpId, obj->catId.oid);
 
980
                        return;
 
981
                case DO_TYPE:
 
982
                        snprintf(buf, bufsize,
 
983
                                         "TYPE %s  (ID %d OID %u)",
 
984
                                         obj->name, obj->dumpId, obj->catId.oid);
 
985
                        return;
 
986
                case DO_FUNC:
 
987
                        snprintf(buf, bufsize,
 
988
                                         "FUNCTION %s  (ID %d OID %u)",
 
989
                                         obj->name, obj->dumpId, obj->catId.oid);
 
990
                        return;
 
991
                case DO_AGG:
 
992
                        snprintf(buf, bufsize,
 
993
                                         "AGGREGATE %s  (ID %d OID %u)",
 
994
                                         obj->name, obj->dumpId, obj->catId.oid);
 
995
                        return;
 
996
                case DO_OPERATOR:
 
997
                        snprintf(buf, bufsize,
 
998
                                         "OPERATOR %s  (ID %d OID %u)",
 
999
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1000
                        return;
 
1001
                case DO_OPCLASS:
 
1002
                        snprintf(buf, bufsize,
 
1003
                                         "OPERATOR CLASS %s  (ID %d OID %u)",
 
1004
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1005
                        return;
 
1006
                case DO_CONVERSION:
 
1007
                        snprintf(buf, bufsize,
 
1008
                                         "CONVERSION %s  (ID %d OID %u)",
 
1009
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1010
                        return;
 
1011
                case DO_TABLE:
 
1012
                        snprintf(buf, bufsize,
 
1013
                                         "TABLE %s  (ID %d OID %u)",
 
1014
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1015
                        return;
 
1016
                case DO_ATTRDEF:
 
1017
                        snprintf(buf, bufsize,
 
1018
                                         "ATTRDEF %s.%s  (ID %d OID %u)",
 
1019
                                         ((AttrDefInfo *) obj)->adtable->dobj.name,
 
1020
                                         ((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
 
1021
                                         obj->dumpId, obj->catId.oid);
 
1022
                        return;
 
1023
                case DO_INDEX:
 
1024
                        snprintf(buf, bufsize,
 
1025
                                         "INDEX %s  (ID %d OID %u)",
 
1026
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1027
                        return;
 
1028
                case DO_RULE:
 
1029
                        snprintf(buf, bufsize,
 
1030
                                         "RULE %s  (ID %d OID %u)",
 
1031
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1032
                        return;
 
1033
                case DO_TRIGGER:
 
1034
                        snprintf(buf, bufsize,
 
1035
                                         "TRIGGER %s  (ID %d OID %u)",
 
1036
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1037
                        return;
 
1038
                case DO_CONSTRAINT:
 
1039
                        snprintf(buf, bufsize,
 
1040
                                         "CONSTRAINT %s  (ID %d OID %u)",
 
1041
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1042
                        return;
 
1043
                case DO_FK_CONSTRAINT:
 
1044
                        snprintf(buf, bufsize,
 
1045
                                         "FK CONSTRAINT %s  (ID %d OID %u)",
 
1046
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1047
                        return;
 
1048
                case DO_PROCLANG:
 
1049
                        snprintf(buf, bufsize,
 
1050
                                         "PROCEDURAL LANGUAGE %s  (ID %d OID %u)",
 
1051
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1052
                        return;
 
1053
                case DO_CAST:
 
1054
                        snprintf(buf, bufsize,
 
1055
                                         "CAST %u to %u  (ID %d OID %u)",
 
1056
                                         ((CastInfo *) obj)->castsource,
 
1057
                                         ((CastInfo *) obj)->casttarget,
 
1058
                                         obj->dumpId, obj->catId.oid);
 
1059
                        return;
 
1060
                case DO_TABLE_DATA:
 
1061
                        snprintf(buf, bufsize,
 
1062
                                         "TABLE DATA %s  (ID %d OID %u)",
 
1063
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1064
                        return;
 
1065
                case DO_TABLE_TYPE:
 
1066
                        snprintf(buf, bufsize,
 
1067
                                         "TABLE TYPE %s  (ID %d OID %u)",
 
1068
                                         obj->name, obj->dumpId, obj->catId.oid);
 
1069
                        return;
 
1070
                case DO_BLOBS:
 
1071
                        snprintf(buf, bufsize,
 
1072
                                         "BLOBS  (ID %d)",
 
1073
                                         obj->dumpId);
 
1074
                        return;
 
1075
        }
 
1076
        /* shouldn't get here */
 
1077
        snprintf(buf, bufsize,
 
1078
                         "object type %d  (ID %d OID %u)",
 
1079
                         (int) obj->objType,
 
1080
                         obj->dumpId, obj->catId.oid);
 
1081
}