/ Check-in [46107da7]
Login

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

Overview
Comment:Handle corrupt journal file headers correctly. (CVS 1674)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:46107da7eddbdda8b582e2ece2dc41222a70330a
User & Date: danielk1977 2004-06-23 01:05:27
Context
2004-06-23
10:43
Test cases to verify recovery after a crash. (CVS 1675) check-in: 41868d79 user: danielk1977 tags: trunk
01:05
Handle corrupt journal file headers correctly. (CVS 1674) check-in: 46107da7 user: danielk1977 tags: trunk
00:23
Add a comment to the output buffer allocation in sqlite3VdbeMemTranslate() (CVS 1673) check-in: e2f7f182 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to main.mk.

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#
TESTSRC = \
  $(TOP)/src/btree.c \
  $(TOP)/src/func.c \
  $(TOP)/src/os_mac.c \
  $(TOP)/src/os_unix.c \
  $(TOP)/src/os_win.c \
  $(TOP)/src/os_test.c \
  $(TOP)/src/pager.c \
  $(TOP)/src/pragma.c \
  $(TOP)/src/printf.c \
  $(TOP)/src/test1.c \
  $(TOP)/src/test2.c \
  $(TOP)/src/test3.c \
  $(TOP)/src/test4.c \
................................................................................
	grep '^case OP_' $(TOP)/src/vdbe.c | \
	  sed -e 's/://' | \
	  awk '{printf "#define %-30s %3d\n", $$2, ++cnt}' >>opcodes.h

os_mac.o:	$(TOP)/src/os_mac.c $(HDR)
	$(TCCX) -c $(TOP)/src/os_mac.c

os_test.o:	$(TOP)/src/os_test.c $(HDR)
	$(TCCX) -c $(TOP)/src/os_test.c

os_unix.o:	$(TOP)/src/os_unix.c $(HDR)
	$(TCCX) -c $(TOP)/src/os_unix.c

os_win.o:	$(TOP)/src/os_win.c $(HDR)
	$(TCCX) -c $(TOP)/src/os_win.c

parse.o:	parse.c $(HDR)
................................................................................
		$(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL)

testfixture$(EXE):	$(TOP)/src/tclsqlite.c libsqlite3.a $(TESTSRC)
	$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -DSQLITE_TEST=1 -o testfixture$(EXE) \
		$(TESTSRC) $(TOP)/src/tclsqlite.c \
		libsqlite3.a $(LIBTCL) $(THREADLIB)

testfixturex:	$(TOP)/src/tclsqlite.c libsqlite3.a $(TESTSRC)
	$(TCCX) $(TCL_FLAGS) -DOS_TEST=1 -DTCLSH=1 -DSQLITE_TEST=1 -o testfixturex \
		$(TESTSRC) $(TOP)/src/tclsqlite.c \
		libsqlite3.a $(LIBTCL) $(THREADLIB)

fulltest:	testfixture$(EXE) sqlite3$(EXE)
	./testfixture$(EXE) $(TOP)/test/all.test

test:	testfixture$(EXE) sqlite3$(EXE)
	./testfixture$(EXE) $(TOP)/test/quick.test







<







 







<
<
<







 







|
|
|







114
115
116
117
118
119
120

121
122
123
124
125
126
127
...
269
270
271
272
273
274
275



276
277
278
279
280
281
282
...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
#
TESTSRC = \
  $(TOP)/src/btree.c \
  $(TOP)/src/func.c \
  $(TOP)/src/os_mac.c \
  $(TOP)/src/os_unix.c \
  $(TOP)/src/os_win.c \

  $(TOP)/src/pager.c \
  $(TOP)/src/pragma.c \
  $(TOP)/src/printf.c \
  $(TOP)/src/test1.c \
  $(TOP)/src/test2.c \
  $(TOP)/src/test3.c \
  $(TOP)/src/test4.c \
................................................................................
	grep '^case OP_' $(TOP)/src/vdbe.c | \
	  sed -e 's/://' | \
	  awk '{printf "#define %-30s %3d\n", $$2, ++cnt}' >>opcodes.h

os_mac.o:	$(TOP)/src/os_mac.c $(HDR)
	$(TCCX) -c $(TOP)/src/os_mac.c




os_unix.o:	$(TOP)/src/os_unix.c $(HDR)
	$(TCCX) -c $(TOP)/src/os_unix.c

