]> granicus.if.org Git - postgresql/blobdiff - src/bin/pg_dump/common.c
Create libpgcommon, and move pg_malloc et al to it
[postgresql] / src / bin / pg_dump / common.c
index a466dafc5da59b50a36a07d47cbbd36c3d3e2ec8..01739ab717c9ca1c50df9223dcb02e8a631c7dd0 100644 (file)
 /*-------------------------------------------------------------------------
  *
  * 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;
@@ -493,83 +877,34 @@ findTableByOid(TableInfo *tblinfo, int numTables, const char *oid)
 
 
 /*
- * 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;
+}