Test cases to verify recovery after a crash. (CVS 1675)
FossilOrigin-Name: 41868d79ac5b3c496c4d87ca6b4ee7c17ef38965
This commit is contained in:
parent
ece80f1e48
commit
ef317ab577
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C Handle\scorrupt\sjournal\sfile\sheaders\scorrectly.\s(CVS\s1674)
|
||||
D 2004-06-23T01:05:27
|
||||
C Test\scases\sto\sverify\srecovery\safter\sa\scrash.\s(CVS\s1675)
|
||||
D 2004-06-23T10:43:10
|
||||
F Makefile.in 0a3d7aaefa50717bd550b0cf568a51072c4c103c
|
||||
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
|
||||
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
||||
@ -44,13 +44,13 @@ F src/os.h 2f5ea879b784bc82aac8022a3e8fe00b73c83d67
|
||||
F src/os_common.h ba1b7306e16e2091718f2c48db0fe6c1d7a31bb8
|
||||
F src/os_mac.c 3d31e26be1411acfb7961033098631b4f3486fdf
|
||||
F src/os_mac.h 51d2445f47e182ed32d3bd6937f81070c6fd9bd4
|
||||
F src/os_test.c db4df491bad874c095b1a9d4db346990cfd56ae0
|
||||
F src/os_test.h acacfe7e7bb78dd99865f16cfa822426b177d2ab
|
||||
F src/os_test.c ab55524911b66395c39ff8f465dfee0ba1ba06e4
|
||||
F src/os_test.h 6a26a4978492e4bbdbf385554958418ff02db162
|
||||
F src/os_unix.c 39e73ed02fc992a6bfc52200ea26704633412cc0
|
||||
F src/os_unix.h 00c1f82b526ab2fb7ee5ddd555ea4ed68363c93a
|
||||
F src/os_win.c 84549f6cc815237533c5d0eb3697352b03478d96
|
||||
F src/os_win.h babd4e912967c6b09088cfe38a45e8005a07ba44
|
||||
F src/pager.c 42297421e9e7646f99b332c69f3f8085c1d765bf
|
||||
F src/pager.c ec34fbae1a23228cb3743cf7cd8eba1af8e4cd5c
|
||||
F src/pager.h bc58d32a9dee464f7268fb68652c130a4216e438
|
||||
F src/parse.y 097438674976355a10cf177bd97326c548820b86
|
||||
F src/pragma.c 0750e1c360647dbe0a991f16133b0fe5e42e5039
|
||||
@ -62,7 +62,7 @@ F src/sqlite.h.in 1f400a561fca3b1df73677d2d97046425d47cae4
|
||||
F src/sqliteInt.h dd796b6abc6d50505fe33c54f0143d7000681a41
|
||||
F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2
|
||||
F src/tclsqlite.c 8d093146332b2f0cbf2a8ebe8597d481619308a3
|
||||
F src/test1.c 7ecde02fdecff651250f11d96da73d484a4ff764
|
||||
F src/test1.c a7e559240e677671224d2d13b4d1dab284e23c20
|
||||
F src/test2.c dafd8bd314a554bf376c6d3a8c83fd69219f5a40
|
||||
F src/test3.c 7247090d15a5a43823079b6fd8dad1ed3cccdedf
|
||||
F src/test4.c a921a69821fd30209589228e64f94e9f715b6fe2
|
||||
@ -104,7 +104,7 @@ F test/collate4.test 0e9fc08ffcf6eddf72e354a15de06688fa86db31
|
||||
F test/collate5.test 1dd5f0f508c46667f9d4606c7950c414b0bdc0d5
|
||||
F test/collate6.test 2a45768914f04c1447a69d1358bbede376552675
|
||||
F test/conflict.test c5b849b01cfbe0a4f63a90cba6f68e2fe3a75f87
|
||||
F test/crash.test fa7c6ef4d1ac1aa2d14d8afd1583cef8f8e2a0e4
|
||||
F test/crash.test 01b4a1cf195678138810f973ec9e2e6cef731d3e
|
||||
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
|
||||
F test/date.test aed5030482ebc02bd8d386c6c86a29f694ab068d
|
||||
F test/delete.test 4f0c86e2bebdc822d179c80697b1ceabe6bbcd07
|
||||
@ -229,7 +229,7 @@ F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075
|
||||
F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
|
||||
F www/version3.tcl 563ba3ac02f64da27ab17f3edbe8e56bfd0293fb
|
||||
F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
|
||||
P e2f7f182987fbfe8611ead8bd1f12b2e8b47f6dc
|
||||
R 8314e42b760c1a8a452b6f1accd15858
|
||||
P 46107da7eddbdda8b582e2ece2dc41222a70330a
|
||||
R 2d06dadbd77bf8c7d6cc2f4d941e75d8
|
||||
U danielk1977
|
||||
Z 0fc9a3cf7f51cb64d701f7be6ce96ff8
|
||||
Z eb0225a30546e9d6f49bf0fdd5127daf
|
||||
|
@ -1 +1 @@
|
||||
46107da7eddbdda8b582e2ece2dc41222a70330a
|
||||
41868d79ac5b3c496c4d87ca6b4ee7c17ef38965
|
@ -60,41 +60,54 @@
|
||||
|
||||
|
||||
/*
|
||||
** The crash-seed. Accessed via functions crashseed() and
|
||||
** sqlite3SetCrashseed().
|
||||
** The following variables control when a simulated crash occurs.
|
||||
**
|
||||
** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of
|
||||
** a file that SQLite will call sqlite3OsSync() on. Each time this happens
|
||||
** iCrashDelay is decremented. If iCrashDelay is zero after being
|
||||
** decremented, a "crash" occurs during the sync() operation.
|
||||
**
|
||||
** In other words, a crash occurs the iCrashDelay'th time zCrashFile is
|
||||
** synced.
|
||||
*/
|
||||
static int crashseed_var = 0;
|
||||
static int iCrashDelay = 0;
|
||||
char zCrashFile[256];
|
||||
|
||||
/*
|
||||
** This function is used to set the value of the 'crash-seed' integer.
|
||||
**
|
||||
** If the crash-seed is 0, the default value, then whenever sqlite3OsSync()
|
||||
** or sqlite3OsClose() is called, the write cache is written to disk before
|
||||
** the os_unix.c Sync() or Close() function is called.
|
||||
**
|
||||
** If the crash-seed is non-zero, then it is used to determine a subset of
|
||||
** the write-cache to actually write to disk before calling Sync() or
|
||||
** Close() in os_unix.c. The actual subset of writes selected is not
|
||||
** significant, except that it is constant for a given value of the
|
||||
** crash-seed and cache contents. Before returning, exit(-1) is invoked.
|
||||
** Set the value of the two crash parameters.
|
||||
*/
|
||||
void sqlite3SetCrashseed(int seed){
|
||||
void sqlite3SetCrashParams(int iDelay, char const *zFile){
|
||||
sqlite3OsEnterMutex();
|
||||
crashseed_var = seed;
|
||||
assert( strlen(zFile)<256 );
|
||||
strcpy(zCrashFile, zFile);
|
||||
iCrashDelay = iDelay;
|
||||
sqlite3OsLeaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve the current value of the crash-seed.
|
||||
** File zPath is being sync()ed. Return non-zero if this should
|
||||
** cause a crash.
|
||||
*/
|
||||
static int crashseed(){
|
||||
int i;
|
||||
static int crashRequired(char const *zPath){
|
||||
int r;
|
||||
int n;
|
||||
sqlite3OsEnterMutex();
|
||||
i = crashseed_var;
|
||||
n = strlen(zCrashFile);
|
||||
if( zCrashFile[n-1]=='*' ){
|
||||
n--;
|
||||
}else if( strlen(zPath)>n ){
|
||||
n = strlen(zPath);
|
||||
}
|
||||
r = (
|
||||
iCrashDelay>0 &&
|
||||
!strncmp(zPath, zCrashFile, n) &&
|
||||
--iCrashDelay==0
|
||||
)?1:0;
|
||||
sqlite3OsLeaveMutex();
|
||||
return i;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static OsTestFile *pAllFiles = 0;
|
||||
|
||||
/*
|
||||
@ -177,6 +190,8 @@ static int cacheBlock(OsTestFile *pFile, int blk){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* #define TRACE_WRITECACHE */
|
||||
|
||||
/*
|
||||
** Write the cache of pFile to disk. If crash is non-zero, randomly
|
||||
** skip blocks when writing. The cache is deleted before returning.
|
||||
@ -197,16 +212,13 @@ static int writeCache2(OsTestFile *pFile, int crash){
|
||||
sqlite3Randomness(1, &random);
|
||||
if( random & 0x01 ){
|
||||
skip = 1;
|
||||
/*
|
||||
printf("Not writing block %d of %s\n", i, pFile->zName);
|
||||
*/
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("Not writing block %d of %s\n", i, pFile->zName);
|
||||
}else{
|
||||
/*
|
||||
printf("Writing block %d of %s\n", i, pFile->zName);
|
||||
*/
|
||||
printf("Writing block %d of %s\n", i, pFile->zName);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3RealSeek(&pFile->fd, BLOCK_OFFSET(i));
|
||||
}
|
||||
@ -235,18 +247,22 @@ static int writeCache2(OsTestFile *pFile, int crash){
|
||||
** Write the cache to disk.
|
||||
*/
|
||||
static int writeCache(OsTestFile *pFile){
|
||||
int cs = crashseed();
|
||||
if( cs==1 ){
|
||||
/* FIX ME: writeCache2() should be called on all open files here. */
|
||||
OsTestFile *pFile;
|
||||
for(pFile=pAllFiles; pFile; pFile=pFile->pNext){
|
||||
writeCache2(pFile, 1);
|
||||
if( pFile->apBlk ){
|
||||
int c = crashRequired(pFile->zName);
|
||||
if( c ){
|
||||
OsTestFile *p;
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("Crash during sync of %s\n", pFile->zName);
|
||||
#endif
|
||||
for(p=pAllFiles; p; p=p->pNext){
|
||||
writeCache2(p, 1);
|
||||
}
|
||||
exit(-1);
|
||||
}else{
|
||||
return writeCache2(pFile, 0);
|
||||
}
|
||||
exit(-1);
|
||||
}else{
|
||||
if( cs>0 ) sqlite3SetCrashseed(cs-1);
|
||||
return writeCache2(pFile, 0);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -34,6 +34,6 @@ struct OsTestFile {
|
||||
OsTestFile *pNext;
|
||||
};
|
||||
|
||||
void sqlite3SetCrashseed(int seed);
|
||||
void sqlite3SetCrashParams(int iDelay, char const *zFile);
|
||||
|
||||
#endif /* _SQLITE_OS_UNIX_H_ */
|
||||
|
32
src/pager.c
32
src/pager.c
@ -18,7 +18,7 @@
|
||||
** 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 $
|
||||
** @(#) $Id: pager.c,v 1.137 2004/06/23 10:43:10 danielk1977 Exp $
|
||||
*/
|
||||
#include "os.h" /* Must be first to enable large file support */
|
||||
#include "sqliteInt.h"
|
||||
@ -479,11 +479,15 @@ static int pager_unwritelock(Pager *pPager){
|
||||
pPg->dirty = 0;
|
||||
pPg->needSync = 0;
|
||||
}
|
||||
pPager->dirtyCache = 0;
|
||||
pPager->nMaster = 0;
|
||||
pPager->nRec = 0;
|
||||
}else{
|
||||
assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
|
||||
}
|
||||
sqlite3OsUnlock(&pPager->fd, SHARED_LOCK);
|
||||
pPager->state = PAGER_SHARED;
|
||||
pPager->origDbSize = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -508,7 +512,12 @@ static int pager_unwritelock(Pager *pPager){
|
||||
** chance of failing the checksum and thus detecting the problem.
|
||||
*/
|
||||
static u32 pager_cksum(Pager *pPager, Pgno pgno, const char *aData){
|
||||
u32 cksum = pPager->cksumInit + pgno;
|
||||
u32 cksum = pPager->cksumInit;
|
||||
int i = pPager->pageSize-200;
|
||||
while( i>0 ){
|
||||
cksum += aData[i];
|
||||
i -= 200;
|
||||
}
|
||||
return cksum;
|
||||
}
|
||||
|
||||
@ -840,7 +849,9 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
||||
/* (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 ){
|
||||
if( nRec==0xffffffff || useJournalSize ||
|
||||
nRec>(szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager)
|
||||
){
|
||||
nRec = (szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager);
|
||||
}
|
||||
|
||||
@ -1007,6 +1018,8 @@ void sqlite3pager_set_cachesize(Pager *pPager, int mxPage){
|
||||
}
|
||||
if( mxPage>10 ){
|
||||
pPager->mxPage = mxPage;
|
||||
}else{
|
||||
pPager->mxPage = 10;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2409,19 +2422,6 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
return rc;
|
||||
}
|
||||
assert( pPager->journalOpen );
|
||||
#if 0
|
||||
rc = syncJournal(pPager, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto commit_abort;
|
||||
}
|
||||
pPg = pager_get_all_dirty_pages(pPager);
|
||||
if( pPg ){
|
||||
rc = pager_write_pagelist(pPg);
|
||||
if( rc || (!pPager->noSync && sqlite3OsSync(&pPager->fd)!=SQLITE_OK) ){
|
||||
goto commit_abort;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
rc = sqlite3pager_sync(pPager, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto commit_abort;
|
||||
|
16
src/test1.c
16
src/test1.c
@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.83 2004/06/22 13:12:52 danielk1977 Exp $
|
||||
** $Id: test1.c,v 1.84 2004/06/23 10:43:11 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@ -984,24 +984,24 @@ bad_args:
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
static int sqlite3_crashseed(
|
||||
static int sqlite3_crashparams(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifdef OS_TEST
|
||||
int seed;
|
||||
if( objc!=2 ) goto bad_args;
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &seed) ) return TCL_ERROR;
|
||||
sqlite3SetCrashseed(seed);
|
||||
int delay;
|
||||
if( objc!=3 ) goto bad_args;
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &delay) ) return TCL_ERROR;
|
||||
sqlite3SetCrashParams(delay, Tcl_GetString(objv[2]));
|
||||
#endif
|
||||
return TCL_OK;
|
||||
|
||||
#ifdef OS_TEST
|
||||
bad_args:
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), "<seed>", 0);
|
||||
Tcl_GetStringFromObj(objv[0], 0), "<delay> <filename>", 0);
|
||||
return TCL_ERROR;
|
||||
#endif
|
||||
}
|
||||
@ -2068,7 +2068,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3OsLock", test_sqlite3OsLock, 0 },
|
||||
{ "sqlite3OsUnlock", test_sqlite3OsUnlock, 0 },
|
||||
{ "add_test_collate", test_collate, 0 },
|
||||
{ "sqlite3_crashseed", sqlite3_crashseed, 0 },
|
||||
{ "sqlite3_crashparams", sqlite3_crashparams, 0 },
|
||||
|
||||
};
|
||||
int i;
|
||||
|
283
test/crash.test
283
test/crash.test
@ -10,74 +10,297 @@
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# $Id: crash.test,v 1.2 2004/06/23 01:05:27 danielk1977 Exp $
|
||||
# $Id: crash.test,v 1.3 2004/06/23 10:43:15 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set repeats 100
|
||||
# set repeats 5
|
||||
|
||||
# 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} {
|
||||
# The crash occurs during a sync() of file $crashfile. When the crash
|
||||
# occurs a random subset of all unsynced writes made by the process are
|
||||
# written into the files on disk. Argument $crashdelay indicates the
|
||||
# number of file syncs to wait before crashing.
|
||||
#
|
||||
# The return value is a list of two elements. The first element is a
|
||||
# boolean, indicating whether or not the process actually crashed or
|
||||
# reported some other error. The second element in the returned list is the
|
||||
# error message. This is "child process exited abnormally" if the crash
|
||||
# occured.
|
||||
proc crashsql {crashdelay crashfile sql} {
|
||||
set cfile [file join [pwd] $crashfile]
|
||||
|
||||
set f [open crash.tcl w]
|
||||
puts $f "sqlite3_crashseed $crashdelay"
|
||||
puts $f "sqlite3_crashparams $crashdelay $cfile"
|
||||
puts $f "sqlite3 db test.db"
|
||||
puts $f "db eval {pragma full_synchronous = 1}"
|
||||
puts $f "db eval {"
|
||||
puts $f "$sql"
|
||||
puts $f "}"
|
||||
close $f
|
||||
|
||||
exec [file join . crashtest] crash.tcl
|
||||
set r [catch {
|
||||
exec [file join . crashtest] crash.tcl
|
||||
} msg]
|
||||
lappend r $msg
|
||||
}
|
||||
|
||||
# The following procedure computes a "signature" for table "abc". If
|
||||
# abc changes in any way, the signature should change.
|
||||
proc signature {} {
|
||||
return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
|
||||
}
|
||||
proc signature2 {} {
|
||||
return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc2}]
|
||||
}
|
||||
|
||||
# Use a small pager-cache for these tests.
|
||||
do_test crash-0.1 {
|
||||
execsql { pragma default_cache_size = 10 }
|
||||
} {}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# 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
|
||||
# the first 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
|
||||
# the first database-sync.
|
||||
# crash-1.5: Ensure the database is in the same state as after crash-1.1.
|
||||
#
|
||||
# Tests 1.6 through 1.9 are the same as 1.2 through 1.5, except the crash
|
||||
# is requested on the second sync of each file. This doesn't happen in
|
||||
# such a small test case, so these tests are just to verify that the
|
||||
# test infrastructure operates as expected.
|
||||
#
|
||||
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);
|
||||
}
|
||||
} {}
|
||||
set ::sig [signature]
|
||||
expr 0
|
||||
} {0}
|
||||
do_test crash-1.2 {
|
||||
catch {
|
||||
crashsql 1 {
|
||||
DELETE FROM abc WHERE a = 1;
|
||||
}
|
||||
} msg
|
||||
set msg
|
||||
} {child process exited abnormally}
|
||||
crashsql 1 test.db-journal {
|
||||
DELETE FROM abc WHERE a = 1;
|
||||
}
|
||||
} {1 {child process exited abnormally}}
|
||||
|
||||
# exit
|
||||
|
||||
do_test crash-1.3 {
|
||||
catchsql {
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {0 {1 2 3 4 5 6}}
|
||||
signature
|
||||
} $::sig
|
||||
do_test crash-1.4 {
|
||||
catch {
|
||||
crashsql 1 {
|
||||
DELETE FROM abc WHERE a = 1;
|
||||
}
|
||||
} msg
|
||||
set msg
|
||||
} {child process exited abnormally}
|
||||
crashsql 1 test.db {
|
||||
DELETE FROM abc WHERE a = 1;
|
||||
}
|
||||
} {1 {child process exited abnormally}}
|
||||
do_test crash-1.5 {
|
||||
signature
|
||||
} $::sig
|
||||
do_test crash-1.6 {
|
||||
crashsql 2 test.db-journal {
|
||||
DELETE FROM abc WHERE a = 1;
|
||||
}
|
||||
} {0 {}}
|
||||
do_test crash-1.7 {
|
||||
catchsql {
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {0 {1 2 3 4 5 6}}
|
||||
} {0 {4 5 6}}
|
||||
do_test crash-1.8 {
|
||||
crashsql 2 test.db {
|
||||
DELETE FROM abc WHERE a = 4;
|
||||
}
|
||||
} {0 {}}
|
||||
do_test crash-1.9 {
|
||||
catchsql {
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {0 {}}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# The following tests test recovery when both the database file and the the
|
||||
# journal file contain corrupt data. This can happen after pages are
|
||||
# written to the database file before a transaction is committed due to
|
||||
# cache-pressure.
|
||||
#
|
||||
# crash-2.1: Insert 18 pages of data into the database.
|
||||
# crash-2.2: Check the database file size looks ok.
|
||||
# crash-2.3: Delete 15 or so pages (with a 10 page page-cache), then crash.
|
||||
# crash-2.4: Ensure the database is in the same state as after crash-2.1.
|
||||
#
|
||||
# Test cases crash-2.5 and crash-2.6 check that the database is OK if the
|
||||
# crash occurs during the main database file sync. But this isn't really
|
||||
# different from the crash-1.* cases.
|
||||
#
|
||||
do_test crash-2.1 {
|
||||
execsql { BEGIN }
|
||||
for {set n 0} {$n < 1000} {incr n} {
|
||||
execsql "INSERT INTO abc VALUES($n, [expr 2*$n], [expr 3*$n])"
|
||||
}
|
||||
execsql { COMMIT }
|
||||
set ::sig [signature]
|
||||
execsql { SELECT sum(a), sum(b), sum(c) from abc }
|
||||
} {499500 999000 1498500}
|
||||
do_test crash-2.2 {
|
||||
expr [file size test.db] / 1024
|
||||
} {19}
|
||||
do_test crash-2.3 {
|
||||
crashsql 2 test.db-journal {
|
||||
DELETE FROM abc WHERE a < 800;
|
||||
}
|
||||
} {1 {child process exited abnormally}}
|
||||
do_test crash-2.4 {
|
||||
signature
|
||||
} $sig
|
||||
do_test crash-2.5 {
|
||||
crashsql 1 test.db {
|
||||
DELETE FROM abc WHERE a<800;
|
||||
}
|
||||
} {1 {child process exited abnormally}}
|
||||
do_test crash-2.6 {
|
||||
signature
|
||||
} $sig
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# The crash-3.* test cases are essentially the same test as test case
|
||||
# crash-2.*, but with a more complicated data set.
|
||||
#
|
||||
# The test is repeated a few times with different seeds for the random
|
||||
# number generator in the crashing executable. Because there is no way to
|
||||
# seed the random number generator directly, some SQL is added to the test
|
||||
# case to 'use up' a different quantity random numbers before the test SQL
|
||||
# is executed.
|
||||
#
|
||||
|
||||
# Make sure the file is much bigger than the pager-cache (10 pages). This
|
||||
# ensures that cache-spills happen regularly.
|
||||
do_test crash-3.0 {
|
||||
execsql {
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
}
|
||||
expr [file size test.db] / 1024
|
||||
} {554}
|
||||
for {set i 1} {$i < $repeats} {incr i} {
|
||||
set sig [signature]
|
||||
do_test crash-3.$i.1 {
|
||||
crashsql [expr $i%5 + 1] test.db-journal "
|
||||
BEGIN;
|
||||
SELECT random() FROM abc LIMIT $i;
|
||||
INSERT INTO abc VALUES(randstr(10,10), 0, 0);
|
||||
DELETE FROM abc WHERE random()%10!=0;
|
||||
COMMIT;
|
||||
"
|
||||
} {1 {child process exited abnormally}}
|
||||
do_test crash-3.$i.2 {
|
||||
signature
|
||||
} $sig
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# The following test cases - crash-4.* - test the correct recovery of the
|
||||
# database when a crash occurs during a multi-file transaction.
|
||||
#
|
||||
# crash-4.1.*: Test recovery when crash occurs during sync() of the
|
||||
# main database journal file.
|
||||
# crash-4.2.*: Test recovery when crash occurs during sync() of an
|
||||
# attached database journal file.
|
||||
# crash-4.3.*: Test recovery when crash occurs during sync() of the master
|
||||
# journal file.
|
||||
#
|
||||
do_test crash-4.0 {
|
||||
file delete -force test2.db
|
||||
file delete -force test2.db-journal
|
||||
sqlite3 db2 test2.db
|
||||
execsql {pragma default_cache_size = 10} db2
|
||||
db2 close
|
||||
execsql {
|
||||
ATTACH 'test2.db' AS aux;
|
||||
CREATE TABLE aux.abc2 AS SELECT 2*a as a, 2*b as b, 2*c as c FROM abc;
|
||||
}
|
||||
expr [file size test2.db] / 1024
|
||||
} {559}
|
||||
|
||||
for {set i 1} {$i < $repeats} {incr i} {
|
||||
set sig [signature]
|
||||
set sig2 [signature2]
|
||||
do_test crash-4.1.$i.1 {
|
||||
crashsql [expr $i%5 + 1] test.db-journal "
|
||||
ATTACH 'test2.db' AS aux;
|
||||
BEGIN;
|
||||
SELECT random() FROM abc LIMIT $i;
|
||||
INSERT INTO abc VALUES(randstr(10,10), 0, 0);
|
||||
DELETE FROM abc WHERE random()%10!=0;
|
||||
INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
|
||||
DELETE FROM abc2 WHERE random()%10!=0;
|
||||
COMMIT;
|
||||
"
|
||||
} {1 {child process exited abnormally}}
|
||||
do_test crash-4.1.$i.2 {
|
||||
signature
|
||||
} $sig
|
||||
do_test crash-4.1.$i.3 {
|
||||
signature2
|
||||
} $sig2
|
||||
}
|
||||
for {set i 1} {$i < $repeats} {incr i} {
|
||||
set sig [signature]
|
||||
set sig2 [signature2]
|
||||
do_test crash-4.2.$i.1 {
|
||||
crashsql [expr $i%5 + 1] test2.db-journal "
|
||||
ATTACH 'test2.db' AS aux;
|
||||
BEGIN;
|
||||
SELECT random() FROM abc LIMIT $i;
|
||||
INSERT INTO abc VALUES(randstr(10,10), 0, 0);
|
||||
DELETE FROM abc WHERE random()%10!=0;
|
||||
INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
|
||||
DELETE FROM abc2 WHERE random()%10!=0;
|
||||
COMMIT;
|
||||
"
|
||||
} {1 {child process exited abnormally}}
|
||||
do_test crash-4.2.$i.2 {
|
||||
signature
|
||||
} $sig
|
||||
do_test crash-4.2.$i.3 {
|
||||
signature2
|
||||
} $sig2
|
||||
}
|
||||
for {set i 1} {$i < 5} {incr i} {
|
||||
set sig [signature]
|
||||
set sig2 [signature2]
|
||||
do_test crash-4.3.$i.1 {
|
||||
crashsql 1 test.db-mj* "
|
||||
ATTACH 'test2.db' AS aux;
|
||||
BEGIN;
|
||||
SELECT random() FROM abc LIMIT $i;
|
||||
INSERT INTO abc VALUES(randstr(10,10), 0, 0);
|
||||
DELETE FROM abc WHERE random()%10!=0;
|
||||
INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
|
||||
DELETE FROM abc2 WHERE random()%10!=0;
|
||||
COMMIT;
|
||||
"
|
||||
} {1 {child process exited abnormally}}
|
||||
do_test crash-4.3.$i.2 {
|
||||
signature
|
||||
} $sig
|
||||
do_test crash-4.3.$i.3 {
|
||||
signature2
|
||||
} $sig2
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user