/ Check-in [1a53f531]
Login

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

Overview
Comment:Infrastructure to make simulated I/O errors persistent instead of transient. (CVS 3689)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 1a53f531ecd51938fab37a59b7088d89d8f8569d
User & Date: drh 2007-03-15 12:17:43
Context
2007-03-15
12:51
Fix the pager so that it handles persistent I/O errors correctly. Update the testing infrastructure so that it tests for persistent I/O errors instead of just transient errors. (CVS 3690) check-in: ea8507a7 user: drh tags: trunk
12:17
Infrastructure to make simulated I/O errors persistent instead of transient. (CVS 3689) check-in: 1a53f531 user: drh tags: trunk
12:05
Catch an IO error code that was being ignored in OP_RowData. (CVS 3688) check-in: 760ebf80 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os_common.h.

86
87
88
89
90
91
92

93
94
95
96
97


98
99
100
101
102
103
104
105
106
** If we compile with the SQLITE_TEST macro set, then the following block
** of code will give us the ability to simulate a disk I/O error.  This
** is used for testing the I/O recovery logic.
*/
#ifdef SQLITE_TEST
int sqlite3_io_error_hit = 0;
int sqlite3_io_error_pending = 0;

int sqlite3_diskfull_pending = 0;
int sqlite3_diskfull = 0;
#define SimulateIOError(CODE)  \
   if( sqlite3_io_error_pending ) \
     if( sqlite3_io_error_pending-- == 1 ){ local_ioerr(); CODE; }


static void local_ioerr(){
  sqlite3_io_error_hit = 1;  /* Really just a place to set a breakpoint */
}
#define SimulateDiskfullError(CODE) \
   if( sqlite3_diskfull_pending ){ \
     if( sqlite3_diskfull_pending == 1 ){ \
       local_ioerr(); \
       sqlite3_diskfull = 1; \
       CODE; \







>




|
>
>

|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
** If we compile with the SQLITE_TEST macro set, then the following block
** of code will give us the ability to simulate a disk I/O error.  This
** is used for testing the I/O recovery logic.
*/
#ifdef SQLITE_TEST
int sqlite3_io_error_hit = 0;
int sqlite3_io_error_pending = 0;
int sqlite3_io_error_persist = 0;
int sqlite3_diskfull_pending = 0;
int sqlite3_diskfull = 0;
#define SimulateIOError(CODE)  \
   if( sqlite3_io_error_pending ) \
     if( sqlite3_io_error_pending-- == 1 \
         || (sqlite3_io_error_persist && sqlite3_io_error_hit) ) \
                { local_ioerr(); CODE; }
static void local_ioerr(){
  sqlite3_io_error_hit = 1;
}
#define SimulateDiskfullError(CODE) \
   if( sqlite3_diskfull_pending ){ \
     if( sqlite3_diskfull_pending == 1 ){ \
       local_ioerr(); \
       sqlite3_diskfull = 1; \
       CODE; \

Changes to src/test2.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
554
555
556
557
558
559
560

561
562
563
564
565
566
567
...
588
589
590
591
592
593
594


595
596
597
598
599
600
601
602
603
604
605
606
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the pager.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test2.c,v 1.39 2006/01/06 14:32:20 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
................................................................................
}
#endif

/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest2_Init(Tcl_Interp *interp){

  extern int sqlite3_io_error_pending;
  extern int sqlite3_io_error_hit;
  extern int sqlite3_diskfull_pending;
  extern int sqlite3_diskfull;
  static struct {
    char *zName;
    Tcl_CmdProc *xProc;
................................................................................
  };
  int i;
  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  }
  Tcl_LinkVar(interp, "sqlite_io_error_pending",
     (char*)&sqlite3_io_error_pending, TCL_LINK_INT);


  Tcl_LinkVar(interp, "sqlite_io_error_hit",
     (char*)&sqlite3_io_error_hit, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_diskfull_pending",
     (char*)&sqlite3_diskfull_pending, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_diskfull",
     (char*)&sqlite3_diskfull, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_pending_byte",
     (char*)&sqlite3_pending_byte, TCL_LINK_INT);
  Tcl_LinkVar(interp, "pager_pagesize",
     (char*)&test_pagesize, TCL_LINK_INT);
  return TCL_OK;
}







|







 







>







 







>
>












9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
...
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the pager.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test2.c,v 1.40 2007/03/15 12:17:43 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
................................................................................
}
#endif

/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest2_Init(Tcl_Interp *interp){
  extern int sqlite3_io_error_persist;
  extern int sqlite3_io_error_pending;
  extern int sqlite3_io_error_hit;
  extern int sqlite3_diskfull_pending;
  extern int sqlite3_diskfull;
  static struct {
    char *zName;
    Tcl_CmdProc *xProc;
................................................................................
  };
  int i;
  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  }
  Tcl_LinkVar(interp, "sqlite_io_error_pending",
     (char*)&sqlite3_io_error_pending, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_io_error_persist",
     (char*)&sqlite3_io_error_persist, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_io_error_hit",
     (char*)&sqlite3_io_error_hit, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_diskfull_pending",
     (char*)&sqlite3_diskfull_pending, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_diskfull",
     (char*)&sqlite3_diskfull, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_pending_byte",
     (char*)&sqlite3_pending_byte, TCL_LINK_INT);
  Tcl_LinkVar(interp, "pager_pagesize",
     (char*)&test_pagesize, TCL_LINK_INT);
  return TCL_OK;
}

