diff options
-rw-r--r-- | ext/session/session1.test | 18 | ||||
-rw-r--r-- | ext/session/sessionB.test | 15 | ||||
-rw-r--r-- | ext/session/sessionfault.test | 52 | ||||
-rw-r--r-- | ext/session/sqlite3session.c | 18 | ||||
-rw-r--r-- | ext/session/sqlite3session.h | 4 | ||||
-rw-r--r-- | ext/session/test_session.c | 99 | ||||
-rw-r--r-- | manifest | 23 | ||||
-rw-r--r-- | manifest.uuid | 2 |
8 files changed, 207 insertions, 24 deletions
diff --git a/ext/session/session1.test b/ext/session/session1.test index e47fb3e84..e6f9c8fd7 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 d61cada98..9798cabfe 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 f17daccfc..4b278098a 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 69d67817b..18dba064e 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 a6af9aca5..ced984ecb 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 fa99f5678..38e4be148 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 ***********************************************************************/ } @@ -441,6 +448,51 @@ static int test_conflict_handler( } /* +** 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<nCol; i++){ + sqlite3_value *pVal; + sqlite3changeset_old(pIter, i, &pVal); + sqlite3_value_text16(pVal); + x++; + } + } + + if( op!=SQLITE_DELETE ){ + for(i=0; i<nCol; i++){ + sqlite3_value *pVal; + sqlite3changeset_new(pIter, i, &pVal); + sqlite3_value_text16(pVal); + x++; + } + } + + if( eConf==SQLITE_CHANGESET_DATA ){ + return SQLITE_CHANGESET_REPLACE; + } + return SQLITE_CHANGESET_OMIT; +} + +/* ** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT? */ static int test_sqlite3changeset_apply( @@ -483,6 +535,41 @@ static int test_sqlite3changeset_apply( } /* +** sqlite3changeset_apply_replace_all DB CHANGESET +*/ +static int test_sqlite3changeset_apply_replace_all( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; /* Database handle */ + Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */ + int rc; /* Return code from changeset_invert() */ + void *pChangeset; /* Buffer containing changeset */ + int nChangeset; /* Size of buffer aChangeset in bytes */ + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB CHANGESET"); + return TCL_ERROR; + } + if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){ + Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0); + return TCL_ERROR; + } + db = *(sqlite3 **)info.objClientData; + pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset); + + rc = sqlite3changeset_apply(db, nChangeset, pChangeset, 0, replace_handler,0); + if( rc!=SQLITE_OK ){ + return test_session_error(interp, rc); + } + Tcl_ResetResult(interp); + return TCL_OK; +} + + +/* ** sqlite3changeset_invert CHANGESET */ static int test_sqlite3changeset_invert( @@ -596,6 +683,14 @@ static int test_sqlite3session_foreach( unsigned char *abPK; int i; + /* Test that _fk_conflicts() returns SQLITE_MISUSE if called on this + ** iterator. */ + int nDummy; + if( SQLITE_MISUSE!=sqlite3changeset_fk_conflicts(pIter, &nDummy) ){ + sqlite3changeset_finalize(pIter); + return TCL_ERROR; + } + sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); pVar = Tcl_NewObj(); Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj( @@ -674,6 +769,10 @@ int TestSession_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand( interp, "sqlite3changeset_apply", test_sqlite3changeset_apply, 0, 0 ); + Tcl_CreateObjCommand( + interp, "sqlite3changeset_apply_replace_all", + test_sqlite3changeset_apply_replace_all, 0, 0 + ); return TCL_OK; } @@ -1,5 +1,5 @@ -C A\sreasonably\scomplete\simplementation\sof\sthe\s"changeset"\scommand-line\stool\nand\sthe\s".sessions"\scommand\sin\sthe\scommand-line\sshell. -D 2014-08-18T20:01:31.177 +C Add\sthe\s"changeset"\scommand-line\stool\sfor\sanalyzing\sand\smanipulating\nchangesets\sin\sfiles\son\sdisk.\s\sAdd\sthe\s".session"\scommand\sto\sthe\scommand-line\ntool. +D 2014-08-18T20:08:25.748 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 639859a6f81bd15921ccd56ddbd6dfd335278377 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -144,7 +144,7 @@ F ext/rtree/sqlite3rtree.h 83349d519fe5f518b3ea025d18dd1fe51b1684bd F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/changeset.c 9be709cea346d65c6d0cc8bf03569956af125462 -F ext/session/session1.test 894e3bc9f497c4fa07a2aa3271e3911f3670c3d8 +F ext/session/session1.test 3733d71eb9a99b14d51fa5219d5b8a82407c3be8 F ext/session/session2.test 99ca0da7ddb617d42bafd83adccf99f18ae0384b F ext/session/session3.test a7a9ce59b8d1e49e2cc23d81421ac485be0eea01 F ext/session/session4.test a6ed685da7a5293c5d6f99855bcf41dbc352ca84 @@ -153,12 +153,12 @@ F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26 F ext/session/session8.test 7d35947ad329b8966f095d34f9617a9eff52dc65 F ext/session/session9.test 776e46785c29c11cda01f5205d0f1e8f8f9a46bf F ext/session/sessionA.test eb05c13e4ef1ca8046a3a6dbf2d5f6f5b04a11d4 -F ext/session/sessionB.test 77fca512719c136b8e1f4cb77b77e4c62b9d09ad +F ext/session/sessionB.test 276267cd7fc37c2e2dd03f1e2ed9ada336a8bdb4 F ext/session/session_common.tcl 1539d8973b2aea0025c133eb0cc4c89fcef541a5 -F ext/session/sessionfault.test 496291b287ba3c0b14ca2e074425e29cc92a64a6 -F ext/session/sqlite3session.c f2385ab5ebac94d8e54cc3aa8dc9ca77f8cdf0d9 -F ext/session/sqlite3session.h ef0dbcd599ab8b83e3f3f2e9912e1734519a3ef4 -F ext/session/test_session.c 920ccb6d6e1df263cd9099563328094c230b2925 +F ext/session/sessionfault.test e7965159a73d385c1a4af12d82c3a039ebdd71a6 +F ext/session/sqlite3session.c b7cc5645ee3de77c650cf7e05161f320ebaa997f +F ext/session/sqlite3session.h 66c14a2f6193c47773770307636e88c43db6f839 +F ext/session/test_session.c a252fb669d3a1b3552ee7b87fe610debc0afeb7b F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -1203,7 +1203,8 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 55bb3544a6b474c04853270067a35ca4b0079f52 -R e1146ecf2575392bb0feff21eda4606c +P 0fac6cfffe628ea02c78ebad065307309ec9eaa1 7b12f1f9c012f33d376242920583807b014b3287 +R 7a7a7eda9e9e796cac11d47296a11e68 +T +closed 7b12f1f9c012f33d376242920583807b014b3287 U drh -Z 0de9dc1123705f1abdab4ea8e245122b +Z bd91644aa3e055b27f4fc846b7cc054f diff --git a/manifest.uuid b/manifest.uuid index 447ba14ee..af042500c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7b12f1f9c012f33d376242920583807b014b3287
\ No newline at end of file +31addb627fdbaeb908e0611ad82f6db7537428ea
\ No newline at end of file |