Make pg_dump --data-only try to order the table dumps so that foreign keys'

referenced tables are dumped before the referencing tables.  This avoids
failures when the data is loaded with the FK constraints already active.
If no such ordering is possible because of circular or self-referential
constraints, print a NOTICE to warn the user about it.
This commit is contained in:
Tom Lane 2008-09-08 15:26:23 +00:00
parent a0b76dc662
commit bc01b45ab9
3 changed files with 98 additions and 8 deletions

View File

@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.499 2008/07/30 19:35:13 tgl Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.500 2008/09/08 15:26:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -166,6 +166,7 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
static void getDependencies(void);
static void getDomainConstraints(TypeInfo *tinfo);
static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
static void getTableDataFKConstraints(void);
static char *format_function_arguments(FuncInfo *finfo, char *funcargs);
static char *format_function_arguments_old(FuncInfo *finfo, int nallargs,
char **allargtypes,
@ -659,7 +660,11 @@ main(int argc, char **argv)
guessConstraintInheritance(tblinfo, numTables);
if (!schemaOnly)
{
getTableData(tblinfo, numTables, oids);
if (dataOnly)
getTableDataFKConstraints();
}
if (outputBlobs && hasBlobs(g_fout))
{
@ -1392,10 +1397,59 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids)
tdinfo->tdtable = &(tblinfo[i]);
tdinfo->oids = oids;
addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId);
tblinfo[i].dataObj = tdinfo;
}
}
}
/*
* getTableDataFKConstraints -
* add dump-order dependencies reflecting foreign key constraints
*
* This code is executed only in a data-only dump --- in schema+data dumps
* we handle foreign key issues by not creating the FK constraints until
* after the data is loaded. In a data-only dump, however, we want to
* order the table data objects in such a way that a table's referenced
* tables are restored first. (In the presence of circular references or
* self-references this may be impossible; we'll detect and complain about
* that during the dependency sorting step.)
*/
static void
getTableDataFKConstraints(void)
{
DumpableObject **dobjs;
int numObjs;
int i;
/* Search through all the dumpable objects for FK constraints */
getDumpableObjects(&dobjs, &numObjs);
for (i = 0; i < numObjs; i++)
{
if (dobjs[i]->objType == DO_FK_CONSTRAINT)
{
ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
TableInfo *ftable;
/* Not interesting unless both tables are to be dumped */
if (cinfo->contable == NULL ||
cinfo->contable->dataObj == NULL)
continue;
ftable = findTableByOid(cinfo->confrelid);
if (ftable == NULL ||
ftable->dataObj == NULL)
continue;
/*
* Okay, make referencing table's TABLE_DATA object depend on
* the referenced table's TABLE_DATA object.
*/
addObjectDependency(&cinfo->contable->dataObj->dobj,
ftable->dataObj->dobj.dumpId);
}
}
free(dobjs);
}
/*
* guessConstraintInheritance:
@ -3626,6 +3680,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
constrinfo[j].condomain = NULL;
constrinfo[j].contype = contype;
constrinfo[j].condef = NULL;
constrinfo[j].confrelid = InvalidOid;
constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
constrinfo[j].conislocal = true;
constrinfo[j].separate = true;
@ -3666,10 +3721,11 @@ getConstraints(TableInfo tblinfo[], int numTables)
ConstraintInfo *constrinfo;
PQExpBuffer query;
PGresult *res;
int i_condef,
i_contableoid,
int i_contableoid,
i_conoid,
i_conname;
i_conname,
i_confrelid,
i_condef;
int ntups;
/* pg_constraint was created in 7.3, so nothing to do if older */
@ -3697,7 +3753,7 @@ getConstraints(TableInfo tblinfo[], int numTables)
resetPQExpBuffer(query);
appendPQExpBuffer(query,
"SELECT tableoid, oid, conname, "
"SELECT tableoid, oid, conname, confrelid, "
"pg_catalog.pg_get_constraintdef(oid) as condef "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
@ -3711,6 +3767,7 @@ getConstraints(TableInfo tblinfo[], int numTables)
i_contableoid = PQfnumber(res, "tableoid");
i_conoid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_confrelid = PQfnumber(res, "confrelid");
i_condef = PQfnumber(res, "condef");
constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));
@ -3727,6 +3784,7 @@ getConstraints(TableInfo tblinfo[], int numTables)
constrinfo[j].condomain = NULL;
constrinfo[j].contype = 'f';
constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
constrinfo[j].conindex = 0;
constrinfo[j].conislocal = true;
constrinfo[j].separate = true;
@ -3810,6 +3868,7 @@ getDomainConstraints(TypeInfo *tinfo)
constrinfo[i].condomain = tinfo;
constrinfo[i].contype = 'c';
constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
constrinfo[i].confrelid = InvalidOid;
constrinfo[i].conindex = 0;
constrinfo[i].conislocal = true;
constrinfo[i].separate = false;
@ -4788,6 +4847,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
constrs[j].condomain = NULL;
constrs[j].contype = 'c';
constrs[j].condef = strdup(PQgetvalue(res, j, 3));
constrs[j].confrelid = InvalidOid;
constrs[j].conindex = 0;
constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
constrs[j].separate = false;

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2008, 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.140 2008/05/09 23:32:04 tgl Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.141 2008/09/08 15:26:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -280,6 +280,7 @@ typedef struct _tableInfo
*/
int numParents; /* number of (immediate) parent tables */
struct _tableInfo **parents; /* TableInfos of immediate parents */
struct _tableDataInfo *dataObj; /* TableDataInfo, if dumping its data */
} TableInfo;
typedef struct _attrDefInfo
@ -352,6 +353,7 @@ typedef struct _constraintInfo
TypeInfo *condomain; /* NULL if table constraint */
char contype;
char *condef; /* definition, if CHECK or FOREIGN KEY */
Oid confrelid; /* referenced table, if FOREIGN KEY */
DumpId conindex; /* identifies associated index if any */
bool conislocal; /* TRUE if constraint has local definition */
bool separate; /* TRUE if must dump as separate item */

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.20 2008/01/01 19:45:55 momjian Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.21 2008/09/08 15:26:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -946,6 +946,30 @@ repairDependencyLoop(DumpableObject **loop,
}
}
/*
* If all the objects are TABLE_DATA items, what we must have is a
* circular set of foreign key constraints (or a single self-referential
* table). Print an appropriate complaint and break the loop arbitrarily.
*/
for (i = 0; i < nLoop; i++)
{
if (loop[i]->objType != DO_TABLE_DATA)
break;
}
if (i >= nLoop)
{
write_msg(NULL, "NOTICE: there are circular foreign-key constraints among these table(s):\n");
for (i = 0; i < nLoop; i++)
write_msg(NULL, " %s\n", loop[i]->name);
write_msg(NULL, "You may not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.\n");
write_msg(NULL, "Consider using a full dump instead of a --data-only dump to avoid this problem.\n");
if (nLoop > 1)
removeObjectDependency(loop[0], loop[1]->dumpId);
else /* must be a self-dependency */
removeObjectDependency(loop[0], loop[0]->dumpId);
return;
}
/*
* If we can't find a principled way to break the loop, complain and break
* it in an arbitrary fashion.
@ -958,7 +982,11 @@ repairDependencyLoop(DumpableObject **loop,
describeDumpableObject(loop[i], buf, sizeof(buf));
write_msg(modulename, " %s\n", buf);
}
removeObjectDependency(loop[0], loop[1]->dumpId);
if (nLoop > 1)
removeObjectDependency(loop[0], loop[1]->dumpId);
else /* must be a self-dependency */
removeObjectDependency(loop[0], loop[0]->dumpId);
}
/*