os_win.o:	$(TOP)/src/os_win.c $(HDR)
	$(TCCX) -c $(TOP)/src/os_win.c

parse.o:	parse.c $(HDR)
................................................................................
		$(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL)

testfixture$(EXE):	$(TOP)/src/tclsqlite.c libsqlite3.a $(TESTSRC)
	$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -DSQLITE_TEST=1 -o testfixture$(EXE) \
		$(TESTSRC) $(TOP)/src/tclsqlite.c \
		libsqlite3.a $(LIBTCL) $(THREADLIB)

crashtest:	$(TOP)/src/tclsqlite.c libsqlite3.a $(TESTSRC) $(TOP)/src/os_test.c
	$(TCCX) $(TCL_FLAGS) -DOS_TEST=1 -DTCLSH=1 -DSQLITE_TEST=1 -o crashtest \
		$(TESTSRC) $(TOP)/src/os_test.c $(TOP)/src/tclsqlite.c \
		libsqlite3.a $(LIBTCL) $(THREADLIB)

fulltest:	testfixture$(EXE) sqlite3$(EXE)
	./testfixture$(EXE) $(TOP)/test/all.test

test:	testfixture$(EXE) sqlite3$(EXE)
	./testfixture$(EXE) $(TOP)/test/quick.test

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
788
789
790
791
792
793
794
795
796
797
798



799
800
801
802
803
804
805
...
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
...
853
854
855
856
857
858
859

860
861
862
863
864
865
866
...
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
** 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.135 2004/06/22 12:18:32 danielk1977 Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
** If the nRec value is 0xffffffff it means that nRec should be computed
** from the file size.  This value is used when the user selects the
** no-sync option for the journal.  A power failure could lead to corruption
** in this case.  But for things like temporary table (which will be
** deleted when the power is restored) we don't care.  
**
** If the file opened as the journal file is not a well-formed
** journal file then the database will likely already be
** corrupted, so the PAGER_ERR_CORRUPT bit is set in pPager->errMask
** and SQLITE_CORRUPT is returned.  If it all works, then this routine
** returns SQLITE_OK.



*/
static int pager_playback(Pager *pPager, int useJournalSize){
  off_t szJ;               /* Size of the journal file in bytes */
  int nRec;                /* Number of Records in the journal */
  int i;                   /* Loop counter */
  Pgno mxPg = 0;           /* Size of the original file in pages */
  unsigned char aMagic[8]; /* A buffer to hold the magic header */
................................................................................
    goto end_playback;
  }

  /* (1) Read the beginning of the journal and verify the magic string
  ** at the beginning of the journal. */
  rc = sqlite3OsRead(&pPager->jfd, aMagic, sizeof(aMagic));
  if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){
    rc = SQLITE_PROTOCOL;
    goto end_playback;
  }

  /* (2) Read the number of pages stored in the journal.  */
  rc = read32bits(&pPager->jfd, (u32*)&nRec);
  if( rc ) goto end_playback;
  if( nRec==0xffffffff || useJournalSize ){
................................................................................
    goto end_playback;
  }

  /* (5) and (6): Check if a master journal file is specified. If one is
  ** specified, only proceed with the playback if it still exists. */
  rc = read32bits(&pPager->jfd, &nMaster);
  if( rc ) goto end_playback;

  if( nMaster>0 ){
    zMaster = sqliteMalloc(nMaster);
    if( !zMaster ){
      rc = SQLITE_NOMEM;
      goto end_playback;
    }
    rc = sqlite3OsRead(&pPager->jfd, zMaster, nMaster);
................................................................................
    ** occur during this process, ignore them.
    */
    if( rc==SQLITE_OK ){
      pager_delmaster(zMaster);
    }
    sqliteFree(zMaster);
  }
  if( rc!=SQLITE_OK ){
    /* FIX ME: We shouldn't delete the journal if an error occured during
    ** rollback. It may have been a transient error and the rollback may
    ** succeed next time it is attempted.
    */
    pager_unwritelock(pPager);
    pPager->errMask |= PAGER_ERR_CORRUPT;
    rc = SQLITE_CORRUPT;
  }else{
    rc = pager_unwritelock(pPager);
  }
  return rc;
}

