From: dan Date: Mon, 14 Jun 2010 07:53:26 +0000 (+0000) Subject: Add some fault-injection tests to improve coverage. X-Git-Tag: version-3.7.2~275^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5d656852f700427e396d101a938ca6f989206a5c;p=thirdparty%2Fsqlite.git Add some fault-injection tests to improve coverage. FossilOrigin-Name: 37b26d125f4b1d8e75bb38800fefd145611f94aa --- diff --git a/manifest b/manifest index 8fef7bc0d7..0ef8f22436 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\ssome\sproblems\swith\shandling\sIO\serrors\son\sthe\sexperimental\sbranch. -D 2010-06-12T12:02:36 +C Add\ssome\sfault-injection\stests\sto\simprove\scoverage. +D 2010-06-14T07:53:26 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in a5cad1f8f3e021356bfcc6c77dc16f6f1952bbc3 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -209,7 +209,7 @@ F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6 F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa F src/test_thread.c aa9919c885a1fe53eafc73492f0898ee6c0a0726 -F src/test_vfs.c c3643e985b000e1f7555bd843a508512a33ab60e +F src/test_vfs.c 15bddcddf6b1bf6360130e09aee950f5f563d5f3 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 25ceb0f0a746ea1d0f9553787f3f0a56853cfaeb F src/trigger.c 8927588cb9e6d47f933b53bfe74200fbb504100d @@ -226,7 +226,7 @@ F src/vdbeblob.c 5327132a42a91e8b7acfb60b9d2c3b1c5c863e0e F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda -F src/wal.c ca3e0bf68c78005dee4e0d44d112e26975476d10 +F src/wal.c 75c55049c0923418f10bbef842b846374637b595 F src/wal.h 4ace25262452d17e7d3ec970c89ee17794004008 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c 1c895bef33d0dfc7ed90fb1f74120435d210ea56 @@ -772,7 +772,7 @@ F test/walbak.test e7650a26eb4b8abeca9b145b1af1e63026dde432 F test/walcksum.test 4efa8fb88c32bed8288ea4385a9cc113a5c8f0bf F test/walcrash.test f6d5fb2bb108876f04848720a488065d9deef69f F test/walcrash2.test 14585ad1a2c85da2de721caa3b4deeea55213008 -F test/walfault.test 0f9524cedbba0e5d48b58cabdc2dd7ae2c375476 +F test/walfault.test f6c1d0eade34fd0102e083726092d9bebd48bf7b F test/walhook.test 67e675127f4acb72f061a12667ce6e5460b06b78 F test/walmode.test 6ca9d710cc9f6545b913abcded6d6b0b15641048 F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933 @@ -820,7 +820,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P ca68472db01c14a899892007d1cbaff5e86ae193 -R 8c9eec4fce430c14365187b617d56771 +P eade8bc238df580412f5cf1b91a91532ae671e46 +R d24014573c5813c3e5f096c2f4031503 U dan -Z 808594079cf8cc56f07144318e40eca2 +Z e43653a42fa5b8fa5bb9c7db25e1bbd8 diff --git a/manifest.uuid b/manifest.uuid index a212b4e19c..c8b6f5bf05 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -eade8bc238df580412f5cf1b91a91532ae671e46 \ No newline at end of file +37b26d125f4b1d8e75bb38800fefd145611f94aa \ No newline at end of file diff --git a/src/test_vfs.c b/src/test_vfs.c index 4d9df73d79..9b212099c6 100644 --- a/src/test_vfs.c +++ b/src/test_vfs.c @@ -659,6 +659,7 @@ static int tvfsShmPage( if( p->pScript && p->mask&TESTVFS_SHMPAGE_MASK ){ Tcl_Obj *pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite)); @@ -666,6 +667,7 @@ static int tvfsShmPage( Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg ); tvfsResultCode(p, &rc); + Tcl_DecrRefCount(pArg); } if( rc==SQLITE_OK && p->mask&TESTVFS_SHMPAGE_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; @@ -828,12 +830,17 @@ static int testvfs_obj_cmd( Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?"); return TCL_ERROR; } - zName = Tcl_GetString(objv[2]); + zName = ckalloc(p->pParent->mxPathname); + p->pParent->xFullPathname( + p->pParent, Tcl_GetString(objv[2]), + p->pParent->mxPathname, zName + ); for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ if( 0==strcmp(pBuffer->zFile, zName) ) break; } + ckfree(zName); if( !pBuffer ){ - Tcl_AppendResult(interp, "no such file: ", zName, 0); + Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } if( objc==4 ){ @@ -870,6 +877,7 @@ static int testvfs_obj_cmd( { "xShmLock", TESTVFS_SHMLOCK_MASK }, { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, { "xShmClose", TESTVFS_SHMCLOSE_MASK }, + { "xShmPage", TESTVFS_SHMPAGE_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, }; @@ -911,6 +919,7 @@ static int testvfs_obj_cmd( ckfree((char *)p->apScript); p->apScript = 0; p->nScript = 0; + p->pScript = 0; } Tcl_GetStringFromObj(objv[2], &nByte); if( nByte>0 ){ @@ -1083,6 +1092,13 @@ static int testvfs_cmd( p = (Testvfs *)ckalloc(nByte); memset(p, 0, nByte); + /* Create the new object command before querying SQLite for a default VFS + ** to use for 'real' IO operations. This is because creating the new VFS + ** may delete an existing [testvfs] VFS of the same name. If such a VFS + ** is currently the default, the new [testvfs] may end up calling the + ** methods of a deleted object. + */ + Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del); p->pParent = sqlite3_vfs_find(0); p->interp = interp; @@ -1099,7 +1115,6 @@ static int testvfs_cmd( p->isNoshm = isNoshm; p->mask = TESTVFS_ALL_MASK; - Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del); sqlite3_vfs_register(pVfs, isDefault); return TCL_OK; diff --git a/src/wal.c b/src/wal.c index 197d0c0222..2f35c20f96 100644 --- a/src/wal.c +++ b/src/wal.c @@ -1281,6 +1281,13 @@ static void walMergesort( #endif } +/* +** Free an iterator allocated by walIteratorInit(). +*/ +static void walIteratorFree(WalIterator *p){ + sqlite3_free(p); +} + /* ** Map the wal-index into memory owned by this thread, if it is not ** mapped already. Then construct a WalInterator object that can be @@ -1337,6 +1344,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ rc = walHashGet(pWal, i, &aHash, &aPgno, &iZero); if( rc!=SQLITE_OK ){ + walIteratorFree(p); return rc; } nEntry = ((i+1)==nSegment)?iLast-iZero:(u32 *)aHash-(u32 *)&aPgno[iZero+1]; @@ -1361,13 +1369,6 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ return SQLITE_OK ; } -/* -** Free an iterator allocated by walIteratorInit(). -*/ -static void walIteratorFree(WalIterator *p){ - sqlite3_free(p); -} - /* ** Copy as much content as we can from the WAL back into the database file ** in response to an sqlite3_wal_checkpoint() request or the equivalent. @@ -2076,30 +2077,33 @@ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ Pgno iMax = pWal->hdr.mxFrame; Pgno iFrame; - rc = walIndexReadHdr(pWal, &unused); - if( rc==SQLITE_OK ){ - for(iFrame=pWal->hdr.mxFrame+1; - ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; - iFrame++ - ){ - /* This call cannot fail. Unless the page for which the page number - ** is passed as the second argument is (a) in the cache and - ** (b) has an outstanding reference, then xUndo is either a no-op - ** (if (a) is false) or simply expels the page from the cache (if (b) - ** is false). - ** - ** If the upper layer is doing a rollback, it is guaranteed that there - ** are no outstanding references to any page other than page 1. And - ** page 1 is never written to the log until the transaction is - ** committed. As a result, the call to xUndo may not fail. - */ - assert( pWal->writeLock ); - assert( walFramePgno(pWal, iFrame)!=1 ); - rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); - } - walCleanupHash(pWal); + /* Restore the clients cache of the wal-index header to the state it + ** was in before the client began writing to the database. + */ + memcpy(&pWal->hdr, walIndexHdr(pWal), sizeof(WalIndexHdr)); + + for(iFrame=pWal->hdr.mxFrame+1; + ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; + iFrame++ + ){ + /* This call cannot fail. Unless the page for which the page number + ** is passed as the second argument is (a) in the cache and + ** (b) has an outstanding reference, then xUndo is either a no-op + ** (if (a) is false) or simply expels the page from the cache (if (b) + ** is false). + ** + ** If the upper layer is doing a rollback, it is guaranteed that there + ** are no outstanding references to any page other than page 1. And + ** page 1 is never written to the log until the transaction is + ** committed. As a result, the call to xUndo may not fail. + */ + assert( pWal->writeLock ); + assert( walFramePgno(pWal, iFrame)!=1 ); + rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); } + walCleanupHash(pWal); } + assert( rc==SQLITE_OK ); return rc; } @@ -2142,9 +2146,7 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){ pWal->hdr.mxFrame = aWalData[0]; pWal->hdr.aFrameCksum[0] = aWalData[1]; pWal->hdr.aFrameCksum[1] = aWalData[2]; - if( rc==SQLITE_OK ){ - walCleanupHash(pWal); - } + walCleanupHash(pWal); } return rc; diff --git a/test/walfault.test b/test/walfault.test index ad7bf97cb2..3b64f8dc73 100644 --- a/test/walfault.test +++ b/test/walfault.test @@ -120,6 +120,7 @@ do_faultsim_test walfault-3 -prep { faultsim_test_result {0 {}} } + #-------------------------------------------------------------------------- # faultsim_delete_and_reopen @@ -152,7 +153,7 @@ do_test walfault-5-pre-1 { do_faultsim_test walfault-5 -faults shmerr* -prep { faultsim_restore_and_reopen execsql { PRAGMA wal_autocheckpoint = 0 } - shmfault filter xShmSize + shmfault filter xShmPage } -body { execsql { CREATE TABLE t1(x); @@ -211,7 +212,7 @@ do_test walfault-6-pre-1 { } {} do_faultsim_test walfault-6 -faults shmerr* -prep { faultsim_restore_and_reopen - shmfault filter xShmSize + shmfault filter xShmPage } -body { execsql { SELECT count(*) FROM t1 } } -test { @@ -326,7 +327,7 @@ do_test walfault-10-pre1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; - PRAGMA wal_checkpoint = 0; + PRAGMA wal_autocheckpoint = 0; CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB); CREATE INDEX zzzz ON z(zzz); INSERT INTO z VALUES(NULL, randomblob(800)); @@ -363,5 +364,84 @@ do_faultsim_test walfault-10 -prep { if {$n != "64 51200"} { error "Incorrect data: $n" } } +#-------------------------------------------------------------------------- +# Test fault injection while checkpointing a large WAL file, if the +# checkpoint is the first operation run after opening the database. +# This means that some of the required wal-index pages are mapped as part of +# the checkpoint process, which means there are a few more opportunities +# for IO errors. +# +# To speed this up, IO errors are only simulated within xShmPage() calls. +# +do_test walfault-11-pre-1 { + sqlite3 db test.db + execsql { + PRAGMA journal_mode = WAL; + PRAGMA wal_autocheckpoint = 0; + BEGIN; + CREATE TABLE abc(a PRIMARY KEY); + INSERT INTO abc VALUES(randomblob(1500)); + INSERT INTO abc VALUES(randomblob(1500)); + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 4 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 8 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 16 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 32 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 64 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 128 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 256 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 512 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 1024 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 2048 + INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 4096 + COMMIT; + } + faultsim_save_and_close +} {} +do_faultsim_test walfault-11 -faults shmerr* -prep { + catch { db2 close } + faultsim_restore_and_reopen + shmfault filter xShmPage +} -body { + db eval { SELECT count(*) FROM abc } + sqlite3 db2 test.db -vfs shmfault + db2 eval { PRAGMA wal_checkpoint } +} -test { + faultsim_test_result {0 {}} +} + +#------------------------------------------------------------------------- +# Test the handling of the various IO/OOM/SHM errors that may occur during +# a log recovery operation undertaken as part of a call to +# sqlite3_wal_checkpoint(). +# +do_test walfault-12-pre-1 { + faultsim_delete_and_reopen + execsql { + PRAGMA journal_mode = WAL; + PRAGMA wal_autocheckpoint = 0; + BEGIN; + CREATE TABLE abc(a PRIMARY KEY); + INSERT INTO abc VALUES(randomblob(1500)); + INSERT INTO abc VALUES(randomblob(1500)); + COMMIT; + } + faultsim_save_and_close +} {} +do_faultsim_test walfault-12 -prep { + if {[info commands shmfault] == ""} { + testvfs shmfault -default true + } + faultsim_restore_and_reopen + db eval { SELECT * FROM sqlite_master } + shmfault shm test.db [string repeat "\000" 40] +} -body { + set rc [sqlite3_wal_checkpoint db] + if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] } +} -test { + db close + faultsim_test_result {0 {}} +} + + finish_test