mirror of https://github.com/postgres/postgres
ignoring errors and call-site error checking.pull/1/head
parent
35e27226b6
commit
3c0afde11a
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* common.h |
||||
* Common header file for the pg_dump, pg_dumpall, and pg_restore |
||||
* |
||||
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* src/bin/pg_dump/common.h |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#ifndef COMMON_H |
||||
#define COMMON_H |
||||
|
||||
#include "postgres_fe.h" |
||||
|
||||
extern char *pg_strdup(const char *string); |
||||
extern void *pg_malloc(size_t size); |
||||
extern void *pg_calloc(size_t nmemb, size_t size); |
||||
extern void *pg_realloc(void *ptr, size_t size); |
||||
|
||||
#endif /* COMMON_H */ |
@ -0,0 +1,978 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* common.c |
||||
* catalog routines used by pg_dump |
||||
* |
||||
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* src/bin/pg_dump/dumpcatalog.c |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres_fe.h" |
||||
|
||||
#include <ctype.h> |
||||
|
||||
#include "catalog/pg_class.h" |
||||
|
||||
#include "pg_backup_archiver.h" |
||||
#include "common.h" |
||||
|
||||
|
||||
/*
|
||||
* Variables for mapping DumpId to DumpableObject |
||||
*/ |
||||
static DumpableObject **dumpIdMap = NULL; |
||||
static int allocedDumpIds = 0; |
||||
static DumpId lastDumpId = 0; |
||||
|
||||
/*
|
||||
* Variables for mapping CatalogId to DumpableObject |
||||
*/ |
||||
static bool catalogIdMapValid = false; |
||||
static DumpableObject **catalogIdMap = NULL; |
||||
static int numCatalogIds = 0; |
||||
|
||||
/*
|
||||
* These variables are static to avoid the notational cruft of having to pass |
||||
* them into findTableByOid() and friends. For each of these arrays, we |
||||
* build a sorted-by-OID index array immediately after it's built, and then |
||||
* we use binary search in findTableByOid() and friends. (qsort'ing the base |
||||
* arrays themselves would be simpler, but it doesn't work because pg_dump.c |
||||
* may have already established pointers between items.) |
||||
*/ |
||||
static TableInfo *tblinfo; |
||||
static TypeInfo *typinfo; |
||||
static FuncInfo *funinfo; |
||||
static OprInfo *oprinfo; |
||||
static int numTables; |
||||
static int numTypes; |
||||
static int numFuncs; |
||||
static int numOperators; |
||||
static int numCollations; |
||||
static DumpableObject **tblinfoindex; |
||||
static DumpableObject **typinfoindex; |
||||
static DumpableObject **funinfoindex; |
||||
static DumpableObject **oprinfoindex; |
||||
static DumpableObject **collinfoindex; |
||||
|
||||
|
||||
static void flagInhTables(TableInfo *tbinfo, int numTables, |
||||
InhInfo *inhinfo, int numInherits); |
||||
static void flagInhAttrs(TableInfo *tblinfo, int numTables); |
||||
static DumpableObject **buildIndexArray(void *objArray, int numObjs, |
||||
Size objSize); |
||||
static int DOCatalogIdCompare(const void *p1, const void *p2); |
||||
static void findParentsByOid(TableInfo *self, |
||||
InhInfo *inhinfo, int numInherits); |
||||
static int strInArray(const char *pattern, char **arr, int arr_size); |
||||
|
||||
|
||||
/*
|
||||
* getSchemaData |
||||
* Collect information about all potentially dumpable objects |
||||
*/ |
||||
TableInfo * |
||||
getSchemaData(int *numTablesPtr) |
||||
{ |
||||
ExtensionInfo *extinfo; |
||||
InhInfo *inhinfo; |
||||
CollInfo *collinfo; |
||||
int numNamespaces; |
||||
int numExtensions; |
||||
int numAggregates; |
||||
int numInherits; |
||||
int numRules; |
||||
int numProcLangs; |
||||
int numCasts; |
||||
int numOpclasses; |
||||
int numOpfamilies; |
||||
int numConversions; |
||||
int numTSParsers; |
||||
int numTSTemplates; |
||||
int numTSDicts; |
||||
int numTSConfigs; |
||||
int numForeignDataWrappers; |
||||
int numForeignServers; |
||||
int numDefaultACLs; |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading schemas\n"); |
||||
getNamespaces(&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. |
||||
*/ |
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined tables\n"); |
||||
tblinfo = getTables(&numTables); |
||||
tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo)); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading extensions\n"); |
||||
extinfo = getExtensions(&numExtensions); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined functions\n"); |
||||
funinfo = getFuncs(&numFuncs); |
||||
funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo)); |
||||
|
||||
/* this must be after getTables and getFuncs */ |
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined types\n"); |
||||
typinfo = getTypes(&numTypes); |
||||
typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo)); |
||||
|
||||
/* this must be after getFuncs, too */ |
||||
if (g_verbose) |
||||
write_msg(NULL, "reading procedural languages\n"); |
||||
getProcLangs(&numProcLangs); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined aggregate functions\n"); |
||||
getAggregates(&numAggregates); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined operators\n"); |
||||
oprinfo = getOperators(&numOperators); |
||||
oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo)); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined operator classes\n"); |
||||
getOpclasses(&numOpclasses); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined operator families\n"); |
||||
getOpfamilies(&numOpfamilies); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined text search parsers\n"); |
||||
getTSParsers(&numTSParsers); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined text search templates\n"); |
||||
getTSTemplates(&numTSTemplates); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined text search dictionaries\n"); |
||||
getTSDictionaries(&numTSDicts); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined text search configurations\n"); |
||||
getTSConfigurations(&numTSConfigs); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined foreign-data wrappers\n"); |
||||
getForeignDataWrappers(&numForeignDataWrappers); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined foreign servers\n"); |
||||
getForeignServers(&numForeignServers); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading default privileges\n"); |
||||
getDefaultACLs(&numDefaultACLs); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined collations\n"); |
||||
collinfo = getCollations(&numCollations); |
||||
collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo)); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading user-defined conversions\n"); |
||||
getConversions(&numConversions); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading type casts\n"); |
||||
getCasts(&numCasts); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading table inheritance information\n"); |
||||
inhinfo = getInherits(&numInherits); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading rewrite rules\n"); |
||||
getRules(&numRules); |
||||
|
||||
/*
|
||||
* Identify extension member objects and mark them as not to be dumped. |
||||
* This must happen after reading all objects that can be direct members |
||||
* of extensions, but before we begin to process table subsidiary objects. |
||||
*/ |
||||
if (g_verbose) |
||||
write_msg(NULL, "finding extension members\n"); |
||||
getExtensionMembership(extinfo, numExtensions); |
||||
|
||||
/* Link tables to parents, mark parents of target tables interesting */ |
||||
if (g_verbose) |
||||
write_msg(NULL, "finding inheritance relationships\n"); |
||||
flagInhTables(tblinfo, numTables, inhinfo, numInherits); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading column info for interesting tables\n"); |
||||
getTableAttrs(tblinfo, numTables); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "flagging inherited columns in subtables\n"); |
||||
flagInhAttrs(tblinfo, numTables); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading indexes\n"); |
||||
getIndexes(tblinfo, numTables); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading constraints\n"); |
||||
getConstraints(tblinfo, numTables); |
||||
|
||||
if (g_verbose) |
||||
write_msg(NULL, "reading triggers\n"); |
||||
getTriggers(tblinfo, numTables); |
||||
|
||||
*numTablesPtr = numTables; |
||||
return tblinfo; |
||||
} |
||||
|
||||
/* flagInhTables -
|
||||
* Fill in parent link fields of every target table, and mark |
||||
* parents of target tables as interesting |
||||
* |
||||
* 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(TableInfo *tblinfo, int numTables, |
||||
InhInfo *inhinfo, int numInherits) |
||||
{ |
||||
int i, |
||||
j; |
||||
int numParents; |
||||
TableInfo **parents; |
||||
|
||||
for (i = 0; i < numTables; i++) |
||||
{ |
||||
/* Sequences and views never have parents */ |
||||
if (tblinfo[i].relkind == RELKIND_SEQUENCE || |
||||
tblinfo[i].relkind == RELKIND_VIEW) |
||||
continue; |
||||
|
||||
/* Don't bother computing anything for non-target tables, either */ |
||||
if (!tblinfo[i].dobj.dump) |
||||
continue; |
||||
|
||||
/* Find all the immediate parent tables */ |
||||
findParentsByOid(&tblinfo[i], inhinfo, numInherits); |
||||
|
||||
/* Mark the parents as interesting for getTableAttrs */ |
||||
numParents = tblinfo[i].numParents; |
||||
parents = tblinfo[i].parents; |
||||
for (j = 0; j < numParents; j++) |
||||
parents[j]->interesting = true; |
||||
} |
||||
} |
||||
|
||||
/* flagInhAttrs -
|
||||
* for each dumpable table in tblinfo, flag its inherited attributes |
||||
* so when we dump the table out, we don't dump out the inherited attributes |
||||
* |
||||
* modifies tblinfo |
||||
*/ |
||||
static void |
||||
flagInhAttrs(TableInfo *tblinfo, int numTables) |
||||
{ |
||||
int i, |
||||
j, |
||||
k; |
||||
|
||||
for (i = 0; i < numTables; i++) |
||||
{ |
||||
TableInfo *tbinfo = &(tblinfo[i]); |
||||
int numParents; |
||||
TableInfo **parents; |
||||
TableInfo *parent; |
||||
|
||||
/* Sequences and views never have parents */ |
||||
if (tbinfo->relkind == RELKIND_SEQUENCE || |
||||
tbinfo->relkind == RELKIND_VIEW) |
||||
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 attr, check the parent info: if no parent has an attr |
||||
* with the same name, then it's not inherited. If there *is* an |
||||
* attr with the same name, then only dump it if: |
||||
* |
||||
* - it is NOT NULL and zero parents are NOT NULL |
||||
* OR |
||||
* - it has a default value AND the default value does not match |
||||
* all parent default values, or no parents specify a default. |
||||
* |
||||
* See discussion on -hackers around 2-Apr-2001. |
||||
*---------------------------------------------------------------- |
||||
*/ |
||||
for (j = 0; j < tbinfo->numatts; j++) |
||||
{ |
||||
bool foundAttr; /* Attr was found in a parent */ |
||||
bool foundNotNull; /* Attr was NOT NULL in a parent */ |
||||
bool defaultsMatch; /* All non-empty defaults match */ |
||||
bool defaultsFound; /* Found a default in a parent */ |
||||
AttrDefInfo *attrDef; |
||||
|
||||
foundAttr = false; |
||||
foundNotNull = false; |
||||
defaultsMatch = true; |
||||
defaultsFound = false; |
||||
|
||||
attrDef = tbinfo->attrdefs[j]; |
||||
|
||||
for (k = 0; k < numParents; k++) |
||||
{ |
||||
int inhAttrInd; |
||||
|
||||
parent = parents[k]; |
||||
inhAttrInd = strInArray(tbinfo->attnames[j], |
||||
parent->attnames, |
||||
parent->numatts); |
||||
|
||||
if (inhAttrInd != -1) |
||||
{ |
||||
AttrDefInfo *inhDef = parent->attrdefs[inhAttrInd]; |
||||
|
||||
foundAttr = true; |
||||
foundNotNull |= parent->notnull[inhAttrInd]; |
||||
if (inhDef != NULL) |
||||
{ |
||||
defaultsFound = true; |
||||
|
||||
/*
|
||||
* If any parent has a default and the child doesn't, |
||||
* we have to emit an explicit DEFAULT NULL clause for |
||||
* the child, else the parent's default will win. |
||||
*/ |
||||
if (attrDef == NULL) |
||||
{ |
||||
attrDef = (AttrDefInfo *) pg_malloc(sizeof(AttrDefInfo)); |
||||
attrDef->dobj.objType = DO_ATTRDEF; |
||||
attrDef->dobj.catId.tableoid = 0; |
||||
attrDef->dobj.catId.oid = 0; |
||||
AssignDumpId(&attrDef->dobj); |
||||
attrDef->adtable = tbinfo; |
||||
attrDef->adnum = j + 1; |
||||
attrDef->adef_expr = pg_strdup("NULL"); |
||||
|
||||
attrDef->dobj.name = pg_strdup(tbinfo->dobj.name); |
||||
attrDef->dobj.namespace = tbinfo->dobj.namespace; |
||||
|
||||
attrDef->dobj.dump = tbinfo->dobj.dump; |
||||
|
||||
attrDef->separate = false; |
||||
addObjectDependency(&tbinfo->dobj, |
||||
attrDef->dobj.dumpId); |
||||
|
||||
tbinfo->attrdefs[j] = attrDef; |
||||
} |
||||
if (strcmp(attrDef->adef_expr, inhDef->adef_expr) != 0) |
||||
{ |
||||
defaultsMatch = false; |
||||
|
||||
/*
|
||||
* Whenever there is a non-matching parent |
||||
* default, add a dependency to force the parent |
||||
* default to be dumped first, in case the |
||||
* defaults end up being dumped as separate |
||||
* commands. Otherwise the parent default will |
||||
* override the child's when it is applied. |
||||
*/ |
||||
addObjectDependency(&attrDef->dobj, |
||||
inhDef->dobj.dumpId); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* Based on the scan of the parents, decide if we can rely on the |
||||
* inherited attr |
||||
*/ |
||||
if (foundAttr) /* Attr was inherited */ |
||||
{ |
||||
/* Set inherited flag by default */ |
||||
tbinfo->inhAttrs[j] = true; |
||||
tbinfo->inhAttrDef[j] = true; |
||||
tbinfo->inhNotNull[j] = true; |
||||
|
||||
/*
|
||||
* Clear it if attr had a default, but parents did not, or |
||||
* mismatch |
||||
*/ |
||||
if ((attrDef != NULL) && (!defaultsFound || !defaultsMatch)) |
||||
{ |
||||
tbinfo->inhAttrs[j] = false; |
||||
tbinfo->inhAttrDef[j] = false; |
||||
} |
||||
|
||||
/*
|
||||
* Clear it if NOT NULL and none of the parents were NOT NULL |
||||
*/ |
||||
if (tbinfo->notnull[j] && !foundNotNull) |
||||
{ |
||||
tbinfo->inhAttrs[j] = false; |
||||
tbinfo->inhNotNull[j] = false; |
||||
} |
||||
|
||||
/* Clear it if attr has local definition */ |
||||
if (tbinfo->attislocal[j]) |
||||
tbinfo->inhAttrs[j] = false; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* AssignDumpId |
||||
* Given a newly-created dumpable object, assign a dump ID, |
||||
* and enter the object into the lookup table. |
||||
* |
||||
* 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 = true; /* default assumption */ |
||||
dobj->ext_member = false; /* default assumption */ |
||||
dobj->dependencies = NULL; |
||||
dobj->nDeps = 0; |
||||
dobj->allocDeps = 0; |
||||
|
||||
while (dobj->dumpId >= allocedDumpIds) |
||||
{ |
||||
int newAlloc; |
||||
|
||||
if (allocedDumpIds <= 0) |
||||
{ |
||||
newAlloc = 256; |
||||
dumpIdMap = (DumpableObject **) |
||||
pg_malloc(newAlloc * sizeof(DumpableObject *)); |
||||
} |
||||
else |
||||
{ |
||||
newAlloc = allocedDumpIds * 2; |
||||
dumpIdMap = (DumpableObject **) |
||||
pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *)); |
||||
} |
||||
memset(dumpIdMap + allocedDumpIds, 0, |
||||
(newAlloc - allocedDumpIds) * sizeof(DumpableObject *)); |
||||
allocedDumpIds = newAlloc; |
||||
} |
||||
dumpIdMap[dobj->dumpId] = dobj; |
||||
|
||||
/* mark catalogIdMap invalid, but don't rebuild it yet */ |
||||
catalogIdMapValid = false; |
||||
} |
||||
|
||||
/*
|
||||
* 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 |
||||
* |
||||
* We use binary search in a sorted list that is built on first call. |
||||
* If AssignDumpId() and findObjectByCatalogId() calls were freely intermixed, |
||||
* the code would work, but possibly be very slow. In the current usage |
||||
* pattern that does not happen, indeed we build the list at most twice. |
||||
*/ |
||||
DumpableObject * |
||||
findObjectByCatalogId(CatalogId catalogId) |
||||
{ |
||||
DumpableObject **low; |
||||
DumpableObject **high; |
||||
|
||||
if (!catalogIdMapValid) |
||||
{ |
||||
if (catalogIdMap) |
||||
free(catalogIdMap); |
||||
getDumpableObjects(&catalogIdMap, &numCatalogIds); |
||||
if (numCatalogIds > 1) |
||||
qsort((void *) catalogIdMap, numCatalogIds, |
||||
sizeof(DumpableObject *), DOCatalogIdCompare); |
||||
catalogIdMapValid = true; |
||||
} |
||||
|
||||
/*
|
||||
* We could use bsearch() here, but the notational cruft of calling |
||||
* bsearch is nearly as bad as doing it ourselves; and the generalized |
||||
* bsearch function is noticeably slower as well. |
||||
*/ |
||||
if (numCatalogIds <= 0) |
||||
return NULL; |
||||
low = catalogIdMap; |
||||
high = catalogIdMap + (numCatalogIds - 1); |
||||
while (low <= high) |
||||
{ |
||||
DumpableObject **middle; |
||||
int difference; |
||||
|
||||
middle = low + (high - low) / 2; |
||||
/* comparison must match DOCatalogIdCompare, below */ |
||||
difference = oidcmp((*middle)->catId.oid, catalogId.oid); |
||||
if (difference == 0) |
||||
difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid); |
||||
if (difference == 0) |
||||
return *middle; |
||||
else if (difference < 0) |
||||
low = middle + 1; |
||||
else |
||||
high = middle - 1; |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
/*
|
||||
* Find a DumpableObject by OID, in a pre-sorted array of one type of object |
||||
* |
||||
* Returns NULL for unknown OID |
||||
*/ |
||||
static DumpableObject * |
||||
findObjectByOid(Oid oid, DumpableObject **indexArray, int numObjs) |
||||
{ |
||||
DumpableObject **low; |
||||
DumpableObject **high; |
||||
|
||||
/*
|
||||
* This is the same as findObjectByCatalogId except we assume we need not |
||||
* look at table OID because the objects are all the same type. |
||||
* |
||||
* We could use bsearch() here, but the notational cruft of calling |
||||
* bsearch is nearly as bad as doing it ourselves; and the generalized |
||||
* bsearch function is noticeably slower as well. |
||||
*/ |
||||
if (numObjs <= 0) |
||||
return NULL; |
||||
low = indexArray; |
||||
high = indexArray + (numObjs - 1); |
||||
while (low <= high) |
||||
{ |
||||
DumpableObject **middle; |
||||
int difference; |
||||
|
||||
middle = low + (high - low) / 2; |
||||
difference = oidcmp((*middle)->catId.oid, oid); |
||||
if (difference == 0) |
||||
return *middle; |
||||
else if (difference < 0) |
||||
low = middle + 1; |
||||
else |
||||
high = middle - 1; |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
/*
|
||||
* Build an index array of DumpableObject pointers, sorted by OID |
||||
*/ |
||||
static DumpableObject ** |
||||
buildIndexArray(void *objArray, int numObjs, Size objSize) |
||||
{ |
||||
DumpableObject **ptrs; |
||||
int i; |
||||
|
||||
ptrs = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *)); |
||||
for (i = 0; i < numObjs; i++) |
||||
ptrs[i] = (DumpableObject *) ((char *) objArray + i * objSize); |
||||
|
||||
/* We can use DOCatalogIdCompare to sort since its first key is OID */ |
||||
if (numObjs > 1) |
||||
qsort((void *) ptrs, numObjs, sizeof(DumpableObject *), |
||||
DOCatalogIdCompare); |
||||
|
||||
return ptrs; |
||||
} |
||||
|
||||
/*
|
||||
* qsort comparator for pointers to DumpableObjects |
||||
*/ |
||||
static int |
||||
DOCatalogIdCompare(const void *p1, const void *p2) |
||||
{ |
||||
const DumpableObject *obj1 = *(DumpableObject * const *) p1; |
||||
const DumpableObject *obj2 = *(DumpableObject * const *) p2; |
||||
int cmpval; |
||||
|
||||
/*
|
||||
* Compare OID first since it's usually unique, whereas there will only be |
||||
* a few distinct values of tableoid. |
||||
*/ |
||||
cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid); |
||||
if (cmpval == 0) |
||||
cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid); |
||||
return cmpval; |
||||
} |
||||
|
||||
/*
|
||||
* 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 = (DumpableObject **) |
||||
pg_malloc(allocedDumpIds * sizeof(DumpableObject *)); |
||||
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 = (DumpId *) |
||||
pg_malloc(dobj->allocDeps * sizeof(DumpId)); |
||||
} |
||||
else |
||||
{ |
||||
dobj->allocDeps *= 2; |
||||
dobj->dependencies = (DumpId *) |
||||
pg_realloc(dobj->dependencies, |
||||
dobj->allocDeps * sizeof(DumpId)); |
||||
} |
||||
} |
||||
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 entry (in tblinfo) of the table with the given oid |
||||
* returns NULL if not found |
||||
*/ |
||||
TableInfo * |
||||
findTableByOid(Oid oid) |
||||
{ |
||||
return (TableInfo *) findObjectByOid(oid, tblinfoindex, numTables); |
||||
} |
||||
|
||||
/*
|
||||
* findTypeByOid |
||||
* finds the entry (in typinfo) of the type with the given oid |
||||
* returns NULL if not found |
||||
*/ |
||||
TypeInfo * |
||||
findTypeByOid(Oid oid) |
||||
{ |
||||
return (TypeInfo *) findObjectByOid(oid, typinfoindex, numTypes); |
||||
} |
||||
|
||||
/*
|
||||
* findFuncByOid |
||||
* finds the entry (in funinfo) of the function with the given oid |
||||
* returns NULL if not found |
||||
*/ |
||||
FuncInfo * |
||||
findFuncByOid(Oid oid) |
||||
{ |
||||
return (FuncInfo *) findObjectByOid(oid, funinfoindex, numFuncs); |
||||
} |
||||
|
||||
/*
|
||||
* findOprByOid |
||||
* finds the entry (in oprinfo) of the operator with the given oid |
||||
* returns NULL if not found |
||||
*/ |
||||
OprInfo * |
||||
findOprByOid(Oid oid) |
||||
{ |
||||
return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators); |
||||
} |
||||
|
||||
/*
|
||||
* findCollationByOid |
||||
* finds the entry (in collinfo) of the collation with the given oid |
||||
* returns NULL if not found |
||||
*/ |
||||
CollInfo * |
||||
findCollationByOid(Oid oid) |
||||
{ |
||||
return (CollInfo *) findObjectByOid(oid, collinfoindex, numCollations); |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* findParentsByOid |
||||
* find a table's parents in tblinfo[] |
||||
*/ |
||||
static void |
||||
findParentsByOid(TableInfo *self, |
||||
InhInfo *inhinfo, int numInherits) |
||||
{ |
||||
Oid oid = self->dobj.catId.oid; |
||||
int i, |
||||
j; |
||||
int numParents; |
||||
|
||||
numParents = 0; |
||||
for (i = 0; i < numInherits; i++) |
||||
{ |
||||
if (inhinfo[i].inhrelid == oid) |
||||
numParents++; |
||||
} |
||||
|
||||
self->numParents = numParents; |
||||
|
||||
if (numParents > 0) |
||||
{ |
||||
self->parents = (TableInfo **) |
||||
pg_malloc(sizeof(TableInfo *) * numParents); |
||||
j = 0; |
||||
for (i = 0; i < numInherits; i++) |
||||
{ |
||||
if (inhinfo[i].inhrelid == oid) |
||||
{ |
||||
TableInfo *parent; |
||||
|
||||
parent = findTableByOid(inhinfo[i].inhparent); |
||||
if (parent == NULL) |
||||
{ |
||||
write_msg(NULL, "failed sanity check, parent OID %u of table \"%s\" (OID %u) not found\n", |
||||
inhinfo[i].inhparent, |
||||
self->dobj.name, |
||||
oid); |
||||
exit_nicely(); |
||||
} |
||||
self->parents[j++] = parent; |
||||
} |
||||
} |
||||
} |
||||
else |
||||
self->parents = NULL; |
||||
} |
||||
|
||||
/*
|
||||
* 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) |
||||
{ |
||||
write_msg(NULL, "could not parse numeric array \"%s\": too many numbers\n", str); |
||||
exit_nicely(); |
||||
} |
||||
temp[j] = '\0'; |
||||
array[argNum++] = atooid(temp); |
||||
j = 0; |
||||
} |
||||
if (s == '\0') |
||||
break; |
||||
} |
||||
else |
||||
{ |
||||
if (!(isdigit((unsigned char) s) || s == '-') || |
||||
j >= sizeof(temp) - 1) |
||||
{ |
||||
write_msg(NULL, "could not parse numeric array \"%s\": invalid character in number\n", str); |
||||
exit_nicely(); |
||||
} |
||||
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; |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* Support for simple list operations |
||||
*/ |
||||
|
||||
void |
||||
simple_oid_list_append(SimpleOidList *list, Oid val) |
||||
{ |
||||
SimpleOidListCell *cell; |
||||
|
||||
cell = (SimpleOidListCell *) pg_malloc(sizeof(SimpleOidListCell)); |
||||
cell->next = NULL; |
||||
cell->val = val; |
||||
|
||||
if (list->tail) |
||||
list->tail->next = cell; |
||||
else |
||||
list->head = cell; |
||||
list->tail = cell; |
||||
} |
||||
|
||||
void |
||||
simple_string_list_append(SimpleStringList *list, const char *val) |
||||
{ |
||||
SimpleStringListCell *cell; |
||||
|
||||
/* this calculation correctly accounts for the null trailing byte */ |
||||
cell = (SimpleStringListCell *) |
||||
pg_malloc(sizeof(SimpleStringListCell) + strlen(val)); |
||||
cell->next = NULL; |
||||
strcpy(cell->val, val); |
||||
|
||||
if (list->tail) |
||||
list->tail->next = cell; |
||||
else |
||||
list->head = cell; |
||||
list->tail = cell; |
||||
} |
||||
|
||||
bool |
||||
simple_oid_list_member(SimpleOidList *list, Oid val) |
||||
{ |
||||
SimpleOidListCell *cell; |
||||
|
||||
for (cell = list->head; cell; cell = cell->next) |
||||
{ |
||||
if (cell->val == val) |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool |
||||
simple_string_list_member(SimpleStringList *list, const char *val) |
||||
{ |
||||
SimpleStringListCell *cell; |
||||
|
||||
for (cell = list->head; cell; cell = cell->next) |
||||
{ |
||||
if (strcmp(cell->val, val) == 0) |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue