]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fixes for locking issues in WAL mode.
authordan <dan@noemail.net>
Wed, 14 Apr 2010 11:23:30 +0000 (11:23 +0000)
committerdan <dan@noemail.net>
Wed, 14 Apr 2010 11:23:30 +0000 (11:23 +0000)
FossilOrigin-Name: a9617eff39177250e2f118f25fdd4b3acb8b0478

manifest
manifest.uuid
src/log.c
src/pragma.c
test/wal.test
test/walthread.test

index faab67f851271551d377c77b0348d5ebfbbafe50..1f3fc5b551b97cd216f503f4f31bbcb26ffafda2 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sexperimental\slocking\sscheme.
-D 2010-04-13T19:27:31
+C Fixes\sfor\slocking\sissues\sin\sWAL\smode.
+D 2010-04-14T11:23:30
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -131,7 +131,7 @@ F src/journal.c b0ea6b70b532961118ab70301c00a33089f9315c
 F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
 F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
 F src/loadext.c 1c7a61ce1281041f437333f366a96aa0d29bb581
-F src/log.c d89988bb26a3cd414858c97642a612b4ce6e540f
+F src/log.c 78575e5bed43578580a0ee86fd3c2e924abe4a88
 F src/log.h a2654af46ce7b5732f4d5a731abfdd180f0a06d9
 F src/main.c c0e7192bad5b90544508b241eb2487ac661de890
 F src/malloc.c a08f16d134f0bfab6b20c3cd142ebf3e58235a6a
@@ -160,7 +160,7 @@ F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e
 F src/pcache.c ace8f6a5ecd4711cc66a1b23053be7109bd437cf
 F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050
 F src/pcache1.c 6dc1871ce8ead9187161c370a58cd06c84221f76
-F src/pragma.c 9f1d53d40b47743f3d6151df15915970367c9de5
+F src/pragma.c f12cb58a8aa0d80cfed282ef87a285ed71beb793
 F src/prepare.c fd1398cb1da54385ba5bd68d93928f10d10a1d9c
 F src/printf.c 5f5b65a83e63f2096a541a340722a509fa0240a7
 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
@@ -756,10 +756,10 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
 F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
-F test/wal.test d5916b3a40d74dd5bf6ff21b42e6fa7f255efd13
+F test/wal.test a4be3c7a36e3db3d9d276dc7664c2ed5eece1e8a
 F test/walcrash.test 45cfbab30bb7cbe0b2e9d5cabe90dbcad10cb89b
 F test/walslow.test 38076d5fad49e3678027be0f8110e6a32d531dc2
-F test/walthread.test 1592116f74261d77ea697d9503b0cd1ab1c15e74
+F test/walthread.test 58cd64b06f186251f09f64e4918fb74a7e52c963
 F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
 F test/where2.test 45eacc126aabb37959a387aa83e59ce1f1f03820
 F test/where3.test 97d3936e6a443b968f1a61cdcc0f673252000e94
@@ -803,7 +803,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P f4e1150fed2c520c7c52612cb1019429d78dc32a
-R da172d13acb33c194abd32bf4ed20cc9
+P 3f958e87c33d667d299b03ffdef58db5dc6363f4
+R 91ba1bf1aecd8bbf6dbeda941d0dbe58
 U dan