Changes to test/ioerr.test.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
...
255
256
257
258
259
260
261
262









263
# 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: ioerr.test,v 1.29 2007/01/04 14:58:14 drh Exp $

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


# If SQLITE_DEFAULT_AUTOVACUUM is set to true, then a simulated IO error
# on the 8th IO operation in the SQL script below doesn't report an error.
................................................................................
  INSERT INTO t1 VALUES(1,2,3);
  INSERT INTO t1 VALUES(4,5,6);
  COMMIT;
  SELECT * FROM t1;
  DELETE FROM t1 WHERE a<100;
} -exclude [expr [string match [execsql {pragma auto_vacuum}] 1] ? 4 : 0]

finish_test
return

# Test for IO errors during a VACUUM. 
#
# The first IO call is excluded from the test. This call attempts to read
# the file-header of the temporary database used by VACUUM. Since the
# database doesn't exist at that point, the IO error is not detected.
# 
# Additionally, if auto-vacuum is enabled, the 12th IO error is not 
................................................................................
    INSERT INTO t1 SELECT (a+500)%900, 'good string' FROM t1;
  }} msg

  if {$msg != "column a is not unique"} {
    error $msg
  }
}










finish_test







|







 







<
<
<







 








>
>
>
>
>
>
>
>
>

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
42
43
44
45
46
47
48



49
50
51
52
53
54
55
...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# 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: ioerr.test,v 1.30 2007/03/15 12:17:43 drh Exp $

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


# If SQLITE_DEFAULT_AUTOVACUUM is set to true, then a simulated IO error
# on the 8th IO operation in the SQL script below doesn't report an error.
................................................................................
  INSERT INTO t1 VALUES(1,2,3);
  INSERT INTO t1 VALUES(4,5,6);
  COMMIT;
  SELECT * FROM t1;
  DELETE FROM t1 WHERE a<100;
} -exclude [expr [string match [execsql {pragma auto_vacuum}] 1] ? 4 : 0]




# Test for IO errors during a VACUUM. 
#
# The first IO call is excluded from the test. This call attempts to read
# the file-header of the temporary database used by VACUUM. Since the
# database doesn't exist at that point, the IO error is not detected.
# 
# Additionally, if auto-vacuum is enabled, the 12th IO error is not 
................................................................................
    INSERT INTO t1 SELECT (a+500)%900, 'good string' FROM t1;
  }} msg

  if {$msg != "column a is not unique"} {
    error $msg
  }
}

# Assertion fault bug reported by alex dimitrov.
#
do_ioerr_test ioerr-11 -erc 1 -sqlprep {
   CREATE TABLE A(Id INTEGER, Name TEXT);
   INSERT INTO A(Id, Name) VALUES(1, 'Name');
} -sqlbody {
   UPDATE A SET Id = 2, Name = 'Name2' WHERE Id = 1;
}

