/*-------------------------------------------------------------------------
*
* common.c
- * common routines between pg_dump and pg4_dump
+ * Catalog routines used by pg_dump; long ago these were shared
+ * by another dump tool, but not anymore.
*
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.49 2001/01/12 15:41:29 pjw Exp $
- *
- * Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2
- *
- * - Fixed dumpTable output to output lengths for char and varchar types!
- * - Added single. quote to twin single quote expansion for 'insert' string
- * mode.
- *
- * Modifications 14-Sep-2000 - pjw@rhyme.com.au
- * - Added enum for findTypeByOid to specify how to handle OID and which
- * string to return - formatted type, or base type. If the base type
- * is returned then fmtId is called on the string.
- *
- * BEWARE: Since fmtId uses a static buffer, using 'useBaseTypeName' on more
- * than one call in a line will cause problems.
+ * src/bin/pg_dump/common.c
*
*-------------------------------------------------------------------------
*/
-
+#include "pg_backup_archiver.h"
#include <ctype.h>
-#include "postgres.h"
+#include "catalog/pg_class.h"
-#include "libpq-fe.h"
-#ifndef HAVE_STRDUP
-#include "strdup.h"
-#endif
-#include "pg_dump.h"
+/*
+ * Variables for mapping DumpId to DumpableObject
+ */
+static DumpableObject **dumpIdMap = NULL;
+static int allocedDumpIds = 0;
+static DumpId lastDumpId = 0;
-static char **findParentsByOid(TableInfo *tbinfo, int numTables,
- InhInfo *inhinfo, int numInherits,
- const char *oid,
- int *numParents);
-static int findTableByOid(TableInfo *tbinfo, int numTables, const char *oid);
-static void flagInhAttrs(TableInfo *tbinfo, int numTables,
- InhInfo *inhinfo, int numInherits);
-static int strInArray(const char *pattern, char **arr, int arr_size);
+/*
+ * Variables for mapping CatalogId to DumpableObject
+ */
+static bool catalogIdMapValid = false;
+static DumpableObject **catalogIdMap = NULL;
+static int numCatalogIds = 0;
/*
- * findTypeByOid
- * given an oid of a type, return its typename
- *
- * if oid is "0", return "opaque" -- this is a special case
- *
- * NOTE: should hash this, but just do linear search for now
+ * 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 NamespaceInfo *nspinfo;
+static int numTables;
+static int numTypes;
+static int numFuncs;
+static int numOperators;
+static int numCollations;
+static int numNamespaces;
+static DumpableObject **tblinfoindex;
+static DumpableObject **typinfoindex;
+static DumpableObject **funinfoindex;
+static DumpableObject **oprinfoindex;
+static DumpableObject **collinfoindex;
+static DumpableObject **nspinfoindex;
+
+
+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);
+
-char *
-findTypeByOid(TypeInfo *tinfo, int numTypes, const char *oid, OidOptions opts)
+/*
+ * getSchemaData
+ * Collect information about all potentially dumpable objects
+ */
+TableInfo *
+getSchemaData(Archive *fout, int *numTablesPtr)
{
- int i;
+ ExtensionInfo *extinfo;
+ InhInfo *inhinfo;
+ CollInfo *collinfo;
+ 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;
+ int numEventTriggers;
- if (strcmp(oid, "0") == 0) {
+ if (g_verbose)
+ write_msg(NULL, "reading schemas\n");
+ nspinfo = getNamespaces(fout, &numNamespaces);
+ nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo));
- if ( (opts & zeroAsOpaque) != 0 ) {
+ /*
+ * 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(fout, &numTables);
+ tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo));
- return g_opaque_type;
+ /* Do this after we've built tblinfoindex */
+ getOwnedSeqs(fout, tblinfo, numTables);
- } else if ( (opts & zeroAsAny) != 0 ) {
+ if (g_verbose)
+ write_msg(NULL, "reading extensions\n");
+ extinfo = getExtensions(fout, &numExtensions);
- return "'any'";
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined functions\n");
+ funinfo = getFuncs(fout, &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(fout, &numTypes);
+ typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo));
- for (i = 0; i < numTypes; i++)
- {
- if (strcmp(tinfo[i].oid, oid) == 0) {
- if ( (opts & useBaseTypeName) != 0 ) {
- return (char*) fmtId(tinfo[i].typname, false);
- } else {
- return tinfo[i].typedefn;
- }
- }
- }
+ /* this must be after getFuncs, too */
+ if (g_verbose)
+ write_msg(NULL, "reading procedural languages\n");
+ getProcLangs(fout, &numProcLangs);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined aggregate functions\n");
+ getAggregates(fout, &numAggregates);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined operators\n");
+ oprinfo = getOperators(fout, &numOperators);
+ oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined operator classes\n");
+ getOpclasses(fout, &numOpclasses);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined operator families\n");
+ getOpfamilies(fout, &numOpfamilies);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined text search parsers\n");
+ getTSParsers(fout, &numTSParsers);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined text search templates\n");
+ getTSTemplates(fout, &numTSTemplates);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined text search dictionaries\n");
+ getTSDictionaries(fout, &numTSDicts);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined text search configurations\n");
+ getTSConfigurations(fout, &numTSConfigs);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined foreign-data wrappers\n");
+ getForeignDataWrappers(fout, &numForeignDataWrappers);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined foreign servers\n");
+ getForeignServers(fout, &numForeignServers);
+
+ if (g_verbose)
+ write_msg(NULL, "reading default privileges\n");
+ getDefaultACLs(fout, &numDefaultACLs);
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined collations\n");
+ collinfo = getCollations(fout, &numCollations);
+ collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo));
+
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined conversions\n");
+ getConversions(fout, &numConversions);
+
+ if (g_verbose)
+ write_msg(NULL, "reading type casts\n");
+ getCasts(fout, &numCasts);
+
+ if (g_verbose)
+ write_msg(NULL, "reading table inheritance information\n");
+ inhinfo = getInherits(fout, &numInherits);
- /* should never get here */
- fprintf(stderr, "failed sanity check, type with oid %s was not found\n",
- oid);
- exit(2);
+ if (g_verbose)
+ write_msg(NULL, "reading rewrite rules\n");
+ getRules(fout, &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(fout, 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(fout, 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(fout, tblinfo, numTables);
+
+ if (g_verbose)
+ write_msg(NULL, "reading constraints\n");
+ getConstraints(fout, tblinfo, numTables);
+
+ if (g_verbose)
+ write_msg(NULL, "reading triggers\n");
+ getTriggers(fout, tblinfo, numTables);
+
+ if (g_verbose)
+ write_msg(NULL, "reading event triggers\n");
+ getEventTriggers(fout, &numEventTriggers);
+
+ *numTablesPtr = numTables;
+ return tblinfo;
}
-/*
- * findOprByOid
- * given the oid of an operator, return the name of the operator
- *
+/* flagInhTables -
+ * Fill in parent link fields of every target table, and mark
+ * parents of target tables as interesting
*
- * NOTE: should hash this, but just do linear search for now
+ * 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
*/
-char *
-findOprByOid(OprInfo *oprinfo, int numOprs, const char *oid)
+static void
+flagInhTables(TableInfo *tblinfo, int numTables,
+ InhInfo *inhinfo, int numInherits)
{
- int i;
+ int i,
+ j;
+ int numParents;
+ TableInfo **parents;
- for (i = 0; i < numOprs; i++)
+ for (i = 0; i < numTables; i++)
{
- if (strcmp(oprinfo[i].oid, oid) == 0)
- return oprinfo[i].oprname;
+ /* 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;
}
-
- /* should never get here */
- fprintf(stderr, "failed sanity check, opr with oid %s was not found\n",
- oid);
- exit(2);
}
-
-/*
- * findParentsByOid
- * given the oid of a class, return the names of its parent classes
- * and assign the number of parents to the last argument.
+/* 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) and child columns that have DEFAULT NULL when their parents had
+ * some non-null default. In the latter 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.
*
- * returns NULL if none
+ * modifies tblinfo
*/
-
-static char **
-findParentsByOid(TableInfo *tblinfo, int numTables,
- InhInfo *inhinfo, int numInherits, const char *oid,
- int *numParentsPtr)
+static void
+flagInhAttrs(TableInfo *tblinfo, int numTables)
{
int i,
- j;
- int parentInd,
- selfInd;
- char **result;
- int numParents;
+ j,
+ k;
- numParents = 0;
- for (i = 0; i < numInherits; i++)
+ for (i = 0; i < numTables; i++)
{
- if (strcmp(inhinfo[i].inhrelid, oid) == 0)
- numParents++;
- }
+ TableInfo *tbinfo = &(tblinfo[i]);
+ int numParents;
+ TableInfo **parents;
- *numParentsPtr = numParents;
+ /* Sequences and views never have parents */
+ if (tbinfo->relkind == RELKIND_SEQUENCE ||
+ tbinfo->relkind == RELKIND_VIEW)
+ continue;
- if (numParents > 0)
- {
- result = (char **) malloc(sizeof(char *) * numParents);
- j = 0;
- for (i = 0; i < numInherits; i++)
+ /* 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++)
{
- if (strcmp(inhinfo[i].inhrelid, oid) == 0)
+ bool foundNotNull; /* Attr was NOT NULL in a parent */
+ bool foundDefault; /* Found a default in a parent */
+
+ /* no point in examining dropped columns */
+ if (tbinfo->attisdropped[j])
+ continue;
+
+ foundNotNull = false;
+ foundDefault = false;
+ for (k = 0; k < numParents; k++)
{
- parentInd = findTableByOid(tblinfo, numTables,
- inhinfo[i].inhparent);
- if (parentInd < 0)
+ TableInfo *parent = parents[k];
+ int inhAttrInd;
+
+ inhAttrInd = strInArray(tbinfo->attnames[j],
+ parent->attnames,
+ parent->numatts);
+ if (inhAttrInd >= 0)
{
- selfInd = findTableByOid(tblinfo, numTables, oid);
- fprintf(stderr,
- "failed sanity check, parent oid %s of table %s (oid %s) was not found\n",
- inhinfo[i].inhparent,
- (selfInd >= 0) ? tblinfo[selfInd].relname : "",
- oid);
- exit(2);
+ foundNotNull |= parent->notnull[inhAttrInd];
+ foundDefault |= (parent->attrdefs[inhAttrInd] != NULL);
}
- result[j++] = tblinfo[parentInd].relname;
+ }
+
+ /* Remember if we found inherited NOT NULL */
+ tbinfo->inhNotNull[j] = foundNotNull;
+
+ /* Manufacture a DEFAULT NULL clause if necessary */
+ if (foundDefault && tbinfo->attrdefs[j] == NULL)
+ {
+ AttrDefInfo *attrDef;
+
+ 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->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(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;
}
}
- return result;
}
- else
- return NULL;
}
/*
- * parseNumericArray
- * parse a string of numbers delimited by spaces into a character array
+ * 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
-parseNumericArray(const char *str, char **array, int arraysize)
+AssignDumpId(DumpableObject *dobj)
{
- int j,
- argNum;
- char temp[100];
- char s;
-
- argNum = 0;
- j = 0;
- for (;;)
+ 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)
{
- s = *str++;
- if (s == ' ' || s == '\0')
+ int newAlloc;
+
+ if (allocedDumpIds <= 0)
{
- if (j > 0)
- {
- if (argNum >= arraysize)
- {
- fprintf(stderr, "parseNumericArray: too many numbers\n");
- exit(2);
- }
- temp[j] = '\0';
- array[argNum++] = strdup(temp);
- j = 0;
- }
- if (s == '\0')
- break;
+ newAlloc = 256;
+ dumpIdMap = (DumpableObject **)
+ pg_malloc(newAlloc * sizeof(DumpableObject *));
}
else
{
- if (!(isdigit((unsigned char) s) || s == '-') ||
- j >= sizeof(temp) - 1)
- {
- fprintf(stderr, "parseNumericArray: bogus number\n");
- exit(2);
- }
- temp[j++] = s;
+ newAlloc = allocedDumpIds * 2;
+ dumpIdMap = (DumpableObject **)
+ pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
}
+ memset(dumpIdMap + allocedDumpIds, 0,
+ (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
+ allocedDumpIds = newAlloc;
}
- while (argNum < arraysize)
- array[argNum++] = strdup("0");
+ 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;
+}
/*
- * 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
+ * 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];
+}
-static int
-strInArray(const char *pattern, char **arr, int arr_size)
+/*
+ * 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)
{
- int i;
+ DumpableObject **low;
+ DumpableObject **high;
- for (i = 0; i < arr_size; i++)
+ if (!catalogIdMapValid)
{
- if (strcmp(pattern, arr[i]) == 0)
- return i;
+ if (catalogIdMap)
+ free(catalogIdMap);
+ getDumpableObjects(&catalogIdMap, &numCatalogIds);
+ if (numCatalogIds > 1)
+ qsort((void *) catalogIdMap, numCatalogIds,
+ sizeof(DumpableObject *), DOCatalogIdCompare);
+ catalogIdMapValid = true;
}
- return -1;
+
+ /*
+ * 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;
}
/*
- * dumpSchema:
- * we have a valid connection, we are now going to dump the schema
- * into the file
+ * Find a DumpableObject by OID, in a pre-sorted array of one type of object
*
+ * Returns NULL for unknown OID
*/
-
-TableInfo *
-dumpSchema(Archive *fout,
- int *numTablesPtr,
- const char *tablename,
- const bool aclsSkip,
- const bool oids,
- const bool schemaOnly,
- const bool dataOnly)
+static DumpableObject *
+findObjectByOid(Oid oid, DumpableObject **indexArray, int numObjs)
{
- int numTypes;
- int numFuncs;
- int numTables;
- int numInherits;
- int numAggregates;
- int numOperators;
- int numIndices;
- TypeInfo *tinfo = NULL;
- FuncInfo *finfo = NULL;
- AggInfo *agginfo = NULL;
- TableInfo *tblinfo = NULL;
- InhInfo *inhinfo = NULL;
- OprInfo *oprinfo = NULL;
- IndInfo *indinfo = NULL;
+ DumpableObject **low;
+ DumpableObject **high;
- if (g_verbose)
- fprintf(stderr, "%s reading user-defined types %s\n",
- g_comment_start, g_comment_end);
- tinfo = getTypes(&numTypes);
-
- if (g_verbose)
- fprintf(stderr, "%s reading user-defined functions %s\n",
- g_comment_start, g_comment_end);
- finfo = getFuncs(&numFuncs);
+ /*
+ * 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;
+}
- if (g_verbose)
- fprintf(stderr, "%s reading user-defined aggregates %s\n",
- g_comment_start, g_comment_end);
- agginfo = getAggregates(&numAggregates);
+/*
+ * Build an index array of DumpableObject pointers, sorted by OID
+ */
+static DumpableObject **
+buildIndexArray(void *objArray, int numObjs, Size objSize)
+{
+ DumpableObject **ptrs;
+ int i;
- if (g_verbose)
- fprintf(stderr, "%s reading user-defined operators %s\n",
- g_comment_start, g_comment_end);
- oprinfo = getOperators(&numOperators);
+ ptrs = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
+ for (i = 0; i < numObjs; i++)
+ ptrs[i] = (DumpableObject *) ((char *) objArray + i * objSize);
- if (g_verbose)
- fprintf(stderr, "%s reading user-defined tables %s\n",
- g_comment_start, g_comment_end);
- tblinfo = getTables(&numTables, finfo, numFuncs);
+ /* We can use DOCatalogIdCompare to sort since its first key is OID */
+ if (numObjs > 1)
+ qsort((void *) ptrs, numObjs, sizeof(DumpableObject *),
+ DOCatalogIdCompare);
- if (g_verbose)
- fprintf(stderr, "%s reading indices information %s\n",
- g_comment_start, g_comment_end);
- indinfo = getIndices(&numIndices);
+ return ptrs;
+}
- if (g_verbose)
- fprintf(stderr, "%s reading table inheritance information %s\n",
- g_comment_start, g_comment_end);
- inhinfo = getInherits(&numInherits);
+/*
+ * 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;
- if (g_verbose)
- fprintf(stderr, "%s finding the attribute names and types for each table %s\n",
- g_comment_start, g_comment_end);
- getTableAttrs(tblinfo, numTables);
+ /*
+ * 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;
+}
- if (g_verbose)
- fprintf(stderr, "%s flagging inherited attributes in subtables %s\n",
- g_comment_start, g_comment_end);
- flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
+/*
+ * 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;
- if (!tablename && !dataOnly)
+ *objs = (DumpableObject **)
+ pg_malloc(allocedDumpIds * sizeof(DumpableObject *));
+ j = 0;
+ for (i = 1; i < allocedDumpIds; i++)
{
- if (g_verbose)
- fprintf(stderr, "%s dumping out database comment %s\n",
- g_comment_start, g_comment_end);
- dumpDBComment(fout);
+ if (dumpIdMap[i])
+ (*objs)[j++] = dumpIdMap[i];
}
+ *numObjs = j;
+}
- if (!tablename && fout)
+/*
+ * 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 (g_verbose)
- fprintf(stderr, "%s dumping out user-defined types %s\n",
- g_comment_start, g_comment_end);
- dumpTypes(fout, finfo, numFuncs, tinfo, numTypes);
+ 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;
+}
- if (g_verbose)
- fprintf(stderr, "%s dumping out tables %s\n",
- g_comment_start, g_comment_end);
-
- dumpTables(fout, tblinfo, numTables, indinfo, numIndices, inhinfo, numInherits,
- tinfo, numTypes, tablename, aclsSkip, oids, schemaOnly, dataOnly);
+/*
+ * 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;
- if (fout && !dataOnly)
+ for (i = 0; i < dobj->nDeps; i++)
{
- if (g_verbose)
- fprintf(stderr, "%s dumping out indices %s\n",
- g_comment_start, g_comment_end);
- dumpIndices(fout, indinfo, numIndices, tblinfo, numTables, tablename);
+ if (dobj->dependencies[i] != refId)
+ dobj->dependencies[j++] = dobj->dependencies[i];
}
+ dobj->nDeps = j;
+}
- if (!tablename && !dataOnly)
- {
- if (g_verbose)
- fprintf(stderr, "%s dumping out user-defined procedural languages %s\n",
- g_comment_start, g_comment_end);
- dumpProcLangs(fout, finfo, numFuncs, tinfo, numTypes);
- }
- if (!tablename && !dataOnly)
- {
- if (g_verbose)
- fprintf(stderr, "%s dumping out user-defined functions %s\n",
- g_comment_start, g_comment_end);
- dumpFuncs(fout, finfo, numFuncs, tinfo, numTypes);
- }
+/*
+ * 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);
+}
- if (!tablename && !dataOnly)
- {
- if (g_verbose)
- fprintf(stderr, "%s dumping out user-defined aggregates %s\n",
- g_comment_start, g_comment_end);
- dumpAggs(fout, agginfo, numAggregates, tinfo, numTypes);
- }
+/*
+ * 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);
+}
- if (!tablename && !dataOnly)
- {
- if (g_verbose)
- fprintf(stderr, "%s dumping out user-defined operators %s\n",
- g_comment_start, g_comment_end);
- dumpOprs(fout, oprinfo, numOperators, tinfo, 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);
+}
- *numTablesPtr = numTables;
- clearAggInfo(agginfo, numAggregates);
- clearOprInfo(oprinfo, numOperators);
- clearTypeInfo(tinfo, numTypes);
- clearFuncInfo(finfo, numFuncs);
- clearInhInfo(inhinfo, numInherits);
- clearIndInfo(indinfo, numIndices);
- return tblinfo;
+/*
+ * 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);
}
-/* flagInhAttrs -
- * for each table in tblinfo, flag its inherited attributes
- * so when we dump the table out, we don't dump out the inherited attributes
- *
- * initializes the parentRels field of each table
- *
- * modifies tblinfo
- *
+/*
+ * 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);
+}
+
+/*
+ * findNamespaceByOid
+ * finds the entry (in nspinfo) of the namespace with the given oid
+ * returns NULL if not found
+ */
+NamespaceInfo *
+findNamespaceByOid(Oid oid)
+{
+ return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces);
+}
+
+
+/*
+ * findParentsByOid
+ * find a table's parents in tblinfo[]
*/
static void
-flagInhAttrs(TableInfo *tblinfo, int numTables,
- InhInfo *inhinfo, int numInherits)
+findParentsByOid(TableInfo *self,
+ InhInfo *inhinfo, int numInherits)
{
+ Oid oid = self->dobj.catId.oid;
int i,
- j,
- k;
- int parentInd;
+ j;
+ int numParents;
- /*
- * we go backwards because the tables in tblinfo are in OID order,
- * meaning the subtables are after the parent tables we flag inherited
- * attributes from child tables first
- */
- for (i = numTables - 1; i >= 0; i--)
+ numParents = 0;
+ for (i = 0; i < numInherits; i++)
+ {
+ if (inhinfo[i].inhrelid == oid)
+ numParents++;
+ }
+
+ self->numParents = numParents;
+
+ if (numParents > 0)
{
- tblinfo[i].parentRels = findParentsByOid(tblinfo, numTables,
- inhinfo, numInherits,
- tblinfo[i].oid,
- &tblinfo[i].numParents);
- for (k = 0; k < tblinfo[i].numParents; k++)
+ self->parents = (TableInfo **)
+ pg_malloc(sizeof(TableInfo *) * numParents);
+ j = 0;
+ for (i = 0; i < numInherits; i++)
{
- parentInd = findTableByName(tblinfo, numTables,
- tblinfo[i].parentRels[k]);
- if (parentInd < 0)
- {
- /* shouldn't happen unless findParentsByOid is broken */
- fprintf(stderr, "failed sanity check, table %s not found by flagInhAttrs\n",
- tblinfo[i].parentRels[k]);
- exit(2);
- }
- for (j = 0; j < tblinfo[i].numatts; j++)
+ if (inhinfo[i].inhrelid == oid)
{
- if (strInArray(tblinfo[i].attnames[j],
- tblinfo[parentInd].attnames,
- tblinfo[parentInd].numatts) != -1)
- tblinfo[i].inhAttrs[j] = 1;
+ 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(1);
+ }
+ self->parents[j++] = parent;
}
}
}
+ else
+ self->parents = NULL;
}
-
/*
- * findTableByName
- * finds the index (in tblinfo) of the table with the given relname
- * returns -1 if not found
+ * parseOidArray
+ * parse a string of numbers delimited by spaces into a character array
*
- * NOTE: should hash this, but just do linear search for now
+ * 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.
*/
-int
-findTableByName(TableInfo *tblinfo, int numTables, const char *relname)
+void
+parseOidArray(const char *str, Oid *array, int arraysize)
{
- int i;
+ int j,
+ argNum;
+ char temp[100];
+ char s;
- for (i = 0; i < numTables; i++)
+ argNum = 0;
+ j = 0;
+ for (;;)
{
- if (strcmp(tblinfo[i].relname, relname) == 0)
- return i;
+ 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(1);
+ }
+ 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(1);
+ }
+ temp[j++] = s;
+ }
}
- return -1;
+
+ while (argNum < arraysize)
+ array[argNum++] = InvalidOid;
}
+
/*
- * findTableByOid
- * finds the index (in tblinfo) of the table with the given oid
- * returns -1 if not found
- *
- * NOTE: should hash this, but just do linear search for now
+ * 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
-findTableByOid(TableInfo *tblinfo, int numTables, const char *oid)
+strInArray(const char *pattern, char **arr, int arr_size)
{
int i;
- for (i = 0; i < numTables; i++)
+ for (i = 0; i < arr_size; i++)
{
- if (strcmp(tblinfo[i].oid, oid) == 0)
+ if (strcmp(pattern, arr[i]) == 0)
return i;
}
return -1;
/*
- * findFuncByName
- * finds the index (in finfo) of the function with the given name
- * returns -1 if not found
- *
- * NOTE: should hash this, but just do linear search for now
+ * Support for simple list operations
*/
-int
-findFuncByName(FuncInfo *finfo, int numFuncs, const char *name)
+void
+simple_oid_list_append(SimpleOidList *list, Oid val)
{
- int i;
+ SimpleOidListCell *cell;
- for (i = 0; i < numFuncs; i++)
- {
- if (strcmp(finfo[i].proname, name) == 0)
- return i;
- }
- return -1;
+ 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;
}
-/*
- * fmtId
- *
- * checks input string for non-lowercase characters
- * returns pointer to input string or string surrounded by double quotes
- *
- * Note that the returned string should be used immediately since it
- * uses a static buffer to hold the string. Non-reentrant but faster?
- */
-const char *
-fmtId(const char *rawid, bool force_quotes)
+bool
+simple_oid_list_member(SimpleOidList *list, Oid val)
{
- static PQExpBuffer id_return = NULL;
- const char *cp;
+ SimpleOidListCell *cell;
- if (!force_quotes)
+ for (cell = list->head; cell; cell = cell->next)
{
- /* do a quick check on the first character... */
- if (!islower((unsigned char) *rawid))
- force_quotes = true;
- /* otherwise check the entire string */
- else
- for (cp = rawid; *cp; cp++)
- {
- if (!(islower((unsigned char) *cp) ||
- isdigit((unsigned char) *cp) ||
- (*cp == '_')))
- {
- force_quotes = true;
- break;
- }
- }
- }
-
- if (!force_quotes)
- return rawid; /* no quoting needed */
-
- if (id_return)
- resetPQExpBuffer(id_return);
- else
- id_return = createPQExpBuffer();
-
- appendPQExpBufferChar(id_return, '\"');
- for (cp = rawid; *cp; cp++)
- {
- /* Did we find a double-quote in the string?
- * Then make this a double double-quote per SQL99.
- * Before, we put in a backslash/double-quote pair.
- * - thomas 2000-08-05 */
- if (*cp == '\"')
- {
- appendPQExpBufferChar(id_return, '\"');
- appendPQExpBufferChar(id_return, '\"');
- }
- appendPQExpBufferChar(id_return, *cp);
+ if (cell->val == val)
+ return true;
}
- appendPQExpBufferChar(id_return, '\"');
-
- return id_return->data;
-} /* fmtId() */
+ return false;
+}