/*
** Playback the statement journal.







|







 







|
|
|
|
>
>
>







 







<







 







>







 







|
<
<
<
<
<
<
<
<







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
...
830
831
832
833
834
835
836

837
838
839
840
841
842
843
...
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
...
907
908
909
910
911
912
913
914








915
916
917
918
919
920
921
** 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.136 2004/06/23 01:05:27 danielk1977 Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
** If the nRec value is 0xffffffff it means that nRec should be computed
** from the file size.  This value is used when the user selects the
** no-sync option for the journal.  A power failure could lead to corruption
** in this case.  But for things like temporary table (which will be
** deleted when the power is restored) we don't care.  
**
** If the file opened as the journal file is not a well-formed
** journal file then all pages up to the first corrupted page are rolled
** back (or no pages if the journal header is corrupted). The journal file
** is then deleted and SQLITE_OK returned, just as if no corruption had
** been encountered.
**
** If an I/O or malloc() error occurs, the journal-file is not deleted
** and an error code is returned.
*/
static int pager_playback(Pager *pPager, int useJournalSize){
  off_t szJ;               /* Size of the journal file in bytes */
  int nRec;                /* Number of Records in the journal */
  int i;                   /* Loop counter */
  Pgno mxPg = 0;           /* Size of the original file in pages */
  unsigned char aMagic[8]; /* A buffer to hold the magic header */
................................................................................
    goto end_playback;
  }

  /* (1) Read the beginning of the journal and verify the magic string
  ** at the beginning of the journal. */
  rc = sqlite3OsRead(&pPager->jfd, aMagic, sizeof(aMagic));
  if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){

    goto end_playback;
  }

  /* (2) Read the number of pages stored in the journal.  */
  rc = read32bits(&pPager->jfd, (u32*)&nRec);
  if( rc ) goto end_playback;
  if( nRec==0xffffffff || useJournalSize ){
................................................................................
    goto end_playback;
  }

  /* (5) and (6): Check if a master journal file is specified. If one is
  ** specified, only proceed with the playback if it still exists. */
  rc = read32bits(&pPager->jfd, &nMaster);
  if( rc ) goto end_playback;
  if( szJ < 24+nMaster ) goto end_playback;
  if( nMaster>0 ){
    zMaster = sqliteMalloc(nMaster);
    if( !zMaster ){
      rc = SQLITE_NOMEM;
      goto end_playback;
    }
    rc = sqlite3OsRead(&pPager->jfd, zMaster, nMaster);
................................................................................
    ** occur during this process, ignore them.
    */
    if( rc==SQLITE_OK ){
      pager_delmaster(zMaster);
    }
    sqliteFree(zMaster);
  }
  if( rc==SQLITE_OK ){








    rc = pager_unwritelock(pPager);
  }
  return rc;
}

