Massive overhaul of pg_dump: make use of dependency information from
pg_depend to determine a safe dump order. Defaults and check constraints can be emitted either as part of a table or domain definition, or separately if that's needed to break a dependency loop. Lots of old half-baked code for controlling dump order removed.
This commit is contained in:
parent
a5ffa8fea4
commit
005a1217fb
@ -1,4 +1,4 @@
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/pg_restore.sgml,v 1.44 2003/11/29 19:51:39 pgsql Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/pg_restore.sgml,v 1.45 2003/12/06 03:00:10 tgl Exp $ -->
|
||||
|
||||
<refentry id="APP-PGRESTORE">
|
||||
<refmeta>
|
||||
@ -227,35 +227,6 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-N</option></term>
|
||||
<term><option>--orig-order</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Restore items in the order they were originally generated within
|
||||
<application>pg_dump</application>. This option has no known
|
||||
practical use, since <application>pg_dump</application> generates
|
||||
the items in an order convenient to it, which is unlikely to be a
|
||||
safe order for restoring them. (This is <emphasis>not</> the order
|
||||
in which the items are ultimately listed in the archive's table of
|
||||
contents.) See also <option>-r</>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-o</option></term>
|
||||
<term><option>--oid-order</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Restore items in order by OID. This option is of limited usefulness,
|
||||
since OID is only an approximate indication of original creation
|
||||
order. This option overrides <option>-N</> if both are specified.
|
||||
See also <option>-r</>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-O</option></term>
|
||||
<term><option>--no-owner</option></term>
|
||||
@ -287,31 +258,6 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-r</option></term>
|
||||
<term><option>--rearrange</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Rearrange items by object type (this occurs after the sorting
|
||||
specified by <option>-N</option> or <option>-o</option>, if
|
||||
given). The rearrangement is intended to give the best possible
|
||||
restore performance.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When none of <option>-N</option>, <option>-o</option>, and
|
||||
<option>-r</> appear, <application>pg_restore</application> restores
|
||||
items in the order they appear in the dump's table of contents,
|
||||
or in the order they appear in the <REPLACEABLE
|
||||
CLASS="PARAMETER">list-file</REPLACEABLE> if <option>-L</> is
|
||||
given. The combination of <option>-o</> and <option>-r</>
|
||||
duplicates the sorting done by <application>pg_dump</application>
|
||||
before creating the dump's table of contents,
|
||||
and so it is normally unnecessary to specify it.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-R</option></term>
|
||||
<term><option>--no-reconnect</option></term>
|
||||
|
@ -5,7 +5,7 @@
|
||||
# Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
# Portions Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
# $PostgreSQL: pgsql/src/bin/pg_dump/Makefile,v 1.41 2003/11/29 19:52:04 pgsql Exp $
|
||||
# $PostgreSQL: pgsql/src/bin/pg_dump/Makefile,v 1.42 2003/12/06 03:00:11 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -24,8 +24,8 @@ override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) -DBINDIR=\"$(bindir)\"
|
||||
|
||||
all: submake-libpq submake-libpgport submake-backend pg_dump pg_restore pg_dumpall
|
||||
|
||||
pg_dump: pg_dump.o common.o $(OBJS) $(libpq_builddir)/libpq.a
|
||||
$(CC) $(CFLAGS) pg_dump.o common.o $(OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@
|
||||
pg_dump: pg_dump.o common.o pg_dump_sort.o $(OBJS) $(libpq_builddir)/libpq.a
|
||||
$(CC) $(CFLAGS) pg_dump.o common.o pg_dump_sort.o $(OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@
|
||||
|
||||
pg_restore: pg_restore.o $(OBJS) $(libpq_builddir)/libpq.a
|
||||
$(CC) $(CFLAGS) pg_restore.o $(OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@
|
||||
@ -50,4 +50,4 @@ uninstall:
|
||||
rm -f $(addprefix $(DESTDIR)$(bindir)/, pg_dump$(X) pg_restore$(X) pg_dumpall$(X))
|
||||
|
||||
clean distclean maintainer-clean:
|
||||
rm -f pg_dump$(X) pg_restore$(X) pg_dumpall$(X) $(OBJS) pg_dump.o common.o pg_restore.o pg_dumpall.o
|
||||
rm -f pg_dump$(X) pg_restore$(X) pg_dumpall$(X) $(OBJS) pg_dump.o common.o pg_dump_sort.o pg_restore.o pg_dumpall.o
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.77 2003/11/29 19:52:04 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.78 2003/12/06 03:00:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -29,10 +29,30 @@
|
||||
#include "strdup.h"
|
||||
#endif
|
||||
|
||||
static void findParentsByOid(TableInfo *tblinfo, int numTables,
|
||||
InhInfo *inhinfo, int numInherits,
|
||||
const char *oid,
|
||||
int *numParentsPtr, int **parentIndexes);
|
||||
|
||||
/*
|
||||
* Variables for mapping DumpId to DumpableObject
|
||||
*/
|
||||
static DumpableObject **dumpIdMap = NULL;
|
||||
static int allocedDumpIds = 0;
|
||||
static DumpId lastDumpId = 0;
|
||||
|
||||
/*
|
||||
* These variables are static to avoid the notational cruft of having to pass
|
||||
* them into findTableByOid() and friends.
|
||||
*/
|
||||
static TableInfo *tblinfo;
|
||||
static TypeInfo *tinfo;
|
||||
static FuncInfo *finfo;
|
||||
static OprInfo *oprinfo;
|
||||
static int numTables;
|
||||
static int numTypes;
|
||||
static int numFuncs;
|
||||
static int numOperators;
|
||||
|
||||
|
||||
static void findParentsByOid(TableInfo *self,
|
||||
InhInfo *inhinfo, int numInherits);
|
||||
static void flagInhTables(TableInfo *tbinfo, int numTables,
|
||||
InhInfo *inhinfo, int numInherits);
|
||||
static void flagInhAttrs(TableInfo *tbinfo, int numTables,
|
||||
@ -41,48 +61,48 @@ static int strInArray(const char *pattern, char **arr, int arr_size);
|
||||
|
||||
|
||||
/*
|
||||
* dumpSchema:
|
||||
* we have a valid connection, we are now going to dump the schema
|
||||
* into the file
|
||||
* getSchemaData
|
||||
* Collect information about all potentially dumpable objects
|
||||
*/
|
||||
|
||||
TableInfo *
|
||||
dumpSchema(Archive *fout,
|
||||
int *numTablesPtr,
|
||||
const bool aclsSkip,
|
||||
const bool schemaOnly,
|
||||
const bool dataOnly)
|
||||
getSchemaData(int *numTablesPtr,
|
||||
const bool schemaOnly,
|
||||
const bool dataOnly)
|
||||
{
|
||||
NamespaceInfo *nsinfo;
|
||||
AggInfo *agginfo;
|
||||
InhInfo *inhinfo;
|
||||
RuleInfo *ruleinfo;
|
||||
ProcLangInfo *proclanginfo;
|
||||
CastInfo *castinfo;
|
||||
OpclassInfo *opcinfo;
|
||||
ConvInfo *convinfo;
|
||||
int numNamespaces;
|
||||
int numTypes;
|
||||
int numFuncs;
|
||||
int numTables;
|
||||
int numInherits;
|
||||
int numAggregates;
|
||||
int numOperators;
|
||||
int numInherits;
|
||||
int numRules;
|
||||
int numProcLangs;
|
||||
int numCasts;
|
||||
int numOpclasses;
|
||||
int numConversions;
|
||||
NamespaceInfo *nsinfo;
|
||||
TypeInfo *tinfo;
|
||||
FuncInfo *finfo;
|
||||
AggInfo *agginfo;
|
||||
TableInfo *tblinfo;
|
||||
InhInfo *inhinfo;
|
||||
OprInfo *oprinfo;
|
||||
OpclassInfo *opcinfo;
|
||||
ConvInfo *convinfo;
|
||||
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading schemas\n");
|
||||
nsinfo = getNamespaces(&numNamespaces);
|
||||
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading user-defined functions\n");
|
||||
finfo = getFuncs(&numFuncs);
|
||||
|
||||
/* this must be after getFuncs */
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading user-defined types\n");
|
||||
tinfo = getTypes(&numTypes);
|
||||
|
||||
/* this must be after getFuncs, too */
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading user-defined functions\n");
|
||||
finfo = getFuncs(&numFuncs);
|
||||
write_msg(NULL, "reading procedural languages\n");
|
||||
proclanginfo = getProcLangs(&numProcLangs);
|
||||
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading user-defined aggregate functions\n");
|
||||
@ -108,6 +128,14 @@ dumpSchema(Archive *fout,
|
||||
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");
|
||||
@ -121,94 +149,24 @@ dumpSchema(Archive *fout,
|
||||
write_msg(NULL, "flagging inherited columns in subtables\n");
|
||||
flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out database comment\n");
|
||||
dumpDBComment(fout);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined schemas\n");
|
||||
dumpNamespaces(fout, nsinfo, numNamespaces);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined types\n");
|
||||
dumpTypes(fout, finfo, numFuncs, tinfo, numTypes);
|
||||
}
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading indexes\n");
|
||||
getIndexes(tblinfo, numTables);
|
||||
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out tables\n");
|
||||
dumpTables(fout, tblinfo, numTables,
|
||||
aclsSkip, schemaOnly, dataOnly);
|
||||
write_msg(NULL, "reading constraints\n");
|
||||
getConstraints(tblinfo, numTables);
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out indexes\n");
|
||||
dumpIndexes(fout, tblinfo, numTables);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined procedural languages\n");
|
||||
dumpProcLangs(fout, finfo, numFuncs);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined functions\n");
|
||||
dumpFuncs(fout, finfo, numFuncs);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined aggregate functions\n");
|
||||
dumpAggs(fout, agginfo, numAggregates);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined operators\n");
|
||||
dumpOprs(fout, oprinfo, numOperators);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined operator classes\n");
|
||||
dumpOpclasses(fout, opcinfo, numOpclasses);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined casts\n");
|
||||
dumpCasts(fout, finfo, numFuncs, tinfo, numTypes);
|
||||
}
|
||||
|
||||
if (!dataOnly)
|
||||
{
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "dumping out user-defined conversions\n");
|
||||
dumpConversions(fout, convinfo, numConversions);
|
||||
}
|
||||
if (g_verbose)
|
||||
write_msg(NULL, "reading triggers\n");
|
||||
getTriggers(tblinfo, numTables);
|
||||
|
||||
*numTablesPtr = numTables;
|
||||
return tblinfo;
|
||||
}
|
||||
|
||||
/* flagInhTables -
|
||||
* Fill in parentIndexes fields of every target table, and mark
|
||||
* 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.
|
||||
@ -224,7 +182,7 @@ flagInhTables(TableInfo *tblinfo, int numTables,
|
||||
int i,
|
||||
j;
|
||||
int numParents;
|
||||
int *parentIndexes;
|
||||
TableInfo **parents;
|
||||
|
||||
for (i = 0; i < numTables; i++)
|
||||
{
|
||||
@ -238,21 +196,13 @@ flagInhTables(TableInfo *tblinfo, int numTables,
|
||||
continue;
|
||||
|
||||
/* Find all the immediate parent tables */
|
||||
findParentsByOid(tblinfo, numTables,
|
||||
inhinfo, numInherits,
|
||||
tblinfo[i].oid,
|
||||
&tblinfo[i].numParents,
|
||||
&tblinfo[i].parentIndexes);
|
||||
numParents = tblinfo[i].numParents;
|
||||
parentIndexes = tblinfo[i].parentIndexes;
|
||||
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++)
|
||||
{
|
||||
int parentInd = parentIndexes[j];
|
||||
|
||||
tblinfo[parentInd].interesting = true;
|
||||
}
|
||||
parents[j]->interesting = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,30 +219,25 @@ flagInhAttrs(TableInfo *tblinfo, int numTables,
|
||||
int i,
|
||||
j,
|
||||
k;
|
||||
int parentInd;
|
||||
int inhAttrInd;
|
||||
int numParents;
|
||||
int *parentIndexes;
|
||||
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 */
|
||||
char *attrDef;
|
||||
char *inhDef;
|
||||
|
||||
for (i = 0; i < numTables; i++)
|
||||
{
|
||||
TableInfo *tbinfo = &(tblinfo[i]);
|
||||
int numParents;
|
||||
TableInfo **parents;
|
||||
TableInfo *parent;
|
||||
|
||||
/* Sequences and views never have parents */
|
||||
if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
|
||||
tblinfo[i].relkind == RELKIND_VIEW)
|
||||
if (tbinfo->relkind == RELKIND_SEQUENCE ||
|
||||
tbinfo->relkind == RELKIND_VIEW)
|
||||
continue;
|
||||
|
||||
/* Don't bother computing anything for non-target tables, either */
|
||||
if (!tblinfo[i].dump)
|
||||
if (!tbinfo->dump)
|
||||
continue;
|
||||
|
||||
numParents = tblinfo[i].numParents;
|
||||
parentIndexes = tblinfo[i].parentIndexes;
|
||||
numParents = tbinfo->numParents;
|
||||
parents = tbinfo->parents;
|
||||
|
||||
if (numParents == 0)
|
||||
continue; /* nothing to see here, move along */
|
||||
@ -310,35 +255,45 @@ flagInhAttrs(TableInfo *tblinfo, int numTables,
|
||||
* See discussion on -hackers around 2-Apr-2001.
|
||||
*----------------------------------------------------------------
|
||||
*/
|
||||
for (j = 0; j < tblinfo[i].numatts; j++)
|
||||
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 = tblinfo[i].adef_expr[j];
|
||||
attrDef = tbinfo->attrdefs[j];
|
||||
|
||||
for (k = 0; k < numParents; k++)
|
||||
{
|
||||
parentInd = parentIndexes[k];
|
||||
inhAttrInd = strInArray(tblinfo[i].attnames[j],
|
||||
tblinfo[parentInd].attnames,
|
||||
tblinfo[parentInd].numatts);
|
||||
int inhAttrInd;
|
||||
|
||||
parent = parents[k];
|
||||
inhAttrInd = strInArray(tbinfo->attnames[j],
|
||||
parent->attnames,
|
||||
parent->numatts);
|
||||
|
||||
if (inhAttrInd != -1)
|
||||
{
|
||||
foundAttr = true;
|
||||
foundNotNull |= tblinfo[parentInd].notnull[inhAttrInd];
|
||||
foundNotNull |= parent->notnull[inhAttrInd];
|
||||
if (attrDef != NULL) /* If we have a default,
|
||||
* check parent */
|
||||
{
|
||||
inhDef = tblinfo[parentInd].adef_expr[inhAttrInd];
|
||||
AttrDefInfo *inhDef;
|
||||
|
||||
inhDef = parent->attrdefs[inhAttrInd];
|
||||
if (inhDef != NULL)
|
||||
{
|
||||
defaultsFound = true;
|
||||
defaultsMatch &= (strcmp(attrDef, inhDef) == 0);
|
||||
defaultsMatch &= (strcmp(attrDef->adef_expr,
|
||||
inhDef->adef_expr) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -351,9 +306,9 @@ flagInhAttrs(TableInfo *tblinfo, int numTables,
|
||||
if (foundAttr) /* Attr was inherited */
|
||||
{
|
||||
/* Set inherited flag by default */
|
||||
tblinfo[i].inhAttrs[j] = true;
|
||||
tblinfo[i].inhAttrDef[j] = true;
|
||||
tblinfo[i].inhNotNull[j] = true;
|
||||
tbinfo->inhAttrs[j] = true;
|
||||
tbinfo->inhAttrDef[j] = true;
|
||||
tbinfo->inhNotNull[j] = true;
|
||||
|
||||
/*
|
||||
* Clear it if attr had a default, but parents did not, or
|
||||
@ -361,181 +316,377 @@ flagInhAttrs(TableInfo *tblinfo, int numTables,
|
||||
*/
|
||||
if ((attrDef != NULL) && (!defaultsFound || !defaultsMatch))
|
||||
{
|
||||
tblinfo[i].inhAttrs[j] = false;
|
||||
tblinfo[i].inhAttrDef[j] = false;
|
||||
tbinfo->inhAttrs[j] = false;
|
||||
tbinfo->inhAttrDef[j] = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear it if NOT NULL and none of the parents were NOT
|
||||
* NULL
|
||||
*/
|
||||
if (tblinfo[i].notnull[j] && !foundNotNull)
|
||||
if (tbinfo->notnull[j] && !foundNotNull)
|
||||
{
|
||||
tblinfo[i].inhAttrs[j] = false;
|
||||
tblinfo[i].inhNotNull[j] = false;
|
||||
tbinfo->inhAttrs[j] = false;
|
||||
tbinfo->inhNotNull[j] = false;
|
||||
}
|
||||
|
||||
/* Clear it if attr has local definition */
|
||||
if (g_fout->remoteVersion >= 70300 && tblinfo[i].attislocal[j])
|
||||
tblinfo[i].inhAttrs[j] = false;
|
||||
if (tbinfo->attislocal[j])
|
||||
tbinfo->inhAttrs[j] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for inherited CHECK constraints. We assume a constraint
|
||||
* is inherited if its expression matches the parent and the name
|
||||
* is the same, *or* both names start with '$'.
|
||||
*/
|
||||
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;
|
||||
|
||||
pconstr = &(parent->checkexprs[l]);
|
||||
if (strcmp(pconstr->condef, constr->condef) != 0)
|
||||
continue;
|
||||
if (strcmp(pconstr->conname, constr->conname) == 0 ||
|
||||
(pconstr->conname[0] == '$' &&
|
||||
constr->conname[0] == '$'))
|
||||
{
|
||||
constr->coninherited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (constr->coninherited)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 catalogId,
|
||||
* but not any of the other standard fields of a DumpableObject.
|
||||
*/
|
||||
void
|
||||
AssignDumpId(DumpableObject *dobj)
|
||||
{
|
||||
dobj->dumpId = ++lastDumpId;
|
||||
dobj->dependencies = NULL;
|
||||
dobj->nDeps = 0;
|
||||
dobj->allocDeps = 0;
|
||||
|
||||
while (dobj->dumpId >= allocedDumpIds)
|
||||
{
|
||||
int newAlloc;
|
||||
|
||||
if (allocedDumpIds <= 0)
|
||||
{
|
||||
newAlloc = 256;
|
||||
dumpIdMap = (DumpableObject **)
|
||||
malloc(newAlloc * sizeof(DumpableObject *));
|
||||
}
|
||||
else
|
||||
{
|
||||
newAlloc = allocedDumpIds * 2;
|
||||
dumpIdMap = (DumpableObject **)
|
||||
realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
|
||||
}
|
||||
if (dumpIdMap == NULL)
|
||||
exit_horribly(NULL, NULL, "out of memory\n");
|
||||
memset(dumpIdMap + allocedDumpIds, 0,
|
||||
(newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
|
||||
allocedDumpIds = newAlloc;
|
||||
}
|
||||
dumpIdMap[dobj->dumpId] = dobj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign a DumpId that's not tied to a DumpableObject.
|
||||
*
|
||||
* This is used when creating a "fixed" ArchiveEntry that doesn't need to
|
||||
* participate in the sorting logic.
|
||||
*/
|
||||
DumpId
|
||||
createDumpId(void)
|
||||
{
|
||||
return ++lastDumpId;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the largest DumpId so far assigned
|
||||
*/
|
||||
DumpId
|
||||
getMaxDumpId(void)
|
||||
{
|
||||
return lastDumpId;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a DumpableObject by dump ID
|
||||
*
|
||||
* Returns NULL for invalid ID
|
||||
*/
|
||||
DumpableObject *
|
||||
findObjectByDumpId(DumpId dumpId)
|
||||
{
|
||||
if (dumpId <= 0 || dumpId >= allocedDumpIds)
|
||||
return NULL; /* out of range? */
|
||||
return dumpIdMap[dumpId];
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a DumpableObject by catalog ID
|
||||
*
|
||||
* Returns NULL for unknown ID
|
||||
*
|
||||
* NOTE: should hash this, but just do linear search for now
|
||||
*/
|
||||
DumpableObject *
|
||||
findObjectByCatalogId(CatalogId catalogId)
|
||||
{
|
||||
DumpId i;
|
||||
|
||||
for (i = 1; i < allocedDumpIds; i++)
|
||||
{
|
||||
DumpableObject *dobj = dumpIdMap[i];
|
||||
|
||||
if (dobj &&
|
||||
dobj->catId.tableoid == catalogId.tableoid &&
|
||||
dobj->catId.oid == catalogId.oid)
|
||||
return dobj;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 **)
|
||||
malloc(allocedDumpIds * sizeof(DumpableObject *));
|
||||
if (*objs == NULL)
|
||||
exit_horribly(NULL, NULL, "out of memory\n");
|
||||
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 *)
|
||||
malloc(dobj->allocDeps * sizeof(DumpId));
|
||||
}
|
||||
else
|
||||
{
|
||||
dobj->allocDeps *= 2;
|
||||
dobj->dependencies = (DumpId *)
|
||||
realloc(dobj->dependencies,
|
||||
dobj->allocDeps * sizeof(DumpId));
|
||||
}
|
||||
if (dobj->dependencies == NULL)
|
||||
exit_horribly(NULL, NULL, "out of memory\n");
|
||||
}
|
||||
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 index (in tblinfo) of the table with the given oid
|
||||
* returns -1 if not found
|
||||
* 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
|
||||
*/
|
||||
int
|
||||
findTableByOid(TableInfo *tblinfo, int numTables, const char *oid)
|
||||
TableInfo *
|
||||
findTableByOid(Oid oid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numTables; i++)
|
||||
{
|
||||
if (strcmp(tblinfo[i].oid, oid) == 0)
|
||||
return i;
|
||||
if (tblinfo[i].dobj.catId.oid == oid)
|
||||
return &tblinfo[i];
|
||||
}
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* findFuncByOid
|
||||
* finds the index (in finfo) of the function with the given OID
|
||||
* returns -1 if not found
|
||||
* findTypeByOid
|
||||
* finds the entry (in tinfo) of the type with the given oid
|
||||
* returns NULL if not found
|
||||
*
|
||||
* NOTE: should hash this, but just do linear search for now
|
||||
*/
|
||||
int
|
||||
findFuncByOid(FuncInfo *finfo, int numFuncs, const char *oid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numFuncs; i++)
|
||||
{
|
||||
if (strcmp(finfo[i].oid, oid) == 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the index (in tinfo) of the type with the given OID. Returns
|
||||
* -1 if not found.
|
||||
*/
|
||||
int
|
||||
findTypeByOid(TypeInfo *tinfo, int numTypes, const char *oid)
|
||||
TypeInfo *
|
||||
findTypeByOid(Oid oid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numTypes; i++)
|
||||
{
|
||||
if (strcmp(tinfo[i].oid, oid) == 0)
|
||||
return i;
|
||||
if (tinfo[i].dobj.catId.oid == oid)
|
||||
return &tinfo[i];
|
||||
}
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* findFuncByOid
|
||||
* finds the entry (in finfo) 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 (finfo[i].dobj.catId.oid == oid)
|
||||
return &finfo[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* findOprByOid
|
||||
* given the oid of an operator, return the name of the operator
|
||||
* 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
|
||||
*/
|
||||
char *
|
||||
findOprByOid(OprInfo *oprinfo, int numOprs, const char *oid)
|
||||
OprInfo *
|
||||
findOprByOid(Oid oid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numOprs; i++)
|
||||
for (i = 0; i < numOperators; i++)
|
||||
{
|
||||
if (strcmp(oprinfo[i].oid, oid) == 0)
|
||||
return oprinfo[i].oprname;
|
||||
if (oprinfo[i].dobj.catId.oid == oid)
|
||||
return &oprinfo[i];
|
||||
}
|
||||
|
||||
/* should never get here */
|
||||
write_msg(NULL, "failed sanity check, operator with OID %s not found\n", oid);
|
||||
|
||||
/* no suitable operator name was found */
|
||||
return (NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* findParentsByOid
|
||||
* given the oid of a class, find its parent classes in tblinfo[]
|
||||
*
|
||||
* Returns the number of parents and their array indexes into the
|
||||
* last two arguments.
|
||||
* find a table's parents in tblinfo[]
|
||||
*/
|
||||
|
||||
static void
|
||||
findParentsByOid(TableInfo *tblinfo, int numTables,
|
||||
InhInfo *inhinfo, int numInherits,
|
||||
const char *oid,
|
||||
int *numParentsPtr, int **parentIndexes)
|
||||
findParentsByOid(TableInfo *self,
|
||||
InhInfo *inhinfo, int numInherits)
|
||||
{
|
||||
Oid oid = self->dobj.catId.oid;
|
||||
int i,
|
||||
j;
|
||||
int parentInd,
|
||||
selfInd;
|
||||
int numParents;
|
||||
|
||||
numParents = 0;
|
||||
for (i = 0; i < numInherits; i++)
|
||||
{
|
||||
if (strcmp(inhinfo[i].inhrelid, oid) == 0)
|
||||
if (inhinfo[i].inhrelid == oid)
|
||||
numParents++;
|
||||
}
|
||||
|
||||
*numParentsPtr = numParents;
|
||||
self->numParents = numParents;
|
||||
|
||||
if (numParents > 0)
|
||||
{
|
||||
*parentIndexes = (int *) malloc(sizeof(int) * numParents);
|
||||
self->parents = (TableInfo **) malloc(sizeof(TableInfo *) * numParents);
|
||||
j = 0;
|
||||
for (i = 0; i < numInherits; i++)
|
||||
{
|
||||
if (strcmp(inhinfo[i].inhrelid, oid) == 0)
|
||||
if (inhinfo[i].inhrelid == oid)
|
||||
{
|
||||
parentInd = findTableByOid(tblinfo, numTables,
|
||||
inhinfo[i].inhparent);
|
||||
if (parentInd < 0)
|
||||
{
|
||||
selfInd = findTableByOid(tblinfo, numTables, oid);
|
||||
if (selfInd >= 0)
|
||||
write_msg(NULL, "failed sanity check, parent OID %s of table \"%s\" (OID %s) not found\n",
|
||||
inhinfo[i].inhparent,
|
||||
tblinfo[selfInd].relname,
|
||||
oid);
|
||||
else
|
||||
write_msg(NULL, "failed sanity check, parent OID %s of table (OID %s) not found\n",
|
||||
inhinfo[i].inhparent,
|
||||
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->relname,
|
||||
oid);
|
||||
exit_nicely();
|
||||
}
|
||||
(*parentIndexes)[j++] = parentInd;
|
||||
self->parents[j++] = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
*parentIndexes = NULL;
|
||||
self->parents = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* parseNumericArray
|
||||
* 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
|
||||
parseNumericArray(const char *str, char **array, int arraysize)
|
||||
parseOidArray(const char *str, Oid *array, int arraysize)
|
||||
{
|
||||
int j,
|
||||
argNum;
|
||||
@ -557,7 +708,7 @@ parseNumericArray(const char *str, char **array, int arraysize)
|
||||
exit_nicely();
|
||||
}
|
||||
temp[j] = '\0';
|
||||
array[argNum++] = strdup(temp);
|
||||
array[argNum++] = atooid(temp);
|
||||
j = 0;
|
||||
}
|
||||
if (s == '\0')
|
||||
@ -576,7 +727,7 @@ parseNumericArray(const char *str, char **array, int arraysize)
|
||||
}
|
||||
|
||||
while (argNum < arraysize)
|
||||
array[argNum++] = strdup("0");
|
||||
array[argNum++] = InvalidOid;
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,19 +15,22 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup.h,v 1.27 2003/11/29 19:52:05 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup.h,v 1.28 2003/12/06 03:00:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef PG_BACKUP__
|
||||
#define PG_BACKUP__
|
||||
#ifndef PG_BACKUP_H
|
||||
#define PG_BACKUP_H
|
||||
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#include "pg_dump.h"
|
||||
|
||||
#include "libpq-fe.h"
|
||||
#include "pqexpbuffer.h"
|
||||
|
||||
|
||||
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
|
||||
#define oidcmp(x,y) ( ((x) < (y) ? -1 : ((x) > (y)) ? 1 : 0) )
|
||||
#define oideq(x,y) ( (x) == (y) )
|
||||
@ -45,7 +48,7 @@ typedef enum _archiveFormat
|
||||
} ArchiveFormat;
|
||||
|
||||
/*
|
||||
* We may want to have so user-readbale data, but in the mean
|
||||
* We may want to have some more user-readable data, but in the mean
|
||||
* time this gives us some abstraction and type checking.
|
||||
*/
|
||||
typedef struct _Archive
|
||||
@ -57,7 +60,7 @@ typedef struct _Archive
|
||||
/* The rest is private */
|
||||
} Archive;
|
||||
|
||||
typedef int (*DataDumperPtr) (Archive *AH, char *oid, void *userArg);
|
||||
typedef int (*DataDumperPtr) (Archive *AH, void *userArg);
|
||||
|
||||
typedef struct _restoreOptions
|
||||
{
|
||||
@ -74,9 +77,6 @@ typedef struct _restoreOptions
|
||||
int aclsSkip;
|
||||
int tocSummary;
|
||||
char *tocFile;
|
||||
int oidOrder;
|
||||
int origOrder;
|
||||
int rearrange;
|
||||
int format;
|
||||
char *formatName;
|
||||
|
||||
@ -98,8 +98,8 @@ typedef struct _restoreOptions
|
||||
int ignoreVersion;
|
||||
int requirePassword;
|
||||
|
||||
int *idWanted;
|
||||
int limitToList;
|
||||
bool *idWanted;
|
||||
bool limitToList;
|
||||
int compression;
|
||||
|
||||
int suppressDumpWarnings; /* Suppress output of WARNING
|
||||
@ -127,11 +127,13 @@ PGconn *ConnectDatabase(Archive *AH,
|
||||
|
||||
|
||||
/* Called to add a TOC entry */
|
||||
extern void ArchiveEntry(Archive *AHX, const char *oid, const char *tag,
|
||||
extern void ArchiveEntry(Archive *AHX,
|
||||
CatalogId catalogId, DumpId dumpId,
|
||||
const char *tag,
|
||||
const char *namespace, const char *owner,
|
||||
const char *desc, const char *((*deps)[]),
|
||||
const char *defn, const char *dropStmt,
|
||||
const char *copyStmt,
|
||||
const char *desc, const char *defn,
|
||||
const char *dropStmt, const char *copyStmt,
|
||||
const DumpId *deps, int nDeps,
|
||||
DataDumperPtr dumpFn, void *dumpArg);
|
||||
|
||||
/* Called to write *data* to the archive */
|
||||
@ -161,19 +163,13 @@ extern void PrintTOCSummary(Archive *AH, RestoreOptions *ropt);
|
||||
extern RestoreOptions *NewRestoreOptions(void);
|
||||
|
||||
/* Rearrange TOC entries */
|
||||
extern void MoveToStart(Archive *AH, const char *oType);
|
||||
extern void MoveToEnd(Archive *AH, const char *oType);
|
||||
extern void SortTocByObjectType(Archive *AH);
|
||||
extern void SortTocByOID(Archive *AH);
|
||||
extern void SortTocByID(Archive *AH);
|
||||
extern void SortTocFromFile(Archive *AH, RestoreOptions *ropt);
|
||||
|
||||
/* Convenience functions used only when writing DATA */
|
||||
extern int archputs(const char *s, Archive *AH);
|
||||
extern int archputc(const char c, Archive *AH);
|
||||
extern int
|
||||
archprintf(Archive *AH, const char *fmt,...)
|
||||
/* This extension allows gcc to check the format string */
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
|
||||
#endif
|
||||
#endif /* PG_BACKUP_H */
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.80 2003/11/29 19:52:05 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.81 2003/12/06 03:00:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -41,9 +41,10 @@ typedef enum _teReqs_
|
||||
REQ_ALL = REQ_SCHEMA + REQ_DATA
|
||||
} teReqs;
|
||||
|
||||
static void _SortToc(ArchiveHandle *AH, TocSortCompareFn fn);
|
||||
static int _tocSortCompareByOIDNum(const void *p1, const void *p2);
|
||||
static int _tocSortCompareByIDNum(const void *p1, const void *p2);
|
||||
const char *progname;
|
||||
static char *modulename = gettext_noop("archiver");
|
||||
|
||||
|
||||
static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt,
|
||||
const int compression, ArchiveMode mode);
|
||||
static int _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData);
|
||||
@ -57,15 +58,9 @@ static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName);
|
||||
static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt);
|
||||
static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
|
||||
static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
|
||||
static TocEntry *_getTocEntry(ArchiveHandle *AH, int id);
|
||||
static TocEntry *getTocEntryByDumpId(ArchiveHandle *AH, DumpId id);
|
||||
static void _moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te);
|
||||
static void _moveBefore(ArchiveHandle *AH, TocEntry *pos, TocEntry *te);
|
||||
static int _discoverArchiveFormat(ArchiveHandle *AH);
|
||||
static void _fixupOidInfo(TocEntry *te);
|
||||
static Oid _findMaxOID(const char *((*deps)[]));
|
||||
|
||||
const char *progname;
|
||||
static char *modulename = gettext_noop("archiver");
|
||||
|
||||
static void _write_msg(const char *modulename, const char *fmt, va_list ap);
|
||||
static void _die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt, va_list ap);
|
||||
@ -73,6 +68,7 @@ static void _die_horribly(ArchiveHandle *AH, const char *modulename, const char
|
||||
static int _canRestoreBlobs(ArchiveHandle *AH);
|
||||
static int _restoringToDB(ArchiveHandle *AH);
|
||||
|
||||
|
||||
/*
|
||||
* Wrapper functions.
|
||||
*
|
||||
@ -534,29 +530,33 @@ WriteData(Archive *AHX, const void *data, size_t dLen)
|
||||
|
||||
/* Public */
|
||||
void
|
||||
ArchiveEntry(Archive *AHX, const char *oid, const char *tag,
|
||||
ArchiveEntry(Archive *AHX,
|
||||
CatalogId catalogId, DumpId dumpId,
|
||||
const char *tag,
|
||||
const char *namespace, const char *owner,
|
||||
const char *desc, const char *((*deps)[]),
|
||||
const char *defn, const char *dropStmt,
|
||||
const char *copyStmt,
|
||||
const char *desc, const char *defn,
|
||||
const char *dropStmt, const char *copyStmt,
|
||||
const DumpId *deps, int nDeps,
|
||||
DataDumperPtr dumpFn, void *dumpArg)
|
||||
{
|
||||
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
||||
TocEntry *newToc;
|
||||
|
||||
AH->lastID++;
|
||||
AH->tocCount++;
|
||||
|
||||
newToc = (TocEntry *) calloc(1, sizeof(TocEntry));
|
||||
if (!newToc)
|
||||
die_horribly(AH, modulename, "out of memory\n");
|
||||
|
||||
AH->tocCount++;
|
||||
if (dumpId > AH->maxDumpId)
|
||||
AH->maxDumpId = dumpId;
|
||||
|
||||
newToc->prev = AH->toc->prev;
|
||||
newToc->next = AH->toc;
|
||||
AH->toc->prev->next = newToc;
|
||||
AH->toc->prev = newToc;
|
||||
|
||||
newToc->id = AH->lastID;
|
||||
newToc->catalogId = catalogId;
|
||||
newToc->dumpId = dumpId;
|
||||
|
||||
newToc->tag = strdup(tag);
|
||||
newToc->namespace = namespace ? strdup(namespace) : NULL;
|
||||
@ -566,24 +566,26 @@ ArchiveEntry(Archive *AHX, const char *oid, const char *tag,
|
||||
newToc->dropStmt = strdup(dropStmt);
|
||||
newToc->copyStmt = copyStmt ? strdup(copyStmt) : NULL;
|
||||
|
||||
newToc->oid = strdup(oid);
|
||||
newToc->depOid = deps; /* NB: not copied */
|
||||
_fixupOidInfo(newToc);
|
||||
if (nDeps > 0)
|
||||
{
|
||||
newToc->dependencies = (DumpId *) malloc(nDeps * sizeof(DumpId));
|
||||
memcpy(newToc->dependencies, deps, nDeps * sizeof(DumpId));
|
||||
newToc->nDeps = nDeps;
|
||||
}
|
||||
else
|
||||
{
|
||||
newToc->dependencies = NULL;
|
||||
newToc->nDeps = 0;
|
||||
}
|
||||
|
||||
newToc->printed = 0;
|
||||
newToc->formatData = NULL;
|
||||
newToc->dataDumper = dumpFn;
|
||||
newToc->dataDumperArg = dumpArg;
|
||||
newToc->hadDumper = dumpFn ? true : false;
|
||||
|
||||
newToc->hadDumper = dumpFn ? 1 : 0;
|
||||
newToc->formatData = NULL;
|
||||
|
||||
if (AH->ArchiveEntryPtr !=NULL)
|
||||
if (AH->ArchiveEntryPtr != NULL)
|
||||
(*AH->ArchiveEntryPtr) (AH, newToc);
|
||||
|
||||
/*
|
||||
* printf("New toc owned by '%s', oid %u\n", newToc->owner,
|
||||
* newToc->oidVal);
|
||||
*/
|
||||
}
|
||||
|
||||
/* Public */
|
||||
@ -627,7 +629,9 @@ PrintTOCSummary(Archive *AHX, RestoreOptions *ropt)
|
||||
while (te != AH->toc)
|
||||
{
|
||||
if (_tocEntryRequired(te, ropt) != 0)
|
||||
ahprintf(AH, "%d; %d %s %s %s\n", te->id, te->oidVal, te->desc, te->tag, te->owner);
|
||||
ahprintf(AH, "%d; %u %u %s %s %s\n", te->dumpId,
|
||||
te->catalogId.tableoid, te->catalogId.oid,
|
||||
te->desc, te->tag, te->owner);
|
||||
te = te->next;
|
||||
}
|
||||
|
||||
@ -781,127 +785,6 @@ EndRestoreBlob(ArchiveHandle *AH, Oid oid)
|
||||
* Sorting and Reordering
|
||||
***********/
|
||||
|
||||
/*
|
||||
* Move TOC entries of the specified type to the START of the TOC.
|
||||
*
|
||||
* This is public, but if you use it anywhere but SortTocByObjectType,
|
||||
* you are risking breaking things.
|
||||
*/
|
||||
void
|
||||
MoveToStart(Archive *AHX, const char *oType)
|
||||
{
|
||||
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
||||
TocEntry *te = AH->toc->next;
|
||||
TocEntry *newTe;
|
||||
|
||||
while (te != AH->toc)
|
||||
{
|
||||
te->_moved = 0;
|
||||
te = te->next;
|
||||
}
|
||||
|
||||
te = AH->toc->prev;
|
||||
while (te != AH->toc && !te->_moved)
|
||||
{
|
||||
newTe = te->prev;
|
||||
if (strcmp(te->desc, oType) == 0)
|
||||
_moveAfter(AH, AH->toc, te);
|
||||
te = newTe;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Move TOC entries of the specified type to the end of the TOC.
|
||||
*
|
||||
* This is public, but if you use it anywhere but SortTocByObjectType,
|
||||
* you are risking breaking things.
|
||||
*/
|
||||
void
|
||||
MoveToEnd(Archive *AHX, const char *oType)
|
||||
{
|
||||
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
||||
TocEntry *te = AH->toc->next;
|
||||
TocEntry *newTe;
|
||||
|
||||
while (te != AH->toc)
|
||||
{
|
||||
te->_moved = 0;
|
||||
te = te->next;
|
||||
}
|
||||
|
||||
te = AH->toc->next;
|
||||
while (te != AH->toc && !te->_moved)
|
||||
{
|
||||
newTe = te->next;
|
||||
if (strcmp(te->desc, oType) == 0)
|
||||
_moveBefore(AH, AH->toc, te);
|
||||
te = newTe;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort TOC by object type (items of same type keep same relative order)
|
||||
*
|
||||
* This is factored out to ensure that pg_dump and pg_restore stay in sync
|
||||
* about the standard ordering.
|
||||
*/
|
||||
void
|
||||
SortTocByObjectType(Archive *AH)
|
||||
{
|
||||
/*
|
||||
* Procedural languages have to be declared just after database and
|
||||
* schema creation, before they are used.
|
||||
*/
|
||||
MoveToStart(AH, "ACL LANGUAGE");
|
||||
MoveToStart(AH, "PROCEDURAL LANGUAGE");
|
||||
MoveToStart(AH, "FUNC PROCEDURAL LANGUAGE");
|
||||
MoveToStart(AH, "SCHEMA");
|
||||
MoveToStart(AH, "<Init>");
|
||||
/* Database entries *must* be at front (see also pg_restore.c) */
|
||||
MoveToStart(AH, "DATABASE");
|
||||
|
||||
MoveToEnd(AH, "TABLE DATA");
|
||||
MoveToEnd(AH, "BLOBS");
|
||||
MoveToEnd(AH, "INDEX");
|
||||
MoveToEnd(AH, "CONSTRAINT");
|
||||
MoveToEnd(AH, "FK CONSTRAINT");
|
||||
MoveToEnd(AH, "TRIGGER");
|
||||
MoveToEnd(AH, "RULE");
|
||||
MoveToEnd(AH, "SEQUENCE SET");
|
||||
|
||||
/*
|
||||
* Moving all comments to end is annoying, but must do it for comments
|
||||
* on stuff we just moved, and we don't seem to have quite enough
|
||||
* dependency structure to get it really right...
|
||||
*/
|
||||
MoveToEnd(AH, "COMMENT");
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort TOC by OID
|
||||
*/
|
||||
/* Public */
|
||||
void
|
||||
SortTocByOID(Archive *AHX)
|
||||
{
|
||||
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
||||
|
||||
_SortToc(AH, _tocSortCompareByOIDNum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort TOC by ID
|
||||
*/
|
||||
/* Public */
|
||||
void
|
||||
SortTocByID(Archive *AHX)
|
||||
{
|
||||
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
||||
|
||||
_SortToc(AH, _tocSortCompareByIDNum);
|
||||
}
|
||||
|
||||
void
|
||||
SortTocFromFile(Archive *AHX, RestoreOptions *ropt)
|
||||
{
|
||||
@ -910,25 +793,14 @@ SortTocFromFile(Archive *AHX, RestoreOptions *ropt)
|
||||
char buf[1024];
|
||||
char *cmnt;
|
||||
char *endptr;
|
||||
int id;
|
||||
DumpId id;
|
||||
TocEntry *te;
|
||||
TocEntry *tePrev;
|
||||
int i;
|
||||
|
||||
/* Allocate space for the 'wanted' array, and init it */
|
||||
ropt->idWanted = (int *) malloc(sizeof(int) * AH->tocCount);
|
||||
for (i = 0; i < AH->tocCount; i++)
|
||||
ropt->idWanted[i] = 0;
|
||||
|
||||
ropt->limitToList = 1;
|
||||
|
||||
/* Mark all entries as 'not moved' */
|
||||
te = AH->toc->next;
|
||||
while (te != AH->toc)
|
||||
{
|
||||
te->_moved = 0;
|
||||
te = te->next;
|
||||
}
|
||||
ropt->idWanted = (bool *) malloc(sizeof(bool) * AH->maxDumpId);
|
||||
memset(ropt->idWanted, 0, sizeof(bool) * AH->maxDumpId);
|
||||
ropt->limitToList = true;
|
||||
|
||||
/* Set prev entry as head of list */
|
||||
tePrev = AH->toc;
|
||||
@ -955,25 +827,27 @@ SortTocFromFile(Archive *AHX, RestoreOptions *ropt)
|
||||
|
||||
/* Get an ID */
|
||||
id = strtol(buf, &endptr, 10);
|
||||
if (endptr == buf)
|
||||
if (endptr == buf || id <= 0 || id > AH->maxDumpId)
|
||||
{
|
||||
write_msg(modulename, "WARNING: line ignored: %s\n", buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find TOC entry */
|
||||
te = _getTocEntry(AH, id);
|
||||
te = getTocEntryByDumpId(AH, id);
|
||||
if (!te)
|
||||
die_horribly(AH, modulename, "could not find entry for ID %d\n", id);
|
||||
die_horribly(AH, modulename, "could not find entry for ID %d\n",
|
||||
id);
|
||||
|
||||
ropt->idWanted[id - 1] = 1;
|
||||
ropt->idWanted[id - 1] = true;
|
||||
|
||||
_moveAfter(AH, tePrev, te);
|
||||
tePrev = te;
|
||||
}
|
||||
|
||||
if (fclose(fh) != 0)
|
||||
die_horribly(AH, modulename, "could not close TOC file: %s\n", strerror(errno));
|
||||
die_horribly(AH, modulename, "could not close TOC file: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
/**********************
|
||||
@ -988,13 +862,6 @@ archputs(const char *s, Archive *AH)
|
||||
return WriteData(AH, s, strlen(s));
|
||||
}
|
||||
|
||||
/* Public */
|
||||
int
|
||||
archputc(const char c, Archive *AH)
|
||||
{
|
||||
return WriteData(AH, &c, 1);
|
||||
}
|
||||
|
||||
/* Public */
|
||||
int
|
||||
archprintf(Archive *AH, const char *fmt,...)
|
||||
@ -1007,9 +874,6 @@ archprintf(Archive *AH, const char *fmt,...)
|
||||
/*
|
||||
* This is paranoid: deal with the possibility that vsnprintf is
|
||||
* willing to ignore trailing null
|
||||
*/
|
||||
|
||||
/*
|
||||
* or returns > 0 even if string does not fit. It may be the case that
|
||||
* it returns cnt = bufsize
|
||||
*/
|
||||
@ -1287,6 +1151,7 @@ exit_horribly(Archive *AH, const char *modulename, const char *fmt,...)
|
||||
|
||||
va_start(ap, fmt);
|
||||
_die_horribly((ArchiveHandle *) AH, modulename, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Archiver use (just different arg declaration) */
|
||||
@ -1297,6 +1162,7 @@ die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt,...)
|
||||
|
||||
va_start(ap, fmt);
|
||||
_die_horribly(AH, modulename, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
@ -1311,10 +1177,10 @@ _moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
|
||||
|
||||
pos->next->prev = te;
|
||||
pos->next = te;
|
||||
|
||||
te->_moved = 1;
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
||||
static void
|
||||
_moveBefore(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
|
||||
{
|
||||
@ -1325,19 +1191,19 @@ _moveBefore(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
|
||||
te->next = pos;
|
||||
pos->prev->next = te;
|
||||
pos->prev = te;
|
||||
|
||||
te->_moved = 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static TocEntry *
|
||||
_getTocEntry(ArchiveHandle *AH, int id)
|
||||
getTocEntryByDumpId(ArchiveHandle *AH, DumpId id)
|
||||
{
|
||||
TocEntry *te;
|
||||
|
||||
te = AH->toc->next;
|
||||
while (te != AH->toc)
|
||||
{
|
||||
if (te->id == id)
|
||||
if (te->dumpId == id)
|
||||
return te;
|
||||
te = te->next;
|
||||
}
|
||||
@ -1345,9 +1211,9 @@ _getTocEntry(ArchiveHandle *AH, int id)
|
||||
}
|
||||
|
||||
int
|
||||
TocIDRequired(ArchiveHandle *AH, int id, RestoreOptions *ropt)
|
||||
TocIDRequired(ArchiveHandle *AH, DumpId id, RestoreOptions *ropt)
|
||||
{
|
||||
TocEntry *te = _getTocEntry(AH, id);
|
||||
TocEntry *te = getTocEntryByDumpId(AH, id);
|
||||
|
||||
if (!te)
|
||||
return 0;
|
||||
@ -1685,7 +1551,6 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
|
||||
|
||||
AH->intSize = sizeof(int);
|
||||
AH->offSize = sizeof(off_t);
|
||||
AH->lastID = 0;
|
||||
if (FileSpec)
|
||||
{
|
||||
AH->fSpec = strdup(FileSpec);
|
||||
@ -1795,7 +1660,7 @@ WriteDataChunks(ArchiveHandle *AH)
|
||||
* The user-provided DataDumper routine needs to call
|
||||
* AH->WriteData
|
||||
*/
|
||||
(*te->dataDumper) ((Archive *) AH, te->oid, te->dataDumperArg);
|
||||
(*te->dataDumper) ((Archive *) AH, te->dataDumperArg);
|
||||
|
||||
if (endPtr != NULL)
|
||||
(*endPtr) (AH, te);
|
||||
@ -1808,18 +1673,24 @@ WriteDataChunks(ArchiveHandle *AH)
|
||||
void
|
||||
WriteToc(ArchiveHandle *AH)
|
||||
{
|
||||
TocEntry *te = AH->toc->next;
|
||||
const char *dep;
|
||||
TocEntry *te;
|
||||
char workbuf[32];
|
||||
int i;
|
||||
|
||||
/* printf("%d TOC Entries to save\n", AH->tocCount); */
|
||||
|
||||
WriteInt(AH, AH->tocCount);
|
||||
while (te != AH->toc)
|
||||
|
||||
for (te = AH->toc->next; te != AH->toc; te = te->next)
|
||||
{
|
||||
WriteInt(AH, te->id);
|
||||
WriteInt(AH, te->dumpId);
|
||||
WriteInt(AH, te->dataDumper ? 1 : 0);
|
||||
WriteStr(AH, te->oid);
|
||||
|
||||
/* OID is recorded as a string for historical reasons */
|
||||
sprintf(workbuf, "%u", te->catalogId.tableoid);
|
||||
WriteStr(AH, workbuf);
|
||||
sprintf(workbuf, "%u", te->catalogId.oid);
|
||||
WriteStr(AH, workbuf);
|
||||
|
||||
WriteStr(AH, te->tag);
|
||||
WriteStr(AH, te->desc);
|
||||
@ -1830,17 +1701,15 @@ WriteToc(ArchiveHandle *AH)
|
||||
WriteStr(AH, te->owner);
|
||||
|
||||
/* Dump list of dependencies */
|
||||
if (te->depOid != NULL)
|
||||
for (i = 0; i < te->nDeps; i++)
|
||||
{
|
||||
i = 0;
|
||||
while ((dep = (*te->depOid)[i++]) != NULL)
|
||||
WriteStr(AH, dep);
|
||||
sprintf(workbuf, "%d", te->dependencies[i]);
|
||||
WriteStr(AH, workbuf);
|
||||
}
|
||||
WriteStr(AH, NULL); /* Terminate List */
|
||||
|
||||
if (AH->WriteExtraTocPtr)
|
||||
(*AH->WriteExtraTocPtr) (AH, te);
|
||||
te = te->next;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1848,27 +1717,43 @@ void
|
||||
ReadToc(ArchiveHandle *AH)
|
||||
{
|
||||
int i;
|
||||
char *((*deps)[]);
|
||||
char *tmp;
|
||||
DumpId *deps;
|
||||
int depIdx;
|
||||
int depSize;
|
||||
|
||||
TocEntry *te = AH->toc->next;
|
||||
|
||||
AH->tocCount = ReadInt(AH);
|
||||
AH->maxDumpId = 0;
|
||||
|
||||
for (i = 0; i < AH->tocCount; i++)
|
||||
{
|
||||
|
||||
te = (TocEntry *) calloc(1, sizeof(TocEntry));
|
||||
te->id = ReadInt(AH);
|
||||
te->dumpId = ReadInt(AH);
|
||||
|
||||
if (te->dumpId > AH->maxDumpId)
|
||||
AH->maxDumpId = te->dumpId;
|
||||
|
||||
/* Sanity check */
|
||||
if (te->id <= 0 || te->id > AH->tocCount)
|
||||
die_horribly(AH, modulename, "entry ID %d out of range -- perhaps a corrupt TOC\n", te->id);
|
||||
if (te->dumpId <= 0)
|
||||
die_horribly(AH, modulename,
|
||||
"entry ID %d out of range -- perhaps a corrupt TOC\n",
|
||||
te->dumpId);
|
||||
|
||||
te->hadDumper = ReadInt(AH);
|
||||
te->oid = ReadStr(AH);
|
||||
te->oidVal = atooid(te->oid);
|
||||
|
||||
if (AH->version >= K_VERS_1_8)
|
||||
{
|
||||
tmp = ReadStr(AH);
|
||||
sscanf(tmp, "%u", &te->catalogId.tableoid);
|
||||
free(tmp);
|
||||
}
|
||||
else
|
||||
te->catalogId.tableoid = InvalidOid;
|
||||
tmp = ReadStr(AH);
|
||||
sscanf(tmp, "%u", &te->catalogId.oid);
|
||||
free(tmp);
|
||||
|
||||
te->tag = ReadStr(AH);
|
||||
te->desc = ReadStr(AH);
|
||||
@ -1887,41 +1772,47 @@ ReadToc(ArchiveHandle *AH)
|
||||
if (AH->version >= K_VERS_1_5)
|
||||
{
|
||||
depSize = 100;
|
||||
deps = malloc(sizeof(char *) * depSize);
|
||||
deps = (DumpId *) malloc(sizeof(DumpId) * depSize);
|
||||
depIdx = 0;
|
||||
do
|
||||
for (;;)
|
||||
{
|
||||
tmp = ReadStr(AH);
|
||||
if (!tmp)
|
||||
break; /* end of list */
|
||||
if (depIdx >= depSize)
|
||||
{
|
||||
depSize *= 2;
|
||||
deps = realloc(deps, sizeof(char *) * depSize);
|
||||
deps = (DumpId *) realloc(deps, sizeof(DumpId) * depSize);
|
||||
}
|
||||
(*deps)[depIdx] = ReadStr(AH);
|
||||
#if 0
|
||||
if ((*deps)[depIdx])
|
||||
write_msg(modulename, "read dependency for %s -> %s\n",
|
||||
te->tag, (*deps)[depIdx]);
|
||||
#endif
|
||||
} while ((*deps)[depIdx++] != NULL);
|
||||
sscanf(tmp, "%d", &deps[depIdx]);
|
||||
free(tmp);
|
||||
depIdx++;
|
||||
}
|
||||
|
||||
if (depIdx > 1) /* We have a non-null entry */
|
||||
te->depOid = realloc(deps, sizeof(char *) * depIdx); /* trim it */
|
||||
if (depIdx > 0) /* We have a non-null entry */
|
||||
{
|
||||
deps = (DumpId *) realloc(deps, sizeof(DumpId) * depIdx);
|
||||
te->dependencies = deps;
|
||||
te->nDeps = depIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(deps);
|
||||
te->depOid = NULL; /* no deps */
|
||||
te->dependencies = NULL;
|
||||
te->nDeps = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
te->depOid = NULL;
|
||||
|
||||
/* Set maxOidVal etc for use in sorting */
|
||||
_fixupOidInfo(te);
|
||||
{
|
||||
te->dependencies = NULL;
|
||||
te->nDeps = 0;
|
||||
}
|
||||
|
||||
if (AH->ReadExtraTocPtr)
|
||||
(*AH->ReadExtraTocPtr) (AH, te);
|
||||
|
||||
ahlog(AH, 3, "read TOC entry %d (ID %d) for %s %s\n", i, te->id, te->desc, te->tag);
|
||||
ahlog(AH, 3, "read TOC entry %d (ID %d) for %s %s\n",
|
||||
i, te->dumpId, te->desc, te->tag);
|
||||
|
||||
te->prev = AH->toc->prev;
|
||||
AH->toc->prev->next = te;
|
||||
@ -2013,7 +1904,7 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt)
|
||||
res = res & ~REQ_SCHEMA;
|
||||
|
||||
/* Finally, if we used a list, limit based on that as well */
|
||||
if (ropt->limitToList && !ropt->idWanted[te->id - 1])
|
||||
if (ropt->limitToList && !ropt->idWanted[te->dumpId - 1])
|
||||
return 0;
|
||||
|
||||
return res;
|
||||
@ -2190,7 +2081,7 @@ _selectOutputSchema(ArchiveHandle *AH, const char *schemaName)
|
||||
static int
|
||||
_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData)
|
||||
{
|
||||
char *pfx;
|
||||
const char *pfx;
|
||||
|
||||
/* Select owner and schema as necessary */
|
||||
_becomeOwner(AH, te);
|
||||
@ -2208,8 +2099,23 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
|
||||
else
|
||||
pfx = "";
|
||||
|
||||
ahprintf(AH, "--\n-- %sTOC entry %d (OID %s)\n-- Name: %s; Type: %s; Schema: %s; Owner: %s\n",
|
||||
pfx, te->id, te->oid, te->tag, te->desc,
|
||||
ahprintf(AH, "--\n");
|
||||
if (AH->public.verbose)
|
||||
{
|
||||
ahprintf(AH, "-- TOC entry %d (class %u OID %u)\n",
|
||||
te->dumpId, te->catalogId.tableoid, te->catalogId.oid);
|
||||
if (te->nDeps > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
ahprintf(AH, "-- Dependencies:");
|
||||
for (i = 0; i < te->nDeps; i++)
|
||||
ahprintf(AH, " %d", te->dependencies[i]);
|
||||
ahprintf(AH, "\n");
|
||||
}
|
||||
}
|
||||
ahprintf(AH, "-- %sName: %s; Type: %s; Schema: %s; Owner: %s\n",
|
||||
pfx, te->tag, te->desc,
|
||||
te->namespace ? te->namespace : "-",
|
||||
te->owner);
|
||||
if (AH->PrintExtraTocPtr !=NULL)
|
||||
@ -2381,181 +2287,3 @@ checkSeek(FILE *fp)
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
_SortToc(ArchiveHandle *AH, TocSortCompareFn fn)
|
||||
{
|
||||
TocEntry **tea;
|
||||
TocEntry *te;
|
||||
int i;
|
||||
|
||||
/* Allocate an array for quicksort (TOC size + head & foot) */
|
||||
tea = (TocEntry **) malloc(sizeof(TocEntry *) * (AH->tocCount + 2));
|
||||
|
||||
/* Build array of toc entries, including header at start and end */
|
||||
te = AH->toc;
|
||||
for (i = 0; i <= AH->tocCount + 1; i++)
|
||||
{
|
||||
/*
|
||||
* printf("%d: %x (%x, %x) - %u\n", i, te, te->prev, te->next,
|
||||
* te->oidVal);
|
||||
*/
|
||||
tea[i] = te;
|
||||
te = te->next;
|
||||
}
|
||||
|
||||
/* Sort it, but ignore the header entries */
|
||||
qsort(&(tea[1]), AH->tocCount, sizeof(TocEntry *), fn);
|
||||
|
||||
/* Rebuild list: this works because we have headers at each end */
|
||||
for (i = 1; i <= AH->tocCount; i++)
|
||||
{
|
||||
tea[i]->next = tea[i + 1];
|
||||
tea[i]->prev = tea[i - 1];
|
||||
}
|
||||
|
||||
|
||||
te = AH->toc;
|
||||
for (i = 0; i <= AH->tocCount + 1; i++)
|
||||
{
|
||||
/*
|
||||
* printf("%d: %x (%x, %x) - %u\n", i, te, te->prev, te->next,
|
||||
* te->oidVal);
|
||||
*/
|
||||
te = te->next;
|
||||
}
|
||||
|
||||
|
||||
AH->toc->next = tea[1];
|
||||
AH->toc->prev = tea[AH->tocCount];
|
||||
}
|
||||
|
||||
static int
|
||||
_tocSortCompareByOIDNum(const void *p1, const void *p2)
|
||||
{
|
||||
TocEntry *te1 = *(TocEntry **) p1;
|
||||
TocEntry *te2 = *(TocEntry **) p2;
|
||||
Oid id1 = te1->maxOidVal;
|
||||
Oid id2 = te2->maxOidVal;
|
||||
int cmpval;
|
||||
|
||||
/* printf("Comparing %u to %u\n", id1, id2); */
|
||||
|
||||
cmpval = oidcmp(id1, id2);
|
||||
|
||||
/* If we have a deterministic answer, return it. */
|
||||
if (cmpval != 0)
|
||||
return cmpval;
|
||||
|
||||
/* More comparisons required */
|
||||
if (oideq(id1, te1->maxDepOidVal)) /* maxOid1 came from deps */
|
||||
{
|
||||
if (oideq(id2, te2->maxDepOidVal)) /* maxOid2 also came from
|
||||
* deps */
|
||||
{
|
||||
cmpval = oidcmp(te1->oidVal, te2->oidVal); /* Just compare base
|
||||
* OIDs */
|
||||
}
|
||||
else
|
||||
/* MaxOid2 was entry OID */
|
||||
{
|
||||
return 1; /* entry1 > entry2 */
|
||||
};
|
||||
}
|
||||
else
|
||||
/* must have oideq(id1, te1->oidVal) => maxOid1 = Oid1 */
|
||||
{
|
||||
if (oideq(id2, te2->maxDepOidVal)) /* maxOid2 came from deps */
|
||||
{
|
||||
return -1; /* entry1 < entry2 */
|
||||
}
|
||||
else
|
||||
/* MaxOid2 was entry OID - deps don't matter */
|
||||
{
|
||||
cmpval = 0;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* If we get here, then we've done another comparison Once again, a 0
|
||||
* result means we require even more
|
||||
*/
|
||||
if (cmpval != 0)
|
||||
return cmpval;
|
||||
|
||||
/*
|
||||
* Entire OID details match, so use ID number (ie. original pg_dump
|
||||
* order)
|
||||
*/
|
||||
return _tocSortCompareByIDNum(te1, te2);
|
||||
}
|
||||
|
||||
static int
|
||||
_tocSortCompareByIDNum(const void *p1, const void *p2)
|
||||
{
|
||||
TocEntry *te1 = *(TocEntry **) p1;
|
||||
TocEntry *te2 = *(TocEntry **) p2;
|
||||
int id1 = te1->id;
|
||||
int id2 = te2->id;
|
||||
|
||||
/* printf("Comparing %d to %d\n", id1, id2); */
|
||||
|
||||
if (id1 < id2)
|
||||
return -1;
|
||||
else if (id1 > id2)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assuming Oid and depOid are set, work out the various
|
||||
* Oid values used in sorting.
|
||||
*/
|
||||
static void
|
||||
_fixupOidInfo(TocEntry *te)
|
||||
{
|
||||
te->oidVal = atooid(te->oid);
|
||||
te->maxDepOidVal = _findMaxOID(te->depOid);
|
||||
|
||||
/* For the purpose of sorting, find the max OID. */
|
||||
if (oidcmp(te->oidVal, te->maxDepOidVal) >= 0)
|
||||
te->maxOidVal = te->oidVal;
|
||||
else
|
||||
te->maxOidVal = te->maxDepOidVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the max OID value for a given list of string Oid values
|
||||
*/
|
||||
static Oid
|
||||
_findMaxOID(const char *((*deps)[]))
|
||||
{
|
||||
const char *dep;
|
||||
int i;
|
||||
Oid maxOid = (Oid) 0;
|
||||
Oid currOid;
|
||||
|
||||
if (!deps)
|
||||
return maxOid;
|
||||
|
||||
i = 0;
|
||||
while ((dep = (*deps)[i++]) != NULL)
|
||||
{
|
||||
currOid = atooid(dep);
|
||||
if (oidcmp(maxOid, currOid) < 0)
|
||||
maxOid = currOid;
|
||||
}
|
||||
|
||||
return maxOid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Maybe I can use this somewhere...
|
||||
*
|
||||
*create table pgdump_blob_path(p text);
|
||||
*insert into pgdump_blob_path values('/home/pjw/work/postgresql-cvs/pgsql/src/bin/pg_dump_140');
|
||||
*
|
||||
*insert into dump_blob_xref select 12345,lo_import(p || '/q.q') from pgdump_blob_path;
|
||||
*/
|
||||
|
@ -17,7 +17,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.53 2003/11/29 19:52:05 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.54 2003/12/06 03:00:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -59,7 +59,7 @@ typedef z_stream *z_streamp;
|
||||
#include "libpq-fe.h"
|
||||
|
||||
#define K_VERS_MAJOR 1
|
||||
#define K_VERS_MINOR 7
|
||||
#define K_VERS_MINOR 8
|
||||
#define K_VERS_REV 0
|
||||
|
||||
/* Data block types */
|
||||
@ -76,7 +76,9 @@ typedef z_stream *z_streamp;
|
||||
#define K_VERS_1_6 (( (1 * 256 + 6) * 256 + 0) * 256 + 0) /* Schema field in TOCs */
|
||||
#define K_VERS_1_7 (( (1 * 256 + 7) * 256 + 0) * 256 + 0) /* File Offset size in
|
||||
* header */
|
||||
#define K_VERS_MAX (( (1 * 256 + 7) * 256 + 255) * 256 + 0)
|
||||
#define K_VERS_1_8 (( (1 * 256 + 8) * 256 + 0) * 256 + 0) /* change interpretation of ID numbers and dependencies */
|
||||
|
||||
#define K_VERS_MAX (( (1 * 256 + 8) * 256 + 255) * 256 + 0)
|
||||
|
||||
/* No of BLOBs to restore in 1 TX */
|
||||
#define BLOB_BATCH_SIZE 100
|
||||
@ -114,8 +116,6 @@ typedef void (*PrintTocDataPtr) (struct _archiveHandle * AH, struct _tocEntry *
|
||||
|
||||
typedef size_t (*CustomOutPtr) (struct _archiveHandle * AH, const void *buf, size_t len);
|
||||
|
||||
typedef int (*TocSortCompareFn) (const void *te1, const void *te2);
|
||||
|
||||
typedef enum _archiveMode
|
||||
{
|
||||
archModeWrite,
|
||||
@ -222,7 +222,6 @@ typedef struct _archiveHandle
|
||||
int createdBlobXref; /* Flag */
|
||||
int blobCount; /* # of blobs restored */
|
||||
|
||||
int lastID; /* Last internal ID for a TOC entry */
|
||||
char *fSpec; /* Archive File Spec */
|
||||
FILE *FH; /* General purpose file handle */
|
||||
void *OF;
|
||||
@ -230,6 +229,8 @@ typedef struct _archiveHandle
|
||||
|
||||
struct _tocEntry *toc; /* List of TOC entries */
|
||||
int tocCount; /* Number of TOC entries */
|
||||
DumpId maxDumpId; /* largest DumpId among all TOC entries */
|
||||
|
||||
struct _tocEntry *currToc; /* Used when dumping data */
|
||||
int compression; /* Compression requested on open */
|
||||
ArchiveMode mode; /* File mode - r or w */
|
||||
@ -252,8 +253,9 @@ typedef struct _tocEntry
|
||||
{
|
||||
struct _tocEntry *prev;
|
||||
struct _tocEntry *next;
|
||||
int id;
|
||||
int hadDumper; /* Archiver was passed a dumper routine
|
||||
CatalogId catalogId;
|
||||
DumpId dumpId;
|
||||
bool hadDumper; /* Archiver was passed a dumper routine
|
||||
* (used in restore) */
|
||||
char *tag; /* index tag */
|
||||
char *namespace; /* null or empty string if not in a schema */
|
||||
@ -262,23 +264,17 @@ typedef struct _tocEntry
|
||||
char *defn;
|
||||
char *dropStmt;
|
||||
char *copyStmt;
|
||||
char *oid; /* Oid of source of entry */
|
||||
Oid oidVal; /* Value of above */
|
||||
const char *((*depOid)[]);
|
||||
Oid maxDepOidVal; /* Value of largest OID in deps */
|
||||
Oid maxOidVal; /* Max of entry OID and max dep OID */
|
||||
DumpId *dependencies; /* dumpIds of objects this one depends on */
|
||||
int nDeps; /* number of dependencies */
|
||||
|
||||
int printed; /* Indicates if entry defn has been dumped */
|
||||
DataDumperPtr dataDumper; /* Routine to dump data for object */
|
||||
void *dataDumperArg; /* Arg for above routine */
|
||||
void *formatData; /* TOC Entry data specific to file format */
|
||||
|
||||
int _moved; /* Marker used when rearranging TOC */
|
||||
|
||||
} TocEntry;
|
||||
|
||||
/* Used everywhere */
|
||||
extern const char *progname;
|
||||
|
||||
extern void die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt,...) __attribute__((format(printf, 3, 4)));
|
||||
extern void write_msg(const char *modulename, const char *fmt,...) __attribute__((format(printf, 2, 3)));
|
||||
|
||||
@ -290,7 +286,7 @@ extern void WriteToc(ArchiveHandle *AH);
|
||||
extern void ReadToc(ArchiveHandle *AH);
|
||||
extern void WriteDataChunks(ArchiveHandle *AH);
|
||||
|
||||
extern int TocIDRequired(ArchiveHandle *AH, int id, RestoreOptions *ropt);
|
||||
extern int TocIDRequired(ArchiveHandle *AH, DumpId id, RestoreOptions *ropt);
|
||||
extern bool checkSeek(FILE *fp);
|
||||
|
||||
/*
|
||||
|
@ -19,7 +19,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_custom.c,v 1.27 2003/11/29 19:52:05 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_custom.c,v 1.28 2003/12/06 03:00:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -303,7 +303,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)
|
||||
tctx->dataState = K_OFFSET_POS_SET;
|
||||
|
||||
_WriteByte(AH, BLK_DATA); /* Block type */
|
||||
WriteInt(AH, te->id); /* For sanity check */
|
||||
WriteInt(AH, te->dumpId); /* For sanity check */
|
||||
|
||||
_StartDataCompressor(AH, te);
|
||||
}
|
||||
@ -371,7 +371,7 @@ _StartBlobs(ArchiveHandle *AH, TocEntry *te)
|
||||
tctx->dataState = K_OFFSET_POS_SET;
|
||||
|
||||
_WriteByte(AH, BLK_BLOBS); /* Block type */
|
||||
WriteInt(AH, te->id); /* For sanity check */
|
||||
WriteInt(AH, te->dumpId); /* For sanity check */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -439,7 +439,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
||||
|
||||
_readBlockHeader(AH, &blkType, &id);
|
||||
|
||||
while (id != te->id)
|
||||
while (id != te->dumpId)
|
||||
{
|
||||
if ((TocIDRequired(AH, id, ropt) & 2) != 0)
|
||||
die_horribly(AH, modulename,
|
||||
@ -475,9 +475,9 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
||||
}
|
||||
|
||||
/* Are we sane? */
|
||||
if (id != te->id)
|
||||
if (id != te->dumpId)
|
||||
die_horribly(AH, modulename, "found unexpected block ID (%d) when reading data -- expected %d\n",
|
||||
id, te->id);
|
||||
id, te->dumpId);
|
||||
|
||||
switch (blkType)
|
||||
{
|
||||
@ -863,7 +863,7 @@ _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
|
||||
if (AH->version < K_VERS_1_3)
|
||||
*type = BLK_DATA;
|
||||
else
|
||||
*type = _ReadByte(AH);;
|
||||
*type = _ReadByte(AH);
|
||||
|
||||
*id = ReadInt(AH);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_files.c,v 1.23 2003/11/29 19:52:05 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_files.c,v 1.24 2003/12/06 03:00:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -172,11 +172,11 @@ _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
|
||||
{
|
||||
#ifdef HAVE_LIBZ
|
||||
if (AH->compression == 0)
|
||||
sprintf(fn, "%d.dat", te->id);
|
||||
sprintf(fn, "%d.dat", te->dumpId);
|
||||
else
|
||||
sprintf(fn, "%d.dat.gz", te->id);
|
||||
sprintf(fn, "%d.dat.gz", te->dumpId);
|
||||
#else
|
||||
sprintf(fn, "%d.dat", te->id);
|
||||
sprintf(fn, "%d.dat", te->dumpId);
|
||||
#endif
|
||||
ctx->filename = strdup(fn);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.12 2003/11/29 19:52:05 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.13 2003/12/06 03:00:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -91,7 +91,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
||||
if (te->dataDumper)
|
||||
{
|
||||
AH->currToc = te;
|
||||
(*te->dataDumper) ((Archive *) AH, te->oid, te->dataDumperArg);
|
||||
(*te->dataDumper) ((Archive *) AH, te->dataDumperArg);
|
||||
AH->currToc = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_tar.c,v 1.39 2003/11/29 19:52:05 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_tar.c,v 1.40 2003/12/06 03:00:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -259,11 +259,11 @@ _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
|
||||
{
|
||||
#ifdef HAVE_LIBZ
|
||||
if (AH->compression == 0)
|
||||
sprintf(fn, "%d.dat", te->id);
|
||||
sprintf(fn, "%d.dat", te->dumpId);
|
||||
else
|
||||
sprintf(fn, "%d.dat.gz", te->id);
|
||||
sprintf(fn, "%d.dat.gz", te->dumpId);
|
||||
#else
|
||||
sprintf(fn, "%d.dat", te->id);
|
||||
sprintf(fn, "%d.dat", te->dumpId);
|
||||
#endif
|
||||
ctx->filename = strdup(fn);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.106 2003/11/29 22:40:46 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.107 2003/12/06 03:00:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -14,10 +14,37 @@
|
||||
#ifndef PG_DUMP_H
|
||||
#define PG_DUMP_H
|
||||
|
||||
#include "pg_backup.h"
|
||||
#include "postgres_fe.h"
|
||||
|
||||
|
||||
/*
|
||||
* The data structures used to store system catalog information
|
||||
* pg_dump uses two different mechanisms for identifying database objects:
|
||||
*
|
||||
* CatalogId represents an object by the tableoid and oid of its defining
|
||||
* entry in the system catalogs. We need this to interpret pg_depend entries,
|
||||
* for instance.
|
||||
*
|
||||
* DumpId is a simple sequential integer counter assigned as dumpable objects
|
||||
* are identified during a pg_dump run. We use DumpId internally in preference
|
||||
* to CatalogId for two reasons: it's more compact, and we can assign DumpIds
|
||||
* to "objects" that don't have a separate CatalogId. For example, it is
|
||||
* convenient to consider a table, its data, and its ACL as three separate
|
||||
* dumpable "objects" with distinct DumpIds --- this lets us reason about the
|
||||
* order in which to dump these things.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Oid tableoid;
|
||||
Oid oid;
|
||||
} CatalogId;
|
||||
|
||||
typedef int DumpId;
|
||||
|
||||
|
||||
/*
|
||||
* The data structures used to store system catalog information. Every
|
||||
* dumpable object is a subclass of DumpableObject.
|
||||
*
|
||||
* NOTE: the structures described here live for the entire pg_dump run;
|
||||
* and in most cases we make a struct for every object we can find in the
|
||||
@ -25,12 +52,46 @@
|
||||
* best to store a minimal amount of per-object info in these structs,
|
||||
* and retrieve additional per-object info when and if we dump a specific
|
||||
* object. In particular, try to avoid retrieving expensive-to-compute
|
||||
* information until it's known to be needed.
|
||||
* information until it's known to be needed. We do, however, have to
|
||||
* store enough info to determine whether an object should be dumped and
|
||||
* what order to dump in.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/* When modifying this enum, update priority table in pg_dump_sort.c! */
|
||||
DO_NAMESPACE,
|
||||
DO_TYPE,
|
||||
DO_FUNC,
|
||||
DO_AGG,
|
||||
DO_OPERATOR,
|
||||
DO_OPCLASS,
|
||||
DO_CONVERSION,
|
||||
DO_TABLE,
|
||||
DO_ATTRDEF,
|
||||
DO_INDEX,
|
||||
DO_RULE,
|
||||
DO_TRIGGER,
|
||||
DO_CONSTRAINT,
|
||||
DO_FK_CONSTRAINT, /* see note for ConstraintInfo */
|
||||
DO_PROCLANG,
|
||||
DO_CAST,
|
||||
DO_TABLE_DATA
|
||||
} DumpableObjectType;
|
||||
|
||||
typedef struct _dumpableObject
|
||||
{
|
||||
DumpableObjectType objType;
|
||||
CatalogId catId; /* zero if not a cataloged object */
|
||||
DumpId dumpId; /* assigned by AssignDumpId() */
|
||||
DumpId *dependencies; /* dumpIds of objects this one depends on */
|
||||
int nDeps; /* number of valid dependencies */
|
||||
int allocDeps; /* allocated size of dependencies[] */
|
||||
} DumpableObject;
|
||||
|
||||
typedef struct _namespaceInfo
|
||||
{
|
||||
char *oid;
|
||||
DumpableObject dobj;
|
||||
char *nspname;
|
||||
char *usename; /* name of owner, or empty string */
|
||||
char *nspacl;
|
||||
@ -39,57 +100,56 @@ typedef struct _namespaceInfo
|
||||
|
||||
typedef struct _typeInfo
|
||||
{
|
||||
char *oid;
|
||||
DumpableObject dobj;
|
||||
char *typname; /* name as seen in catalog */
|
||||
/* Note: format_type might produce something different than typname */
|
||||
NamespaceInfo *typnamespace; /* link to containing namespace */
|
||||
char *usename; /* name of owner, or empty string */
|
||||
char *typelem; /* OID */
|
||||
char *typrelid; /* OID */
|
||||
Oid typinput;
|
||||
Oid typelem;
|
||||
Oid typrelid;
|
||||
char typrelkind; /* 'r', 'v', 'c', etc */
|
||||
char typtype; /* 'b', 'c', etc */
|
||||
bool isArray; /* true if user-defined array type */
|
||||
bool isDefined; /* true if typisdefined */
|
||||
/* If it's a domain, we store links to its constraints here: */
|
||||
int nDomChecks;
|
||||
struct _constraintInfo *domChecks;
|
||||
} TypeInfo;
|
||||
|
||||
typedef struct _funcInfo
|
||||
{
|
||||
char *oid;
|
||||
DumpableObject dobj;
|
||||
char *proname;
|
||||
NamespaceInfo *pronamespace; /* link to containing namespace */
|
||||
char *usename; /* name of owner, or empty string */
|
||||
Oid lang;
|
||||
int nargs;
|
||||
char **argtypes; /* OIDs */
|
||||
char *prorettype; /* OID */
|
||||
Oid *argtypes;
|
||||
Oid prorettype;
|
||||
char *proacl;
|
||||
bool dumped; /* true if already dumped */
|
||||
} FuncInfo;
|
||||
|
||||
/* AggInfo is a superset of FuncInfo */
|
||||
typedef struct _aggInfo
|
||||
{
|
||||
char *oid;
|
||||
char *aggname;
|
||||
char *aggbasetype; /* OID */
|
||||
NamespaceInfo *aggnamespace; /* link to containing namespace */
|
||||
char *usename;
|
||||
char *aggacl;
|
||||
FuncInfo aggfn;
|
||||
bool anybasetype; /* is the basetype "any"? */
|
||||
char *fmtbasetype; /* formatted type name */
|
||||
} AggInfo;
|
||||
|
||||
typedef struct _oprInfo
|
||||
{
|
||||
char *oid;
|
||||
DumpableObject dobj;
|
||||
char *oprname;
|
||||
NamespaceInfo *oprnamespace; /* link to containing namespace */
|
||||
char *usename;
|
||||
char *oprcode; /* as OID, not regproc name */
|
||||
Oid oprcode;
|
||||
} OprInfo;
|
||||
|
||||
typedef struct _opclassInfo
|
||||
{
|
||||
char *oid;
|
||||
DumpableObject dobj;
|
||||
char *opcname;
|
||||
NamespaceInfo *opcnamespace; /* link to containing namespace */
|
||||
char *usename;
|
||||
@ -97,7 +157,7 @@ typedef struct _opclassInfo
|
||||
|
||||
typedef struct _convInfo
|
||||
{
|
||||
char *oid;
|
||||
DumpableObject dobj;
|
||||
char *conname;
|
||||
NamespaceInfo *connamespace; /* link to containing namespace */
|
||||
char *usename;
|
||||
@ -108,7 +168,7 @@ typedef struct _tableInfo
|
||||
/*
|
||||
* These fields are collected for every table in the database.
|
||||
*/
|
||||
char *oid;
|
||||
DumpableObject dobj;
|
||||
char *relname;
|
||||
NamespaceInfo *relnamespace; /* link to containing namespace */
|
||||
char *usename; /* name of owner, or empty string */
|
||||
@ -120,7 +180,7 @@ typedef struct _tableInfo
|
||||
int ncheck; /* # of CHECK expressions */
|
||||
int ntrig; /* # of triggers */
|
||||
/* these two are set only if table is a SERIAL column's sequence: */
|
||||
char *owning_tab; /* OID of table owning sequence */
|
||||
Oid owning_tab; /* OID of table owning sequence */
|
||||
int owning_col; /* attr # of column owning sequence */
|
||||
|
||||
bool interesting; /* true if need to collect more data */
|
||||
@ -143,40 +203,127 @@ typedef struct _tableInfo
|
||||
bool *attisserial; /* true if attr is serial or bigserial */
|
||||
|
||||
/*
|
||||
* Note: we need to store per-attribute notnull and default stuff for
|
||||
* all interesting tables so that we can tell which constraints were
|
||||
* inherited.
|
||||
* Note: we need to store per-attribute notnull, default, and constraint
|
||||
* stuff for all interesting tables so that we can tell which constraints
|
||||
* were inherited.
|
||||
*/
|
||||
bool *notnull; /* Not null constraints on attributes */
|
||||
char **adef_expr; /* DEFAULT expressions */
|
||||
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
|
||||
bool *inhAttrs; /* true if each attribute is inherited */
|
||||
bool *inhAttrDef; /* true if attr's default is inherited */
|
||||
bool *inhNotNull; /* true if NOT NULL is inherited */
|
||||
struct _constraintInfo *checkexprs; /* CHECK constraints */
|
||||
|
||||
/*
|
||||
* Stuff computed only for dumpable tables.
|
||||
*/
|
||||
int numParents; /* number of (immediate) parent tables */
|
||||
int *parentIndexes; /* TableInfo indexes of immediate parents */
|
||||
|
||||
char *viewoid; /* OID of view - should be >= oid of table
|
||||
* important because views may be
|
||||
* constructed manually from rules, and
|
||||
* rule may ref things created after the
|
||||
* base table was created. */
|
||||
struct _tableInfo **parents; /* TableInfos of immediate parents */
|
||||
} TableInfo;
|
||||
|
||||
typedef struct _attrDefInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
TableInfo *adtable; /* link to table of attribute */
|
||||
int adnum;
|
||||
char *adef_expr; /* decompiled DEFAULT expression */
|
||||
bool separate; /* TRUE if must dump as separate item */
|
||||
} AttrDefInfo;
|
||||
|
||||
typedef struct _tableDataInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
TableInfo *tdtable; /* link to table to dump */
|
||||
bool oids; /* include OIDs in data? */
|
||||
} TableDataInfo;
|
||||
|
||||
typedef struct _indxInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
char *indexname;
|
||||
TableInfo *indextable; /* link to table the index is for */
|
||||
char *indexdef;
|
||||
int indnkeys;
|
||||
Oid *indkeys;
|
||||
bool indisclustered;
|
||||
/* if there is an associated constraint object, its dumpId: */
|
||||
DumpId indexconstraint;
|
||||
} IndxInfo;
|
||||
|
||||
typedef struct _ruleInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
char *rulename;
|
||||
TableInfo *ruletable; /* link to table the rule is for */
|
||||
char ev_type;
|
||||
bool is_instead;
|
||||
} RuleInfo;
|
||||
|
||||
typedef struct _triggerInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
TableInfo *tgtable; /* link to table the trigger is for */
|
||||
char *tgname;
|
||||
char *tgfname;
|
||||
int tgtype;
|
||||
int tgnargs;
|
||||
char *tgargs;
|
||||
bool tgisconstraint;
|
||||
char *tgconstrname;
|
||||
Oid tgconstrrelid;
|
||||
char *tgconstrrelname;
|
||||
bool tgdeferrable;
|
||||
bool tginitdeferred;
|
||||
} TriggerInfo;
|
||||
|
||||
/*
|
||||
* struct ConstraintInfo is used for all constraint types. However we
|
||||
* use a different objType for foreign key constraints, to make it easier
|
||||
* to sort them the way we want.
|
||||
*/
|
||||
typedef struct _constraintInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
char *conname;
|
||||
TableInfo *contable; /* NULL if domain constraint */
|
||||
TypeInfo *condomain; /* NULL if table constraint */
|
||||
char contype;
|
||||
char *condef; /* definition, if CHECK or FOREIGN KEY */
|
||||
DumpId conindex; /* identifies associated index if any */
|
||||
bool coninherited; /* TRUE if appears to be inherited */
|
||||
bool separate; /* TRUE if must dump as separate item */
|
||||
} ConstraintInfo;
|
||||
|
||||
typedef struct _procLangInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
char *lanname;
|
||||
bool lanpltrusted;
|
||||
Oid lanplcallfoid;
|
||||
Oid lanvalidator;
|
||||
char *lanacl;
|
||||
} ProcLangInfo;
|
||||
|
||||
typedef struct _castInfo
|
||||
{
|
||||
DumpableObject dobj;
|
||||
Oid castsource;
|
||||
Oid casttarget;
|
||||
Oid castfunc;
|
||||
char castcontext;
|
||||
} CastInfo;
|
||||
|
||||
/* InhInfo isn't a DumpableObject, just temporary state */
|
||||
typedef struct _inhInfo
|
||||
{
|
||||
char *inhrelid; /* OID of a child table */
|
||||
char *inhparent; /* OID of its parent */
|
||||
Oid inhrelid; /* OID of a child table */
|
||||
Oid inhparent; /* OID of its parent */
|
||||
} InhInfo;
|
||||
|
||||
|
||||
/* global decls */
|
||||
extern bool force_quotes; /* double-quotes for identifiers flag */
|
||||
extern bool g_verbose; /* verbose flag */
|
||||
extern Archive *g_fout; /* the script file */
|
||||
|
||||
/* placeholders for comment starting and ending delimiters */
|
||||
extern char g_comment_start[10];
|
||||
@ -188,9 +335,7 @@ extern char g_opaque_type[10]; /* name for the opaque type */
|
||||
* common utility functions
|
||||
*/
|
||||
|
||||
extern TableInfo *dumpSchema(Archive *fout,
|
||||
int *numTablesPtr,
|
||||
const bool aclsSkip,
|
||||
extern TableInfo *getSchemaData(int *numTablesPtr,
|
||||
const bool schemaOnly,
|
||||
const bool dataOnly);
|
||||
|
||||
@ -202,15 +347,28 @@ typedef enum _OidOptions
|
||||
zeroAsNone = 8
|
||||
} OidOptions;
|
||||
|
||||
extern int findTableByOid(TableInfo *tbinfo, int numTables, const char *oid);
|
||||
extern char *findOprByOid(OprInfo *oprinfo, int numOprs, const char *oid);
|
||||
extern int findFuncByOid(FuncInfo *finfo, int numFuncs, const char *oid);
|
||||
extern int findTypeByOid(TypeInfo *tinfo, int numTypes, const char *oid);
|
||||
extern void AssignDumpId(DumpableObject *dobj);
|
||||
extern DumpId createDumpId(void);
|
||||
extern DumpId getMaxDumpId(void);
|
||||
extern DumpableObject *findObjectByDumpId(DumpId dumpId);
|
||||
extern DumpableObject *findObjectByCatalogId(CatalogId catalogId);
|
||||
extern void getDumpableObjects(DumpableObject ***objs, int *numObjs);
|
||||
|
||||
extern void addObjectDependency(DumpableObject *dobj, DumpId refId);
|
||||
extern void removeObjectDependency(DumpableObject *dobj, DumpId refId);
|
||||
|
||||
extern TableInfo *findTableByOid(Oid oid);
|
||||
extern TypeInfo *findTypeByOid(Oid oid);
|
||||
extern FuncInfo *findFuncByOid(Oid oid);
|
||||
extern OprInfo *findOprByOid(Oid oid);
|
||||
|
||||
extern void check_conn_and_db(void);
|
||||
extern void exit_nicely(void);
|
||||
|
||||
extern void parseNumericArray(const char *str, char **array, int arraysize);
|
||||
extern void parseOidArray(const char *str, Oid *array, int arraysize);
|
||||
|
||||
extern void sortDumpableObjects(DumpableObject **objs, int numObjs);
|
||||
extern void sortDumpableObjectsByType(DumpableObject **objs, int numObjs);
|
||||
|
||||
/*
|
||||
* version specific routines
|
||||
@ -224,26 +382,12 @@ extern OpclassInfo *getOpclasses(int *numOpclasses);
|
||||
extern ConvInfo *getConversions(int *numConversions);
|
||||
extern TableInfo *getTables(int *numTables);
|
||||
extern InhInfo *getInherits(int *numInherits);
|
||||
|
||||
extern void getIndexes(TableInfo tblinfo[], int numTables);
|
||||
extern void getConstraints(TableInfo tblinfo[], int numTables);
|
||||
extern RuleInfo *getRules(int *numRules);
|
||||
extern void getTriggers(TableInfo tblinfo[], int numTables);
|
||||
extern ProcLangInfo *getProcLangs(int *numProcLangs);
|
||||
extern CastInfo *getCasts(int *numCasts);
|
||||
extern void getTableAttrs(TableInfo *tbinfo, int numTables);
|
||||
extern void dumpDBComment(Archive *outfile);
|
||||
extern void dumpNamespaces(Archive *fout,
|
||||
NamespaceInfo *nsinfo, int numNamespaces);
|
||||
extern void dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
|
||||
TypeInfo *tinfo, int numTypes);
|
||||
extern void dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs);
|
||||
extern void dumpFuncs(Archive *fout, FuncInfo finfo[], int numFuncs);
|
||||
extern void dumpCasts(Archive *fout, FuncInfo *finfo, int numFuncs,
|
||||
TypeInfo *tinfo, int numTypes);
|
||||
extern void dumpAggs(Archive *fout, AggInfo agginfo[], int numAggregates);
|
||||
extern void dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators);
|
||||
extern void dumpOpclasses(Archive *fout,
|
||||
OpclassInfo *opcinfo, int numOpclasses);
|
||||
extern void dumpConversions(Archive *fout,
|
||||
ConvInfo *coninfo, int numConversions);
|
||||
extern void dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
|
||||
const bool aclsSkip,
|
||||
const bool schemaOnly, const bool dataOnly);
|
||||
extern void dumpIndexes(Archive *fout, TableInfo *tbinfo, int numTables);
|
||||
|
||||
#endif /* PG_DUMP_H */
|
||||
|
727
src/bin/pg_dump/pg_dump_sort.c
Normal file
727
src/bin/pg_dump/pg_dump_sort.c
Normal file
@ -0,0 +1,727 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pg_dump_sort.c
|
||||
* Sort the items of a dump into a safe order for dumping
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.1 2003/12/06 03:00:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "pg_dump.h"
|
||||
#include "pg_backup_archiver.h"
|
||||
|
||||
|
||||
static char *modulename = gettext_noop("sorter");
|
||||
|
||||
/*
|
||||
* Sort priority for object types. Objects are sorted by priority,
|
||||
* and within an equal priority level by OID. (This is a relatively
|
||||
* crude hack to provide semi-reasonable behavior for old databases
|
||||
* without full dependency info.)
|
||||
*/
|
||||
static const int objectTypePriority[] =
|
||||
{
|
||||
1, /* DO_NAMESPACE */
|
||||
2, /* DO_TYPE */
|
||||
2, /* DO_FUNC */
|
||||
2, /* DO_AGG */
|
||||
3, /* DO_OPERATOR */
|
||||
4, /* DO_OPCLASS */
|
||||
5, /* DO_CONVERSION */
|
||||
6, /* DO_TABLE */
|
||||
7, /* DO_ATTRDEF */
|
||||
10, /* DO_INDEX */
|
||||
11, /* DO_RULE */
|
||||
12, /* DO_TRIGGER */
|
||||
9, /* DO_CONSTRAINT */
|
||||
13, /* DO_FK_CONSTRAINT */
|
||||
2, /* DO_PROCLANG */
|
||||
2, /* DO_CAST */
|
||||
8 /* DO_TABLE_DATA */
|
||||
};
|
||||
|
||||
|
||||
static int DOTypeCompare(const void *p1, const void *p2);
|
||||
static bool TopoSort(DumpableObject **objs,
|
||||
int numObjs,
|
||||
DumpableObject **ordering,
|
||||
int *nOrdering);
|
||||
static bool findLoop(DumpableObject *obj,
|
||||
int depth,
|
||||
DumpableObject **ordering,
|
||||
int *nOrdering);
|
||||
static void repairDependencyLoop(DumpableObject **loop,
|
||||
int nLoop);
|
||||
static void describeDumpableObject(DumpableObject *obj,
|
||||
char *buf, int bufsize);
|
||||
|
||||
|
||||
/*
|
||||
* Sort the given objects into a type/OID-based ordering
|
||||
*
|
||||
* Normally this is just the starting point for the dependency-based
|
||||
* ordering.
|
||||
*/
|
||||
void
|
||||
sortDumpableObjectsByType(DumpableObject **objs, int numObjs)
|
||||
{
|
||||
if (numObjs > 1)
|
||||
qsort((void *) objs, numObjs, sizeof(DumpableObject *), DOTypeCompare);
|
||||
}
|
||||
|
||||
static int
|
||||
DOTypeCompare(const void *p1, const void *p2)
|
||||
{
|
||||
DumpableObject *obj1 = *(DumpableObject **) p1;
|
||||
DumpableObject *obj2 = *(DumpableObject **) p2;
|
||||
int cmpval;
|
||||
|
||||
cmpval = objectTypePriority[obj1->objType] -
|
||||
objectTypePriority[obj2->objType];
|
||||
|
||||
if (cmpval != 0)
|
||||
return cmpval;
|
||||
|
||||
return oidcmp(obj1->catId.oid, obj2->catId.oid);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sort the given objects into a safe dump order using dependency
|
||||
* information (to the extent we have it available).
|
||||
*/
|
||||
void
|
||||
sortDumpableObjects(DumpableObject **objs, int numObjs)
|
||||
{
|
||||
DumpableObject **ordering;
|
||||
int nOrdering;
|
||||
|
||||
ordering = (DumpableObject **) malloc(numObjs * sizeof(DumpableObject *));
|
||||
if (ordering == NULL)
|
||||
exit_horribly(NULL, modulename, "out of memory\n");
|
||||
|
||||
while (!TopoSort(objs, numObjs, ordering, &nOrdering))
|
||||
repairDependencyLoop(ordering, nOrdering);
|
||||
|
||||
memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
|
||||
|
||||
free(ordering);
|
||||
}
|
||||
|
||||
/*
|
||||
* TopoSort -- topological sort of a dump list
|
||||
*
|
||||
* Generate a re-ordering of the dump list that satisfies all the dependency
|
||||
* constraints shown in the dump list. (Each such constraint is a fact of a
|
||||
* partial ordering.) Minimize rearrangement of the list not needed to
|
||||
* achieve the partial ordering.
|
||||
*
|
||||
* This is a lot simpler and slower than, for example, the topological sort
|
||||
* algorithm shown in Knuth's Volume 1. However, Knuth's method doesn't
|
||||
* try to minimize the damage to the existing order.
|
||||
*
|
||||
* Returns TRUE if able to build an ordering that satisfies all the
|
||||
* constraints, FALSE if not (there are contradictory constraints).
|
||||
*
|
||||
* On success (TRUE result), ordering[] is filled with an array of
|
||||
* DumpableObject pointers, of length equal to the input list length.
|
||||
*
|
||||
* On failure (FALSE result), ordering[] is filled with an array of
|
||||
* DumpableObject pointers of length *nOrdering, representing a circular set
|
||||
* of dependency constraints. (If there is more than one cycle in the given
|
||||
* constraints, one is picked at random to return.)
|
||||
*
|
||||
* The caller is responsible for allocating sufficient space at *ordering.
|
||||
*/
|
||||
static bool
|
||||
TopoSort(DumpableObject **objs,
|
||||
int numObjs,
|
||||
DumpableObject **ordering, /* output argument */
|
||||
int *nOrdering) /* output argument */
|
||||
{
|
||||
DumpId maxDumpId = getMaxDumpId();
|
||||
bool result = true;
|
||||
DumpableObject **topoItems;
|
||||
DumpableObject *obj;
|
||||
int *beforeConstraints;
|
||||
int i,
|
||||
j,
|
||||
k,
|
||||
last;
|
||||
|
||||
/* First, create work array with the dump items in their current order */
|
||||
topoItems = (DumpableObject **) malloc(numObjs * sizeof(DumpableObject *));
|
||||
if (topoItems == NULL)
|
||||
exit_horribly(NULL, modulename, "out of memory\n");
|
||||
memcpy(topoItems, objs, numObjs * sizeof(DumpableObject *));
|
||||
|
||||
*nOrdering = numObjs; /* for success return */
|
||||
|
||||
/*
|
||||
* Scan the constraints, and for each item in the array, generate a
|
||||
* count of the number of constraints that say it must be before
|
||||
* something else. The count for the item with dumpId j is
|
||||
* stored in beforeConstraints[j].
|
||||
*/
|
||||
beforeConstraints = (int *) malloc((maxDumpId + 1) * sizeof(int));
|
||||
if (beforeConstraints == NULL)
|
||||
exit_horribly(NULL, modulename, "out of memory\n");
|
||||
memset(beforeConstraints, 0, (maxDumpId + 1) * sizeof(int));
|
||||
for (i = 0; i < numObjs; i++)
|
||||
{
|
||||
obj = topoItems[i];
|
||||
for (j = 0; j < obj->nDeps; j++)
|
||||
{
|
||||
k = obj->dependencies[j];
|
||||
if (k <= 0 || k > maxDumpId)
|
||||
exit_horribly(NULL, modulename, "invalid dependency %d\n", k);
|
||||
beforeConstraints[k]++;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------
|
||||
* Now scan the topoItems array backwards. At each step, output the
|
||||
* last item that has no remaining before-constraints, and decrease
|
||||
* the beforeConstraints count of each of the items it was constrained
|
||||
* against.
|
||||
* i = index of ordering[] entry we want to output this time
|
||||
* j = search index for topoItems[]
|
||||
* k = temp for scanning constraint list for item j
|
||||
* last = last non-null index in topoItems (avoid redundant searches)
|
||||
*--------------------
|
||||
*/
|
||||
last = numObjs - 1;
|
||||
for (i = numObjs; --i >= 0;)
|
||||
{
|
||||
/* Find next candidate to output */
|
||||
while (topoItems[last] == NULL)
|
||||
last--;
|
||||
for (j = last; j >= 0; j--)
|
||||
{
|
||||
obj = topoItems[j];
|
||||
if (obj != NULL && beforeConstraints[obj->dumpId] == 0)
|
||||
break;
|
||||
}
|
||||
/* If no available candidate, topological sort fails */
|
||||
if (j < 0)
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
/* Output candidate, and mark it done by zeroing topoItems[] entry */
|
||||
ordering[i] = obj = topoItems[j];
|
||||
topoItems[j] = NULL;
|
||||
/* Update beforeConstraints counts of its predecessors */
|
||||
for (k = 0; k < obj->nDeps; k++)
|
||||
beforeConstraints[obj->dependencies[k]]--;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we failed, report one of the circular constraint sets
|
||||
*/
|
||||
if (!result)
|
||||
{
|
||||
for (j = last; j >= 0; j--)
|
||||
{
|
||||
ordering[0] = obj = topoItems[j];
|
||||
if (obj && findLoop(obj, 1, ordering, nOrdering))
|
||||
break;
|
||||
}
|
||||
if (j < 0)
|
||||
exit_horribly(NULL, modulename,
|
||||
"could not find dependency loop\n");
|
||||
}
|
||||
|
||||
/* Done */
|
||||
free(topoItems);
|
||||
free(beforeConstraints);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively search for a circular dependency loop
|
||||
*/
|
||||
static bool
|
||||
findLoop(DumpableObject *obj,
|
||||
int depth,
|
||||
DumpableObject **ordering, /* output argument */
|
||||
int *nOrdering) /* output argument */
|
||||
{
|
||||
DumpId startPoint = ordering[0]->dumpId;
|
||||
int j;
|
||||
int k;
|
||||
|
||||
/* See if we've found a loop back to the starting point */
|
||||
for (j = 0; j < obj->nDeps; j++)
|
||||
{
|
||||
if (obj->dependencies[j] == startPoint)
|
||||
{
|
||||
*nOrdering = depth;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* Try each outgoing branch */
|
||||
for (j = 0; j < obj->nDeps; j++)
|
||||
{
|
||||
DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[j]);
|
||||
|
||||
if (!nextobj)
|
||||
continue; /* ignore dependencies on undumped objects */
|
||||
for (k = 0; k < depth; k++)
|
||||
{
|
||||
if (ordering[k] == nextobj)
|
||||
break;
|
||||
}
|
||||
if (k < depth)
|
||||
continue; /* ignore loops not including start point */
|
||||
ordering[depth] = nextobj;
|
||||
if (findLoop(nextobj,
|
||||
depth + 1,
|
||||
ordering,
|
||||
nOrdering))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* A user-defined datatype will have a dependency loop with each of its
|
||||
* I/O functions (since those have the datatype as input or output).
|
||||
* We want the dump ordering to be the input function, then any other
|
||||
* I/O functions, then the datatype. So we break the circularity in
|
||||
* favor of the functions, and add a dependency from any non-input
|
||||
* function to the input function.
|
||||
*/
|
||||
static void
|
||||
repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
|
||||
{
|
||||
TypeInfo *typeInfo = (TypeInfo *) typeobj;
|
||||
FuncInfo *inputFuncInfo;
|
||||
|
||||
/* remove function's dependency on type */
|
||||
removeObjectDependency(funcobj, typeobj->dumpId);
|
||||
|
||||
/* if this isn't the input function, make it depend on same */
|
||||
if (funcobj->catId.oid == typeInfo->typinput)
|
||||
return; /* it is the input function */
|
||||
inputFuncInfo = findFuncByOid(typeInfo->typinput);
|
||||
if (inputFuncInfo == NULL)
|
||||
return;
|
||||
addObjectDependency(funcobj, inputFuncInfo->dobj.dumpId);
|
||||
/*
|
||||
* Make sure the input function's dependency on type gets removed too;
|
||||
* if it hasn't been done yet, we'd end up with loops involving the
|
||||
* type and two or more functions, which repairDependencyLoop() is not
|
||||
* smart enough to handle.
|
||||
*/
|
||||
removeObjectDependency(&inputFuncInfo->dobj, typeobj->dumpId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Because we force a view to depend on its ON SELECT rule, while there
|
||||
* will be an implicit dependency in the other direction, we need to break
|
||||
* the loop. We can always do this by removing the implicit dependency.
|
||||
*/
|
||||
static void
|
||||
repairViewRuleLoop(DumpableObject *viewobj,
|
||||
DumpableObject *ruleobj)
|
||||
{
|
||||
/* remove rule's dependency on view */
|
||||
removeObjectDependency(ruleobj, viewobj->dumpId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Because we make tables depend on their CHECK constraints, while there
|
||||
* will be an automatic dependency in the other direction, we need to break
|
||||
* the loop. If there are no other objects in the loop then we can remove
|
||||
* the automatic dependency and leave the CHECK constraint non-separate.
|
||||
*/
|
||||
static void
|
||||
repairTableConstraintLoop(DumpableObject *tableobj,
|
||||
DumpableObject *constraintobj)
|
||||
{
|
||||
/* remove constraint's dependency on table */
|
||||
removeObjectDependency(constraintobj, tableobj->dumpId);
|
||||
}
|
||||
|
||||
/*
|
||||
* However, if there are other objects in the loop, we must break the loop
|
||||
* by making the CHECK constraint a separately-dumped object.
|
||||
*
|
||||
* Because findLoop() finds shorter cycles before longer ones, it's likely
|
||||
* that we will have previously fired repairTableConstraintLoop() and
|
||||
* removed the constraint's dependency on the table. Put it back to ensure
|
||||
* the constraint won't be emitted before the table...
|
||||
*/
|
||||
static void
|
||||
repairTableConstraintMultiLoop(DumpableObject *tableobj,
|
||||
DumpableObject *constraintobj)
|
||||
{
|
||||
/* remove table's dependency on constraint */
|
||||
removeObjectDependency(tableobj, constraintobj->dumpId);
|
||||
/* mark constraint as needing its own dump */
|
||||
((ConstraintInfo *) constraintobj)->separate = true;
|
||||
/* put back constraint's dependency on table */
|
||||
addObjectDependency(constraintobj, tableobj->dumpId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attribute defaults behave exactly the same as CHECK constraints...
|
||||
*/
|
||||
static void
|
||||
repairTableAttrDefLoop(DumpableObject *tableobj,
|
||||
DumpableObject *attrdefobj)
|
||||
{
|
||||
/* remove attrdef's dependency on table */
|
||||
removeObjectDependency(attrdefobj, tableobj->dumpId);
|
||||
}
|
||||
|
||||
static void
|
||||
repairTableAttrDefMultiLoop(DumpableObject *tableobj,
|
||||
DumpableObject *attrdefobj)
|
||||
{
|
||||
/* remove table's dependency on attrdef */
|
||||
removeObjectDependency(tableobj, attrdefobj->dumpId);
|
||||
/* mark attrdef as needing its own dump */
|
||||
((AttrDefInfo *) attrdefobj)->separate = true;
|
||||
/* put back attrdef's dependency on table */
|
||||
addObjectDependency(attrdefobj, tableobj->dumpId);
|
||||
}
|
||||
|
||||
/*
|
||||
* CHECK constraints on domains work just like those on tables ...
|
||||
*/
|
||||
static void
|
||||
repairDomainConstraintLoop(DumpableObject *domainobj,
|
||||
DumpableObject *constraintobj)
|
||||
{
|
||||
/* remove constraint's dependency on domain */
|
||||
removeObjectDependency(constraintobj, domainobj->dumpId);
|
||||
}
|
||||
|
||||
static void
|
||||
repairDomainConstraintMultiLoop(DumpableObject *domainobj,
|
||||
DumpableObject *constraintobj)
|
||||
{
|
||||
/* remove domain's dependency on constraint */
|
||||
removeObjectDependency(domainobj, constraintobj->dumpId);
|
||||
/* mark constraint as needing its own dump */
|
||||
((ConstraintInfo *) constraintobj)->separate = true;
|
||||
/* put back constraint's dependency on domain */
|
||||
addObjectDependency(constraintobj, domainobj->dumpId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix a dependency loop, or die trying ...
|
||||
*
|
||||
* This routine is mainly concerned with reducing the multiple ways that
|
||||
* a loop might appear to common cases, which it passes off to the
|
||||
* "fixer" routines above.
|
||||
*/
|
||||
static void
|
||||
repairDependencyLoop(DumpableObject **loop,
|
||||
int nLoop)
|
||||
{
|
||||
int i,
|
||||
j;
|
||||
|
||||
/* Datatype and one of its I/O functions */
|
||||
if (nLoop == 2 &&
|
||||
loop[0]->objType == DO_TYPE &&
|
||||
loop[1]->objType == DO_FUNC)
|
||||
{
|
||||
repairTypeFuncLoop(loop[0], loop[1]);
|
||||
return;
|
||||
}
|
||||
if (nLoop == 2 &&
|
||||
loop[1]->objType == DO_TYPE &&
|
||||
loop[0]->objType == DO_FUNC)
|
||||
{
|
||||
repairTypeFuncLoop(loop[1], loop[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* View and its ON SELECT rule */
|
||||
if (nLoop == 2 &&
|
||||
loop[0]->objType == DO_TABLE &&
|
||||
loop[1]->objType == DO_RULE &&
|
||||
((RuleInfo *) loop[1])->ev_type == '1' &&
|
||||
((RuleInfo *) loop[1])->is_instead)
|
||||
{
|
||||
repairViewRuleLoop(loop[0], loop[1]);
|
||||
return;
|
||||
}
|
||||
if (nLoop == 2 &&
|
||||
loop[1]->objType == DO_TABLE &&
|
||||
loop[0]->objType == DO_RULE &&
|
||||
((RuleInfo *) loop[0])->ev_type == '1' &&
|
||||
((RuleInfo *) loop[0])->is_instead)
|
||||
{
|
||||
repairViewRuleLoop(loop[1], loop[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Table and CHECK constraint */
|
||||
if (nLoop == 2 &&
|
||||
loop[0]->objType == DO_TABLE &&
|
||||
loop[1]->objType == DO_CONSTRAINT &&
|
||||
((ConstraintInfo *) loop[1])->contype == 'c' &&
|
||||
((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
|
||||
{
|
||||
repairTableConstraintLoop(loop[0], loop[1]);
|
||||
return;
|
||||
}
|
||||
if (nLoop == 2 &&
|
||||
loop[1]->objType == DO_TABLE &&
|
||||
loop[0]->objType == DO_CONSTRAINT &&
|
||||
((ConstraintInfo *) loop[0])->contype == 'c' &&
|
||||
((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
|
||||
{
|
||||
repairTableConstraintLoop(loop[1], loop[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Indirect loop involving table and CHECK constraint */
|
||||
if (nLoop > 2)
|
||||
{
|
||||
for (i = 0; i < nLoop; i++)
|
||||
{
|
||||
if (loop[i]->objType == DO_TABLE)
|
||||
{
|
||||
for (j = 0; j < nLoop; j++)
|
||||
{
|
||||
if (loop[j]->objType == DO_CONSTRAINT &&
|
||||
((ConstraintInfo *) loop[j])->contype == 'c' &&
|
||||
((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
|
||||
{
|
||||
repairTableConstraintMultiLoop(loop[i], loop[j]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Table and attribute default */
|
||||
if (nLoop == 2 &&
|
||||
loop[0]->objType == DO_TABLE &&
|
||||
loop[1]->objType == DO_ATTRDEF &&
|
||||
((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
|
||||
{
|
||||
repairTableAttrDefLoop(loop[0], loop[1]);
|
||||
return;
|
||||
}
|
||||
if (nLoop == 2 &&
|
||||
loop[1]->objType == DO_TABLE &&
|
||||
loop[0]->objType == DO_ATTRDEF &&
|
||||
((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
|
||||
{
|
||||
repairTableAttrDefLoop(loop[1], loop[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Indirect loop involving table and attribute default */
|
||||
if (nLoop > 2)
|
||||
{
|
||||
for (i = 0; i < nLoop; i++)
|
||||
{
|
||||
if (loop[i]->objType == DO_TABLE)
|
||||
{
|
||||
for (j = 0; j < nLoop; j++)
|
||||
{
|
||||
if (loop[j]->objType == DO_ATTRDEF &&
|
||||
((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
|
||||
{
|
||||
repairTableAttrDefMultiLoop(loop[i], loop[j]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Domain and CHECK constraint */
|
||||
if (nLoop == 2 &&
|
||||
loop[0]->objType == DO_TYPE &&
|
||||
loop[1]->objType == DO_CONSTRAINT &&
|
||||
((ConstraintInfo *) loop[1])->contype == 'c' &&
|
||||
((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
|
||||
{
|
||||
repairDomainConstraintLoop(loop[0], loop[1]);
|
||||
return;
|
||||
}
|
||||
if (nLoop == 2 &&
|
||||
loop[1]->objType == DO_TYPE &&
|
||||
loop[0]->objType == DO_CONSTRAINT &&
|
||||
((ConstraintInfo *) loop[0])->contype == 'c' &&
|
||||
((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
|
||||
{
|
||||
repairDomainConstraintLoop(loop[1], loop[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Indirect loop involving domain and CHECK constraint */
|
||||
if (nLoop > 2)
|
||||
{
|
||||
for (i = 0; i < nLoop; i++)
|
||||
{
|
||||
if (loop[i]->objType == DO_TYPE)
|
||||
{
|
||||
for (j = 0; j < nLoop; j++)
|
||||
{
|
||||
if (loop[j]->objType == DO_CONSTRAINT &&
|
||||
((ConstraintInfo *) loop[j])->contype == 'c' &&
|
||||
((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
|
||||
{
|
||||
repairDomainConstraintMultiLoop(loop[i], loop[j]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we can't find a principled way to break the loop, complain and
|
||||
* break it in an arbitrary fashion.
|
||||
*/
|
||||
write_msg(modulename, "WARNING: could not resolve dependency loop among these items:\n");
|
||||
for (i = 0; i < nLoop; i++)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
describeDumpableObject(loop[i], buf, sizeof(buf));
|
||||
write_msg(modulename, " %s\n", buf);
|
||||
}
|
||||
removeObjectDependency(loop[0], loop[1]->dumpId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Describe a dumpable object usefully for errors
|
||||
*
|
||||
* This should probably go somewhere else...
|
||||
*/
|
||||
static void
|
||||
describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
|
||||
{
|
||||
switch (obj->objType)
|
||||
{
|
||||
case DO_NAMESPACE:
|
||||
snprintf(buf, bufsize,
|
||||
"SCHEMA %s (ID %d OID %u)",
|
||||
((NamespaceInfo *) obj)->nspname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_TYPE:
|
||||
snprintf(buf, bufsize,
|
||||
"TYPE %s (ID %d OID %u)",
|
||||
((TypeInfo *) obj)->typname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_FUNC:
|
||||
snprintf(buf, bufsize,
|
||||
"FUNCTION %s (ID %d OID %u)",
|
||||
((FuncInfo *) obj)->proname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_AGG:
|
||||
snprintf(buf, bufsize,
|
||||
"AGGREGATE %s (ID %d OID %u)",
|
||||
((AggInfo *) obj)->aggfn.proname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_OPERATOR:
|
||||
snprintf(buf, bufsize,
|
||||
"OPERATOR %s (ID %d OID %u)",
|
||||
((OprInfo *) obj)->oprname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_OPCLASS:
|
||||
snprintf(buf, bufsize,
|
||||
"OPERATOR CLASS %s (ID %d OID %u)",
|
||||
((OpclassInfo *) obj)->opcname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_CONVERSION:
|
||||
snprintf(buf, bufsize,
|
||||
"CONVERSION %s (ID %d OID %u)",
|
||||
((ConvInfo *) obj)->conname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_TABLE:
|
||||
snprintf(buf, bufsize,
|
||||
"TABLE %s (ID %d OID %u)",
|
||||
((TableInfo *) obj)->relname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_ATTRDEF:
|
||||
snprintf(buf, bufsize,
|
||||
"ATTRDEF %s.%s (ID %d OID %u)",
|
||||
((AttrDefInfo *) obj)->adtable->relname,
|
||||
((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_INDEX:
|
||||
snprintf(buf, bufsize,
|
||||
"INDEX %s (ID %d OID %u)",
|
||||
((IndxInfo *) obj)->indexname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_RULE:
|
||||
snprintf(buf, bufsize,
|
||||
"RULE %s (ID %d OID %u)",
|
||||
((RuleInfo *) obj)->rulename,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_TRIGGER:
|
||||
snprintf(buf, bufsize,
|
||||
"TRIGGER %s (ID %d OID %u)",
|
||||
((TriggerInfo *) obj)->tgname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_CONSTRAINT:
|
||||
snprintf(buf, bufsize,
|
||||
"CONSTRAINT %s (ID %d OID %u)",
|
||||
((ConstraintInfo *) obj)->conname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_FK_CONSTRAINT:
|
||||
snprintf(buf, bufsize,
|
||||
"FK CONSTRAINT %s (ID %d OID %u)",
|
||||
((ConstraintInfo *) obj)->conname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_PROCLANG:
|
||||
snprintf(buf, bufsize,
|
||||
"PROCEDURAL LANGUAGE %s (ID %d OID %u)",
|
||||
((ProcLangInfo *) obj)->lanname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_CAST:
|
||||
snprintf(buf, bufsize,
|
||||
"CAST %u to %u (ID %d OID %u)",
|
||||
((CastInfo *) obj)->castsource,
|
||||
((CastInfo *) obj)->casttarget,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
case DO_TABLE_DATA:
|
||||
snprintf(buf, bufsize,
|
||||
"TABLE DATA %s (ID %d OID %u)",
|
||||
((TableDataInfo *) obj)->tdtable->relname,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
return;
|
||||
}
|
||||
/* shouldn't get here */
|
||||
snprintf(buf, bufsize,
|
||||
"object type %d (ID %d OID %u)",
|
||||
(int) obj->objType,
|
||||
obj->dumpId, obj->catId.oid);
|
||||
}
|
@ -34,7 +34,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_restore.c,v 1.54 2003/11/29 19:52:05 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_restore.c,v 1.55 2003/12/06 03:00:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -101,10 +101,7 @@ main(int argc, char **argv)
|
||||
{"no-owner", 0, NULL, 'O'},
|
||||
{"no-reconnect", 0, NULL, 'R'},
|
||||
{"port", 1, NULL, 'p'},
|
||||
{"oid-order", 0, NULL, 'o'},
|
||||
{"orig-order", 0, NULL, 'N'},
|
||||
{"password", 0, NULL, 'W'},
|
||||
{"rearrange", 0, NULL, 'r'},
|
||||
{"schema-only", 0, NULL, 's'},
|
||||
{"superuser", 1, NULL, 'S'},
|
||||
{"table", 1, NULL, 't'},
|
||||
@ -147,7 +144,7 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
while ((c = getopt_long(argc, argv, "acCd:f:F:h:iI:lL:NoOp:P:rRsS:t:T:uU:vWxX:",
|
||||
while ((c = getopt_long(argc, argv, "acCd:f:F:h:iI:lL:Op:P:RsS:t:T:uU:vWxX:",
|
||||
cmdopts, NULL)) != -1)
|
||||
{
|
||||
switch (c)
|
||||
@ -188,12 +185,6 @@ main(int argc, char **argv)
|
||||
opts->tocFile = strdup(optarg);
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
opts->origOrder = 1;
|
||||
break;
|
||||
case 'o':
|
||||
opts->oidOrder = 1;
|
||||
break;
|
||||
case 'O':
|
||||
opts->noOwner = 1;
|
||||
break;
|
||||
@ -201,9 +192,6 @@ main(int argc, char **argv)
|
||||
if (strlen(optarg) != 0)
|
||||
opts->pgport = strdup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
opts->rearrange = 1;
|
||||
break;
|
||||
case 'R':
|
||||
/* no-op, still accepted for backwards compatibility */
|
||||
break;
|
||||
@ -338,19 +326,6 @@ main(int argc, char **argv)
|
||||
if (opts->tocFile)
|
||||
SortTocFromFile(AH, opts);
|
||||
|
||||
if (opts->oidOrder)
|
||||
SortTocByOID(AH);
|
||||
else if (opts->origOrder)
|
||||
SortTocByID(AH);
|
||||
|
||||
if (opts->rearrange)
|
||||
SortTocByObjectType(AH);
|
||||
else
|
||||
{
|
||||
/* Database MUST be at start (see also SortTocByObjectType) */
|
||||
MoveToStart(AH, "DATABASE");
|
||||
}
|
||||
|
||||
if (opts->tocSummary)
|
||||
PrintTOCSummary(AH, opts);
|
||||
else
|
||||
@ -385,12 +360,9 @@ usage(const char *progname)
|
||||
printf(_(" -I, --index=NAME restore named index\n"));
|
||||
printf(_(" -L, --use-list=FILENAME use specified table of contents for ordering\n"
|
||||
" output from this file\n"));
|
||||
printf(_(" -N, --orig-order restore in original dump order\n"));
|
||||
printf(_(" -o, --oid-order restore in OID order\n"));
|
||||
printf(_(" -O, --no-owner do not output commands to set object ownership\n"));
|
||||
printf(_(" -P, --function=NAME(args)\n"
|
||||
" restore named function\n"));
|
||||
printf(_(" -r, --rearrange rearrange output to put indexes etc. at end\n"));
|
||||
printf(_(" -s, --schema-only restore only the schema, no data\n"));
|
||||
printf(_(" -S, --superuser=NAME specify the superuser user name to use for\n"
|
||||
" disabling triggers\n"));
|
||||
|
Loading…
x
Reference in New Issue
Block a user