-Z cd10eeca0187a805032040fdd27191f0
+Z d3b26d31320c12902d5354af18b08451
index 43b6c91f024bd415c09e183d9bc543a78f564246..8cd6641af060eacef43c657c8ba68936fc2af137 100644 (file)
@@ -1 +1 @@
-3f958e87c33d667d299b03ffdef58db5dc6363f4
\ No newline at end of file
+a9617eff39177250e2f118f25fdd4b3acb8b0478
\ No newline at end of file
index 2b2764f361c39931c30758b07b7667e1215c71ba..c7fb8738d2b04d2f71af2ac0465050696585395e 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -617,7 +617,7 @@ int sqlite3LogOpen(
   const char *zDb,                /* Name of database file */
   Log **ppLog                     /* OUT: Allocated Log handle */
 ){
-  int rc;                         /* Return Code */
+  int rc = SQLITE_OK;             /* Return Code */
   Log *pRet;                      /* Object to allocate and return */
   LogSummary *pSummary = 0;       /* Summary object */
   sqlite3_mutex *mutex = 0;       /* LOG_SUMMARY_MUTEX mutex */
@@ -806,7 +806,7 @@ static int logCheckpoint(
   int pgsz = pLog->hdr.pgsz;      /* Database page-size */
   LogCheckpoint *pIter = 0;       /* Log iterator context */
   u32 iDbpage = 0;                /* Next database page to write */
-  u32 iFrame;                     /* Log frame containing data for iDbpage */
+  u32 iFrame = 0;                 /* Log frame containing data for iDbpage */
 
   /* Allocate the iterator */
   pIter = logCheckpointInit(pLog);
@@ -1080,6 +1080,77 @@ static int logLockRegion(Log *pLog, u32 mRegion, int op){
   return SQLITE_OK;
 }
 
+/*
+** Try to read the log-summary header. Attempt to verify the header
+** checksum. If the checksum can be verified, copy the log-summary
+** header into structure pLog->hdr. If the contents of pLog->hdr are
+** modified by this and pChanged is not NULL, set *pChanged to 1. 
+** Otherwise leave *pChanged unmodified.
+**
+** If the checksum cannot be verified return SQLITE_ERROR.
+*/
+int logSummaryTryHdr(Log *pLog, int *pChanged){
+  u32 aCksum[2] = {1, 1};
+  u32 aHdr[LOGSUMMARY_HDR_NFIELD+2];
+
+  /* First try to read the header without a lock. Verify the checksum
+  ** before returning. This will almost always work.  
+  */
+  memcpy(aHdr, pLog->pSummary->aData, sizeof(aHdr));
+  logChecksumBytes((u8*)aHdr, sizeof(u32)*LOGSUMMARY_HDR_NFIELD, aCksum);
+  if( aCksum[0]!=aHdr[LOGSUMMARY_HDR_NFIELD]
+   || aCksum[1]!=aHdr[LOGSUMMARY_HDR_NFIELD+1]
+  ){
+    return SQLITE_ERROR;
+  }
+
+  if( memcmp(&pLog->hdr, aHdr, sizeof(LogSummaryHdr)) ){
+    if( pChanged ){
+      *pChanged = 1;
+    }
+    memcpy(&pLog->hdr, aHdr, sizeof(LogSummaryHdr));
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Read the log-summary header from the log-summary file into structure 
+** pLog->hdr. If attempting to verify the header checksum fails, try
+** to recover the log before returning.
+**
+** If the log-summary header is successfully read, return SQLITE_OK. 
+** Otherwise an SQLite error code.
+*/
+int logSummaryReadHdr(Log *pLog, int *pChanged){
+  int rc;
+
+  /* First try to read the header without a lock. Verify the checksum
+  ** before returning. This will almost always work.  
+  */
+  if( SQLITE_OK==logSummaryTryHdr(pLog, pChanged) ){
+    return SQLITE_OK;
+  }
+
+  /* If the first attempt to read the header failed, lock the log-summary
+  ** file and try again. If the header checksum verification fails this
+  ** time as well, run log recovery.
+  */
+  if( SQLITE_OK==(rc = logEnterMutex(pLog)) ){
+    if( SQLITE_OK!=logSummaryTryHdr(pLog, pChanged) ){
+      if( pChanged ){
+        *pChanged = 1;
+      }
+      rc = logSummaryRecover(pLog->pSummary, pLog->pFd);
+      if( rc==SQLITE_OK ){
+        rc = logSummaryTryHdr(pLog, 0);
+      }
+    }
+    logLeaveMutex(pLog);
+  }
+
+  return rc;
+}
+
 /*
 ** Lock a snapshot.
 **
@@ -1122,31 +1193,7 @@ int sqlite3LogOpenSnapshot(Log *pLog, int *pChanged){
       return rc;
     }
 
-    if( SQLITE_OK==(rc = logEnterMutex(pLog)) ){
-      u32 aCksum[2] = {1, 1};
-      u32 aHdr[LOGSUMMARY_HDR_NFIELD+2];
-      memcpy(aHdr, pLog->pSummary->aData, sizeof(aHdr));
-
-      /* Verify the checksum on the log-summary header. If it fails,
-      ** recover the log-summary from the log file.
-      */
-      logChecksumBytes((u8*)aHdr, sizeof(u32)*LOGSUMMARY_HDR_NFIELD, aCksum);
-      if( aCksum[0]!=aHdr[LOGSUMMARY_HDR_NFIELD]
-       || aCksum[1]!=aHdr[LOGSUMMARY_HDR_NFIELD+1]
-      ){
-        rc = logSummaryRecover(pLog->pSummary, pLog->pFd);
-        memcpy(aHdr, pLog->pSummary->aData, sizeof(aHdr));
-        *pChanged = 1;
-      }
-      if( rc==SQLITE_OK ){
-        if( memcmp(&pLog->hdr, aHdr, sizeof(LogSummaryHdr)) ){
-          *pChanged = 1;
-          memcpy(&pLog->hdr, aHdr, LOGSUMMARY_HDR_NFIELD*sizeof(u32));
-        }
-      }
-      logLeaveMutex(pLog);
-    }
-
+    rc = logSummaryReadHdr(pLog, pChanged);
     if( rc!=SQLITE_OK ){
       /* An error occured while attempting log recovery. */
       sqlite3LogCloseSnapshot(pLog);
@@ -1409,8 +1456,13 @@ int sqlite3LogFrames(
 }
 
 /* 
-** Checkpoint the database. When this function is called the caller
-** must hold an exclusive lock on the database file.
+** Checkpoint the database:
+**
+**   1. Wait for an EXCLUSIVE lock on regions B and C.
+**   2. Wait for an EXCLUSIVE lock on region A.
+**   3. Copy the contents of the log into the database file.
+**   4. Zero the log-summary header (so new readers will ignore the log).
+**   5. Drop the locks obtained in steps 1 and 2.
 */
 int sqlite3LogCheckpoint(
   Log *pLog,                      /* Log connection */
@@ -1419,20 +1471,30 @@ int sqlite3LogCheckpoint(
   int (*xBusyHandler)(void *),    /* Pointer to busy-handler function */
   void *pBusyHandlerArg           /* Argument to pass to xBusyHandler */
 ){
-  int rc;
+  int rc;                         /* Return code */
 
+  /* Wait for a write-lock on regions B and C. */
   do {
     rc = logLockRegion(pLog, LOG_REGION_B|LOG_REGION_C, LOG_WRLOCK);
   }while( rc==SQLITE_BUSY && xBusyHandler(pBusyHandlerArg) );
   if( rc!=SQLITE_OK ) return rc;
 
+  /* Wait for a write-lock on region A. */
   do {
     rc = logLockRegion(pLog, LOG_REGION_A, LOG_WRLOCK);
   }while( rc==SQLITE_BUSY && xBusyHandler(pBusyHandlerArg) );
-  if( rc!=SQLITE_OK ) return rc;
-  
-  rc = logCheckpoint(pLog, pFd, zBuf);
+  if( rc!=SQLITE_OK ){
+    logLockRegion(pLog, LOG_REGION_B|LOG_REGION_C, LOG_UNLOCK);
+    return rc;
+  }
+
+  /* Copy data from the log to the database file. */
+  rc = logSummaryReadHdr(pLog, 0);
+  if( rc==SQLITE_OK ){
+    rc = logCheckpoint(pLog, pFd, zBuf);
+  }
 
+  /* Release the locks. */
   logLockRegion(pLog, LOG_REGION_A|LOG_REGION_B|LOG_REGION_C, LOG_UNLOCK);
   return rc;
 }
index 95310e336c5cdb82d855b22f755bb81919e713b9..137ff510d98e9c9d0d9a72d033c9261a0a20f4bd 100644 (file)
@@ -1386,7 +1386,6 @@ void sqlite3Pragma(
 
   if( sqlite3StrICmp(zLeft, "checkpoint")==0 ){
     sqlite3VdbeUsesBtree(v, iDb);
-    sqlite3VdbeAddOp2(v, OP_Transaction, iDb, 1);
     sqlite3VdbeAddOp3(v, OP_Checkpoint, iDb, 0, 0);
   }else
 
index b12782b66326edc44d9fe815e70066a7c81f40f3..cafda909a4bd408358e5275279f0a9163f7d1d08 100644 (file)
@@ -27,7 +27,7 @@ proc range {args} {
 }
 
 proc reopen_db {} {
-  db close
+  catch { db close }
   file delete -force test.db test.db-wal
   sqlite3_wal db test.db
   #register_logtest
@@ -223,7 +223,6 @@ do_test wal-7.1 {
     CREATE TABLE t1(a, b);
     INSERT INTO t1 VALUES(1, 2);
   }
-
   list [file size test.db] [file size test.db-wal]
 } [list 0 [expr (1024+20)*3]]
 do_test wal-7.2 {
@@ -231,272 +230,17 @@ do_test wal-7.2 {
   list [file size test.db] [file size test.db-wal]
 } [list 2048 [expr (1024+20)*3]]
 
-# db close
-# sqlite3_wal db test.db
-# register_logsummary_module db
-# # Warm-body tests of the virtual tables used for testing.
-# # 
-# do_test wal-8.1 {
-#   execsql { CREATE VIRTUAL TABLE temp.logsummary USING logsummary }
-#   execsql { CREATE VIRTUAL TABLE temp.logcontent USING logcontent }
-#   execsql { CREATE VIRTUAL TABLE temp.loglock USING loglock }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 0 0 0 0 0 0]
-# 
-# do_test wal-8.2 {
-#   sqlite3_wal db2 test.db
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 2 0 0 0 0 0 0]
-# do_test wal-8.3 {
-#   db2 close
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 0 0 0 0 0 0]
-# do_test wal-8.4 {
-#   execsql { INSERT INTO t1 VALUES(3, 4) }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 2 2 3 0 0]
-# do_test wal-8.5 {
-#   execsql { PRAGMA checkpoint }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 0 0 0 0 0]
-# 
-# do_test wal-8.6 {
-#   execsql { INSERT INTO t1 VALUES(5, 6) }
-#   execsql { PRAGMA checkpoint('1 1 1') }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 2 0 3 0 0]
-# do_test wal-8.7 {
-#   execsql { SELECT logpage, dbpage FROM logcontent }
-# } {}
-# do_test wal-8.8 {
-#   execsql { INSERT INTO t1 VALUES(7, 8) }
-#   execsql { SELECT logpage, dbpage FROM logcontent }
-# } {4 T:4 5 2}
-# do_test wal-8.9 {
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 2 4 5 0 0]
-# do_test wal-8.10 {
-#   execsql { SELECT * FROM loglock }
-# } [list [file join [pwd] test.db] 0 0 0]
-# do_test wal-8.11 {
-#   execsql { BEGIN; SELECT * FROM t1; }
-#   execsql { SELECT * FROM loglock }
-# } [list [file join [pwd] test.db] 0 0 4]
-# 
-# # Try making the log wrap around.
-# #
-# reopen_db
-# 
-# do_test wal-9.1 {
-#   execsql {
-#     BEGIN;
-#     CREATE TABLE t1(a PRIMARY KEY, b);
-#   }
-#   for {set i 0} {$i < 100} {incr i} {
-#     execsql { INSERT INTO t1 VALUES($i, randomblob(100)) }
-#   }
-#   execsql COMMIT
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 2 2 17 0 0]
-# do_test wal-9.2 {
-#   execsql { SELECT logpage, dbpage FROM logcontent }
-# } {2 T:2 3 1 4 2 5 3 6 4 7 5 8 6 9 7 10 8 11 9 12 10 13 11 14 12 15 13 16 14 17 15}
-# do_test wal-9.3 {
-#   execsql { PRAGMA checkpoint('12, 12') }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 2 15 17 0 0]
-# do_test wal-9.4 {
-#   execsql { SELECT logpage, dbpage FROM logcontent }
-# } {15 13 16 14 17 15}
-# do_test wal-9.5 {
-#   execsql { SELECT count(*) FROM t1 }
-# } {100}
-# do_test wal-9.6 {
-#   execsql { INSERT INTO t1 VALUES(100, randomblob(100)) }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 2 15 20 0 0]
-# 
-# do_test wal-9.7 {
-#   execsql { SELECT count(*) FROM t1 }
-# } {101}
-# do_test wal-9.8 {
-#   db close
-#   sqlite3_wal db test.db
-#   register_logtest
-#   execsql { SELECT count(*) FROM t1 }
-# } {101}
-# do_test wal-9.9 {
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 0 0 0 0 0 0]
-# 
-# reopen_db
-# do_test wal-10.1 {
-#   execsql {
-#     PRAGMA page_size = 1024;
-#     CREATE TABLE t1(x PRIMARY KEY);
-#     INSERT INTO t1 VALUES(randomblob(900));
-#     INSERT INTO t1 VALUES(randomblob(900));
-#     INSERT INTO t1 SELECT randomblob(900) FROM t1;         -- 4
-#     INSERT INTO t1 SELECT randomblob(900) FROM t1;         -- 8
-#     INSERT INTO t1 SELECT randomblob(900) FROM t1;         -- 16
-#   }
-#   list [file size test.db] [file size test.db-wal]
-# } {0 55296}
-# do_test wal-10.2 {
-#   execsql { PRAGMA checkpoint('20 30') }
-# } {}
-# do_test wal-10.3 {
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 34 38 54 0 0]
-# do_test wal-10.4 {
-#   execsql { SELECT dbpage FROM logcontent }
-# } {21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37}
-# do_test wal-10.5 {
-#   execsql { INSERT INTO t1 VALUES(randomblob(900)) }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 34 38 6 34 54]
-# do_test wal-10.6 {
-#   execsql { SELECT count(*) FROM t1 WHERE x NOT NULL }
-# } {17}
-# do_test wal-10.8 {
-#   execsql { SELECT logpage FROM logcontent }
-# } [range 38 54  1 6]
-# do_test wal-10.9 {
-#   execsql { INSERT INTO t1 SELECT randomblob(900) FROM t1 }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 34 38 68 34 54]
-# 
-# do_test wal-10.10 {
-#   execsql { SELECT logpage FROM logcontent }
-# } [range 38 54  1 33  55 68]
-# 
-# do_test wal-10.11 {
-#   execsql { SELECT count(*) FROM t1 WHERE x NOT NULL }
-# } {34}
-# 
-# do_test wal-10.12 {
-#   execsql { PRAGMA checkpoint('35 35') }
-# } {}
-# do_test wal-10.13 {
-#   execsql { SELECT logpage FROM logcontent }
-# } [range 22 68]
-# do_test wal-10.13a {
-#   execsql { SELECT dbpage FROM logcontent }
-# } [list \
-#   50 51 52 53 54 55 56 57 58 59 60 61    \
-#   0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \
-#   62 63 64 65 66 67 68 69 70 71 72 73 74 75 \
-# ]
-# 
-# do_test wal-10.14 {
-#   execsql { SELECT count(*) FROM t1 WHERE x NOT NULL }
-# } {34}
-# do_test wal-10.15 {
-#   execsql { PRAGMA integrity_check }
-# } {ok}
-# do_test wal-10.16 {
-#   execsql { PRAGMA checkpoint('20 20') }
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 7 63 68 0 0]
-# do_test wal-10.17 {
-#   execsql { SELECT logpage FROM logcontent }
-# } [range 63 68]
-# do_test wal-10.17a {
-#   execsql { SELECT dbpage FROM logcontent }
-# } {70 71 72 73 74 75}
-# 
-# do_test wal-10.18 {
-#   execsql { INSERT INTO t1 SELECT randomblob(900) FROM t1 }
-#   execsql { SELECT logpage FROM logcontent }
-# } [range 63 147]
-# integrity_check wal-10.19
-# 
-# do_test wal-10.20 {
-#   execsql { PRAGMA checkpoint('52 52') }
-#   execsql { SELECT logpage FROM logcontent }
-# } [range 116 147]
-# do_test wal-10.20a {
-#   execsql { SELECT * FROM logsummary }
-# } [list [file join [pwd] test.db] 1 1024 69 116 147 0 0]
-# integrity_check wal-10.20.integrity
-# 
-# do_test wal-10.21 {
-#   execsql { INSERT INTO t1 VALUES( randomblob(900) ) }
-#   execsql { SELECT logpage FROM logcontent }
-# } [range 116 152]
-# do_test wal-10.22 {
-#   execsql { PRAGMA integrity_check }
-# } {ok}
-# 
-# file delete -force testX.db testX.db-wal
-# file copy test.db testX.db
-# file copy test.db-wal testX.db-wal
-# do_test wal-10.23 {
-#   sqlite3_wal db2 testX.db
-#   register_logtest db2
-#   execsql { SELECT logpage FROM logcontent WHERE db LIKE '%testX%' } db2
-# } [range 34 54  1 33  55 152]
-# 
-# do_test wal-10.24 {
-#   execsql { PRAGMA integrity_check } db2
-# } {ok}
-# db2 close
-# 
-# do_test wal-11.1 {
-#   reopen_db
-#   sqlite3_wal db2 test.db
-# 
-#   execsql {
-#     BEGIN;
-#       CREATE TABLE t1(x);
-#       CREATE TABLE t2(x PRIMARY KEY);
-#       INSERT INTO t1 VALUES(randomblob(900));
-#       INSERT INTO t1 VALUES(randomblob(900));
-#       INSERT INTO t1 SELECT randomblob(900) FROM t1;       /*  4 */
-#       INSERT INTO t1 SELECT randomblob(900) FROM t1;       /*  8 */
-#       INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 16 */
-#       INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 32 */
-#       INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 64 */
-# 
-#       INSERT INTO t2 VALUES('x');
-#       INSERT INTO t2 VALUES('y');
-#       INSERT INTO t2 VALUES('z');
-#     COMMIT;
-#     SELECT * FROM logsummary;
-#   }
-# } [list [file join [pwd] test.db] 2 1024 2 2 70 0 0]
-# 
-# do_test wal-11.2 {
-#   execsql {
-#     BEGIN; SELECT x FROM t2;
-#   } db2
-# } {x y z}
-# do_test wal-11.2 {
-#   execsql {
-#     INSERT INTO t1 VALUES(randomblob(900));
-#     PRAGMA checkpoint('10 100');
-#     INSERT INTO t1 VALUES(randomblob(900));
-#     INSERT INTO t2 VALUES('0');
-#     SELECT * FROM logsummary;
-#   }
-# } [list [file join [pwd] test.db] 2 1024 71 71 7 71 73]
-# do_test wal-12.3 {
-#   execsql { PRAGMA integrity_check } db2
-# } {ok}
-# db2 close
-
-
 # Execute some transactions in auto-vacuum mode to test database file
 # truncation.
 #
