SQLite

Check-in [46107da7ed]
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
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 46107da7eddbdda8b582e2ece2dc41222a70330a
User & Date: danielk1977 2004-06-23 01:05:27.000
Context
2004-06-23
10:43
Test cases to verify recovery after a crash. (CVS 1675) (check-in: 41868d79ac user: danielk1977 tags: trunk)
01:05
Handle corrupt journal file headers correctly. (CVS 1674) (check-in: 46107da7ed user: danielk1977 tags: trunk)
00:23
Add a comment to the output buffer allocation in sqlite3VdbeMemTranslate() (CVS 1673) (check-in: e2f7f18298 user: danielk1977 tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to main.mk.
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#
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 \







<







114
115
116
117
118
119
120

121
122
123
124
125
126
127
#
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 \
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
	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)







<
<
<







269
270
271
272
273
274
275



276
277
278
279
280
281
282
	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)
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
		$(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







|
|
|







351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
		$(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
** 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>








|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
** 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>

788
789
790
791
792
793
794
795
796
797

798


799
800
801
802
803
804
805
** 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 */







|
|
|
>
|
>
>







788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
** 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 */
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
    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 ){







<







830
831
832
833
834
835
836

837
838
839
840
841
842
843
    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 ){
853
854
855
856
857
858
859

860
861
862
863
864
865
866
    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);







>







855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
    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);
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
    ** 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.







|
<
<
<
<
<
<
<
<







907
908
909
910
911
912
913
914








915
916
917
918
919
920
921
    ** 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.
1
2
3
4
5
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
# 2001 September 15
#
# The author disclaims copyright to this source code.  In place of
# 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.
#
#***********************************************************************
# 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














|




>
>
>
>
>
>
>
|

>
>
>
>
|


|


>
>
>
>
>
>
>
>
>
>







<

|
|
<
<
|
|
<
<



<





<

|
|
<
|
|
<
<



<

|


|




1
2
3
4
5
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
# 2001 September 15
#
# The author disclaims copyright to this source code.  In place of
# 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.
#
#***********************************************************************
# 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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2001 September 15
#
# The author disclaims copyright to this source code.  In place of
# 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.
#
#***********************************************************************
# 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 {













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2001 September 15
#
# The author disclaims copyright to this source code.  In place of
# 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.
#
#***********************************************************************
# 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 {
388
389
390
391
392
393
394

395
396
397
398
399
400
401
# 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');
  }
} {}







>







388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# 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');
  }
} {}
429
430
431
432
433
434
435
436
437
438
  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








<

430
431
432
433
434
435
436
437

438
  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