]> granicus.if.org Git - postgresql/commitdiff
Fix pg_restore so parallel restore doesn't fail when the input file doesn't
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 27 Jun 2010 19:07:24 +0000 (19:07 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 27 Jun 2010 19:07:24 +0000 (19:07 +0000)
contain data offsets (which it won't, if pg_dump thought its output wasn't
seekable).  To do that, remove an unnecessarily aggressive error check, and
instead fail if we get to the end of the archive without finding the desired
data item.  Also improve the error message to be more specific about the
cause of the problem.  Per discussion of recent report from Igor Neyman.

Back-patch to 8.4 where parallel restore was introduced.

src/bin/pg_dump/pg_backup_custom.c

index 607044660aa2738a5c59b1423b457db97fef51b6..11d71a667987d0b6fc8c609029d70ff2d5581e8c 100644 (file)
@@ -19,7 +19,7 @@
  *
  *
  * IDENTIFICATION
- *             $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_custom.c,v 1.44 2009/08/24 14:15:09 alvherre Exp $
+ *             $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_custom.c,v 1.45 2010/06/27 19:07:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -438,26 +438,24 @@ static void
 _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
 {
        lclContext *ctx = (lclContext *) AH->formatData;
-       int                     id;
        lclTocEntry *tctx = (lclTocEntry *) te->formatData;
        int                     blkType;
+       int                     id;
 
        if (tctx->dataState == K_OFFSET_NO_DATA)
                return;
 
        if (!ctx->hasSeek || tctx->dataState == K_OFFSET_POS_NOT_SET)
        {
-               /* Skip over unnecessary blocks until we get the one we want. */
-
+               /*
+                * We cannot seek directly to the desired block.  Instead, skip
+                * over block headers until we find the one we want.  This could
+                * fail if we are asked to restore items out-of-order.
+                */
                _readBlockHeader(AH, &blkType, &id);
 
-               while (id != te->dumpId)
+               while (blkType != EOF && id != te->dumpId)
                {
-                       if ((TocIDRequired(AH, id, ropt) & REQ_DATA) != 0)
-                               die_horribly(AH, modulename,
-                                                        "dumping a specific TOC data block out of order is not supported"
-                                         " without ID on this input stream (fseek required)\n");
-
                        switch (blkType)
                        {
                                case BLK_DATA:
@@ -479,13 +477,33 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
        }
        else
        {
-               /* Grab it */
+               /* We can just seek to the place we need to be. */
                if (fseeko(AH->FH, tctx->dataPos, SEEK_SET) != 0)
-                       die_horribly(AH, modulename, "error during file seek: %s\n", strerror(errno));
+                       die_horribly(AH, modulename, "error during file seek: %s\n",
+                                                strerror(errno));
 
                _readBlockHeader(AH, &blkType, &id);
        }
 
+       /* Produce suitable failure message if we fell off end of file */
+       if (blkType == EOF)
+       {
+               if (tctx->dataState == K_OFFSET_POS_NOT_SET)
+                       die_horribly(AH, modulename, "could not find block ID %d in archive -- "
+                                                "possibly due to out-of-order restore request, "
+                                                "which cannot be handled due to lack of data offsets in archive\n",
+                                                te->dumpId);
+               else if (!ctx->hasSeek)
+                       die_horribly(AH, modulename, "could not find block ID %d in archive -- "
+                                                "possibly due to out-of-order restore request, "
+                                                "which cannot be handled due to non-seekable input file\n",
+                                                te->dumpId);
+               else                                    /* huh, the dataPos led us to EOF? */
+                       die_horribly(AH, modulename, "could not find block ID %d in archive -- "
+                                                "possibly corrupt archive\n",
+                                                te->dumpId);
+       }
+
        /* Are we sane? */
        if (id != te->dumpId)
                die_horribly(AH, modulename, "found unexpected block ID (%d) when reading data -- expected %d\n",
@@ -907,15 +925,35 @@ _getFilePos(ArchiveHandle *AH, lclContext *ctx)
 
 /*
  * Read a data block header. The format changed in V1.3, so we
- * put the code here for simplicity.
+ * centralize the code here for simplicity.  Returns *type = EOF
+ * if at EOF.
  */
 static void
 _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
 {
+       lclContext *ctx = (lclContext *) AH->formatData;
+       int                     byt;
+
+       /*
+        * Note: if we are at EOF with a pre-1.3 input file, we'll die_horribly
+        * inside ReadInt rather than returning EOF.  It doesn't seem worth
+        * jumping through hoops to deal with that case better, because no such
+        * files are likely to exist in the wild: only some 7.1 development
+        * versions of pg_dump ever generated such files.
+        */
        if (AH->version < K_VERS_1_3)
                *type = BLK_DATA;
        else
-               *type = _ReadByte(AH);
+       {
+               byt = getc(AH->FH);
+               *type = byt;
+               if (byt == EOF)
+               {
+                       *id = 0;                        /* don't return an uninitialized value */
+                       return;
+               }
+               ctx->filePos += 1;
+       }
 
        *id = ReadInt(AH);
 }