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