finish_test

Changes to test/tester.tcl.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
338
339
340
341
342
343
344

345
346
347
348
349
350
351
352
353
354
355

356
357
358
359
360
361
362
...
388
389
390
391
392
393
394

395
396
397
398
399
400
401
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.72 2007/01/04 14:58:14 drh Exp $

# Make sure tclsqlite3 was compiled correctly.  Abort now with an
# error message if not.
#
if {[sqlite3 -tcl-uses-utf]} {
  if {"\u1234"=="u1234"} {
    puts stderr "***** BUILD PROBLEM *****"
................................................................................
#
#     -tclprep          TCL script to run to prepare test.
#     -sqlprep          SQL script to run to prepare test.
#     -tclbody          TCL script to run with IO error simulation.
#     -sqlbody          TCL script to run with IO error simulation.
#     -exclude          List of 'N' values not to test.
#     -erc              Use extended result codes

#     -start            Value of 'N' to begin with (default 1)
#
#     -cksum            Boolean. If true, test that the database does
#                       not change during the execution of the test case.
#
proc do_ioerr_test {testname args} {

  set ::ioerropts(-start) 1
  set ::ioerropts(-cksum) 0
  set ::ioerropts(-erc) 0
  set ::ioerropts(-count) 100000000

  array set ::ioerropts $args

  set ::go 1
  for {set n $::ioerropts(-start)} {$::go} {incr n} {
    incr ::ioerropts(-count) -1
    if {$::ioerropts(-count)<0} break
 
................................................................................
    # Read the 'checksum' of the database.
    if {$::ioerropts(-cksum)} {
      set checksum [cksum]
    }
  
    # Set the Nth IO error to fail.
    do_test $testname.$n.2 [subst {

      set ::sqlite_io_error_pending $n
    }] $n
  
    # Create a single TCL script from the TCL and SQL specified
    # as the body of the test.
    set ::ioerrorbody {}
    if {[info exists ::ioerropts(-tclbody)]} {







|







 







>











>







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
...
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.73 2007/03/15 12:17:43 drh Exp $

# Make sure tclsqlite3 was compiled correctly.  Abort now with an
# error message if not.
#
if {[sqlite3 -tcl-uses-utf]} {
  if {"\u1234"=="u1234"} {
    puts stderr "***** BUILD PROBLEM *****"
................................................................................
#
#     -tclprep          TCL script to run to prepare test.
#     -sqlprep          SQL script to run to prepare test.
#     -tclbody          TCL script to run with IO error simulation.
#     -sqlbody          TCL script to run with IO error simulation.
#     -exclude          List of 'N' values not to test.
#     -erc              Use extended result codes
#     -persist          Make simulated I/O errors persistent
#     -start            Value of 'N' to begin with (default 1)
#
#     -cksum            Boolean. If true, test that the database does
#                       not change during the execution of the test case.
#
proc do_ioerr_test {testname args} {

  set ::ioerropts(-start) 1
  set ::ioerropts(-cksum) 0
  set ::ioerropts(-erc) 0
  set ::ioerropts(-count) 100000000
  set ::ioerropts(-persist) 1
  array set ::ioerropts $args

  set ::go 1
  for {set n $::ioerropts(-start)} {$::go} {incr n} {
    incr ::ioerropts(-count) -1
    if {$::ioerropts(-count)<0} break
 
................................................................................
    # Read the 'checksum' of the database.
    if {$::ioerropts(-cksum)} {
      set checksum [cksum]
    }
  
    # Set the Nth IO error to fail.
    do_test $testname.$n.2 [subst {
      set ::sqlite_io_error_persist $::ioerropts(-persist)
      set ::sqlite_io_error_pending $n
    }] $n
  
    # Create a single TCL script from the TCL and SQL specified
    # as the body of the test.
    set ::ioerrorbody {}
    if {[info exists ::ioerropts(-tclbody)]} {