1 /*------------------------------------------------------------------------- 2 * 3 * common.c 4 * Catalog routines used by pg_dump; long ago these were shared 5 * by another dump tool, but not anymore. 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/common.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 <ctype.h> 23 24 #include "catalog/pg_class_d.h" 25 #include "fe_utils/string_utils.h" 26 27 28 /* 29 * Variables for mapping DumpId to DumpableObject 30 */ 31 static DumpableObject **dumpIdMap = NULL; 32 static int allocedDumpIds = 0; 33 static DumpId lastDumpId = 0; /* Note: 0 is InvalidDumpId */ 34 35 /* 36 * Variables for mapping CatalogId to DumpableObject 37 */ 38 static bool catalogIdMapValid = false; 39 static DumpableObject **catalogIdMap = NULL; 40 static int numCatalogIds = 0; 41 42 /* 43 * These variables are static to avoid the notational cruft of having to pass 44 * them into findTableByOid() and friends. For each of these arrays, we build 45 * a sorted-by-OID index array immediately after the objects are fetched, 46 * and then we use binary search in findTableByOid() and friends. (qsort'ing 47 * the object arrays themselves would be simpler, but it doesn't work because 48 * pg_dump.c may have already established pointers between items.) 49 */ 50 static DumpableObject **tblinfoindex; 51 static DumpableObject **typinfoindex; 52 static DumpableObject **funinfoindex; 53 static DumpableObject **oprinfoindex; 54 static DumpableObject **collinfoindex; 55 static DumpableObject **nspinfoindex; 56 static DumpableObject **extinfoindex; 57 static DumpableObject **pubinfoindex; 58 static int numTables; 59 static int numTypes; 60 static int numFuncs; 61 static int numOperators; 62 static int numCollations; 63 static int numNamespaces; 64 static int numExtensions; 65 static int numPublications; 66 67 /* This is an array of object identities, not actual DumpableObjects */ 68 static ExtensionMemberId *extmembers; 69 static int numextmembers; 70 71 static void flagInhTables(Archive *fout, TableInfo *tbinfo, int numTables, 72 InhInfo *inhinfo, int numInherits); 73 static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, int numTables); 74 static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables); 75 static DumpableObject **buildIndexArray(void *objArray, int numObjs, 76 Size objSize); 77 static int DOCatalogIdCompare(const void *p1, const void *p2); 78 static int ExtensionMemberIdCompare(const void *p1, const void *p2); 79 static void findParentsByOid(TableInfo *self, 80 InhInfo *inhinfo, int numInherits); 81 static int strInArray(const char *pattern, char **arr, int arr_size); 82 static IndxInfo *findIndexByOid(Oid oid, DumpableObject **idxinfoindex, 83 int numIndexes); 84 85 86 /* 87 * getSchemaData 88 * Collect information about all potentially dumpable objects 89 */ 90 TableInfo * 91 getSchemaData(Archive *fout, int *numTablesPtr) 92 { 93 TableInfo *tblinfo; 94 TypeInfo *typinfo; 95 FuncInfo *funinfo; 96 OprInfo *oprinfo; 97 CollInfo *collinfo; 98 NamespaceInfo *nspinfo; 99 ExtensionInfo *extinfo; 100 PublicationInfo *pubinfo; 101 InhInfo *inhinfo; 102 int numAggregates; 103 int numInherits; 104 int numRules; 105 int numProcLangs; 106 int numCasts; 107 int numTransforms; 108 int numAccessMethods; 109 int numOpclasses; 110 int numOpfamilies; 111 int numConversions; 112 int numTSParsers; 113 int numTSTemplates; 114 int numTSDicts; 115 int numTSConfigs; 116 int numForeignDataWrappers; 117 int numForeignServers; 118 int numDefaultACLs; 119 int numEventTriggers; 120 121 /* 122 * We must read extensions and extension membership info first, because 123 * extension membership needs to be consultable during decisions about 124 * whether other objects are to be dumped. 125 */ 126 pg_log_info("reading extensions"); 127 extinfo = getExtensions(fout, &numExtensions); 128 extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo)); 129 130 pg_log_info("identifying extension members"); 131 getExtensionMembership(fout, extinfo, numExtensions); 132 133 pg_log_info("reading schemas"); 134 nspinfo = getNamespaces(fout, &numNamespaces); 135 nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo)); 136 137 /* 138 * getTables should be done as soon as possible, so as to minimize the 139 * window between starting our transaction and acquiring per-table locks. 140 * However, we have to do getNamespaces first because the tables get 141 * linked to their containing namespaces during getTables. 142 */ 143 pg_log_info("reading user-defined tables"); 144 tblinfo = getTables(fout, &numTables); 145 tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo)); 146 147 /* Do this after we've built tblinfoindex */ 148 getOwnedSeqs(fout, tblinfo, numTables); 149 150 pg_log_info("reading user-defined functions"); 151 funinfo = getFuncs(fout, &numFuncs); 152 funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo)); 153 154 /* this must be after getTables and getFuncs */ 155 pg_log_info("reading user-defined types"); 156 typinfo = getTypes(fout, &numTypes); 157 typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo)); 158 159 /* this must be after getFuncs, too */ 160 pg_log_info("reading procedural languages"); 161 getProcLangs(fout, &numProcLangs); 162 163 pg_log_info("reading user-defined aggregate functions"); 164 getAggregates(fout, &numAggregates); 165 166 pg_log_info("reading user-defined operators"); 167 oprinfo = getOperators(fout, &numOperators); 168 oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo)); 169 170 pg_log_info("reading user-defined access methods"); 171 getAccessMethods(fout, &numAccessMethods); 172 173 pg_log_info("reading user-defined operator classes"); 174 getOpclasses(fout, &numOpclasses); 175 176 pg_log_info("reading user-defined operator families"); 177 getOpfamilies(fout, &numOpfamilies); 178 179 pg_log_info("reading user-defined text search parsers"); 180 getTSParsers(fout, &numTSParsers); 181 182 pg_log_info("reading user-defined text search templates"); 183 getTSTemplates(fout, &numTSTemplates); 184 185 pg_log_info("reading user-defined text search dictionaries"); 186 getTSDictionaries(fout, &numTSDicts); 187 188 pg_log_info("reading user-defined text search configurations"); 189 getTSConfigurations(fout, &numTSConfigs); 190 191 pg_log_info("reading user-defined foreign-data wrappers"); 192 getForeignDataWrappers(fout, &numForeignDataWrappers); 193 194 pg_log_info("reading user-defined foreign servers"); 195 getForeignServers(fout, &numForeignServers); 196 197 pg_log_info("reading default privileges"); 198 getDefaultACLs(fout, &numDefaultACLs); 199 200 pg_log_info("reading user-defined collations"); 201 collinfo = getCollations(fout, &numCollations); 202 collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo)); 203 204 pg_log_info("reading user-defined conversions"); 205 getConversions(fout, &numConversions); 206 207 pg_log_info("reading type casts"); 208 getCasts(fout, &numCasts); 209 210 pg_log_info("reading transforms"); 211 getTransforms(fout, &numTransforms); 212 213 pg_log_info("reading table inheritance information"); 214 inhinfo = getInherits(fout, &numInherits); 215 216 pg_log_info("reading event triggers"); 217 getEventTriggers(fout, &numEventTriggers); 218 219 /* Identify extension configuration tables that should be dumped */ 220 pg_log_info("finding extension tables"); 221 processExtensionTables(fout, extinfo, numExtensions); 222 223 /* Link tables to parents, mark parents of target tables interesting */ 224 pg_log_info("finding inheritance relationships"); 225 flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits); 226 227 pg_log_info("reading column info for interesting tables"); 228 getTableAttrs(fout, tblinfo, numTables); 229 230 pg_log_info("flagging inherited columns in subtables"); 231 flagInhAttrs(fout->dopt, tblinfo, numTables); 232 233 pg_log_info("reading indexes"); 234 getIndexes(fout, tblinfo, numTables); 235 236 pg_log_info("flagging indexes in partitioned tables"); 237 flagInhIndexes(fout, tblinfo, numTables); 238 239 pg_log_info("reading extended statistics"); 240 getExtendedStatistics(fout); 241 242 pg_log_info("reading constraints"); 243 getConstraints(fout, tblinfo, numTables); 244 245 pg_log_info("reading triggers"); 246 getTriggers(fout, tblinfo, numTables); 247 248 pg_log_info("reading rewrite rules"); 249 getRules(fout, &numRules); 250 251 pg_log_info("reading policies"); 252 getPolicies(fout, tblinfo, numTables); 253 254 pg_log_info("reading publications"); 255 pubinfo = getPublications(fout, &numPublications); 256 pubinfoindex = buildIndexArray(pubinfo, numPublications, 257 sizeof(PublicationInfo)); 258 259 pg_log_info("reading publication membership"); 260 getPublicationTables(fout, tblinfo, numTables); 261 262 pg_log_info("reading subscriptions"); 263 getSubscriptions(fout); 264 265 *numTablesPtr = numTables; 266 return tblinfo; 267 } 268 269 /* flagInhTables - 270 * Fill in parent link fields of tables for which we need that information, 271 * and mark parents of target tables as interesting 272 * 273 * Note that only direct ancestors of targets are marked interesting. 274 * This is sufficient; we don't much care whether they inherited their 275 * attributes or not. 276 * 277 * modifies tblinfo 278 */ 279 static void 280 flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables, 281 InhInfo *inhinfo, int numInherits) 282 { 283 DumpOptions *dopt = fout->dopt; 284 int i, 285 j; 286 287 for (i = 0; i < numTables; i++) 288 { 289 bool find_parents = true; 290 bool mark_parents = true; 291 292 /* Some kinds never have parents */ 293 if (tblinfo[i].relkind == RELKIND_SEQUENCE || 294 tblinfo[i].relkind == RELKIND_VIEW || 295 tblinfo[i].relkind == RELKIND_MATVIEW) 296 continue; 297 298 /* 299 * Normally, we don't bother computing anything for non-target tables, 300 * but if load-via-partition-root is specified, we gather information 301 * on every partition in the system so that getRootTableInfo can trace 302 * from any given to leaf partition all the way up to the root. (We 303 * don't need to mark them as interesting for getTableAttrs, though.) 304 */ 305 if (!tblinfo[i].dobj.dump) 306 { 307 mark_parents = false; 308 309 if (!dopt->load_via_partition_root || 310 !tblinfo[i].ispartition) 311 find_parents = false; 312 } 313 314 /* If needed, find all the immediate parent tables. */ 315 if (find_parents) 316 findParentsByOid(&tblinfo[i], inhinfo, numInherits); 317 318 /* 319 * If needed, mark the parents as interesting for getTableAttrs and 320 * getIndexes. 321 */ 322 if (mark_parents) 323 { 324 int numParents = tblinfo[i].numParents; 325 TableInfo **parents = tblinfo[i].parents; 326 327 for (j = 0; j < numParents; j++) 328 parents[j]->interesting = true; 329 } 330 } 331 } 332 333 /* 334 * flagInhIndexes - 335 * Create IndexAttachInfo objects for partitioned indexes, and add 336 * appropriate dependency links. 337 */ 338 static void 339 flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables) 340 { 341 int i, 342 j, 343 k; 344 DumpableObject ***parentIndexArray; 345 346 parentIndexArray = (DumpableObject ***) 347 pg_malloc0(getMaxDumpId() * sizeof(DumpableObject **)); 348 349 for (i = 0; i < numTables; i++) 350 { 351 TableInfo *parenttbl; 352 IndexAttachInfo *attachinfo; 353 354 if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0) 355 continue; 356 357 Assert(tblinfo[i].numParents == 1); 358 parenttbl = tblinfo[i].parents[0]; 359 360 /* 361 * We need access to each parent table's index list, but there is no 362 * index to cover them outside of this function. To avoid having to 363 * sort every parent table's indexes each time we come across each of 364 * its partitions, create an indexed array for each parent the first 365 * time it is required. 366 */ 367 if (parentIndexArray[parenttbl->dobj.dumpId] == NULL) 368 parentIndexArray[parenttbl->dobj.dumpId] = 369 buildIndexArray(parenttbl->indexes, 370 parenttbl->numIndexes, 371 sizeof(IndxInfo)); 372 373 attachinfo = (IndexAttachInfo *) 374 pg_malloc0(tblinfo[i].numIndexes * sizeof(IndexAttachInfo)); 375 for (j = 0, k = 0; j < tblinfo[i].numIndexes; j++) 376 { 377 IndxInfo *index = &(tblinfo[i].indexes[j]); 378 IndxInfo *parentidx; 379 380 if (index->parentidx == 0) 381 continue; 382 383 parentidx = findIndexByOid(index->parentidx, 384 parentIndexArray[parenttbl->dobj.dumpId], 385 parenttbl->numIndexes); 386 if (parentidx == NULL) 387 continue; 388 389 attachinfo[k].dobj.objType = DO_INDEX_ATTACH; 390 attachinfo[k].dobj.catId.tableoid = 0; 391 attachinfo[k].dobj.catId.oid = 0; 392 AssignDumpId(&attachinfo[k].dobj); 393 attachinfo[k].dobj.name = pg_strdup(index->dobj.name); 394 attachinfo[k].dobj.namespace = index->indextable->dobj.namespace; 395 attachinfo[k].parentIdx = parentidx; 396 attachinfo[k].partitionIdx = index; 397 398 /* 399 * We must state the DO_INDEX_ATTACH object's dependencies 400 * explicitly, since it will not match anything in pg_depend. 401 * 402 * Give it dependencies on both the partition index and the parent 403 * index, so that it will not be executed till both of those 404 * exist. (There's no need to care what order those are created 405 * in.) 406 * 407 * In addition, give it dependencies on the indexes' underlying 408 * tables. This does nothing of great value so far as serial 409 * restore ordering goes, but it ensures that a parallel restore 410 * will not try to run the ATTACH concurrently with other 411 * operations on those tables. 412 */ 413 addObjectDependency(&attachinfo[k].dobj, index->dobj.dumpId); 414 addObjectDependency(&attachinfo[k].dobj, parentidx->dobj.dumpId); 415 addObjectDependency(&attachinfo[k].dobj, 416 index->indextable->dobj.dumpId); 417 addObjectDependency(&attachinfo[k].dobj, 418 parentidx->indextable->dobj.dumpId); 419 420 /* keep track of the list of partitions in the parent index */ 421 simple_ptr_list_append(&parentidx->partattaches, &attachinfo[k].dobj); 422 423 k++; 424 } 425 } 426 427 for (i = 0; i < numTables; i++) 428 if (parentIndexArray[i]) 429 pg_free(parentIndexArray[i]); 430 pg_free(parentIndexArray); 431 } 432 433 /* flagInhAttrs - 434 * for each dumpable table in tblinfo, flag its inherited attributes 435 * 436 * What we need to do here is: 437 * 438 * - Detect child columns that inherit NOT NULL bits from their parents, so 439 * that we needn't specify that again for the child. 440 * 441 * - Detect child columns that have DEFAULT NULL when their parents had some 442 * non-null default. In this case, we make up a dummy AttrDefInfo object so 443 * that we'll correctly emit the necessary DEFAULT NULL clause; otherwise 444 * the backend will apply an inherited default to the column. 445 * 446 * - Detect child columns that have a generation expression when their parents 447 * also have one. Generation expressions are always inherited, so there is 448 * no need to set them again in child tables, and there is no syntax for it 449 * either. Exceptions: If it's a partition or we are in binary upgrade 450 * mode, we dump them because in those cases inherited tables are recreated 451 * standalone first and then reattached to the parent. (See also the logic 452 * in dumpTableSchema().) In that situation, the generation expressions 453 * must match the parent, enforced by ALTER TABLE. 454 * 455 * modifies tblinfo 456 */ 457 static void 458 flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables) 459 { 460 int i, 461 j, 462 k; 463 464 for (i = 0; i < numTables; i++) 465 { 466 TableInfo *tbinfo = &(tblinfo[i]); 467 int numParents; 468 TableInfo **parents; 469 470 /* Some kinds never have parents */ 471 if (tbinfo->relkind == RELKIND_SEQUENCE || 472 tbinfo->relkind == RELKIND_VIEW || 473 tbinfo->relkind == RELKIND_MATVIEW) 474 continue; 475 476 /* Don't bother computing anything for non-target tables, either */ 477 if (!tbinfo->dobj.dump) 478 continue; 479 480 numParents = tbinfo->numParents; 481 parents = tbinfo->parents; 482 483 if (numParents == 0) 484 continue; /* nothing to see here, move along */ 485 486 /* For each column, search for matching column names in parent(s) */ 487 for (j = 0; j < tbinfo->numatts; j++) 488 { 489 bool foundNotNull; /* Attr was NOT NULL in a parent */ 490 bool foundDefault; /* Found a default in a parent */ 491 bool foundGenerated; /* Found a generated in a parent */ 492 493 /* no point in examining dropped columns */ 494 if (tbinfo->attisdropped[j]) 495 continue; 496 497 foundNotNull = false; 498 foundDefault = false; 499 foundGenerated = false; 500 for (k = 0; k < numParents; k++) 501 { 502 TableInfo *parent = parents[k]; 503 int inhAttrInd; 504 505 inhAttrInd = strInArray(tbinfo->attnames[j], 506 parent->attnames, 507 parent->numatts); 508 if (inhAttrInd >= 0) 509 { 510 foundNotNull |= parent->notnull[inhAttrInd]; 511 foundDefault |= (parent->attrdefs[inhAttrInd] != NULL && !parent->attgenerated[inhAttrInd]); 512 foundGenerated |= parent->attgenerated[inhAttrInd]; 513 } 514 } 515 516 /* Remember if we found inherited NOT NULL */ 517 tbinfo->inhNotNull[j] = foundNotNull; 518 519 /* Manufacture a DEFAULT NULL clause if necessary */ 520 if (foundDefault && tbinfo->attrdefs[j] == NULL) 521 { 522 AttrDefInfo *attrDef; 523 524 attrDef = (AttrDefInfo *) pg_malloc(sizeof(AttrDefInfo)); 525 attrDef->dobj.objType = DO_ATTRDEF; 526 attrDef->dobj.catId.tableoid = 0; 527 attrDef->dobj.catId.oid = 0; 528 AssignDumpId(&attrDef->dobj); 529 attrDef->dobj.name = pg_strdup(tbinfo->dobj.name); 530 attrDef->dobj.namespace = tbinfo->dobj.namespace; 531 attrDef->dobj.dump = tbinfo->dobj.dump; 532 533 attrDef->adtable = tbinfo; 534 attrDef->adnum = j + 1; 535 attrDef->adef_expr = pg_strdup("NULL"); 536 537 /* Will column be dumped explicitly? */ 538 if (shouldPrintColumn(dopt, tbinfo, j)) 539 { 540 attrDef->separate = false; 541 /* No dependency needed: NULL cannot have dependencies */ 542 } 543 else 544 { 545 /* column will be suppressed, print default separately */ 546 attrDef->separate = true; 547 /* ensure it comes out after the table */ 548 addObjectDependency(&attrDef->dobj, 549 tbinfo->dobj.dumpId); 550 } 551 552 tbinfo->attrdefs[j] = attrDef; 553 } 554 555 /* Remove generation expression from child */ 556 if (foundGenerated && !tbinfo->ispartition && !dopt->binary_upgrade) 557 tbinfo->attrdefs[j] = NULL; 558 } 559 } 560 } 561 562 /* 563 * AssignDumpId 564 * Given a newly-created dumpable object, assign a dump ID, 565 * and enter the object into the lookup table. 566 * 567 * The caller is expected to have filled in objType and catId, 568 * but not any of the other standard fields of a DumpableObject. 569 */ 570 void 571 AssignDumpId(DumpableObject *dobj) 572 { 573 dobj->dumpId = ++lastDumpId; 574 dobj->name = NULL; /* must be set later */ 575 dobj->namespace = NULL; /* may be set later */ 576 dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */ 577 dobj->ext_member = false; /* default assumption */ 578 dobj->depends_on_ext = false; /* default assumption */ 579 dobj->dependencies = NULL; 580 dobj->nDeps = 0; 581 dobj->allocDeps = 0; 582 583 while (dobj->dumpId >= allocedDumpIds) 584 { 585 int newAlloc; 586 587 if (allocedDumpIds <= 0) 588 { 589 newAlloc = 256; 590 dumpIdMap = (DumpableObject **) 591 pg_malloc(newAlloc * sizeof(DumpableObject *)); 592 } 593 else 594 { 595 newAlloc = allocedDumpIds * 2; 596 dumpIdMap = (DumpableObject **) 597 pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *)); 598 } 599 memset(dumpIdMap + allocedDumpIds, 0, 600 (newAlloc - allocedDumpIds) * sizeof(DumpableObject *)); 601 allocedDumpIds = newAlloc; 602 } 603 dumpIdMap[dobj->dumpId] = dobj; 604 605 /* mark catalogIdMap invalid, but don't rebuild it yet */ 606 catalogIdMapValid = false; 607 } 608 609 /* 610 * Assign a DumpId that's not tied to a DumpableObject. 611 * 612 * This is used when creating a "fixed" ArchiveEntry that doesn't need to 613 * participate in the sorting logic. 614 */ 615 DumpId 616 createDumpId(void) 617 { 618 return ++lastDumpId; 619 } 620 621 /* 622 * Return the largest DumpId so far assigned 623 */ 624 DumpId 625 getMaxDumpId(void) 626 { 627 return lastDumpId; 628 } 629 630 /* 631 * Find a DumpableObject by dump ID 632 * 633 * Returns NULL for invalid ID 634 */ 635 DumpableObject * 636 findObjectByDumpId(DumpId dumpId) 637 { 638 if (dumpId <= 0 || dumpId >= allocedDumpIds) 639 return NULL; /* out of range? */ 640 return dumpIdMap[dumpId]; 641 } 642 643 /* 644 * Find a DumpableObject by catalog ID 645 * 646 * Returns NULL for unknown ID 647 * 648 * We use binary search in a sorted list that is built on first call. 649 * If AssignDumpId() and findObjectByCatalogId() calls were freely intermixed, 650 * the code would work, but possibly be very slow. In the current usage 651 * pattern that does not happen, indeed we build the list at most twice. 652 */ 653 DumpableObject * 654 findObjectByCatalogId(CatalogId catalogId) 655 { 656 DumpableObject **low; 657 DumpableObject **high; 658 659 if (!catalogIdMapValid) 660 { 661 if (catalogIdMap) 662 free(catalogIdMap); 663 getDumpableObjects(&catalogIdMap, &numCatalogIds); 664 if (numCatalogIds > 1) 665 qsort((void *) catalogIdMap, numCatalogIds, 666 sizeof(DumpableObject *), DOCatalogIdCompare); 667 catalogIdMapValid = true; 668 } 669 670 /* 671 * We could use bsearch() here, but the notational cruft of calling 672 * bsearch is nearly as bad as doing it ourselves; and the generalized 673 * bsearch function is noticeably slower as well. 674 */ 675 if (numCatalogIds <= 0) 676 return NULL; 677 low = catalogIdMap; 678 high = catalogIdMap + (numCatalogIds - 1); 679 while (low <= high) 680 { 681 DumpableObject **middle; 682 int difference; 683 684 middle = low + (high - low) / 2; 685 /* comparison must match DOCatalogIdCompare, below */ 686 difference = oidcmp((*middle)->catId.oid, catalogId.oid); 687 if (difference == 0) 688 difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid); 689 if (difference == 0) 690 return *middle; 691 else if (difference < 0) 692 low = middle + 1; 693 else 694 high = middle - 1; 695 } 696 return NULL; 697 } 698 699 /* 700 * Find a DumpableObject by OID, in a pre-sorted array of one type of object 701 * 702 * Returns NULL for unknown OID 703 */ 704 static DumpableObject * 705 findObjectByOid(Oid oid, DumpableObject **indexArray, int numObjs) 706 { 707 DumpableObject **low; 708 DumpableObject **high; 709 710 /* 711 * This is the same as findObjectByCatalogId except we assume we need not 712 * look at table OID because the objects are all the same type. 713 * 714 * We could use bsearch() here, but the notational cruft of calling 715 * bsearch is nearly as bad as doing it ourselves; and the generalized 716 * bsearch function is noticeably slower as well. 717 */ 718 if (numObjs <= 0) 719 return NULL; 720 low = indexArray; 721 high = indexArray + (numObjs - 1); 722 while (low <= high) 723 { 724 DumpableObject **middle; 725 int difference; 726 727 middle = low + (high - low) / 2; 728 difference = oidcmp((*middle)->catId.oid, oid); 729 if (difference == 0) 730 return *middle; 731 else if (difference < 0) 732 low = middle + 1; 733 else 734 high = middle - 1; 735 } 736 return NULL; 737 } 738 739 /* 740 * Build an index array of DumpableObject pointers, sorted by OID 741 */ 742 static DumpableObject ** 743 buildIndexArray(void *objArray, int numObjs, Size objSize) 744 { 745 DumpableObject **ptrs; 746 int i; 747 748 ptrs = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *)); 749 for (i = 0; i < numObjs; i++) 750 ptrs[i] = (DumpableObject *) ((char *) objArray + i * objSize); 751 752 /* We can use DOCatalogIdCompare to sort since its first key is OID */ 753 if (numObjs > 1) 754 qsort((void *) ptrs, numObjs, sizeof(DumpableObject *), 755 DOCatalogIdCompare); 756 757 return ptrs; 758 } 759 760 /* 761 * qsort comparator for pointers to DumpableObjects 762 */ 763 static int 764 DOCatalogIdCompare(const void *p1, const void *p2) 765 { 766 const DumpableObject *obj1 = *(DumpableObject *const *) p1; 767 const DumpableObject *obj2 = *(DumpableObject *const *) p2; 768 int cmpval; 769 770 /* 771 * Compare OID first since it's usually unique, whereas there will only be 772 * a few distinct values of tableoid. 773 */ 774 cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid); 775 if (cmpval == 0) 776 cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid); 777 return cmpval; 778 } 779 780 /* 781 * Build an array of pointers to all known dumpable objects 782 * 783 * This simply creates a modifiable copy of the internal map. 784 */ 785 void 786 getDumpableObjects(DumpableObject ***objs, int *numObjs) 787 { 788 int i, 789 j; 790 791 *objs = (DumpableObject **) 792 pg_malloc(allocedDumpIds * sizeof(DumpableObject *)); 793 j = 0; 794 for (i = 1; i < allocedDumpIds; i++) 795 { 796 if (dumpIdMap[i]) 797 (*objs)[j++] = dumpIdMap[i]; 798 } 799 *numObjs = j; 800 } 801 802 /* 803 * Add a dependency link to a DumpableObject 804 * 805 * Note: duplicate dependencies are currently not eliminated 806 */ 807 void 808 addObjectDependency(DumpableObject *dobj, DumpId refId) 809 { 810 if (dobj->nDeps >= dobj->allocDeps) 811 { 812 if (dobj->allocDeps <= 0) 813 { 814 dobj->allocDeps = 16; 815 dobj->dependencies = (DumpId *) 816 pg_malloc(dobj->allocDeps * sizeof(DumpId)); 817 } 818 else 819 { 820 dobj->allocDeps *= 2; 821 dobj->dependencies = (DumpId *) 822 pg_realloc(dobj->dependencies, 823 dobj->allocDeps * sizeof(DumpId)); 824 } 825 } 826 dobj->dependencies[dobj->nDeps++] = refId; 827 } 828 829 /* 830 * Remove a dependency link from a DumpableObject 831 * 832 * If there are multiple links, all are removed 833 */ 834 void 835 removeObjectDependency(DumpableObject *dobj, DumpId refId) 836 { 837 int i; 838 int j = 0; 839 840 for (i = 0; i < dobj->nDeps; i++) 841 { 842 if (dobj->dependencies[i] != refId) 843 dobj->dependencies[j++] = dobj->dependencies[i]; 844 } 845 dobj->nDeps = j; 846 } 847 848 849 /* 850 * findTableByOid 851 * finds the entry (in tblinfo) of the table with the given oid 852 * returns NULL if not found 853 */ 854 TableInfo * 855 findTableByOid(Oid oid) 856 { 857 return (TableInfo *) findObjectByOid(oid, tblinfoindex, numTables); 858 } 859 860 /* 861 * findTypeByOid 862 * finds the entry (in typinfo) of the type with the given oid 863 * returns NULL if not found 864 */ 865 TypeInfo * 866 findTypeByOid(Oid oid) 867 { 868 return (TypeInfo *) findObjectByOid(oid, typinfoindex, numTypes); 869 } 870 871 /* 872 * findFuncByOid 873 * finds the entry (in funinfo) of the function with the given oid 874 * returns NULL if not found 875 */ 876 FuncInfo * 877 findFuncByOid(Oid oid) 878 { 879 return (FuncInfo *) findObjectByOid(oid, funinfoindex, numFuncs); 880 } 881 882 /* 883 * findOprByOid 884 * finds the entry (in oprinfo) of the operator with the given oid 885 * returns NULL if not found 886 */ 887 OprInfo * 888 findOprByOid(Oid oid) 889 { 890 return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators); 891 } 892 893 /* 894 * findCollationByOid 895 * finds the entry (in collinfo) of the collation with the given oid 896 * returns NULL if not found 897 */ 898 CollInfo * 899 findCollationByOid(Oid oid) 900 { 901 return (CollInfo *) findObjectByOid(oid, collinfoindex, numCollations); 902 } 903 904 /* 905 * findNamespaceByOid 906 * finds the entry (in nspinfo) of the namespace with the given oid 907 * returns NULL if not found 908 */ 909 NamespaceInfo * 910 findNamespaceByOid(Oid oid) 911 { 912 return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces); 913 } 914 915 /* 916 * findExtensionByOid 917 * finds the entry (in extinfo) of the extension with the given oid 918 * returns NULL if not found 919 */ 920 ExtensionInfo * 921 findExtensionByOid(Oid oid) 922 { 923 return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions); 924 } 925 926 /* 927 * findPublicationByOid 928 * finds the entry (in pubinfo) of the publication with the given oid 929 * returns NULL if not found 930 */ 931 PublicationInfo * 932 findPublicationByOid(Oid oid) 933 { 934 return (PublicationInfo *) findObjectByOid(oid, pubinfoindex, numPublications); 935 } 936 937 /* 938 * findIndexByOid 939 * find the entry of the index with the given oid 940 * 941 * This one's signature is different from the previous ones because we lack a 942 * global array of all indexes, so caller must pass their array as argument. 943 */ 944 static IndxInfo * 945 findIndexByOid(Oid oid, DumpableObject **idxinfoindex, int numIndexes) 946 { 947 return (IndxInfo *) findObjectByOid(oid, idxinfoindex, numIndexes); 948 } 949 950 /* 951 * setExtensionMembership 952 * accept and save data about which objects belong to extensions 953 */ 954 void 955 setExtensionMembership(ExtensionMemberId *extmems, int nextmems) 956 { 957 /* Sort array in preparation for binary searches */ 958 if (nextmems > 1) 959 qsort((void *) extmems, nextmems, sizeof(ExtensionMemberId), 960 ExtensionMemberIdCompare); 961 /* And save */ 962 extmembers = extmems; 963 numextmembers = nextmems; 964 } 965 966 /* 967 * findOwningExtension 968 * return owning extension for specified catalog ID, or NULL if none 969 */ 970 ExtensionInfo * 971 findOwningExtension(CatalogId catalogId) 972 { 973 ExtensionMemberId *low; 974 ExtensionMemberId *high; 975 976 /* 977 * We could use bsearch() here, but the notational cruft of calling 978 * bsearch is nearly as bad as doing it ourselves; and the generalized 979 * bsearch function is noticeably slower as well. 980 */ 981 if (numextmembers <= 0) 982 return NULL; 983 low = extmembers; 984 high = extmembers + (numextmembers - 1); 985 while (low <= high) 986 { 987 ExtensionMemberId *middle; 988 int difference; 989 990 middle = low + (high - low) / 2; 991 /* comparison must match ExtensionMemberIdCompare, below */ 992 difference = oidcmp(middle->catId.oid, catalogId.oid); 993 if (difference == 0) 994 difference = oidcmp(middle->catId.tableoid, catalogId.tableoid); 995 if (difference == 0) 996 return middle->ext; 997 else if (difference < 0) 998 low = middle + 1; 999 else 1000 high = middle - 1; 1001 } 1002 return NULL; 1003 } 1004 1005 /* 1006 * qsort comparator for ExtensionMemberIds 1007 */ 1008 static int 1009 ExtensionMemberIdCompare(const void *p1, const void *p2) 1010 { 1011 const ExtensionMemberId *obj1 = (const ExtensionMemberId *) p1; 1012 const ExtensionMemberId *obj2 = (const ExtensionMemberId *) p2; 1013 int cmpval; 1014 1015 /* 1016 * Compare OID first since it's usually unique, whereas there will only be 1017 * a few distinct values of tableoid. 1018 */ 1019 cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid); 1020 if (cmpval == 0) 1021 cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid); 1022 return cmpval; 1023 } 1024 1025 1026 /* 1027 * findParentsByOid 1028 * find a table's parents in tblinfo[] 1029 */ 1030 static void 1031 findParentsByOid(TableInfo *self, 1032 InhInfo *inhinfo, int numInherits) 1033 { 1034 Oid oid = self->dobj.catId.oid; 1035 int i, 1036 j; 1037 int numParents; 1038 1039 numParents = 0; 1040 for (i = 0; i < numInherits; i++) 1041 { 1042 if (inhinfo[i].inhrelid == oid) 1043 numParents++; 1044 } 1045 1046 self->numParents = numParents; 1047 1048 if (numParents > 0) 1049 { 1050 self->parents = (TableInfo **) 1051 pg_malloc(sizeof(TableInfo *) * numParents); 1052 j = 0; 1053 for (i = 0; i < numInherits; i++) 1054 { 1055 if (inhinfo[i].inhrelid == oid) 1056 { 1057 TableInfo *parent; 1058 1059 parent = findTableByOid(inhinfo[i].inhparent); 1060 if (parent == NULL) 1061 { 1062 pg_log_error("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found", 1063 inhinfo[i].inhparent, 1064 self->dobj.name, 1065 oid); 1066 exit_nicely(1); 1067 } 1068 self->parents[j++] = parent; 1069 } 1070 } 1071 } 1072 else 1073 self->parents = NULL; 1074 } 1075 1076 /* 1077 * parseOidArray 1078 * parse a string of numbers delimited by spaces into a character array 1079 * 1080 * Note: actually this is used for both Oids and potentially-signed 1081 * attribute numbers. This should cause no trouble, but we could split 1082 * the function into two functions with different argument types if it does. 1083 */ 1084 1085 void 1086 parseOidArray(const char *str, Oid *array, int arraysize) 1087 { 1088 int j, 1089 argNum; 1090 char temp[100]; 1091 char s; 1092 1093 argNum = 0; 1094 j = 0; 1095 for (;;) 1096 { 1097 s = *str++; 1098 if (s == ' ' || s == '\0') 1099 { 1100 if (j > 0) 1101 { 1102 if (argNum >= arraysize) 1103 { 1104 pg_log_error("could not parse numeric array \"%s\": too many numbers", str); 1105 exit_nicely(1); 1106 } 1107 temp[j] = '\0'; 1108 array[argNum++] = atooid(temp); 1109 j = 0; 1110 } 1111 if (s == '\0') 1112 break; 1113 } 1114 else 1115 { 1116 if (!(isdigit((unsigned char) s) || s == '-') || 1117 j >= sizeof(temp) - 1) 1118 { 1119 pg_log_error("could not parse numeric array \"%s\": invalid character in number", str); 1120 exit_nicely(1); 1121 } 1122 temp[j++] = s; 1123 } 1124 } 1125 1126 while (argNum < arraysize) 1127 array[argNum++] = InvalidOid; 1128 } 1129 1130 1131 /* 1132 * strInArray: 1133 * takes in a string and a string array and the number of elements in the 1134 * string array. 1135 * returns the index if the string is somewhere in the array, -1 otherwise 1136 */ 1137 1138 static int 1139 strInArray(const char *pattern, char **arr, int arr_size) 1140 { 1141 int i; 1142 1143 for (i = 0; i < arr_size; i++) 1144 { 1145 if (strcmp(pattern, arr[i]) == 0) 1146 return i; 1147 } 1148 return -1; 1149 } 1150