-do_test wal-12.1 {
+do_test wal-8.1 {
   reopen_db
   execsql {
     PRAGMA auto_vacuum = 1;
     PRAGMA auto_vacuum;
   }
 } {1}
-do_test wal-12.2 {
+do_test wal-8.2 {
   execsql {
     PRAGMA page_size = 1024;
     CREATE TABLE t1(x);
@@ -511,7 +255,7 @@ do_test wal-12.2 {
   }
   file size test.db
 } [expr 68*1024]
-do_test wal-12.3 {
+do_test wal-8.3 {
   execsql { 
     DELETE FROM t1 WHERE rowid<54;
     PRAGMA checkpoint;
@@ -522,7 +266,7 @@ do_test wal-12.3 {
 # Run some "warm-body" tests to ensure that log-summary files with more
 # than 256 entries (log summaries that contain index blocks) work Ok.
 #
-do_test wal-13.1 {
+do_test wal-9.1 {
   reopen_db
   execsql {
     PRAGMA page_size = 1024;
@@ -539,12 +283,12 @@ do_test wal-13.1 {
   }
   file size test.db
 } 0
-do_test wal-13.2 {
+do_test wal-9.2 {
   sqlite3_wal db2 test.db
   execsql {PRAGMA integrity_check } db2
 } {ok}
 
-do_test wal-13.3 {
+do_test wal-9.3 {
   file delete -force test2.db test2.db-wal
   file copy test.db test2.db
   file copy test.db-wal test2.db-wal
@@ -553,8 +297,7 @@ do_test wal-13.3 {
 } {ok}
 db3 close
 
-do_test wal-13.4 {
-breakpoint
+do_test wal-9.4 {
   execsql { PRAGMA checkpoint }
   db2 close
   sqlite3_wal db2 test.db
@@ -564,5 +307,102 @@ breakpoint
 foreach handle {db db2 db3} { catch { $handle close } }
 unset handle
 
+#-------------------------------------------------------------------------
+# The following block of tests - wal-10.* - test that the WAL locking 
+# scheme works for clients in a single process.
+#
+reopen_db
+sqlite3_wal db2 test.db
+sqlite3_wal db3 test.db
+
+do_test wal-10.1 {
+  execsql {
+    CREATE TABLE t1(a, b);
+    INSERT INTO t1 VALUES(1, 2);
+    BEGIN;
+      INSERT INTO t1 VALUES(3, 4);
+  }
+  execsql "SELECT * FROM t1" db2
+} {1 2}
+do_test wal-10.2 {
+  execsql { COMMIT }
+  execsql "SELECT * FROM t1" db2
+} {1 2 3 4}
+do_test wal-10.3 {
+  execsql { 
+    BEGIN;
+      SELECT * FROM t1;
+  } db2
+} {1 2 3 4}
+do_test wal-10.4 {
+  catchsql { PRAGMA checkpoint } 
+} {1 {database is locked}}
+do_test wal-10.5 {
+  execsql { INSERT INTO t1 VALUES(5, 6) }
+  execsql { SELECT * FROM t1 } db2
+} {1 2 3 4}
+
+# Connection [db2] is holding a lock on a snapshot, preventing [db] from
+# checkpointing the database. Add a busy-handler to [db]. If [db2] completes
+# its transaction from within the busy-handler, [db] is able to complete
+# the checkpoint operation.
+#
+proc busyhandler x {
+  if {$x==4} {
+    execsql { COMMIT } db2
+  }
+  if {$x<5} {return 0}
+  return 1
+}
+db busy busyhandler
+do_test wal-10.6 {
+  execsql { PRAGMA checkpoint } 
+} {}
+
+# Similar to the test above. Except this time, a new read transaction is
+# started (db3) while the checkpointer is waiting for an old one to finish.
+# The checkpointer can finish, but any subsequent write operations must
+# wait until after db3 has closed the read transaction.
+#
+db busy {}
+do_test wal-10.7 {
+  execsql { 
+    BEGIN;
+      SELECT * FROM t1;
+  } db2
+} {1 2 3 4 5 6}
+do_test wal-10.8 {
+  execsql { INSERT INTO t1 VALUES(7, 8) }
+  catchsql { PRAGMA checkpoint } 
+} {1 {database is locked}}
+proc busyhandler x {
+  if {$x==3} { execsql { BEGIN; SELECT * FROM t1 } db3 }
+  if {$x==4} { execsql { COMMIT } db2 }
+  if {$x<5}  { return 0 }
+  return 1
+}
+db busy busyhandler
+do_test wal-10.9 {
+  execsql { PRAGMA checkpoint } 
+} {}
+do_test wal-10.10 {
+  execsql { SELECT * FROM t1 } db3
+} {1 2 3 4 5 6 7 8}
+do_test wal-10.11 {
+  catchsql { INSERT INTO t1 VALUES(9, 10) }
+} {1 {database is locked}}
+do_test wal-10.12 {
+  execsql { SELECT * FROM t1 }
+} {1 2 3 4 5 6 7 8}
+do_test wal-10.13 {
+  execsql { COMMIT } db3
+} {}
+do_test wal-10.14 {
+  execsql { INSERT INTO t1 VALUES(9, 10) }
+  execsql { SELECT * FROM t1 }
+} {1 2 3 4 5 6 7 8 9 10}
+
+foreach handle {db db2 db3} { catch { $handle close } }
+unset handle
 finish_test
 
index 7727ab375991102e4985e5649a0e72ddf3f551fd..fbd6c653cc9e2f158732e701856994d66d668266 100644 (file)
@@ -62,7 +62,7 @@ do_test walthread-1.4 {
 # Each of the N threads runs N read transactions followed by a single write
 # transaction in a loop as fast as possible.
 #
-# There is also a single checkpointer thread. It runs the following loop:
+# Ther is also a single checkpointer thread. It runs the following loop:
 #
 #   1) Execute "CHECKPOINT main 32 -1 1"
 #   2) Sleep for 500 ms.
@@ -85,7 +85,7 @@ set thread_program {
     set rc [sqlite3_finalize $stmt]
 
     if {$rc ne "SQLITE_OK"} {
-      error $rc 
+      error "$rc: [sqlite3_errmsg $DB]"
     }
     return $res
   }