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