/ 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 Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1640
1641
1642
1643
1644
1645
1646

1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657




1658
1659
1660
1661
1662
1663
1664
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.457 2008/05/07 19:11:03 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
** well-formed database file, then SQLITE_CORRUPT is returned.
** SQLITE_BUSY is returned if the database is locked.  SQLITE_NOMEM
** is returned if we run out of memory. 
*/
static int lockBtree(BtShared *pBt){
  int rc;
  MemPage *pPage1;


  assert( sqlite3_mutex_held(pBt->mutex) );
  if( pBt->pPage1 ) return SQLITE_OK;
  rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0);
  if( rc!=SQLITE_OK ) return rc;

  /* Do some checking to help insure the file we opened really is
  ** a valid database file. 
  */
  rc = SQLITE_NOTADB;
  if( sqlite3PagerPagecount(pBt->pPager)>0 ){




    int pageSize;
    int usableSize;
    u8 *page1 = pPage1->aData;
    if( memcmp(page1, zMagicHeader, 16)!=0 ){
      goto page1_init_failed;
    }
    if( page1[18]>1 ){







|







 







>










|
>
>
>
>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.458 2008/05/09 16:57:51 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
** well-formed database file, then SQLITE_CORRUPT is returned.
** SQLITE_BUSY is returned if the database is locked.  SQLITE_NOMEM
** is returned if we run out of memory. 
*/
static int lockBtree(BtShared *pBt){
  int rc;
  MemPage *pPage1;
  int nPage;

  assert( sqlite3_mutex_held(pBt->mutex) );
  if( pBt->pPage1 ) return SQLITE_OK;
  rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0);
  if( rc!=SQLITE_OK ) return rc;

  /* Do some checking to help insure the file we opened really is
  ** a valid database file. 
  */
  rc = SQLITE_NOTADB;
  nPage = sqlite3PagerPagecount(pBt->pPager);
  if( nPage<0 ){
    rc = SQLITE_IOERR;
    goto page1_init_failed;
  }else if( nPage>0 ){
    int pageSize;
    int usableSize;
    u8 *page1 = pPage1->aData;
    if( memcmp(page1, zMagicHeader, 16)!=0 ){
      goto page1_init_failed;
    }
    if( page1[18]>1 ){

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1393
1394
1395
1396
1397
1398
1399
1400
1401

1402

1403
1404
1405
1406
1407
1408
1409
....
3408
3409
3410
3411
3412
3413
3414
3415
3416

3417
3418
3419
3420
3421
3422
3423
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.443 2008/05/07 19:11:03 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  }
  sqlite3PagerStmtCommit(pPager);
  if( pPager->stmtOpen && !pPager->exclusiveMode ){
    sqlite3OsClose(pPager->stfd);
    pPager->stmtOpen = 0;
  }
  if( pPager->journalOpen ){
    if( (pPager->exclusiveMode ||
         pPager->journalMode==PAGER_JOURNALMODE_PERSIST) 

       && (rc = zeroJournalHdr(pPager, hasMaster))==SQLITE_OK ){

      pPager->journalOff = 0;
      pPager->journalStarted = 0;
    }else{
      sqlite3OsClose(pPager->jfd);
      pPager->journalOpen = 0;
      if( rc==SQLITE_OK ){
        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
................................................................................
  ** the error. Discard the contents of the pager-cache and treat any
  ** open journal file as a hot-journal.
  */
  if( !MEMDB && pPager->exclusiveMode && pPager->nRef==0 && pPager->errCode ){
    if( pPager->journalOpen ){
      isHot = 1;
    }
    pager_reset(pPager);
    pPager->errCode = SQLITE_OK;

  }

  /* If the pager is still in an error state, do not proceed. The error 
  ** state will be cleared at some point in the future when all page 
  ** references are dropped and the cache can be discarded.
  */
  if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){







|







 







|
|
>
|
>







 







<

>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
....
3410
3411
3412
3413
3414
3415
3416

3417
3418
3419
3420
3421
3422
3423
3424
3425
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.444 2008/05/09 16:57:51 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  }
  sqlite3PagerStmtCommit(pPager);
  if( pPager->stmtOpen && !pPager->exclusiveMode ){
    sqlite3OsClose(pPager->stfd);
    pPager->stmtOpen = 0;
  }
  if( pPager->journalOpen ){
    if( pPager->exclusiveMode 
     || pPager->journalMode==PAGER_JOURNALMODE_PERSIST
    ){
      rc = zeroJournalHdr(pPager, hasMaster);
      pager_error(pPager, rc);
      pPager->journalOff = 0;
      pPager->journalStarted = 0;
    }else{
      sqlite3OsClose(pPager->jfd);
      pPager->journalOpen = 0;
      if( rc==SQLITE_OK ){
        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
................................................................................
  ** the error. Discard the contents of the pager-cache and treat any
  ** open journal file as a hot-journal.
  */
  if( !MEMDB && pPager->exclusiveMode && pPager->nRef==0 && pPager->errCode ){
    if( pPager->journalOpen ){
      isHot = 1;
    }

    pPager->errCode = SQLITE_OK;
    pager_reset(pPager);
  }

  /* If the pager is still in an error state, do not proceed. The error 
  ** state will be cleared at some point in the future when all page 
  ** references are dropped and the cache can be discarded.
  */
  if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){

Changes to src/test_osinst.c.

299
300
301
302
303
304
305

306

307
308
309
310
311
312
313
...
627
628
629
630
631
632
633




























634
635
636
637
638
639
640
...
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
...
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
...
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
...
746
747
748
749
750
751
752
753
754

755
756
757
758
759
760
761
...
859
860
861
862
863
864
865

866
867


868
869

870







871

872

873
874
875
876
877
878
879
880
881
882
883
884
  OS_TIME_IO(OS_WRITE, iAmt, iOfst, p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst));
}

/*
** Truncate an inst-file.
*/
static int instTruncate(sqlite3_file *pFile, sqlite_int64 size){

  OS_TIME_IO(OS_TRUNCATE, 0, size, p->pReal->pMethods->xTruncate(p->pReal, size));

}

/*
** Sync an inst-file.
*/
static int instSync(sqlite3_file *pFile, int flags){
  OS_TIME_IO(OS_SYNC, flags, 0, p->pReal->pMethods->xSync(p->pReal, flags));
................................................................................

static void put32bits(unsigned char *p, unsigned int v){
  p[0] = v>>24;
  p[1] = v>>16;
  p[2] = v>>8;
  p[3] = v;
}





























static void binarylog_xcall(
  void *p,
  int eEvent,
  int iFileId,
  sqlite3_int64 nClick,
  int return_code,
................................................................................
  int flags,
  int nByte,
  sqlite3_int64 iOffset
){
  InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
  unsigned char *zRec;
  if( (28+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
    sqlite3_file *pFile = pLog->pOut;
    pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
    pLog->iOffset += pLog->nBuf;
    pLog->nBuf = 0;
  }
  zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
  put32bits(&zRec[0], eEvent);
  put32bits(&zRec[4], (int)iFileId);
  put32bits(&zRec[8], (int)nClick);
  put32bits(&zRec[12], return_code);
  put32bits(&zRec[16], flags);
................................................................................
static void binarylog_xdel(void *p){
  /* Close the log file and free the memory allocated for the 
  ** InstVfsBinaryLog structure.
  */
  InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
  sqlite3_file *pFile = pLog->pOut;
  if( pLog->nBuf ){
    pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
  }
  pFile->pMethods->xClose(pFile);
  sqlite3_free(pLog->pOut);
  sqlite3_free(pLog->zBuf);
  sqlite3_free(pLog);
}

................................................................................
  pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
  if( nBlob<0 ){
    nBlob = strlen(zBlob);
  }
  nWrite = nBlob + 28;

  if( (nWrite+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
    sqlite3_file *pFile = pLog->pOut;
    pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
    pLog->iOffset += pLog->nBuf;
    pLog->nBuf = 0;
  }

  zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
  memset(zRec, 0, nWrite);
  put32bits(&zRec[0], BINARYLOG_STRING);
  put32bits(&zRec[4], (int)nBlob);
  memcpy(&zRec[28], zBlob, nBlob);
................................................................................
  p->zOut = (char *)&p[1];
  p->pOut = (sqlite3_file *)sqlite3_malloc(pParent->szOsFile);
  pParent->xFullPathname(pParent, zLog, pParent->mxPathname, p->zOut);
  flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL;
  pParent->xDelete(pParent, p->zOut, 0);
  rc = pParent->xOpen(pParent, p->zOut, p->pOut, flags, &flags);
  if( rc==SQLITE_OK ){
    rc = p->pOut->pMethods->xWrite(p->pOut, "sqlite_ostrace1.....", 20, 0);
    p->iOffset = 20;

  }
  if( rc ){
    binarylog_xdel(p);
    return 0;
  }

  pVfs = sqlite3_instvfs_create(zVfs, zParentVfs);
................................................................................
      }
      Tcl_SetObjResult(interp, objv[2]);
      break;
    }
    case IV_BINARYLOG: {
      char *zName = 0;
      char *zLog = 0;

      sqlite3_vfs *p;
      int isDefault = 0;


      if( objc>2 && 0==strcmp("-default", Tcl_GetString(objv[2])) ){
        isDefault = 1;

      }







      if( (objc-isDefault)!=4 ){

        Tcl_WrongNumArgs(interp, 2, objv, "?-default? NAME LOGFILE");

        return TCL_ERROR;
      }
      zName = Tcl_GetString(objv[2+isDefault]);
      zLog = Tcl_GetString(objv[3+isDefault]);
      p = sqlite3_instvfs_binarylog(zName, 0, zLog);
      if( !p ){
        Tcl_AppendResult(interp, "error creating vfs ", 0);
        return TCL_ERROR;
      }
      if( isDefault ){
        sqlite3_vfs_register(p, 1);
      }







>
|
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
<
<
<







 







|







 







|
<
<
<







 







|
|
>







 







>


>
>
|

>

>
>
>
>
>
>
>
|
>
|
>


|
|
|







299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
...
672
673
674
675
676
677
678
679



680
681
682
683
684
685
686
...
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
...
721
722
723
724
725
726
727
728



729
730
731
732
733
734
735
...
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
...
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
  OS_TIME_IO(OS_WRITE, iAmt, iOfst, p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst));
}

/*
** Truncate an inst-file.
*/
static int instTruncate(sqlite3_file *pFile, sqlite_int64 size){
  OS_TIME_IO(OS_TRUNCATE, 0, (int)size, 
    p->pReal->pMethods->xTruncate(p->pReal, size)
  );
}

/*
** Sync an inst-file.
*/
static int instSync(sqlite3_file *pFile, int flags){
  OS_TIME_IO(OS_SYNC, flags, 0, p->pReal->pMethods->xSync(p->pReal, flags));
................................................................................

static void put32bits(unsigned char *p, unsigned int v){
  p[0] = v>>24;
  p[1] = v>>16;
  p[2] = v>>8;
  p[3] = v;
}

static void binarylog_flush(InstVfsBinaryLog *pLog){
  sqlite3_file *pFile = pLog->pOut;

#ifdef SQLITE_TEST
  extern int sqlite3_io_error_pending;
  extern int sqlite3_io_error_persist;
  extern int sqlite3_diskfull_pending;

  int pending = sqlite3_io_error_pending;
  int persist = sqlite3_io_error_persist;
  int diskfull = sqlite3_diskfull_pending;

  sqlite3_io_error_pending = 0;
  sqlite3_io_error_persist = 0;
  sqlite3_diskfull_pending = 0;
#endif

  pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
  pLog->iOffset += pLog->nBuf;
  pLog->nBuf = 0;

#ifdef SQLITE_TEST
  sqlite3_io_error_pending = pending;
  sqlite3_io_error_persist = persist;
  sqlite3_diskfull_pending = diskfull;
#endif
}

static void binarylog_xcall(
  void *p,
  int eEvent,
  int iFileId,
  sqlite3_int64 nClick,
  int return_code,
................................................................................
  int flags,
  int nByte,
  sqlite3_int64 iOffset
){
  InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
  unsigned char *zRec;
  if( (28+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
    binarylog_flush(pLog);



  }
  zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
  put32bits(&zRec[0], eEvent);
  put32bits(&zRec[4], (int)iFileId);
  put32bits(&zRec[8], (int)nClick);
  put32bits(&zRec[12], return_code);
  put32bits(&zRec[16], flags);
................................................................................
static void binarylog_xdel(void *p){
  /* Close the log file and free the memory allocated for the 
  ** InstVfsBinaryLog structure.
  */
  InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
  sqlite3_file *pFile = pLog->pOut;
  if( pLog->nBuf ){
    binarylog_flush(pLog);
  }
  pFile->pMethods->xClose(pFile);
  sqlite3_free(pLog->pOut);
  sqlite3_free(pLog->zBuf);
  sqlite3_free(pLog);
}

................................................................................
  pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
  if( nBlob<0 ){
    nBlob = strlen(zBlob);
  }
  nWrite = nBlob + 28;

  if( (nWrite+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
    binarylog_flush(pLog);



  }

  zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
  memset(zRec, 0, nWrite);
  put32bits(&zRec[0], BINARYLOG_STRING);
  put32bits(&zRec[4], (int)nBlob);
  memcpy(&zRec[28], zBlob, nBlob);
................................................................................
  p->zOut = (char *)&p[1];
  p->pOut = (sqlite3_file *)sqlite3_malloc(pParent->szOsFile);
  pParent->xFullPathname(pParent, zLog, pParent->mxPathname, p->zOut);
  flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL;
  pParent->xDelete(pParent, p->zOut, 0);
  rc = pParent->xOpen(pParent, p->zOut, p->pOut, flags, &flags);
  if( rc==SQLITE_OK ){
    memcpy(p->zBuf, "sqlite_ostrace1.....", 20);
    p->iOffset = 0;
    p->nBuf = 20;
  }
  if( rc ){
    binarylog_xdel(p);
    return 0;
  }

  pVfs = sqlite3_instvfs_create(zVfs, zParentVfs);
................................................................................
      }
      Tcl_SetObjResult(interp, objv[2]);
      break;
    }
    case IV_BINARYLOG: {
      char *zName = 0;
      char *zLog = 0;
      char *zParent = 0;
      sqlite3_vfs *p;
      int isDefault = 0;
      int argbase = 2;

      if( objc>2 && 0==strcmp("-default", Tcl_GetString(objv[argbase])) ){
        isDefault = 1;
        argbase++;
      }
      if( objc>(argbase+1) 
       && 0==strcmp("-parent", Tcl_GetString(objv[argbase])) 
      ){
        zParent = Tcl_GetString(objv[argbase+1]);
        argbase += 2;
      }

      if( (objc-argbase)!=2 ){
        Tcl_WrongNumArgs(
            interp, 2, objv, "?-default? ?-parent VFS? NAME LOGFILE"
        );
        return TCL_ERROR;
      }
      zName = Tcl_GetString(objv[argbase]);
      zLog = Tcl_GetString(objv[argbase+1]);
      p = sqlite3_instvfs_binarylog(zName, zParent, zLog);
      if( !p ){
        Tcl_AppendResult(interp, "error creating vfs ", 0);
        return TCL_ERROR;
      }
      if( isDefault ){
        sqlite3_vfs_register(p, 1);
      }

Changes to test/incrvacuum_ioerr.test.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28


29
30
31
32
33
34
35
...
100
101
102
103
104
105
106
107











































































108

# This file implements regression tests for SQLite library.  The
# focus of this file is testing for correct handling of I/O errors
# such as writes failing because the disk is full.
# 
# The tests in this file use special facilities that are only
# available in the SQLite test fixture.
#
# $Id: incrvacuum_ioerr.test,v 1.3 2008/05/06 18:13:26 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# If this build of the library does not support auto-vacuum, omit this
# whole file.
ifcapable {!autovacuum} {
  finish_test
  return
}



do_ioerr_test incrvacuum-ioerr-1 -cksum 1 -sqlprep {
  PRAGMA auto_vacuum = 'incremental';
  CREATE TABLE abc(a);
  INSERT INTO abc VALUES(randstr(1500,1500));
} -sqlbody {
  BEGIN;
................................................................................
  PRAGMA incremental_vacuum(5);
} -cleanup {
  sqlite3 db test.db
  integrity_check incrvacuum-ioerr-2.$n.integritycheck
  db close
}













































































finish_test








|










>
>







 







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# This file implements regression tests for SQLite library.  The
# focus of this file is testing for correct handling of I/O errors
# such as writes failing because the disk is full.
# 
# The tests in this file use special facilities that are only
# available in the SQLite test fixture.
#
# $Id: incrvacuum_ioerr.test,v 1.4 2008/05/09 16:57:51 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# If this build of the library does not support auto-vacuum, omit this
# whole file.
ifcapable {!autovacuum} {
  finish_test
  return
}

if 0 {

do_ioerr_test incrvacuum-ioerr-1 -cksum 1 -sqlprep {
  PRAGMA auto_vacuum = 'incremental';
  CREATE TABLE abc(a);
  INSERT INTO abc VALUES(randstr(1500,1500));
} -sqlbody {
  BEGIN;
................................................................................
  PRAGMA incremental_vacuum(5);
} -cleanup {
  sqlite3 db test.db
  integrity_check incrvacuum-ioerr-2.$n.integritycheck
  db close
}

}


ifcapable shared_cache {

  catch { db close }
  file delete -force test.db
  set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
  
  # Create two connections to a single shared-cache:
  #
  sqlite3 db1 test.db
  sqlite3 db2 test.db
  
  # Create a database with around 20 free pages.
  #
  do_test incrvacuum-ioerr-4.0 {
    execsql {
      PRAGMA page_size = 1024;
      PRAGMA locking_mode = exclusive;
      PRAGMA auto_vacuum = 'incremental';
      BEGIN;
      CREATE TABLE a(i integer, b blob);
    } db1
    for {set ii 0} {$ii < 20} {incr ii} {
      execsql { INSERT INTO a VALUES($ii, randstr(800,1500)); } db1
    }
    execsql COMMIT db1
    execsql {DELETE FROM a WHERE oid} db1
  } {}
  
  set ::rc 1
  for {set iTest 1} {$::rc && $iTest<2000} {incr iTest} {
  
    # Figure out how big the database is and how many free pages it
    # has before running incremental-vacuum.
    #
    set nPage [expr {[file size test.db]/1024}]
    set nFree [execsql {pragma freelist_count} db1]
  
    # Now run incremental-vacuum to vacuum 5 pages from the db file.
    # The iTest'th I/O call is set to fail.
    #
    set ::sqlite_io_error_pending $iTest
    set ::sqlite_io_error_persist 1
    do_test incrvacuum-ioerr-4.$iTest.1 {
      set ::rc [catch {execsql {pragma incremental_vacuum(5)} db1} msg]
      expr {$::rc==0 || $msg eq "disk I/O error"}
    } {1}
  
    set ::sqlite_io_error_pending 0
    set ::sqlite_io_error_persist 0
    set ::sqlite_io_error_hit 0
    set ::sqlite_io_error_hardhit 0
  
    set nFree2 [execsql {pragma freelist_count} db1]
    set nPage2 [expr {[file size test.db]/1024}]
  
    do_test incrvacuum-ioerr-4.$iTest.2 {
      set shrink [expr {$nPage-$nPage2}]
      expr {$shrink==0 || $shrink==5}
    } {1}
  
    do_test incrvacuum-ioerr-4.$iTest.3 {
      expr {$nPage - $nPage2}
    } [expr {$nFree - $nFree2}]
  }
  
  # Close the two database connections and restore the default
  # shared-cache mode setting.
  #
  db1 close
  db2 close
  sqlite3_enable_shared_cache $::enable_shared_cache
}

finish_test