YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

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