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