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.
##########################################################################
{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
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
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
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);
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}
# 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);
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
}
--- /dev/null
+
+
+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 ""
+}
+
+
+
--- /dev/null
+# 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
}else{
rc = sqlite3_preupdate_old(db, i, &pVal);
}
+ if( rc!=SQLITE_OK ) return rc;
eType = sqlite3_value_type(pVal);
h = HASH_APPEND(h, eType);
}
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;
}
}
}
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;
+ }
}
/*
}
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){
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;
}
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;
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);
-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
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
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
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
-aed4273054cbd150c86b36ea951d17c981633ba0
\ No newline at end of file
+32e95164d1192b87b1ab019549fd2394642cd3fe
\ No newline at end of file
/* 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 ){