diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
commit | 293913568e6a7a86fd1479e1cff8e2ecb58d6568 (patch) | |
tree | fc3b469a3ec5ab71b36ea97cc7aaddb838423a0c /src/bin/pg_dump/common.c | |
parent | Initial commit. (diff) | |
download | postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.tar.xz postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.zip |
Adding upstream version 16.2.upstream/16.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/bin/pg_dump/common.c')
-rw-r--r-- | src/bin/pg_dump/common.c | 1089 |
1 files changed, 1089 insertions, 0 deletions
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c new file mode 100644 index 0000000..5d98898 --- /dev/null +++ b/src/bin/pg_dump/common.c @@ -0,0 +1,1089 @@ +/*------------------------------------------------------------------------- + * + * common.c + * Catalog routines used by pg_dump; long ago these were shared + * by another dump tool, but not anymore. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/bin/pg_dump/common.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include <ctype.h> + +#include "catalog/pg_class_d.h" +#include "catalog/pg_collation_d.h" +#include "catalog/pg_extension_d.h" +#include "catalog/pg_namespace_d.h" +#include "catalog/pg_operator_d.h" +#include "catalog/pg_proc_d.h" +#include "catalog/pg_publication_d.h" +#include "catalog/pg_type_d.h" +#include "common/hashfn.h" +#include "fe_utils/string_utils.h" +#include "pg_backup_archiver.h" +#include "pg_backup_utils.h" +#include "pg_dump.h" + +/* + * Variables for mapping DumpId to DumpableObject + */ +static DumpableObject **dumpIdMap = NULL; +static int allocedDumpIds = 0; +static DumpId lastDumpId = 0; /* Note: 0 is InvalidDumpId */ + +/* + * Infrastructure for mapping CatalogId to DumpableObject + * + * We use a hash table generated by simplehash.h. That infrastructure + * requires all the hash table entries to be the same size, and it also + * expects that it can move them around when resizing the table. So we + * cannot make the DumpableObjects be elements of the hash table directly; + * instead, the hash table elements contain pointers to DumpableObjects. + * + * It turns out to be convenient to also use this data structure to map + * CatalogIds to owning extensions, if any. Since extension membership + * data is read before creating most DumpableObjects, either one of dobj + * and ext could be NULL. + */ +typedef struct _catalogIdMapEntry +{ + CatalogId catId; /* the indexed CatalogId */ + uint32 status; /* hash status */ + uint32 hashval; /* hash code for the CatalogId */ + DumpableObject *dobj; /* the associated DumpableObject, if any */ + ExtensionInfo *ext; /* owning extension, if any */ +} CatalogIdMapEntry; + +#define SH_PREFIX catalogid +#define SH_ELEMENT_TYPE CatalogIdMapEntry +#define SH_KEY_TYPE CatalogId +#define SH_KEY catId +#define SH_HASH_KEY(tb, key) hash_bytes((const unsigned char *) &(key), sizeof(CatalogId)) +#define SH_EQUAL(tb, a, b) ((a).oid == (b).oid && (a).tableoid == (b).tableoid) +#define SH_STORE_HASH +#define SH_GET_HASH(tb, a) (a)->hashval +#define SH_SCOPE static inline +#define SH_RAW_ALLOCATOR pg_malloc0 +#define SH_DECLARE +#define SH_DEFINE +#include "lib/simplehash.h" + +#define CATALOGIDHASH_INITIAL_SIZE 10000 + +static catalogid_hash *catalogIdHash = NULL; + +static void flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables, + InhInfo *inhinfo, int numInherits); +static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, int numTables); +static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables); +static int strInArray(const char *pattern, char **arr, int arr_size); +static IndxInfo *findIndexByOid(Oid oid); + + +/* + * getSchemaData + * Collect information about all potentially dumpable objects + */ +TableInfo * +getSchemaData(Archive *fout, int *numTablesPtr) +{ + TableInfo *tblinfo; + ExtensionInfo *extinfo; + InhInfo *inhinfo; + int numTables; + int numTypes; + int numFuncs; + int numOperators; + int numCollations; + int numNamespaces; + int numExtensions; + int numPublications; + int numAggregates; + int numInherits; + int numRules; + int numProcLangs; + int numCasts; + int numTransforms; + int numAccessMethods; + int numOpclasses; + int numOpfamilies; + int numConversions; + int numTSParsers; + int numTSTemplates; + int numTSDicts; + int numTSConfigs; + int numForeignDataWrappers; + int numForeignServers; + int numDefaultACLs; + int numEventTriggers; + + /* + * We must read extensions and extension membership info first, because + * extension membership needs to be consultable during decisions about + * whether other objects are to be dumped. + */ + pg_log_info("reading extensions"); + extinfo = getExtensions(fout, &numExtensions); + + pg_log_info("identifying extension members"); + getExtensionMembership(fout, extinfo, numExtensions); + + pg_log_info("reading schemas"); + (void) getNamespaces(fout, &numNamespaces); + + /* + * getTables should be done as soon as possible, so as to minimize the + * window between starting our transaction and acquiring per-table locks. + * However, we have to do getNamespaces first because the tables get + * linked to their containing namespaces during getTables. + */ + pg_log_info("reading user-defined tables"); + tblinfo = getTables(fout, &numTables); + + getOwnedSeqs(fout, tblinfo, numTables); + + pg_log_info("reading user-defined functions"); + (void) getFuncs(fout, &numFuncs); + + /* this must be after getTables and getFuncs */ + pg_log_info("reading user-defined types"); + (void) getTypes(fout, &numTypes); + + /* this must be after getFuncs, too */ + pg_log_info("reading procedural languages"); + getProcLangs(fout, &numProcLangs); + + pg_log_info("reading user-defined aggregate functions"); + getAggregates(fout, &numAggregates); + + pg_log_info("reading user-defined operators"); + (void) getOperators(fout, &numOperators); + + pg_log_info("reading user-defined access methods"); + getAccessMethods(fout, &numAccessMethods); + + pg_log_info("reading user-defined operator classes"); + getOpclasses(fout, &numOpclasses); + + pg_log_info("reading user-defined operator families"); + getOpfamilies(fout, &numOpfamilies); + + pg_log_info("reading user-defined text search parsers"); + getTSParsers(fout, &numTSParsers); + + pg_log_info("reading user-defined text search templates"); + getTSTemplates(fout, &numTSTemplates); + + pg_log_info("reading user-defined text search dictionaries"); + getTSDictionaries(fout, &numTSDicts); + + pg_log_info("reading user-defined text search configurations"); + getTSConfigurations(fout, &numTSConfigs); + + pg_log_info("reading user-defined foreign-data wrappers"); + getForeignDataWrappers(fout, &numForeignDataWrappers); + + pg_log_info("reading user-defined foreign servers"); + getForeignServers(fout, &numForeignServers); + + pg_log_info("reading default privileges"); + getDefaultACLs(fout, &numDefaultACLs); + + pg_log_info("reading user-defined collations"); + (void) getCollations(fout, &numCollations); + + pg_log_info("reading user-defined conversions"); + getConversions(fout, &numConversions); + + pg_log_info("reading type casts"); + getCasts(fout, &numCasts); + + pg_log_info("reading transforms"); + getTransforms(fout, &numTransforms); + + pg_log_info("reading table inheritance information"); + inhinfo = getInherits(fout, &numInherits); + + pg_log_info("reading event triggers"); + getEventTriggers(fout, &numEventTriggers); + + /* Identify extension configuration tables that should be dumped */ + pg_log_info("finding extension tables"); + processExtensionTables(fout, extinfo, numExtensions); + + /* Link tables to parents, mark parents of target tables interesting */ + pg_log_info("finding inheritance relationships"); + flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits); + + pg_log_info("reading column info for interesting tables"); + getTableAttrs(fout, tblinfo, numTables); + + pg_log_info("flagging inherited columns in subtables"); + flagInhAttrs(fout->dopt, tblinfo, numTables); + + pg_log_info("reading partitioning data"); + getPartitioningInfo(fout); + + pg_log_info("reading indexes"); + getIndexes(fout, tblinfo, numTables); + + pg_log_info("flagging indexes in partitioned tables"); + flagInhIndexes(fout, tblinfo, numTables); + + pg_log_info("reading extended statistics"); + getExtendedStatistics(fout); + + pg_log_info("reading constraints"); + getConstraints(fout, tblinfo, numTables); + + pg_log_info("reading triggers"); + getTriggers(fout, tblinfo, numTables); + + pg_log_info("reading rewrite rules"); + getRules(fout, &numRules); + + pg_log_info("reading policies"); + getPolicies(fout, tblinfo, numTables); + + pg_log_info("reading publications"); + (void) getPublications(fout, &numPublications); + + pg_log_info("reading publication membership of tables"); + getPublicationTables(fout, tblinfo, numTables); + + pg_log_info("reading publication membership of schemas"); + getPublicationNamespaces(fout); + + pg_log_info("reading subscriptions"); + getSubscriptions(fout); + + free(inhinfo); /* not needed any longer */ + + *numTablesPtr = numTables; + return tblinfo; +} + +/* flagInhTables - + * Fill in parent link fields of tables for which we need that information, + * mark parents of target tables as interesting, and create + * TableAttachInfo objects for partitioned tables with appropriate + * dependency links. + * + * Note that only direct ancestors of targets are marked interesting. + * This is sufficient; we don't much care whether they inherited their + * attributes or not. + * + * modifies tblinfo + */ +static void +flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables, + InhInfo *inhinfo, int numInherits) +{ + TableInfo *child = NULL; + TableInfo *parent = NULL; + int i, + j; + + /* + * Set up links from child tables to their parents. + * + * We used to attempt to skip this work for tables that are not to be + * dumped; but the optimizable cases are rare in practice, and setting up + * these links in bulk is cheaper than the old way. (Note in particular + * that it's very rare for a child to have more than one parent.) + */ + for (i = 0; i < numInherits; i++) + { + /* + * Skip a hashtable lookup if it's same table as last time. This is + * unlikely for the child, but less so for the parent. (Maybe we + * should ask the backend for a sorted array to make it more likely? + * Not clear the sorting effort would be repaid, though.) + */ + if (child == NULL || + child->dobj.catId.oid != inhinfo[i].inhrelid) + { + child = findTableByOid(inhinfo[i].inhrelid); + + /* + * If we find no TableInfo, assume the pg_inherits entry is for a + * partitioned index, which we don't need to track. + */ + if (child == NULL) + continue; + } + if (parent == NULL || + parent->dobj.catId.oid != inhinfo[i].inhparent) + { + parent = findTableByOid(inhinfo[i].inhparent); + if (parent == NULL) + pg_fatal("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found", + inhinfo[i].inhparent, + child->dobj.name, + child->dobj.catId.oid); + } + /* Add this parent to the child's list of parents. */ + if (child->numParents > 0) + child->parents = pg_realloc_array(child->parents, + TableInfo *, + child->numParents + 1); + else + child->parents = pg_malloc_array(TableInfo *, 1); + child->parents[child->numParents++] = parent; + } + + /* + * Now consider all child tables and mark parents interesting as needed. + */ + for (i = 0; i < numTables; i++) + { + /* + * If needed, mark the parents as interesting for getTableAttrs and + * getIndexes. We only need this for direct parents of dumpable + * tables. + */ + if (tblinfo[i].dobj.dump) + { + int numParents = tblinfo[i].numParents; + TableInfo **parents = tblinfo[i].parents; + + for (j = 0; j < numParents; j++) + parents[j]->interesting = true; + } + + /* Create TableAttachInfo object if needed */ + if ((tblinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) && + tblinfo[i].ispartition) + { + TableAttachInfo *attachinfo; + + /* With partitions there can only be one parent */ + if (tblinfo[i].numParents != 1) + pg_fatal("invalid number of parents %d for table \"%s\"", + tblinfo[i].numParents, + tblinfo[i].dobj.name); + + attachinfo = (TableAttachInfo *) palloc(sizeof(TableAttachInfo)); + attachinfo->dobj.objType = DO_TABLE_ATTACH; + attachinfo->dobj.catId.tableoid = 0; + attachinfo->dobj.catId.oid = 0; + AssignDumpId(&attachinfo->dobj); + attachinfo->dobj.name = pg_strdup(tblinfo[i].dobj.name); + attachinfo->dobj.namespace = tblinfo[i].dobj.namespace; + attachinfo->parentTbl = tblinfo[i].parents[0]; + attachinfo->partitionTbl = &tblinfo[i]; + + /* + * We must state the DO_TABLE_ATTACH object's dependencies + * explicitly, since it will not match anything in pg_depend. + * + * Give it dependencies on both the partition table and the parent + * table, so that it will not be executed till both of those + * exist. (There's no need to care what order those are created + * in.) + */ + addObjectDependency(&attachinfo->dobj, tblinfo[i].dobj.dumpId); + addObjectDependency(&attachinfo->dobj, tblinfo[i].parents[0]->dobj.dumpId); + } + } +} + +/* + * flagInhIndexes - + * Create IndexAttachInfo objects for partitioned indexes, and add + * appropriate dependency links. + */ +static void +flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables) +{ + int i, + j; + + for (i = 0; i < numTables; i++) + { + if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0) + continue; + + Assert(tblinfo[i].numParents == 1); + + for (j = 0; j < tblinfo[i].numIndexes; j++) + { + IndxInfo *index = &(tblinfo[i].indexes[j]); + IndxInfo *parentidx; + IndexAttachInfo *attachinfo; + + if (index->parentidx == 0) + continue; + + parentidx = findIndexByOid(index->parentidx); + if (parentidx == NULL) + continue; + + attachinfo = pg_malloc_object(IndexAttachInfo); + + attachinfo->dobj.objType = DO_INDEX_ATTACH; + attachinfo->dobj.catId.tableoid = 0; + attachinfo->dobj.catId.oid = 0; + AssignDumpId(&attachinfo->dobj); + attachinfo->dobj.name = pg_strdup(index->dobj.name); + attachinfo->dobj.namespace = index->indextable->dobj.namespace; + attachinfo->parentIdx = parentidx; + attachinfo->partitionIdx = index; + + /* + * We must state the DO_INDEX_ATTACH object's dependencies + * explicitly, since it will not match anything in pg_depend. + * + * Give it dependencies on both the partition index and the parent + * index, so that it will not be executed till both of those + * exist. (There's no need to care what order those are created + * in.) + * + * In addition, give it dependencies on the indexes' underlying + * tables. This does nothing of great value so far as serial + * restore ordering goes, but it ensures that a parallel restore + * will not try to run the ATTACH concurrently with other + * operations on those tables. + */ + addObjectDependency(&attachinfo->dobj, index->dobj.dumpId); + addObjectDependency(&attachinfo->dobj, parentidx->dobj.dumpId); + addObjectDependency(&attachinfo->dobj, + index->indextable->dobj.dumpId); + addObjectDependency(&attachinfo->dobj, + parentidx->indextable->dobj.dumpId); + + /* keep track of the list of partitions in the parent index */ + simple_ptr_list_append(&parentidx->partattaches, &attachinfo->dobj); + } + } +} + +/* flagInhAttrs - + * for each dumpable table in tblinfo, flag its inherited attributes + * + * What we need to do here is: + * + * - Detect child columns that inherit NOT NULL bits from their parents, so + * that we needn't specify that again for the child. + * + * - Detect child columns that have DEFAULT NULL when their parents had some + * non-null default. In this case, we make up a dummy AttrDefInfo object so + * that we'll correctly emit the necessary DEFAULT NULL clause; otherwise + * the backend will apply an inherited default to the column. + * + * - Detect child columns that have a generation expression and all their + * parents also have the same generation expression, and if so suppress the + * child's expression. The child will inherit the generation expression + * automatically, so there's no need to dump it. This improves the dump's + * compatibility with pre-v16 servers, which didn't allow the child's + * expression to be given explicitly. Exceptions: If it's a partition or + * we are in binary upgrade mode, we dump such expressions anyway because + * in those cases inherited tables are recreated standalone first and then + * reattached to the parent. (See also the logic in dumpTableSchema().) + * + * modifies tblinfo + */ +static void +flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables) +{ + int i, + j, + k; + + /* + * We scan the tables in OID order, since that's how tblinfo[] is sorted. + * Hence we will typically visit parents before their children --- but + * that is *not* guaranteed. Thus this loop must be careful that it does + * not alter table properties in a way that could change decisions made at + * child tables during other iterations. + */ + for (i = 0; i < numTables; i++) + { + TableInfo *tbinfo = &(tblinfo[i]); + int numParents; + TableInfo **parents; + + /* Some kinds never have parents */ + if (tbinfo->relkind == RELKIND_SEQUENCE || + tbinfo->relkind == RELKIND_VIEW || + tbinfo->relkind == RELKIND_MATVIEW) + continue; + + /* Don't bother computing anything for non-target tables, either */ + if (!tbinfo->dobj.dump) + continue; + + numParents = tbinfo->numParents; + parents = tbinfo->parents; + + if (numParents == 0) + continue; /* nothing to see here, move along */ + + /* For each column, search for matching column names in parent(s) */ + for (j = 0; j < tbinfo->numatts; j++) + { + bool foundNotNull; /* Attr was NOT NULL in a parent */ + bool foundDefault; /* Found a default in a parent */ + bool foundSameGenerated; /* Found matching GENERATED */ + bool foundDiffGenerated; /* Found non-matching GENERATED */ + + /* no point in examining dropped columns */ + if (tbinfo->attisdropped[j]) + continue; + + foundNotNull = false; + foundDefault = false; + foundSameGenerated = false; + foundDiffGenerated = false; + for (k = 0; k < numParents; k++) + { + TableInfo *parent = parents[k]; + int inhAttrInd; + + inhAttrInd = strInArray(tbinfo->attnames[j], + parent->attnames, + parent->numatts); + if (inhAttrInd >= 0) + { + AttrDefInfo *parentDef = parent->attrdefs[inhAttrInd]; + + foundNotNull |= parent->notnull[inhAttrInd]; + foundDefault |= (parentDef != NULL && + strcmp(parentDef->adef_expr, "NULL") != 0 && + !parent->attgenerated[inhAttrInd]); + if (parent->attgenerated[inhAttrInd]) + { + /* these pointer nullness checks are just paranoia */ + if (parentDef != NULL && + tbinfo->attrdefs[j] != NULL && + strcmp(parentDef->adef_expr, + tbinfo->attrdefs[j]->adef_expr) == 0) + foundSameGenerated = true; + else + foundDiffGenerated = true; + } + } + } + + /* Remember if we found inherited NOT NULL */ + tbinfo->inhNotNull[j] = foundNotNull; + + /* + * Manufacture a DEFAULT NULL clause if necessary. This breaks + * the advice given above to avoid changing state that might get + * inspected in other loop iterations. We prevent trouble by + * having the foundDefault test above check whether adef_expr is + * "NULL", so that it will reach the same conclusion before or + * after this is done. + */ + if (foundDefault && tbinfo->attrdefs[j] == NULL) + { + AttrDefInfo *attrDef; + + attrDef = pg_malloc_object(AttrDefInfo); + attrDef->dobj.objType = DO_ATTRDEF; + attrDef->dobj.catId.tableoid = 0; + attrDef->dobj.catId.oid = 0; + AssignDumpId(&attrDef->dobj); + attrDef->dobj.name = pg_strdup(tbinfo->dobj.name); + attrDef->dobj.namespace = tbinfo->dobj.namespace; + attrDef->dobj.dump = tbinfo->dobj.dump; + + attrDef->adtable = tbinfo; + attrDef->adnum = j + 1; + attrDef->adef_expr = pg_strdup("NULL"); + + /* Will column be dumped explicitly? */ + if (shouldPrintColumn(dopt, tbinfo, j)) + { + attrDef->separate = false; + /* No dependency needed: NULL cannot have dependencies */ + } + else + { + /* column will be suppressed, print default separately */ + attrDef->separate = true; + /* ensure it comes out after the table */ + addObjectDependency(&attrDef->dobj, + tbinfo->dobj.dumpId); + } + + tbinfo->attrdefs[j] = attrDef; + } + + /* No need to dump generation expression if it's inheritable */ + if (foundSameGenerated && !foundDiffGenerated && + !tbinfo->ispartition && !dopt->binary_upgrade) + tbinfo->attrdefs[j]->dobj.dump = DUMP_COMPONENT_NONE; + } + } +} + +/* + * AssignDumpId + * Given a newly-created dumpable object, assign a dump ID, + * and enter the object into the lookup tables. + * + * The caller is expected to have filled in objType and catId, + * but not any of the other standard fields of a DumpableObject. + */ +void +AssignDumpId(DumpableObject *dobj) +{ + dobj->dumpId = ++lastDumpId; + dobj->name = NULL; /* must be set later */ + dobj->namespace = NULL; /* may be set later */ + dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */ + dobj->dump_contains = DUMP_COMPONENT_ALL; /* default assumption */ + /* All objects have definitions; we may set more components bits later */ + dobj->components = DUMP_COMPONENT_DEFINITION; + dobj->ext_member = false; /* default assumption */ + dobj->depends_on_ext = false; /* default assumption */ + dobj->dependencies = NULL; + dobj->nDeps = 0; + dobj->allocDeps = 0; + + /* Add object to dumpIdMap[], enlarging that array if need be */ + while (dobj->dumpId >= allocedDumpIds) + { + int newAlloc; + + if (allocedDumpIds <= 0) + { + newAlloc = 256; + dumpIdMap = pg_malloc_array(DumpableObject *, newAlloc); + } + else + { + newAlloc = allocedDumpIds * 2; + dumpIdMap = pg_realloc_array(dumpIdMap, DumpableObject *, newAlloc); + } + memset(dumpIdMap + allocedDumpIds, 0, + (newAlloc - allocedDumpIds) * sizeof(DumpableObject *)); + allocedDumpIds = newAlloc; + } + dumpIdMap[dobj->dumpId] = dobj; + + /* If it has a valid CatalogId, enter it into the hash table */ + if (OidIsValid(dobj->catId.tableoid)) + { + CatalogIdMapEntry *entry; + bool found; + + /* Initialize CatalogId hash table if not done yet */ + if (catalogIdHash == NULL) + catalogIdHash = catalogid_create(CATALOGIDHASH_INITIAL_SIZE, NULL); + + entry = catalogid_insert(catalogIdHash, dobj->catId, &found); + if (!found) + { + entry->dobj = NULL; + entry->ext = NULL; + } + Assert(entry->dobj == NULL); + entry->dobj = dobj; + } +} + +/* + * Assign a DumpId that's not tied to a DumpableObject. + * + * This is used when creating a "fixed" ArchiveEntry that doesn't need to + * participate in the sorting logic. + */ +DumpId +createDumpId(void) +{ + return ++lastDumpId; +} + +/* + * Return the largest DumpId so far assigned + */ +DumpId +getMaxDumpId(void) +{ + return lastDumpId; +} + +/* + * Find a DumpableObject by dump ID + * + * Returns NULL for invalid ID + */ +DumpableObject * +findObjectByDumpId(DumpId dumpId) +{ + if (dumpId <= 0 || dumpId >= allocedDumpIds) + return NULL; /* out of range? */ + return dumpIdMap[dumpId]; +} + +/* + * Find a DumpableObject by catalog ID + * + * Returns NULL for unknown ID + */ +DumpableObject * +findObjectByCatalogId(CatalogId catalogId) +{ + CatalogIdMapEntry *entry; + + if (catalogIdHash == NULL) + return NULL; /* no objects exist yet */ + + entry = catalogid_lookup(catalogIdHash, catalogId); + if (entry == NULL) + return NULL; + return entry->dobj; +} + +/* + * Build an array of pointers to all known dumpable objects + * + * This simply creates a modifiable copy of the internal map. + */ +void +getDumpableObjects(DumpableObject ***objs, int *numObjs) +{ + int i, + j; + + *objs = pg_malloc_array(DumpableObject *, allocedDumpIds); + j = 0; + for (i = 1; i < allocedDumpIds; i++) + { + if (dumpIdMap[i]) + (*objs)[j++] = dumpIdMap[i]; + } + *numObjs = j; +} + +/* + * Add a dependency link to a DumpableObject + * + * Note: duplicate dependencies are currently not eliminated + */ +void +addObjectDependency(DumpableObject *dobj, DumpId refId) +{ + if (dobj->nDeps >= dobj->allocDeps) + { + if (dobj->allocDeps <= 0) + { + dobj->allocDeps = 16; + dobj->dependencies = pg_malloc_array(DumpId, dobj->allocDeps); + } + else + { + dobj->allocDeps *= 2; + dobj->dependencies = pg_realloc_array(dobj->dependencies, + DumpId, dobj->allocDeps); + } + } + dobj->dependencies[dobj->nDeps++] = refId; +} + +/* + * Remove a dependency link from a DumpableObject + * + * If there are multiple links, all are removed + */ +void +removeObjectDependency(DumpableObject *dobj, DumpId refId) +{ + int i; + int j = 0; + + for (i = 0; i < dobj->nDeps; i++) + { + if (dobj->dependencies[i] != refId) + dobj->dependencies[j++] = dobj->dependencies[i]; + } + dobj->nDeps = j; +} + + +/* + * findTableByOid + * finds the DumpableObject for the table with the given oid + * returns NULL if not found + */ +TableInfo * +findTableByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = RelationRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || dobj->objType == DO_TABLE); + return (TableInfo *) dobj; +} + +/* + * findIndexByOid + * finds the DumpableObject for the index with the given oid + * returns NULL if not found + */ +static IndxInfo * +findIndexByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = RelationRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || dobj->objType == DO_INDEX); + return (IndxInfo *) dobj; +} + +/* + * findTypeByOid + * finds the DumpableObject for the type with the given oid + * returns NULL if not found + */ +TypeInfo * +findTypeByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = TypeRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || + dobj->objType == DO_TYPE || dobj->objType == DO_DUMMY_TYPE); + return (TypeInfo *) dobj; +} + +/* + * findFuncByOid + * finds the DumpableObject for the function with the given oid + * returns NULL if not found + */ +FuncInfo * +findFuncByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = ProcedureRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || dobj->objType == DO_FUNC); + return (FuncInfo *) dobj; +} + +/* + * findOprByOid + * finds the DumpableObject for the operator with the given oid + * returns NULL if not found + */ +OprInfo * +findOprByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = OperatorRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || dobj->objType == DO_OPERATOR); + return (OprInfo *) dobj; +} + +/* + * findCollationByOid + * finds the DumpableObject for the collation with the given oid + * returns NULL if not found + */ +CollInfo * +findCollationByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = CollationRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || dobj->objType == DO_COLLATION); + return (CollInfo *) dobj; +} + +/* + * findNamespaceByOid + * finds the DumpableObject for the namespace with the given oid + * returns NULL if not found + */ +NamespaceInfo * +findNamespaceByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = NamespaceRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || dobj->objType == DO_NAMESPACE); + return (NamespaceInfo *) dobj; +} + +/* + * findExtensionByOid + * finds the DumpableObject for the extension with the given oid + * returns NULL if not found + */ +ExtensionInfo * +findExtensionByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = ExtensionRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || dobj->objType == DO_EXTENSION); + return (ExtensionInfo *) dobj; +} + +/* + * findPublicationByOid + * finds the DumpableObject for the publication with the given oid + * returns NULL if not found + */ +PublicationInfo * +findPublicationByOid(Oid oid) +{ + CatalogId catId; + DumpableObject *dobj; + + catId.tableoid = PublicationRelationId; + catId.oid = oid; + dobj = findObjectByCatalogId(catId); + Assert(dobj == NULL || dobj->objType == DO_PUBLICATION); + return (PublicationInfo *) dobj; +} + + +/* + * recordExtensionMembership + * Record that the object identified by the given catalog ID + * belongs to the given extension + */ +void +recordExtensionMembership(CatalogId catId, ExtensionInfo *ext) +{ + CatalogIdMapEntry *entry; + bool found; + + /* CatalogId hash table must exist, if we have an ExtensionInfo */ + Assert(catalogIdHash != NULL); + + /* Add reference to CatalogId hash */ + entry = catalogid_insert(catalogIdHash, catId, &found); + if (!found) + { + entry->dobj = NULL; + entry->ext = NULL; + } + Assert(entry->ext == NULL); + entry->ext = ext; +} + +/* + * findOwningExtension + * return owning extension for specified catalog ID, or NULL if none + */ +ExtensionInfo * +findOwningExtension(CatalogId catalogId) +{ + CatalogIdMapEntry *entry; + + if (catalogIdHash == NULL) + return NULL; /* no objects exist yet */ + + entry = catalogid_lookup(catalogIdHash, catalogId); + if (entry == NULL) + return NULL; + return entry->ext; +} + + +/* + * parseOidArray + * parse a string of numbers delimited by spaces into a character array + * + * Note: actually this is used for both Oids and potentially-signed + * attribute numbers. This should cause no trouble, but we could split + * the function into two functions with different argument types if it does. + */ + +void +parseOidArray(const char *str, Oid *array, int arraysize) +{ + int j, + argNum; + char temp[100]; + char s; + + argNum = 0; + j = 0; + for (;;) + { + s = *str++; + if (s == ' ' || s == '\0') + { + if (j > 0) + { + if (argNum >= arraysize) + pg_fatal("could not parse numeric array \"%s\": too many numbers", str); + temp[j] = '\0'; + array[argNum++] = atooid(temp); + j = 0; + } + if (s == '\0') + break; + } + else + { + if (!(isdigit((unsigned char) s) || s == '-') || + j >= sizeof(temp) - 1) + pg_fatal("could not parse numeric array \"%s\": invalid character in number", str); + temp[j++] = s; + } + } + + while (argNum < arraysize) + array[argNum++] = InvalidOid; +} + + +/* + * strInArray: + * takes in a string and a string array and the number of elements in the + * string array. + * returns the index if the string is somewhere in the array, -1 otherwise + */ + +static int +strInArray(const char *pattern, char **arr, int arr_size) +{ + int i; + + for (i = 0; i < arr_size; i++) + { + if (strcmp(pattern, arr[i]) == 0) + return i; + } + return -1; +} |