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