Make pg_dump --data-only try to order the table dumps so that foreign keys'
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 8 Sep 2008 15:26:23 +0000 (15:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 8 Sep 2008 15:26:23 +0000 (15:26 +0000)
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.

src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dump_sort.c

index 56aa5c3966de465169dfbe44d2ea1b5683a1b924..25b0f58e7d4a5e05df979f17888d7f84c53e4ecf 100644 (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;
index 8fbe98221b6c8e9e67cd1589e7d8d9a5517c683d..f443d9de590bffab4321bef37a82143225e3d50d 100644 (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 */
index e5807475e4ef72a277e235db34603b41f05684f2..7206d4eef4619d5d173383bf0817a50ca487b89b 100644 (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);
 }
 
 /*