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