Add start of fault-injection tests for session module. Fix some bugs related to the same.

FossilOrigin-Name: 32e95164d1192b87b1ab019549fd2394642cd3fe
This commit is contained in:
dan 2011-03-21 16:17:42 +00:00
parent 27453faef8
commit 12ca0b5695
7 changed files with 299 additions and 123 deletions

View File

@ -28,67 +28,6 @@ proc test_reset {} {
sqlite3 db2 test.db2
}
proc do_common_sql {sql} {
execsql $sql db
execsql $sql db2
}
proc xConflict args { return "OMIT" }
proc do_then_apply_sql {sql {dbname main}} {
sqlite3session S db $dbname
db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
S attach $name
}
db eval $sql
sqlite3changeset_apply db2 [S changeset] xConflict
S delete
}
proc do_iterator_test {tn tbl_list sql res} {
sqlite3session S db main
foreach t $tbl_list {S attach $t}
execsql $sql
set r [list]
foreach v $res { lappend r $v }
set x [list]
sqlite3session_foreach c [S changeset] { lappend x $c }
uplevel do_test $tn [list [list set {} $x]] [list $r]
S delete
}
# Compare the contents of all tables in [db1] and [db2]. Throw an error if
# they are not identical, or return an empty string if they are.
#
proc compare_db {db1 db2} {
set sql {SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name}
set lot1 [$db1 eval $sql]
set lot2 [$db2 eval $sql]
if {$lot1 != $lot2} { error "databases contain different tables" }
foreach tbl $lot1 {
set col1 [list]
set col2 [list]
$db1 eval "PRAGMA table_info = $tbl" { lappend col1 $name }
$db2 eval "PRAGMA table_info = $tbl" { lappend col2 $name }
if {$col1 != $col2} { error "table $tbl schema mismatch" }
set sql "SELECT * FROM $tbl ORDER BY [join $col1 ,]"
set data1 [$db1 eval $sql]
set data2 [$db2 eval $sql]
if {$data1 != $data2} { error "table $tbl data mismatch" }
}
return ""
}
##########################################################################
# End of proc definitions. Start of tests.
##########################################################################
@ -105,12 +44,13 @@ do_iterator_test 1.1 t1 {
{DELETE t1 {t i t one} {}}
{INSERT t1 {} {t ii t two}}
}
do_iterator_test 1.1 t1 {
do_iterator_test 1.2 t1 {
INSERT INTO t1 VALUES(1.5, 99.9)
} {
{INSERT t1 {} {f 1.5 f 99.9}}
}
# Execute each of the following blocks of SQL on database [db1]. Collect
# changes using a session object. Apply the resulting changeset to
# database [db2]. Then check that the contents of the two databases are
@ -178,6 +118,38 @@ set set_of_tests {
12 {
INSERT INTO %T2% VALUES(NULL, NULL);
}
13 {
DELETE FROM %T1% WHERE 1;
-- Insert many rows with real primary keys. Enough to force the session
-- objects hash table to resize.
INSERT INTO %T1% VALUES(0.1, 0.1);
INSERT INTO %T1% SELECT a+0.1, b+0.1 FROM %T1%;
INSERT INTO %T1% SELECT a+0.2, b+0.2 FROM %T1%;
INSERT INTO %T1% SELECT a+0.4, b+0.4 FROM %T1%;
INSERT INTO %T1% SELECT a+0.8, b+0.8 FROM %T1%;
INSERT INTO %T1% SELECT a+1.6, b+1.6 FROM %T1%;
INSERT INTO %T1% SELECT a+3.2, b+3.2 FROM %T1%;
INSERT INTO %T1% SELECT a+6.4, b+6.4 FROM %T1%;
INSERT INTO %T1% SELECT a+12.8, b+12.8 FROM %T1%;
INSERT INTO %T1% SELECT a+25.6, b+25.6 FROM %T1%;
INSERT INTO %T1% SELECT a+51.2, b+51.2 FROM %T1%;
INSERT INTO %T1% SELECT a+102.4, b+102.4 FROM %T1%;
INSERT INTO %T1% SELECT a+204.8, b+204.8 FROM %T1%;
}
14 {
DELETE FROM %T1% WHERE 1;
}
15 {
INSERT INTO %T1% VALUES(1, 1);
INSERT INTO %T1% SELECT a+2, b+2 FROM %T1%;
INSERT INTO %T1% SELECT a+4, b+4 FROM %T1%;
INSERT INTO %T1% SELECT a+8, b+8 FROM %T1%;
INSERT INTO %T1% SELECT a+256, b+256 FROM %T1%;
}
}
test_reset
@ -189,7 +161,7 @@ do_common_sql {
foreach {tn sql} [string map {%T1% t1 %T2% t2 %T3% t3} $set_of_tests] {
do_then_apply_sql $sql
do_test 1.$tn { compare_db db db2 } {}
do_test 2.$tn { compare_db db db2 } {}
}
# The following block of tests is similar to the last, except that the
@ -200,7 +172,7 @@ foreach {tn sql} [string map {%T1% t1 %T2% t2 %T3% t3} $set_of_tests] {
test_reset
forcedelete test.db3
sqlite3 db3 test.db3
do_test 2.0 {
do_test 3.0 {
execsql {
ATTACH 'test.db3' AS 'aux';
CREATE TABLE t1(a, b PRIMARY KEY);
@ -224,7 +196,7 @@ foreach {tn sql} [
string map {%T1% aux.t1 %T2% aux.t2 %T3% aux.t3} $set_of_tests
] {
do_then_apply_sql $sql aux
do_test 2.$tn { compare_db db3 db2 } {}
do_test 3.$tn { compare_db db3 db2 } {}
}
catch {db3 close}
@ -234,7 +206,7 @@ catch {db3 close}
# handled correctly by the session module.
#
test_reset
do_execsql_test 3.0 {
do_execsql_test 4.0 {
CREATE TABLE t1(a PRIMARY KEY);
CREATE TABLE t2(a, b, c, PRIMARY KEY(c, b));
CREATE TABLE t3(a, b INTEGER PRIMARY KEY);
@ -272,7 +244,7 @@ foreach {tn sql changeset} {
9 { DELETE FROM t2 WHERE 1 } { {DELETE t2 {i 1 i 2 i 3} {}} }
} {
do_iterator_test 3.$tn {t1 t2 t3} $sql $changeset
do_iterator_test 4.$tn {t1 t2 t3} $sql $changeset
}

View File

@ -0,0 +1,106 @@
proc do_conflict_test {tn args} {
proc xConflict {args} {
lappend ::xConflict $args
return ""
}
proc bgerror {args} { set ::background_error $args }
set O(-tables) [list]
set O(-sql) [list]
set O(-conflicts) [list]
array set V $args
foreach key [array names V] {
if {![info exists O($key)]} {error "no such option: $key"}
}
array set O $args
sqlite3session S db main
foreach t $O(-tables) { S attach $t }
execsql $O(-sql)
set ::xConflict [list]
sqlite3changeset_apply db2 [S changeset] xConflict
set conflicts [list]
foreach c $O(-conflicts) {
lappend conflicts $c
}
after 1 {set go 1}
vwait go
uplevel do_test $tn [list { set ::xConflict }] [list $conflicts]
S delete
}
proc do_common_sql {sql} {
execsql $sql db
execsql $sql db2
}
proc do_then_apply_sql {sql {dbname main}} {
proc xConflict args { return "OMIT" }
set rc [catch {
sqlite3session S db $dbname
db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
S attach $name
}
db eval $sql
sqlite3changeset_apply db2 [S changeset] xConflict
} msg]
catch { S delete }
if {$rc} {error $msg}
}
proc do_iterator_test {tn tbl_list sql res} {
sqlite3session S db main
foreach t $tbl_list {S attach $t}
execsql $sql
set r [list]
foreach v $res { lappend r $v }
set x [list]
sqlite3session_foreach c [S changeset] { lappend x $c }
uplevel do_test $tn [list [list set {} $x]] [list $r]
S delete
}
# Compare the contents of all tables in [db1] and [db2]. Throw an error if
# they are not identical, or return an empty string if they are.
#
proc compare_db {db1 db2} {
set sql {SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name}
set lot1 [$db1 eval $sql]
set lot2 [$db2 eval $sql]
if {$lot1 != $lot2} { error "databases contain different tables" }
foreach tbl $lot1 {
set col1 [list]
set col2 [list]
$db1 eval "PRAGMA table_info = $tbl" { lappend col1 $name }
$db2 eval "PRAGMA table_info = $tbl" { lappend col2 $name }
if {$col1 != $col2} { error "table $tbl schema mismatch" }
set sql "SELECT * FROM $tbl ORDER BY [join $col1 ,]"
set data1 [$db1 eval $sql]
set data2 [$db2 eval $sql]
if {$data1 != $data2} { error "table $tbl data mismatch" }
}
return ""
}

View File

@ -0,0 +1,85 @@
# 2011 Mar 21
#
# 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.
#
#***********************************************************************
#
# The focus of this file is testing the session module.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
set testprefix sessionfault
forcedelete test.db2
sqlite3 db2 test.db2
do_common_sql {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
}
faultsim_save_and_close
db2 close
# Test OOM error handling when collecting and applying a simple changeset.
#
do_faultsim_test pagerfault-1 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
sqlite3 db2 test.db2
} -body {
do_then_apply_sql {
INSERT INTO t1 VALUES(7, 8, 9);
UPDATE t1 SET c = 10 WHERE a = 1;
DELETE FROM t1 WHERE a = 4;
}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
faultsim_integrity_check
if {$testrc==0} { compare_db db db2 }
}
# This test case is designed so that a malloc() failure occurs while
# resizing the session object hash-table from 256 to 512 buckets. This
# is not an error, just a sub-optimal condition.
#
do_faultsim_test pagerfault-2 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
sqlite3 db2 test.db2
sqlite3session S db main
S attach t1
execsql { BEGIN }
for {set i 0} {$i < 125} {incr i} {
execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)}
}
} -body {
for {set i 125} {$i < 133} {incr i} {
execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)}
}
S changeset
set {} {}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
sqlite3changeset_apply db2 [S changeset] xConflict
compare_db db db2
}
catch { S delete }
faultsim_integrity_check
}
finish_test

