blob: f6ae0c234b7d07ac104351603e7cedcafbfe8bb6 [file] [log] [blame]
/*-------------------------------------------------------------------------
*
* common.c
* common routines between pg_dump and pg4_dump
*
* Since pg4_dump is long-dead code, there is no longer any useful distinction
* between this file and pg_dump.c.
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.94 2006/10/09 23:36:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <ctype.h>
#include "dumputils.h"
#include "postgres.h"
#include "catalog/pg_class.h"
#include "pg_backup_archiver.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.
*/
static TableInfo *tblinfo;
static TypeInfo *typinfo;
static TypeStorageOptions *typestorageoptions;
static FuncInfo *funinfo;
static OprInfo *oprinfo;
static int numTables;
static int numTypes;
static int numTypeStorageOptions;
static int numFuncs;
static int numOperators;
static void flagInhTables(TableInfo *tbinfo, int numTables,
InhInfo *inhinfo, int numInherits);
static void flagInhAttrs(TableInfo *tbinfo, int numTables,
InhInfo *inhinfo, int numInherits);
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);
void reset(void);
/*
* getSchemaData
* Collect information about all potentially dumpable objects
*/
TableInfo *
getSchemaData(int *numTablesPtr)
{
NamespaceInfo *nsinfo;
AggInfo *agginfo;
InhInfo *inhinfo;
RuleInfo *ruleinfo;
ProcLangInfo *proclanginfo;
CastInfo *castinfo;
OpclassInfo *opcinfo;
ConvInfo *convinfo;
FdwInfo *fdwinfo;
ExtProtInfo *ptcinfo;
ForeignServerInfo *srvinfo;
int numNamespaces;
int numAggregates;
int numInherits;
int numRules;
int numProcLangs;
int numCasts;
int numOpclasses;
int numConversions;
int numForeignDataWrappers;
int numForeignServers;
int numExtProtocols;
if (g_verbose)
write_msg(NULL, "reading schemas\n");
nsinfo = getNamespaces(&numNamespaces);
if (g_verbose)
write_msg(NULL, "reading user-defined functions\n");
funinfo = getFuncs(&numFuncs);
/* this must be after getFuncs */
if (g_verbose)
write_msg(NULL, "reading user-defined types\n");
typinfo = getTypes(&numTypes);
/* this must be after getFuncs */
if (g_verbose)
write_msg(NULL, "reading type storage options\n");
typestorageoptions = getTypeStorageOptions(&numTypeStorageOptions);
/* this must be after getFuncs, too */
if (g_verbose)
write_msg(NULL, "reading procedural languages\n");
proclanginfo = getProcLangs(&numProcLangs);
if (g_verbose)
write_msg(NULL, "reading user-defined aggregate functions\n");
agginfo = getAggregates(&numAggregates);
if (g_verbose)
write_msg(NULL, "reading user-defined operators\n");
oprinfo = getOperators(&numOperators);
if (testExtProtocolSupport())
{
if (g_verbose)
write_msg(NULL, "reading user-defined external protocols\n");
ptcinfo = getExtProtocols(&numExtProtocols);
}
if (g_verbose)
write_msg(NULL, "reading user-defined operator classes\n");
opcinfo = getOpclasses(&numOpclasses);
if (testSqlMedSupport())
{
if (g_verbose)
write_msg(NULL, "reading user-defined foreign-data wrappers\n");
fdwinfo = getForeignDataWrappers(&numForeignDataWrappers);
if (g_verbose)
write_msg(NULL, "reading user-defined foreign servers\n");
srvinfo = getForeignServers(&numForeignServers);
}
else
{
fdwinfo = NULL;
srvinfo = NULL;
numForeignDataWrappers = 0;
numForeignServers = 0;
}
if (g_verbose)
write_msg(NULL, "reading user-defined conversions\n");
convinfo = getConversions(&numConversions);
if (g_verbose)
write_msg(NULL, "reading user-defined tables\n");
tblinfo = getTables(&numTables);
if (g_verbose)
write_msg(NULL, "reading table inheritance information\n");
inhinfo = getInherits(&numInherits);
if (g_verbose)
write_msg(NULL, "reading rewrite rules\n");
ruleinfo = getRules(&numRules);
if (g_verbose)
write_msg(NULL, "reading type casts\n");
castinfo = getCasts(&numCasts);
/* 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, inhinfo, numInherits);
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, views and external tables never have parents */
if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
tblinfo[i].relkind == RELKIND_VIEW ||
tblinfo[i].relstorage == RELSTORAGE_EXTERNAL ||
tblinfo[i].relstorage == RELSTORAGE_FOREIGN)
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,
InhInfo *inhinfo, int numInherits)
{
int i,
j,
k;
for (i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &(tblinfo[i]);
int numParents;
TableInfo **parents;
TableInfo *parent;
/* Sequences, views and external tables never have parents */
if (tbinfo->relkind == RELKIND_SEQUENCE ||
tbinfo->relkind == RELKIND_VIEW ||
tbinfo->relstorage == RELSTORAGE_EXTERNAL ||
tbinfo->relstorage == RELSTORAGE_FOREIGN)
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 *) 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 = strdup("NULL");
attrDef->dobj.name = 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;
}
}
/*
* Check for inherited CHECK constraints. We assume a constraint is
* inherited if its name matches the name of any constraint in the
* parent. Originally this code tried to compare the expression
* texts, but that can fail if the parent and child tables are in
* different schemas, because reverse-listing of function calls may
* produce different text (schema-qualified or not) depending on
* search path. We really need a more bulletproof way of detecting
* inherited constraints --- pg_constraint should record this
* explicitly!
*/
for (j = 0; j < tbinfo->ncheck; j++)
{
ConstraintInfo *constr;
constr = &(tbinfo->checkexprs[j]);
for (k = 0; k < numParents; k++)
{
int l;
parent = parents[k];
for (l = 0; l < parent->ncheck; l++)
{
ConstraintInfo *pconstr = &(parent->checkexprs[l]);
if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
{
constr->coninherited = true;
break;
}
}
if (constr->coninherited)
break;
}
}
}
}
/*
* MPP-1890
*
* If the user explicitly DROP'ed a CHECK constraint on a child but it
* still exists on the parent when they dump and restore that constraint
* will exist on the child since it will again inherit it from the
* parent. Therefore we look here for constraints that exist on the
* parent but not on the child and mark them to be dropped from the
* child after the child table is defined.
*
* Loop through each parent and for each parent constraint see if it
* exists on the child as well. If it doesn't it means that the child
* dropped it. Mark it.
*/
void
DetectChildConstraintDropped(TableInfo *tbinfo, PQExpBuffer q)
{
TableInfo *parent;
TableInfo **parents = tbinfo->parents;
int j,
k,
l;
int numParents = tbinfo->numParents;
for (k = 0; k < numParents; k++)
{
parent = parents[k];
/* for each CHECK constraint of this parent */
for (l = 0; l < parent->ncheck; l++)
{
ConstraintInfo *pconstr = &(parent->checkexprs[l]);
ConstraintInfo *cconstr;
bool constr_on_child = false;
/* for each child CHECK constraint */
for (j = 0; j < tbinfo->ncheck; j++)
{
cconstr = &(tbinfo->checkexprs[j]);
if (strcmp(pconstr->dobj.name, cconstr->dobj.name) == 0)
{
/* parent constr exists on child. hence wasn't dropped */
constr_on_child = true;
break;
}
}
/* this parent constr is not on child, issue a DROP for it */
if (!constr_on_child)
{
appendPQExpBuffer(q, "ALTER TABLE %s.",
fmtId(tbinfo->dobj.namespace->dobj.name));
appendPQExpBuffer(q, "%s ",
fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, "DROP CONSTRAINT %s;\n",
fmtId(pconstr->dobj.name));
constr_on_child = 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->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 intermixed,
* the code would work, but possibly be very slow. In the current usage
* pattern that does not happen, indeed we only need to build the list once.
*/
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;
}
static int
DOCatalogIdCompare(const void *p1, const void *p2)
{
DumpableObject *obj1 = *(DumpableObject **) p1;
DumpableObject *obj2 = *(DumpableObject **) 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
*
* NOTE: should hash this, but just do linear search for now
*/
TableInfo *
findTableByOid(Oid oid)
{
int i;
for (i = 0; i < numTables; i++)
{
if (tblinfo[i].dobj.catId.oid == oid)
return &tblinfo[i];
}
return NULL;
}
/*
* findTypeByOid
* finds the entry (in typinfo) of the type with the given oid
* returns NULL if not found
*
* NOTE: should hash this, but just do linear search for now
*/
TypeInfo *
findTypeByOid(Oid oid)
{
int i;
for (i = 0; i < numTypes; i++)
{
if (typinfo[i].dobj.catId.oid == oid)
return &typinfo[i];
}
return NULL;
}
/*
* findFuncByOid
* finds the entry (in funinfo) of the function with the given oid
* returns NULL if not found
*
* NOTE: should hash this, but just do linear search for now
*/
FuncInfo *
findFuncByOid(Oid oid)
{
int i;
for (i = 0; i < numFuncs; i++)
{
if (funinfo[i].dobj.catId.oid == oid)
return &funinfo[i];
}
return NULL;
}
/*
* findOprByOid
* finds the entry (in oprinfo) of the operator with the given oid
* returns NULL if not found
*
* NOTE: should hash this, but just do linear search for now
*/
OprInfo *
findOprByOid(Oid oid)
{
int i;
for (i = 0; i < numOperators; i++)
{
if (oprinfo[i].dobj.catId.oid == oid)
return &oprinfo[i];
}
return NULL;
}
/*
* 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;
}
/* cdb addition */
void
reset(void)
{
free(dumpIdMap);
dumpIdMap = NULL;
allocedDumpIds = 0;
lastDumpId = 0;
/*
* Variables for mapping CatalogId to DumpableObject
*/
catalogIdMapValid = false;
free(catalogIdMap);
catalogIdMap = NULL;
numCatalogIds = 0;
numTables = 0;
numTypes = 0;
numFuncs = 0;
numOperators = 0;
}
/* end cdb_addition */
/*
* 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;
}
/*
* openFileAndAppendToList: Read parameters from file
* and append values to given list.
* (Used to read multiple include/exclude tables.)
*
* reason - list name, to be logged.
*
* File format: one value per line.
*/
bool
open_file_and_append_to_list(const char *fileName, SimpleStringList *list, const char *reason)
{
char buf[1024];
write_msg(NULL, "Opening file %s for %s\n", fileName, reason);
FILE* file = fopen(fileName, "r");
if (file == NULL)
return false;
int lineNum = 0;
while (fgets(buf, sizeof(buf), file) != NULL)
{
int size = strlen(buf);
if (buf[size-1] == '\n')
buf[size-1] = '\0'; /* remove new line */
write_msg(NULL, "Line #%d, value: %s\n", ++lineNum, buf);
simple_string_list_append(list, buf);
}
write_msg(NULL, "Got %d lines from file %s\n", lineNum, fileName);
if (fclose(file) != 0)
return false;
write_msg(NULL, "Finished reading file %s successfully\n", fileName);
return true;
}
/*
* Safer versions of some standard C library functions. If an
* out-of-memory condition occurs, these functions will bail out
* safely; therefore, their return value is guaranteed to be non-NULL.
*
* XXX need to refactor things so that these can be in a file that can be
* shared by pg_dumpall and pg_restore as well as pg_dump.
*/
char *
pg_strdup(const char *string)
{
char *tmp;
if (!string)
exit_horribly(NULL, NULL, "cannot duplicate null pointer\n");
tmp = strdup(string);
if (!tmp)
exit_horribly(NULL, NULL, "out of memory\n");
return tmp;
}
void *
pg_malloc(size_t size)
{
void *tmp;
tmp = malloc(size);
if (!tmp)
exit_horribly(NULL, NULL, "out of memory\n");
return tmp;
}
void *
pg_calloc(size_t nmemb, size_t size)
{
void *tmp;
tmp = calloc(nmemb, size);
if (!tmp)
exit_horribly(NULL, NULL, "out of memory\n");
return tmp;
}
void *
pg_realloc(void *ptr, size_t size)
{
void *tmp;
tmp = realloc(ptr, size);
if (!tmp)
exit_horribly(NULL, NULL, "out of memory\n");
return tmp;
}