/*
** Playback the statement journal.

Changes to test/crash.test.

6
7
8
9
10
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#    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.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# $Id: crash.test,v 1.1 2004/06/22 13:12:52 danielk1977 Exp $

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

proc run_testfixturex {script} {







  set f [open crash.tcl w]




  puts $f $script
  close $f

  exec ./testfixturex crash.tcl

}











do_test crash-1.1 {
  execsql {
    CREATE TABLE abc(a, b, c);
    INSERT INTO abc VALUES(1, 2, 3);
    INSERT INTO abc VALUES(4, 5, 6);
  }
} {}

do_test crash-1.2 {
  set script {

    sqlite3_crashseed 1
    sqlite3 db test.db 
    db eval {pragma synchronous=full;}
    db eval {DELETE FROM abc WHERE a = 1;}
  }
  catch {
    run_testfixturex $script
  } msg
  set msg
} {child process exited abnormally}

do_test crash-1.3 {
  catchsql {
    SELECT * FROM abc;
  }
} {0 {1 2 3 4 5 6}}

do_test crash-1.4 {
  set script {

    sqlite3_crashseed 2
    sqlite3 db test.db 
    db eval {DELETE FROM abc WHERE a = 1;}
  }
  catch {
    run_testfixturex $script
  } msg
  set msg
} {child process exited abnormally}

do_test crash-1.5 {
  catch {
    SELECT * FROM abc;
  }
} {1 2 3 4 5 6}

finish_test









|




|
>
>
>
>
>
>
>

>
>
>
>
|


<
>


>
>
>
>
>
>
>
>
>
>







<

<
>
|
<
<
|
|
<
<



<





<

<
>
|
<
|
|
<
<



<

|


|




6
7
8
9
10
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54

55
56


57
58


59
60
61

62
63
64
65
66

67

68
69

70
71


72
73
74

75
76
77
78
79
80
81
82
83
#    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.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# $Id: crash.test,v 1.2 2004/06/23 01:05:27 danielk1977 Exp $

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

# This proc execs a seperate process that crashes midway through executing
# the SQL script $sql on database test.db.
#
# Argument $crashdelay indicates the number of file closes or syncs to wait
# before crashing. When a crash occurs a random subset of unsynced writes
# are written into any open files.
proc crashsql {crashdelay sql} {

  set f [open crash.tcl w]
  puts $f "sqlite3_crashseed $crashdelay"
  puts $f "sqlite3 db test.db"
  puts $f "db eval {"
  puts $f   "$sql"
  puts $f "}"
  close $f


  exec [file join . crashtest] crash.tcl
}

# Simple crash test:
#
# crash-1.1: Create a database with a table with two rows.
# crash-1.2: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
#            journal-sync
# crash-1.3: Ensure the database is in the same state as after crash-1.1.
# crash-1.4: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
#            database-sync
# crash-1.5: Ensure the database is in the same state as after crash-1.1.
#
do_test crash-1.1 {
  execsql {
    CREATE TABLE abc(a, b, c);
    INSERT INTO abc VALUES(1, 2, 3);
    INSERT INTO abc VALUES(4, 5, 6);
  }
} {}

do_test crash-1.2 {

  catch {
    crashsql 1 {


      DELETE FROM abc WHERE a = 1;
    }


  } msg
  set msg
} {child process exited abnormally}

do_test crash-1.3 {
  catchsql {
    SELECT * FROM abc;
  }
} {0 {1 2 3 4 5 6}}

do_test crash-1.4 {

  catch {
   crashsql 1 {

      DELETE FROM abc WHERE a = 1;
    }


  } msg
  set msg
} {child process exited abnormally}

do_test crash-1.5 {
  catchsql {
    SELECT * FROM abc;
  }
} {0 {1 2 3 4 5 6}}

finish_test


Changes to test/func.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
388
389
390
391
392
393
394

395
396
397
398
399
400
401
...
429
430
431
432
433
434
435
436
437
438
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing built-in functions.
#
# $Id: func.test,v 1.24 2004/06/19 17:33:08 drh Exp $

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

# Create a table to work with.
#
do_test func-0.0 {
................................................................................
# a special user-defined function only available in test builds,
# test_auxdata(). Function test_auxdata() takes any number of arguments.
do_test func-13.1 {
  execsql {
    SELECT test_auxdata('hello world');
  }
} {0}

do_test func-13.2 {
  execsql {
    CREATE TABLE t4(a, b);
    INSERT INTO t4 VALUES('abc', 'def');
    INSERT INTO t4 VALUES('ghi', 'jkl');
  }
} {}
................................................................................
  sqlite3_bind_text $STMT 1 hello -1
  set res [list]
  while { "SQLITE_ROW"==[sqlite3_step $STMT] } {
    lappend res [sqlite3_column_text $STMT 0]
  }
  lappend res [sqlite3_finalize $STMT]
} {{0 0} {1 0} SQLITE_OK}


finish_test







|







 







>







 








<

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
...
430
431
432
433
434
435
436
437

438
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing built-in functions.
#
# $Id: func.test,v 1.25 2004/06/23 01:05:27 danielk1977 Exp $

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

# Create a table to work with.
#
do_test func-0.0 {
................................................................................
# a special user-defined function only available in test builds,
# test_auxdata(). Function test_auxdata() takes any number of arguments.
do_test func-13.1 {
  execsql {
    SELECT test_auxdata('hello world');
  }
} {0}

do_test func-13.2 {
  execsql {
    CREATE TABLE t4(a, b);
    INSERT INTO t4 VALUES('abc', 'def');
    INSERT INTO t4 VALUES('ghi', 'jkl');
  }
} {}
................................................................................
  sqlite3_bind_text $STMT 1 hello -1
  set res [list]
  while { "SQLITE_ROW"==[sqlite3_step $STMT] } {
    lappend res [sqlite3_column_text $STMT 0]
  }
  lappend res [sqlite3_finalize $STMT]
} {{0 0} {1 0} SQLITE_OK}


finish_test