From 082c96dffa373ed10d000001edce62ae757f13ec Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 18 Aug 2014 16:03:46 +0000 Subject: [PATCH] Add miscellaneous test cases to improve coverage of sessions module. FossilOrigin-Name: 0fac6cfffe628ea02c78ebad065307309ec9eaa1 --- ext/session/session1.test | 18 +++++++ ext/session/sessionB.test | 15 +++++- ext/session/sessionfault.test | 52 ++++++++++++++++-- ext/session/sqlite3session.c | 18 ++++--- ext/session/sqlite3session.h | 4 ++ ext/session/test_session.c | 99 +++++++++++++++++++++++++++++++++++ manifest | 24 ++++----- manifest.uuid | 2 +- 8 files changed, 207 insertions(+), 25 deletions(-) diff --git a/ext/session/session1.test b/ext/session/session1.test index e47fb3e845..e6f9c8fd7f 100644 --- a/ext/session/session1.test +++ b/ext/session/session1.test @@ -492,6 +492,24 @@ do_test 8.3 { } {1} do_test 8.4 { S delete } {} +do_test 8.5 { + sqlite3session S db main + S attach t5 + S attach t6 + execsql { INSERT INTO t5 VALUES(1, 2) } + S isempty +} {0} + +do_test 8.6 { + S delete + sqlite3session S db main + S attach t5 + S attach t6 + execsql { INSERT INTO t6 VALUES(1, 2) } + S isempty +} {0} +do_test 8.7 { S delete } {} + #------------------------------------------------------------------------- # do_execsql_test 9.1 { diff --git a/ext/session/sessionB.test b/ext/session/sessionB.test index d61cada98f..9798cabfee 100644 --- a/ext/session/sessionB.test +++ b/ext/session/sessionB.test @@ -453,9 +453,11 @@ proc do_patchset_changeset_test {tn initsql args} { foreach tstcmd {patchset changeset} { reset_db execsql $initsql + set x 0 foreach sql $args { + incr x set lSql [split $sql ";"] - uplevel [list do_patchset_test $tn.$tstcmd $tstcmd $lSql] + uplevel [list do_patchset_test $tn.$tstcmd.$x $tstcmd $lSql] } } } @@ -501,6 +503,17 @@ do_patchset_changeset_test 5.2 { UPDATE t1 SET b = b+1; } +set initsql { CREATE TABLE t1(a, b, c, PRIMARY KEY(c, b)); } +for {set i 0} {$i < 1000} {incr i} { + append insert "INSERT INTO t1 VALUES($i, $i, $i);" + append delete "DELETE FROM t1 WHERE b=$i;" +} +do_patchset_changeset_test 5.3 \ + $initsql $insert $delete \ + $insert $delete \ + "$insert $delete" \ + $delete + finish_test diff --git a/ext/session/sessionfault.test b/ext/session/sessionfault.test index f17daccfc7..4b278098a9 100644 --- a/ext/session/sessionfault.test +++ b/ext/session/sessionfault.test @@ -20,8 +20,6 @@ source $testdir/tester.tcl set testprefix sessionfault -if 1 { - forcedelete test.db2 sqlite3 db2 test.db2 do_common_sql { @@ -399,8 +397,6 @@ do_faultsim_test 9.1 -faults oom-transient -prep { } } -} - faultsim_delete_and_reopen do_test 9.2.prep { execsql { @@ -438,6 +434,54 @@ do_faultsim_test 9.2 -faults oom-transient -prep { } } +#------------------------------------------------------------------------- +# Test that if a conflict-handler encounters an OOM in +# sqlite3_value_text() but goes on to return SQLITE_CHANGESET_REPLACE +# anyway, the OOM is picked up by the sessions module. +set bigstr [string repeat abcdefghij 100] +faultsim_delete_and_reopen +do_test 10.prep.1 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES($bigstr, $bigstr); + } + + sqlite3session S db main + S attach * + execsql { UPDATE t1 SET b = b||'x' } + set C [S changeset] + S delete + execsql { UPDATE t1 SET b = b||'xyz' } +} {} +faultsim_save_and_close + +faultsim_restore_and_reopen +do_test 10.prep.2 { + proc xConflict {args} { return "ABORT" } + list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg +} {1 SQLITE_ABORT} +do_execsql_test 10.prep.3 { SELECT b=$bigstr||'x' FROM t1 } 0 +do_test 10.prep.4 { + proc xConflict {args} { return "REPLACE" } + list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg +} {0 {}} +do_execsql_test 10.prep.5 { SELECT b=$bigstr||'x' FROM t1 } 1 +db close + +do_faultsim_test 10 -faults oom-tra* -prep { + faultsim_restore_and_reopen +} -body { + sqlite3changeset_apply_replace_all db $::C +} -test { + faultsim_test_result {0 {}} {1 SQLITE_NOMEM} + if {$testrc==0} { + if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} { + error "data does not look right" + } + } +} + + finish_test diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 69d67817bf..18dba064ec 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -2703,10 +2703,14 @@ static int sessionBindValue( sqlite3_value *pVal /* Value to bind */ ){ int eType = sqlite3_value_type(pVal); + /* COVERAGE: The (pVal->z==0) branch is never true using current versions + ** of SQLite. If a malloc fails in an sqlite3_value_xxx() function, either + ** the (pVal->z) variable remains as it was or the type of the value is + ** set to SQLITE_NULL. */ if( (eType==SQLITE_TEXT || eType==SQLITE_BLOB) && pVal->z==0 ){ /* This condition occurs when an earlier OOM in a call to ** sqlite3_value_text() or sqlite3_value_blob() (perhaps from within - ** a conflict-hanler) has zeroed the pVal->z pointer. Return NOMEM. */ + ** a conflict-handler) has zeroed the pVal->z pointer. Return NOMEM. */ return SQLITE_NOMEM; } return sqlite3_bind_value(pStmt, i, pVal); @@ -3052,6 +3056,8 @@ int sqlite3changeset_apply( int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ SessionApplyCtx sApply; /* changeset_apply() context object */ + assert( xConflict!=0 ); + memset(&sApply, 0, sizeof(sApply)); rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset); if( rc!=SQLITE_OK ) return rc; @@ -3169,12 +3175,10 @@ int sqlite3changeset_apply( sqlite3_db_status(db, SQLITE_DBSTATUS_DEFERRED_FKS, &nFk, ¬Used, 0); if( nFk!=0 ){ int res = SQLITE_CHANGESET_ABORT; - if( xConflict ){ - sqlite3_changeset_iter sIter; - memset(&sIter, 0, sizeof(sIter)); - sIter.nCol = nFk; - res = xConflict(pCtx, SQLITE_CHANGESET_FOREIGN_KEY, &sIter); - } + sqlite3_changeset_iter sIter; + memset(&sIter, 0, sizeof(sIter)); + sIter.nCol = nFk; + res = xConflict(pCtx, SQLITE_CHANGESET_FOREIGN_KEY, &sIter); if( res!=SQLITE_CHANGESET_OMIT ){ rc = SQLITE_CONSTRAINT; } diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index a6af9aca57..ced984ecbe 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -735,6 +735,10 @@ int sqlite3changeset_concat( ** invoked. A description of exactly when the conflict handler is invoked for ** each type of change is below. ** +** Unlike the xFilter argument, xConflict may not be passed NULL. The results +** of passing anything other than a valid function pointer as the xConflict +** argument are undefined. +** ** Each time the conflict handler function is invoked, it must return one ** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or ** [SQLITE_CHANGESET_REPLACE]. SQLITE_CHANGESET_REPLACE may only be returned diff --git a/ext/session/test_session.c b/ext/session/test_session.c index fa99f56786..38e4be1481 100644 --- a/ext/session/test_session.c +++ b/ext/session/test_session.c @@ -417,6 +417,13 @@ static int test_conflict_handler( rc = sqlite3changeset_old(pIter, nCol, &pVal); assert( rc==SQLITE_RANGE ); } + if( eConf!=SQLITE_CHANGESET_FOREIGN_KEY ){ + /* eConf!=FOREIGN_KEY is always true at this point. The condition is + ** just there to make it clearer what is being tested. */ + int nDummy; + int rc = sqlite3changeset_fk_conflicts(pIter, &nDummy); + assert( rc==SQLITE_MISUSE ); + } /* End of testing block ***********************************************************************/ } @@ -440,6 +447,51 @@ static int test_conflict_handler( return ret; } +/* +** The conflict handler used by sqlite3changeset_apply_replace_all(). +** This conflict handler calls sqlite3_value_text16() on all available +** sqlite3_value objects and then returns CHANGESET_REPLACE, or +** CHANGESET_OMIT if REPLACE is not applicable. This is used to test the +** effect of a malloc failure within an sqlite3_value_xxx() function +** invoked by a conflict-handler callback. +*/ +static int replace_handler( + void *pCtx, /* Pointer to TestConflictHandler structure */ + int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *pIter /* Handle describing change and conflict */ +){ + int op; /* SQLITE_UPDATE, DELETE or INSERT */ + const char *zTab; /* Name of table conflict is on */ + int nCol; /* Number of columns in table zTab */ + int i; + int x = 0; + + sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0); + + if( op!=SQLITE_INSERT ){ + for(i=0; i