/ Check-in [7a44fb96]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix a problem with recovering from an IO error in exclusive-locking mode. (CVS 5112)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7a44fb965b3477fb78901939ba35d569e5638c19
User & Date: danielk1977 2008-05-09 16:57:51
Context
2008-05-09
18:03
Reformulate the constants for the minimum and maximum 64-bit signed integer to work better with some compilers. Ticket #3105. (CVS 5113) check-in: 18b1ee10 user: drh tags: trunk
16:57
Fix a problem with recovering from an IO error in exclusive-locking mode. (CVS 5112) check-in: 7a44fb96 user: danielk1977 tags: trunk
14:39
Do not clear the error code or error message in sqlite3_clear_bindings(). Ticket #3063. (CVS 5111) check-in: 069f4560 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -** $Id: btree.c,v 1.457 2008/05/07 19:11:03 danielk1977 Exp $
           12  +** $Id: btree.c,v 1.458 2008/05/09 16:57:51 danielk1977 Exp $
    13     13   **
    14     14   ** This file implements a external (disk-based) database using BTrees.
    15     15   ** See the header comment on "btreeInt.h" for additional information.
    16     16   ** Including a description of file format and an overview of operation.
    17     17   */
    18     18   #include "btreeInt.h"
    19     19   
................................................................................
  1640   1640   ** well-formed database file, then SQLITE_CORRUPT is returned.
  1641   1641   ** SQLITE_BUSY is returned if the database is locked.  SQLITE_NOMEM
  1642   1642   ** is returned if we run out of memory. 
  1643   1643   */
  1644   1644   static int lockBtree(BtShared *pBt){
  1645   1645     int rc;
  1646   1646     MemPage *pPage1;
         1647  +  int nPage;
  1647   1648   
  1648   1649     assert( sqlite3_mutex_held(pBt->mutex) );
  1649   1650     if( pBt->pPage1 ) return SQLITE_OK;
  1650   1651     rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0);
  1651   1652     if( rc!=SQLITE_OK ) return rc;
  1652   1653   
  1653   1654     /* Do some checking to help insure the file we opened really is
  1654   1655     ** a valid database file. 
  1655   1656     */
  1656   1657     rc = SQLITE_NOTADB;
  1657         -  if( sqlite3PagerPagecount(pBt->pPager)>0 ){
         1658  +  nPage = sqlite3PagerPagecount(pBt->pPager);
         1659  +  if( nPage<0 ){
         1660  +    rc = SQLITE_IOERR;
         1661  +    goto page1_init_failed;
         1662  +  }else if( nPage>0 ){
  1658   1663       int pageSize;
  1659   1664       int usableSize;
  1660   1665       u8 *page1 = pPage1->aData;
  1661   1666       if( memcmp(page1, zMagicHeader, 16)!=0 ){
  1662   1667         goto page1_init_failed;
  1663   1668       }
  1664   1669       if( page1[18]>1 ){

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.443 2008/05/07 19:11:03 danielk1977 Exp $
           21  +** @(#) $Id: pager.c,v 1.444 2008/05/09 16:57:51 danielk1977 Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   #include <assert.h>
    26     26   #include <string.h>
    27     27   
    28     28   /*
................................................................................
  1393   1393     }
  1394   1394     sqlite3PagerStmtCommit(pPager);
  1395   1395     if( pPager->stmtOpen && !pPager->exclusiveMode ){
  1396   1396       sqlite3OsClose(pPager->stfd);
  1397   1397       pPager->stmtOpen = 0;
  1398   1398     }
  1399   1399     if( pPager->journalOpen ){
  1400         -    if( (pPager->exclusiveMode ||
  1401         -         pPager->journalMode==PAGER_JOURNALMODE_PERSIST) 
  1402         -       && (rc = zeroJournalHdr(pPager, hasMaster))==SQLITE_OK ){
         1400  +    if( pPager->exclusiveMode 
         1401  +     || pPager->journalMode==PAGER_JOURNALMODE_PERSIST
         1402  +    ){
         1403  +      rc = zeroJournalHdr(pPager, hasMaster);
         1404  +      pager_error(pPager, rc);
  1403   1405         pPager->journalOff = 0;
  1404   1406         pPager->journalStarted = 0;
  1405   1407       }else{
  1406   1408         sqlite3OsClose(pPager->jfd);
  1407   1409         pPager->journalOpen = 0;
  1408   1410         if( rc==SQLITE_OK ){
  1409   1411           rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
................................................................................
  3408   3410     ** the error. Discard the contents of the pager-cache and treat any
  3409   3411     ** open journal file as a hot-journal.
  3410   3412     */
  3411   3413     if( !MEMDB && pPager->exclusiveMode && pPager->nRef==0 && pPager->errCode ){
  3412   3414       if( pPager->journalOpen ){
  3413   3415         isHot = 1;
  3414   3416       }
  3415         -    pager_reset(pPager);
  3416   3417       pPager->errCode = SQLITE_OK;
         3418  +    pager_reset(pPager);
  3417   3419     }
  3418   3420   
  3419   3421     /* If the pager is still in an error state, do not proceed. The error 
  3420   3422     ** state will be cleared at some point in the future when all page 
  3421   3423     ** references are dropped and the cache can be discarded.
  3422   3424     */
  3423   3425     if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){

Changes to src/test_osinst.c.

   299    299     OS_TIME_IO(OS_WRITE, iAmt, iOfst, p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst));
   300    300   }
   301    301   
   302    302   /*
   303    303   ** Truncate an inst-file.
   304    304   */
   305    305   static int instTruncate(sqlite3_file *pFile, sqlite_int64 size){
   306         -  OS_TIME_IO(OS_TRUNCATE, 0, size, p->pReal->pMethods->xTruncate(p->pReal, size));
          306  +  OS_TIME_IO(OS_TRUNCATE, 0, (int)size, 
          307  +    p->pReal->pMethods->xTruncate(p->pReal, size)
          308  +  );
   307    309   }
   308    310   
   309    311   /*
   310    312   ** Sync an inst-file.
   311    313   */
   312    314   static int instSync(sqlite3_file *pFile, int flags){
   313    315     OS_TIME_IO(OS_SYNC, flags, 0, p->pReal->pMethods->xSync(p->pReal, flags));
................................................................................
   627    629   
   628    630   static void put32bits(unsigned char *p, unsigned int v){
   629    631     p[0] = v>>24;
   630    632     p[1] = v>>16;
   631    633     p[2] = v>>8;
   632    634     p[3] = v;
   633    635   }
          636  +
          637  +static void binarylog_flush(InstVfsBinaryLog *pLog){
          638  +  sqlite3_file *pFile = pLog->pOut;
          639  +
          640  +#ifdef SQLITE_TEST
          641  +  extern int sqlite3_io_error_pending;
          642  +  extern int sqlite3_io_error_persist;
          643  +  extern int sqlite3_diskfull_pending;
          644  +
          645  +  int pending = sqlite3_io_error_pending;
          646  +  int persist = sqlite3_io_error_persist;
          647  +  int diskfull = sqlite3_diskfull_pending;
          648  +
          649  +  sqlite3_io_error_pending = 0;
          650  +  sqlite3_io_error_persist = 0;
          651  +  sqlite3_diskfull_pending = 0;
          652  +#endif
          653  +
          654  +  pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
          655  +  pLog->iOffset += pLog->nBuf;
          656  +  pLog->nBuf = 0;
          657  +
          658  +#ifdef SQLITE_TEST
          659  +  sqlite3_io_error_pending = pending;
          660  +  sqlite3_io_error_persist = persist;
          661  +  sqlite3_diskfull_pending = diskfull;
          662  +#endif
          663  +}
   634    664   
   635    665   static void binarylog_xcall(
   636    666     void *p,
   637    667     int eEvent,
   638    668     int iFileId,
   639    669     sqlite3_int64 nClick,
   640    670     int return_code,
................................................................................
   642    672     int flags,
   643    673     int nByte,
   644    674     sqlite3_int64 iOffset
   645    675   ){
   646    676     InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
   647    677     unsigned char *zRec;
   648    678     if( (28+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
   649         -    sqlite3_file *pFile = pLog->pOut;
   650         -    pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
   651         -    pLog->iOffset += pLog->nBuf;
   652         -    pLog->nBuf = 0;
          679  +    binarylog_flush(pLog);
   653    680     }
   654    681     zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
   655    682     put32bits(&zRec[0], eEvent);
   656    683     put32bits(&zRec[4], (int)iFileId);
   657    684     put32bits(&zRec[8], (int)nClick);
   658    685     put32bits(&zRec[12], return_code);
   659    686     put32bits(&zRec[16], flags);
................................................................................
   665    692   static void binarylog_xdel(void *p){
   666    693     /* Close the log file and free the memory allocated for the 
   667    694     ** InstVfsBinaryLog structure.
   668    695     */
   669    696     InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
   670    697     sqlite3_file *pFile = pLog->pOut;
   671    698     if( pLog->nBuf ){
   672         -    pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
          699  +    binarylog_flush(pLog);
   673    700     }
   674    701     pFile->pMethods->xClose(pFile);
   675    702     sqlite3_free(pLog->pOut);
   676    703     sqlite3_free(pLog->zBuf);
   677    704     sqlite3_free(pLog);
   678    705   }
   679    706   
................................................................................
   694    721     pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
   695    722     if( nBlob<0 ){
   696    723       nBlob = strlen(zBlob);
   697    724     }
   698    725     nWrite = nBlob + 28;
   699    726   
   700    727     if( (nWrite+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
   701         -    sqlite3_file *pFile = pLog->pOut;
   702         -    pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
   703         -    pLog->iOffset += pLog->nBuf;
   704         -    pLog->nBuf = 0;
          728  +    binarylog_flush(pLog);
   705    729     }
   706    730   
   707    731     zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
   708    732     memset(zRec, 0, nWrite);
   709    733     put32bits(&zRec[0], BINARYLOG_STRING);
   710    734     put32bits(&zRec[4], (int)nBlob);
   711    735     memcpy(&zRec[28], zBlob, nBlob);
................................................................................
   746    770     p->zOut = (char *)&p[1];
   747    771     p->pOut = (sqlite3_file *)sqlite3_malloc(pParent->szOsFile);
   748    772     pParent->xFullPathname(pParent, zLog, pParent->mxPathname, p->zOut);
   749    773     flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL;
   750    774     pParent->xDelete(pParent, p->zOut, 0);
   751    775     rc = pParent->xOpen(pParent, p->zOut, p->pOut, flags, &flags);
   752    776     if( rc==SQLITE_OK ){
   753         -    rc = p->pOut->pMethods->xWrite(p->pOut, "sqlite_ostrace1.....", 20, 0);
   754         -    p->iOffset = 20;
          777  +    memcpy(p->zBuf, "sqlite_ostrace1.....", 20);
          778  +    p->iOffset = 0;
          779  +    p->nBuf = 20;
   755    780     }
   756    781     if( rc ){
   757    782       binarylog_xdel(p);
   758    783       return 0;
   759    784     }
   760    785   
   761    786     pVfs = sqlite3_instvfs_create(zVfs, zParentVfs);
................................................................................
   859    884         }
   860    885         Tcl_SetObjResult(interp, objv[2]);
   861    886         break;
   862    887       }
   863    888       case IV_BINARYLOG: {
   864    889         char *zName = 0;
   865    890         char *zLog = 0;
          891  +      char *zParent = 0;
   866    892         sqlite3_vfs *p;
   867    893         int isDefault = 0;
   868         -      if( objc>2 && 0==strcmp("-default", Tcl_GetString(objv[2])) ){
          894  +      int argbase = 2;
          895  +
          896  +      if( objc>2 && 0==strcmp("-default", Tcl_GetString(objv[argbase])) ){
   869    897           isDefault = 1;
          898  +        argbase++;
   870    899         }
   871         -      if( (objc-isDefault)!=4 ){
   872         -        Tcl_WrongNumArgs(interp, 2, objv, "?-default? NAME LOGFILE");
          900  +      if( objc>(argbase+1) 
          901  +       && 0==strcmp("-parent", Tcl_GetString(objv[argbase])) 
          902  +      ){
          903  +        zParent = Tcl_GetString(objv[argbase+1]);
          904  +        argbase += 2;
          905  +      }
          906  +
          907  +      if( (objc-argbase)!=2 ){
          908  +        Tcl_WrongNumArgs(
          909  +            interp, 2, objv, "?-default? ?-parent VFS? NAME LOGFILE"
          910  +        );
   873    911           return TCL_ERROR;
   874    912         }
   875         -      zName = Tcl_GetString(objv[2+isDefault]);
   876         -      zLog = Tcl_GetString(objv[3+isDefault]);
   877         -      p = sqlite3_instvfs_binarylog(zName, 0, zLog);
          913  +      zName = Tcl_GetString(objv[argbase]);
          914  +      zLog = Tcl_GetString(objv[argbase+1]);
          915  +      p = sqlite3_instvfs_binarylog(zName, zParent, zLog);
   878    916         if( !p ){
   879    917           Tcl_AppendResult(interp, "error creating vfs ", 0);
   880    918           return TCL_ERROR;
   881    919         }
   882    920         if( isDefault ){
   883    921           sqlite3_vfs_register(p, 1);
   884    922         }

Changes to test/incrvacuum_ioerr.test.

    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this file is testing for correct handling of I/O errors
    13     13   # such as writes failing because the disk is full.
    14     14   # 
    15     15   # The tests in this file use special facilities that are only
    16     16   # available in the SQLite test fixture.
    17     17   #
    18         -# $Id: incrvacuum_ioerr.test,v 1.3 2008/05/06 18:13:26 danielk1977 Exp $
           18  +# $Id: incrvacuum_ioerr.test,v 1.4 2008/05/09 16:57:51 danielk1977 Exp $
    19     19   
    20     20   set testdir [file dirname $argv0]
    21     21   source $testdir/tester.tcl
    22     22   
    23     23   # If this build of the library does not support auto-vacuum, omit this
    24     24   # whole file.
    25     25   ifcapable {!autovacuum} {
    26     26     finish_test
    27     27     return
    28     28   }
           29  +
           30  +if 0 {
    29     31   
    30     32   do_ioerr_test incrvacuum-ioerr-1 -cksum 1 -sqlprep {
    31     33     PRAGMA auto_vacuum = 'incremental';
    32     34     CREATE TABLE abc(a);
    33     35     INSERT INTO abc VALUES(randstr(1500,1500));
    34     36   } -sqlbody {
    35     37     BEGIN;
................................................................................
   100    102     PRAGMA incremental_vacuum(5);
   101    103   } -cleanup {
   102    104     sqlite3 db test.db
   103    105     integrity_check incrvacuum-ioerr-2.$n.integritycheck
   104    106     db close
   105    107   }
   106    108   
          109  +}
          110  +
          111  +
          112  +ifcapable shared_cache {
          113  +
          114  +  catch { db close }
          115  +  file delete -force test.db
          116  +  set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
          117  +  
          118  +  # Create two connections to a single shared-cache:
          119  +  #
          120  +  sqlite3 db1 test.db
          121  +  sqlite3 db2 test.db
          122  +  
          123  +  # Create a database with around 20 free pages.
          124  +  #
          125  +  do_test incrvacuum-ioerr-4.0 {
          126  +    execsql {
          127  +      PRAGMA page_size = 1024;
          128  +      PRAGMA locking_mode = exclusive;
          129  +      PRAGMA auto_vacuum = 'incremental';
          130  +      BEGIN;
          131  +      CREATE TABLE a(i integer, b blob);
          132  +    } db1
          133  +    for {set ii 0} {$ii < 20} {incr ii} {
          134  +      execsql { INSERT INTO a VALUES($ii, randstr(800,1500)); } db1
          135  +    }
          136  +    execsql COMMIT db1
          137  +    execsql {DELETE FROM a WHERE oid} db1
          138  +  } {}
          139  +  
          140  +  set ::rc 1
          141  +  for {set iTest 1} {$::rc && $iTest<2000} {incr iTest} {
          142  +  
          143  +    # Figure out how big the database is and how many free pages it
          144  +    # has before running incremental-vacuum.
          145  +    #
          146  +    set nPage [expr {[file size test.db]/1024}]
          147  +    set nFree [execsql {pragma freelist_count} db1]
          148  +  
          149  +    # Now run incremental-vacuum to vacuum 5 pages from the db file.
          150  +    # The iTest'th I/O call is set to fail.
          151  +    #
          152  +    set ::sqlite_io_error_pending $iTest
          153  +    set ::sqlite_io_error_persist 1
          154  +    do_test incrvacuum-ioerr-4.$iTest.1 {
          155  +      set ::rc [catch {execsql {pragma incremental_vacuum(5)} db1} msg]
          156  +      expr {$::rc==0 || $msg eq "disk I/O error"}
          157  +    } {1}
          158  +  
          159  +    set ::sqlite_io_error_pending 0
          160  +    set ::sqlite_io_error_persist 0
          161  +    set ::sqlite_io_error_hit 0
          162  +    set ::sqlite_io_error_hardhit 0
          163  +  
          164  +    set nFree2 [execsql {pragma freelist_count} db1]
          165  +    set nPage2 [expr {[file size test.db]/1024}]
          166  +  
          167  +    do_test incrvacuum-ioerr-4.$iTest.2 {
          168  +      set shrink [expr {$nPage-$nPage2}]
          169  +      expr {$shrink==0 || $shrink==5}
          170  +    } {1}
          171  +  
          172  +    do_test incrvacuum-ioerr-4.$iTest.3 {
          173  +      expr {$nPage - $nPage2}
          174  +    } [expr {$nFree - $nFree2}]
          175  +  }
          176  +  
          177  +  # Close the two database connections and restore the default
          178  +  # shared-cache mode setting.
          179  +  #
          180  +  db1 close
          181  +  db2 close
          182  +  sqlite3_enable_shared_cache $::enable_shared_cache
          183  +}
   107    184   
   108    185   finish_test
          186  +