View File

@ -305,6 +305,7 @@ static unsigned int sessionPreupdateHash(
}else{
rc = sqlite3_preupdate_old(db, i, &pVal);
}
if( rc!=SQLITE_OK ) return rc;
eType = sqlite3_value_type(pVal);
h = HASH_APPEND(h, eType);
@ -424,33 +425,35 @@ static int sessionPreupdateEqual(
}
if( rc!=SQLITE_OK || sqlite3_value_type(pVal)!=eType ) return rc;
switch( eType ){
case SQLITE_INTEGER:
case SQLITE_FLOAT: {
i64 iVal = sessionGetI64(a);
a += 8;
if( eType==SQLITE_INTEGER ){
if( sqlite3_value_int64(pVal)!=iVal ) return SQLITE_OK;
}else{
double rVal;
assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
memcpy(&rVal, &iVal, 8);
if( sqlite3_value_double(pVal)!=rVal ) return SQLITE_OK;
}
break;
/* A SessionChange object never has a NULL value in a PK column */
assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
|| eType==SQLITE_BLOB || eType==SQLITE_TEXT
);
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
i64 iVal = sessionGetI64(a);
a += 8;
if( eType==SQLITE_INTEGER ){
if( sqlite3_value_int64(pVal)!=iVal ) return SQLITE_OK;
}else{
double rVal;
assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
memcpy(&rVal, &iVal, 8);
if( sqlite3_value_double(pVal)!=rVal ) return SQLITE_OK;
}
case SQLITE_TEXT:
case SQLITE_BLOB: {
int n;
const u8 *z;
a += sessionVarintGet(a, &n);
if( sqlite3_value_bytes(pVal)!=n ) return SQLITE_OK;
z = eType==SQLITE_TEXT ?
sqlite3_value_text(pVal) : sqlite3_value_blob(pVal);
if( memcmp(a, z, n) ) return SQLITE_OK;
a += n;
break;
}else{
int n;
const u8 *z;
a += sessionVarintGet(a, &n);
if( sqlite3_value_bytes(pVal)!=n ) return SQLITE_OK;
if( eType==SQLITE_TEXT ){
z = sqlite3_value_text(pVal);
}else{
z = sqlite3_value_blob(pVal);
}
if( memcmp(a, z, n) ) return SQLITE_OK;
a += n;
break;
}
}
}
@ -734,20 +737,22 @@ static void sessionPreupdateOneChange(
rc = sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
}
}
pChange->nRecord = nByte;
/* If an error has occurred, mark the session object as failed. */
if( rc!=SQLITE_OK ){
sqlite3_free(pChange);
pSession->rc = rc;
}else{
if( rc==SQLITE_OK ){
/* Add the change back to the hash-table */
pChange->nRecord = nByte;
pChange->bInsert = (op==SQLITE_INSERT);
pChange->pNext = pTab->apChange[iHash];
pTab->apChange[iHash] = pChange;
}else{
sqlite3_free(pChange);
}
}
}
/* If an error has occurred, mark the session object as failed. */
if( rc!=SQLITE_OK ){
pSession->rc = rc;
}
}
/*
@ -1345,7 +1350,7 @@ int sqlite3session_changeset(
}
nNoop = buf.nBuf;
for(i=0; i<pTab->nChange; i++){
for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
SessionChange *p; /* Used to iterate through changes */
for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
@ -1366,13 +1371,14 @@ int sqlite3session_changeset(
sessionAppendByte(&buf, SQLITE_DELETE, &rc);
sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
}
rc = sqlite3_reset(pSel);
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
}
}
}
}
sqlite3_finalize(pSel);
if( buf.nBuf==nNoop ){
buf.nBuf = nRewind;
}
@ -1674,7 +1680,9 @@ int sqlite3changeset_conflict(
int sqlite3changeset_finalize(sqlite3_changeset_iter *p){
int i; /* Used to iterate through p->apValue[] */
int rc = p->rc; /* Return code */
for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
if( p->apValue ){
for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
}
sqlite3_free(p->apValue);
sqlite3_free(p);
return rc;
@ -2316,7 +2324,8 @@ int sqlite3changeset_apply(
SessionApplyCtx sApply; /* changeset_apply() context object */
memset(&sApply, 0, sizeof(sApply));
sqlite3changeset_start(&pIter, nChangeset, pChangeset);
rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
if( rc!=SQLITE_OK ) return rc;
sqlite3_mutex_enter(sqlite3_db_mutex(db));
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);

View File

@ -1,5 +1,5 @@
C Clarify\shandling\sof\sNULL\svalues\sin\sPK\scolumns\sin\ssqlite3session.h.\sAdd\stests\sand\sfixes\sfor\sthe\ssame.
D 2011-03-21T11:55:07
C Add\sstart\sof\sfault-injection\stests\sfor\ssession\smodule.\sFix\ssome\sbugs\srelated\sto\sthe\ssame.
D 2011-03-21T16:17:42
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -100,8 +100,10 @@ F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F ext/session/session1.test 3f982c74ee4ba97069917cc35aae25b4ed858e6a
F ext/session/session2.test 3ef304f660b2a929e6bfec2df125c1809f5501ff
F ext/session/sqlite3session.c 70b19f80eadf7060836eaa90928f08a58aa3b35f
F ext/session/session2.test 6462c21d3795d9e48ffea2e7550d1b2d6da66dfb
F ext/session/session_common.tcl 880b554b0bcadcabe1331afb87d58ad1ed2510c4
F ext/session/sessionfault.test 4190de237b2c76ca7529ef415778a862d7d0fa30
F ext/session/sqlite3session.c c5a60c2cf21f8892f9ae4850fad2d7859c2c3692
F ext/session/sqlite3session.h 2c071ee5925e82c21c7c9c296a0422c039607106
F ext/session/test_session.c 2559ef68e421c7fb83e2c19ef08a17343b70d535
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
@ -239,7 +241,7 @@ F src/vacuum.c 924bd1bcee2dfb05376f79845bd3b4cec7b54b2f
F src/vdbe.c c90edafd941481506f001b17cd8523683fdac853
F src/vdbe.h edef9c4f0be83e1f1dccd049da37b40e021b63d9
F src/vdbeInt.h 2cf77c1d151a4a54facd596d52be6d60c1cb26e8
F src/vdbeapi.c c4692c74b31f4abe638fbcbae3944c52b54e2c36
F src/vdbeapi.c 3d620e00cb74b6034343009af42d5ff5eb7c19dc
F src/vdbeaux.c 0216b2c37509a44c3833b297765bee7bdd04fa2f
F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
@ -921,7 +923,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P a192d04f4e3a9e4960a4d96d1d3ee8635bc1034d
R 0282a54a03134becd2cdef1929ff705c
P aed4273054cbd150c86b36ea951d17c981633ba0
R 3ee0056f48f594a086fa203e62ad6366
U dan
Z 9cc92283568709d19359195efd9bde16
Z 9a232200bb7ff725d52774ff9ca627f1

View File

@ -1 +1 @@
aed4273054cbd150c86b36ea951d17c981633ba0
32e95164d1192b87b1ab019549fd2394642cd3fe

View File

@ -1348,21 +1348,23 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
/* If the old.* record has not yet been loaded into memory, do so now. */
if( p->pUnpacked==0 ){
u32 nRecord;
u8 *aRecord;
u32 nRec;
u8 *aRec;
rc = sqlite3BtreeDataSize(p->pCsr->pCursor, &nRecord);
rc = sqlite3BtreeDataSize(p->pCsr->pCursor, &nRec);
if( rc!=SQLITE_OK ) goto preupdate_old_out;
aRecord = sqlite3DbMallocRaw(db, nRecord);
if( !aRecord ) goto preupdate_old_out;
rc = sqlite3BtreeData(p->pCsr->pCursor, 0, nRecord, aRecord);
aRec = sqlite3DbMallocRaw(db, nRec);
if( !aRec ) goto preupdate_old_out;
rc = sqlite3BtreeData(p->pCsr->pCursor, 0, nRec, aRec);
if( rc==SQLITE_OK ){
p->pUnpacked = sqlite3VdbeRecordUnpack(&p->keyinfo, nRec, aRec, 0, 0);
if( !p->pUnpacked ) rc = SQLITE_NOMEM;
}
if( rc!=SQLITE_OK ){
sqlite3DbFree(db, aRecord);
sqlite3DbFree(db, aRec);
goto preupdate_old_out;
}
p->pUnpacked = sqlite3VdbeRecordUnpack(&p->keyinfo, nRecord, aRecord, 0, 0);
p->aRecord = aRecord;
p->aRecord = aRec;
}
if( iIdx>=p->pUnpacked->nField ){