]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add some fault-injection tests to improve coverage.
authordan <dan@noemail.net>
Mon, 14 Jun 2010 07:53:26 +0000 (07:53 +0000)
committerdan <dan@noemail.net>
Mon, 14 Jun 2010 07:53:26 +0000 (07:53 +0000)
FossilOrigin-Name: 37b26d125f4b1d8e75bb38800fefd145611f94aa

manifest
manifest.uuid
src/test_vfs.c
src/wal.c
test/walfault.test

index 8fef7bc0d71ffc3da4fc504c3ffaf04edf34ba21..0ef8f2243693def68873bc343b504bb979dd031c 100644 (file)
--- 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
index a212b4e19c8c755e4494b85f978de1f1c54594b4..c8b6f5bf05c35fda2e74635f0665a24af39491f1 100644 (file)
@@ -1 +1 @@
-eade8bc238df580412f5cf1b91a91532ae671e46
\ No newline at end of file
+37b26d125f4b1d8e75bb38800fefd145611f94aa
\ No newline at end of file
index 4d9df73d799a490376697ea851fc92ea993e1edb..9b212099c61ba429a6262fa9ac2ea876f439c524 100644 (file)
@@ -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;
index 197d0c02225dbbfaf28cce83602f735d8f8235f7..2f35c20f964c1d2a06613ebadb2c9553a5320ad8 100644 (file)
--- 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;
index ad7bf97cb2f3389b3d409283b5a5accd242b2572..3b64f8dc732706b8a564e20f8fcaad8fd8caef2e 100644 (file)
@@ -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