Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -44,10 +44,20 @@ ** plus implementations of sqlite3_os_init() and sqlite3_os_end(). */ #include "sqliteInt.h" #if SQLITE_OS_UNIX /* This file is used on unix only */ +/* Turn this feature on in all builds for now */ +#define SQLITE_MUTEXFREE_SHMLOCK 1 +#define SQLITE_MFS_EXCLUSIVE 255 +#ifndef SQLITE_MFS_NSHARD +# define SQLITE_MFS_NSHARD 8 +#endif +#if SQLITE_MFS_NSHARD<1 +# error "SQLITE_MFS_NSHARD must be greater than 0" +#endif + /* ** There are various methods for file locking used for concurrency ** control: ** ** 1. POSIX locking (the default), @@ -4237,11 +4247,44 @@ #ifdef SQLITE_DEBUG u8 exclMask; /* Mask of exclusive locks held */ u8 sharedMask; /* Mask of shared locks held */ u8 nextShmId; /* Next available unixShm.id value */ #endif + +#ifdef SQLITE_MUTEXFREE_SHMLOCK + /* In unix-excl mode, if SQLITE_MUTEXFREE_SHMLOCK is defined, all locks + ** are stored in the following 64-bit value. There are in total 8 + ** shm-locking slots, each of which are assigned 8-bits from the 64-bit + ** value. The least-significant 8 bits correspond to shm-locking slot + ** 0, and so on. + ** + ** If the 8-bits corresponding to a shm-locking locking slot are set to + ** 0xFF, then a write-lock is held on the slot. Or, if they are set to + ** a non-zero value smaller than 0xFF, then they represent the total + ** number of read-locks held on the slot. There is no way to distinguish + ** between a write-lock and 255 read-locks. */ + struct LockingSlot { + u32 nLock; + u64 aPadding[7]; + } aMFSlot[3 + SQLITE_MFS_NSHARD*5]; +#endif }; + +/* +** Atomic CAS primitive used in multi-process mode. Equivalent to: +** +** int unixCompareAndSwap(u32 *ptr, u32 oldval, u32 newval){ +** if( *ptr==oldval ){ +** *ptr = newval; +** return 1; +** } +** return 0; +** } +*/ +#define unixCompareAndSwap(ptr,oldval,newval) \ + __sync_bool_compare_and_swap(ptr,oldval,newval) + /* ** Structure used internally by this VFS to record the state of an ** open shared memory connection. ** @@ -4259,10 +4302,13 @@ unixShm *pNext; /* Next unixShm with the same unixShmNode */ u8 hasMutex; /* True if holding the unixShmNode->pShmMutex */ u8 id; /* Id of this connection within its unixShmNode */ u16 sharedMask; /* Mask of shared locks held */ u16 exclMask; /* Mask of exclusive locks held */ +#ifdef SQLITE_MUTEXFREE_SHMLOCK + u8 aMFCurrent[8]; /* Current slot used for each shared lock */ +#endif }; /* ** Constants used for locking */ @@ -4763,10 +4809,83 @@ if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; sqlite3_mutex_leave(pShmNode->pShmMutex); return rc; } +#ifdef SQLITE_MUTEXFREE_SHMLOCK +static int unixMutexFreeShmlock( + unixFile *pFd, /* Database file holding the shared memory */ + int ofst, /* First lock to acquire or release */ + int n, /* Number of locks to acquire or release */ + int flags /* What to do with the lock */ +){ + struct LockMapEntry { + int iFirst; + int nSlot; + } aMap[9] = { + { 0, 1 }, + { 1, 1 }, + { 2, 1 }, + { 3+0*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD }, + { 3+1*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD }, + { 3+2*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD }, + { 3+3*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD }, + { 3+4*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD }, + { 3+5*SQLITE_MFS_NSHARD, 0 }, + }; + + unixShm *p = pFd->pShm; /* The shared memory being locked */ + unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */ + + if( flags & SQLITE_SHM_SHARED ){ + /* SHARED locks */ + u32 iOld, iNew, *ptr; + int iIncr = -1; + if( (flags & SQLITE_SHM_UNLOCK)==0 ){ + p->aMFCurrent[ofst] = (p->aMFCurrent[ofst] + 1) % aMap[ofst].nSlot; + iIncr = 1; + } + ptr = &pShmNode->aMFSlot[aMap[ofst].iFirst + p->aMFCurrent[ofst]].nLock; + do { + iOld = *ptr; + iNew = iOld + iIncr; + if( iNew>SQLITE_MFS_EXCLUSIVE ){ + return SQLITE_BUSY; + } + }while( 0==unixCompareAndSwap(ptr, iOld, iNew) ); + }else{ + /* EXCLUSIVE locks */ + int iFirst = aMap[ofst].iFirst; + int iLast = aMap[ofst+n].iFirst; + int i; + for(i=iFirst; iaMFSlot[i].nLock; + if( flags & SQLITE_SHM_UNLOCK ){ + assert( (*ptr)==SQLITE_MFS_EXCLUSIVE ); + *ptr = 0; + }else{ + u32 iOld; + do { + iOld = *ptr; + if( iOld>0 ){ + while( i>iFirst ){ + i--; + pShmNode->aMFSlot[i].nLock = 0; + } + return SQLITE_BUSY; + } + }while( 0==unixCompareAndSwap(ptr, iOld, SQLITE_MFS_EXCLUSIVE) ); + } + } + } + + return SQLITE_OK; +} +#else +# define unixMutexFreeShmlock(a,b,c,d) SQLITE_OK +#endif + /* ** Change the lock state for a shared-memory segment. ** ** Note that the relationship between SHAREd and EXCLUSIVE locks is a little ** different here than in posix. In xShmLock(), one can go from unlocked @@ -4795,13 +4914,25 @@ || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); + + if( pDbFd->pInode->bProcessLock ){ + return unixMutexFreeShmlock(pDbFd, ofst, n, flags); + } mask = (1<<(ofst+n)) - (1<1 || mask==(1<sharedMask&mask)==0 ); + assert( !(flags&SQLITE_SHM_EXCLUSIVE) || !(p->exclMask&mask) ); + }else{ + assert( !(flags&SQLITE_SHM_SHARED) || (p->sharedMask&mask)==mask ); + assert( !(flags&SQLITE_SHM_EXCLUSIVE) || (p->exclMask&mask)==mask ); + } + sqlite3_mutex_enter(pShmNode->pShmMutex); if( flags & SQLITE_SHM_UNLOCK ){ u16 allMask = 0; /* Mask of locks held by siblings */ /* See if any siblings hold this same lock */ @@ -4887,16 +5018,20 @@ */ static void unixShmBarrier( sqlite3_file *fd /* Database file holding the shared memory */ ){ UNUSED_PARAMETER(fd); +#ifdef SQLITE_MUTEXFREE_SHMLOCK + __sync_synchronize(); +#else sqlite3MemoryBarrier(); /* compiler-defined memory barrier */ assert( fd->pMethods->xLock==nolockLock || unixFileMutexNotheld((unixFile*)fd) ); unixEnterMutex(); /* Also mutex, for redundancy */ unixLeaveMutex(); +#endif } /* ** Close a connection to shared-memory. Delete the underlying ** storage if deleteFlag is true. Index: src/test_superlock.c ================================================================== --- src/test_superlock.c +++ src/test_superlock.c @@ -39,10 +39,12 @@ ** actually a pointer to an instance of this structure. */ struct Superlock { sqlite3 *db; /* Database handle used to lock db */ int bWal; /* True if db is a WAL database */ + int bRecoveryLocked; /* True if WAL RECOVERY lock is held */ + int bReaderLocked; /* True if WAL READER locks are held */ }; typedef struct Superlock Superlock; /* ** The pCtx pointer passed to this function is actually a pointer to a @@ -105,26 +107,29 @@ /* ** Obtain the extra locks on the database file required for WAL databases. ** Invoke the supplied busy-handler as required. */ static int superlockWalLock( - sqlite3 *db, /* Database handle open on WAL database */ + Superlock *pLock, /* Superlock handle */ SuperlockBusy *pBusy /* Busy handler wrapper object */ ){ int rc; /* Return code */ sqlite3_file *fd = 0; /* Main database file handle */ void volatile *p = 0; /* Pointer to first page of shared memory */ + sqlite3 *db = pLock->db; /* Obtain a pointer to the sqlite3_file object open on the main db file. */ rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd); if( rc!=SQLITE_OK ) return rc; /* Obtain the "recovery" lock. Normally, this lock is only obtained by ** clients running database recovery. */ + assert( pLock->bRecoveryLocked==0 ); rc = superlockShmLock(fd, 2, 1, pBusy); if( rc!=SQLITE_OK ) return rc; + pLock->bRecoveryLocked = 1; /* Zero the start of the first shared-memory page. This means that any ** clients that open read or write transactions from this point on will ** have to run recovery before proceeding. Since they need the "recovery" ** lock that this process is holding to do that, no new read or write @@ -137,11 +142,13 @@ /* Obtain exclusive locks on all the "read-lock" slots. Once these locks ** are held, it is guaranteed that there are no active reader, writer or ** checkpointer clients. */ + assert( pLock->bReaderLocked==0 ); rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy); + if( rc==SQLITE_OK ) pLock->bReaderLocked = 1; return rc; } /* ** Release a superlock held on a database file. The argument passed to @@ -154,12 +161,18 @@ int rc; /* Return code */ int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE; sqlite3_file *fd = 0; rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd); if( rc==SQLITE_OK ){ - fd->pMethods->xShmLock(fd, 2, 1, flags); - fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags); + if( p->bRecoveryLocked ){ + fd->pMethods->xShmLock(fd, 2, 1, flags); + p->bRecoveryLocked = 0; + } + if( p->bReaderLocked ){ + fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags); + p->bReaderLocked = 0; + } } } sqlite3_close(p->db); sqlite3_free(p); } @@ -230,11 +243,11 @@ */ if( rc==SQLITE_OK ){ if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){ rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0); if( rc==SQLITE_OK ){ - rc = superlockWalLock(pLock->db, &busy); + rc = superlockWalLock(pLock, &busy); } } } if( rc!=SQLITE_OK ){ Index: src/test_vfs.c ================================================================== --- src/test_vfs.c +++ src/test_vfs.c @@ -1561,12 +1561,66 @@ bad_args: Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-fullshm BOOL? ?-default BOOL? ?-mxpathname INT? ?-szosfile INT? ?-iversion INT?"); return TCL_ERROR; } + +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); +extern const char *sqlite3ErrName(int); + +/* +** tclcmd: vfs_shmlock DB DBNAME (shared|exclusive) (lock|unlock) OFFSET N +*/ +static int SQLITE_TCLAPI test_vfs_shmlock( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + const char *azArg1[] = {"shared", "exclusive", 0}; + const char *azArg2[] = {"lock", "unlock", 0}; + sqlite3 *db = 0; + int rc = SQLITE_OK; + const char *zDbname = 0; + int iArg1 = 0; + int iArg2 = 0; + int iOffset = 0; + int n = 0; + sqlite3_file *pFd; + + if( objc!=7 ){ + Tcl_WrongNumArgs(interp, 1, objv, + "DB DBNAME (shared|exclusive) (lock|unlock) OFFSET N" + ); + return TCL_ERROR; + } + + zDbname = Tcl_GetString(objv[2]); + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) + || Tcl_GetIndexFromObj(interp, objv[3], azArg1, "ARG", 0, &iArg1) + || Tcl_GetIndexFromObj(interp, objv[4], azArg2, "ARG", 0, &iArg2) + || Tcl_GetIntFromObj(interp, objv[5], &iOffset) + || Tcl_GetIntFromObj(interp, objv[6], &n) + ){ + return TCL_ERROR; + } + + sqlite3_file_control(db, zDbname, SQLITE_FCNTL_FILE_POINTER, (void*)&pFd); + if( pFd==0 ){ + return TCL_ERROR; + } + rc = pFd->pMethods->xShmLock(pFd, iOffset, n, + (iArg1==0 ? SQLITE_SHM_SHARED : SQLITE_SHM_EXCLUSIVE) + | (iArg2==0 ? SQLITE_SHM_LOCK : SQLITE_SHM_UNLOCK) + ); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + return TCL_OK; +} + int Sqlitetestvfs_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0); + Tcl_CreateObjCommand(interp, "vfs_shmlock", test_vfs_shmlock, 0, 0); return TCL_OK; } #endif Index: src/wal.c ================================================================== --- src/wal.c +++ src/wal.c @@ -2775,23 +2775,18 @@ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 ); assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame ); - /* It is possible that there is a checkpointer thread running - ** concurrent with this code. If this is the case, it may be that the - ** checkpointer has already determined that it will checkpoint + /* If it were possible for a checkpointer thread to run concurrent + ** with this code, it would be a problem. In this case, it could be + ** that the checkpointer has already determined that it will checkpoint ** snapshot X, where X is later in the wal file than pSnapshot, but ** has not yet set the pInfo->nBackfillAttempted variable to indicate - ** its intent. To avoid the race condition this leads to, ensure that - ** there is no checkpointer process by taking a shared CKPT lock - ** before checking pInfo->nBackfillAttempted. - ** - ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing - ** this already? - */ - rc = walLockShared(pWal, WAL_CKPT_LOCK); + ** its intent. Fortunately this is not possible, as the call to + ** sqlite3WalSnapshotOpen() that sets pWal->pSnapshot also takes a + ** SHARED lock on the checkpointer slot. */ if( rc==SQLITE_OK ){ /* Check that the wal file has not been wrapped. Assuming that it has ** not, also check that no checkpointer has attempted to checkpoint any ** frames beyond pSnapshot->mxFrame. If either of these conditions are @@ -2806,12 +2801,10 @@ *pChanged = bChanged; }else{ rc = SQLITE_ERROR_SNAPSHOT; } - /* Release the shared CKPT lock obtained above. */ - walUnlockShared(pWal, WAL_CKPT_LOCK); pWal->minFrame = 1; } if( rc!=SQLITE_OK ){ Index: test/lock_common.tcl ================================================================== --- test/lock_common.tcl +++ test/lock_common.tcl @@ -13,22 +13,24 @@ # processes) to test locking. # proc do_multiclient_test {varname script} { - foreach code [list { + foreach {tn code} [list 1 { if {[info exists ::G(valgrind)]} { db close ; continue } set ::code2_chan [launch_testfixture] set ::code3_chan [launch_testfixture] proc code2 {tcl} { testfixture $::code2_chan $tcl } proc code3 {tcl} { testfixture $::code3_chan $tcl } - set tn 1 - } { + } 2 { proc code2 {tcl} { uplevel #0 $tcl } proc code3 {tcl} { uplevel #0 $tcl } - set tn 2 }] { + # Do not run multi-process tests with the unix-excl VFS. + # + if {$tn==1 && [permutation]=="unix-excl"} continue + faultsim_delete_and_reopen proc code1 {tcl} { uplevel #0 $tcl } # Open connections [db2] and [db3]. Depending on which iteration this Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -609,10 +609,20 @@ set ::G(perm:sqlite3_args) [list -vfs fs] } -files { conflict.test insert.test insert2.test insert3.test rollback.test select1.test select2.test select3.test } + +# Run some tests using the "unix-excl" VFS. +# +test_suite "unix-excl" -description { + Run some tests using the "unix-excl" VFS +} -initialize { + set ::G(perm:sqlite3_args) [list -vfs unix-excl] +} -files { + shmlock.test +} # Run some tests using UTF-16 databases. # test_suite "utf16" -description { Run tests using UTF-16 databases ADDED test/shmlock.test Index: test/shmlock.test ================================================================== --- /dev/null +++ test/shmlock.test @@ -0,0 +1,173 @@ +# 2018 December 6 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix shmlock + +ifcapable !wal {finish_test ; return } + +sqlite3 db2 test.db +sqlite3 db3 test.db + +do_execsql_test 1.0 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); +} {wal} +do_test 1.1 { execsql { SELECT * FROM t1 } db2 } {1 2} +do_test 1.2 { execsql { SELECT * FROM t1 } db3 } {1 2} + +foreach {tn dbhandle cmd res} { + 1 db {shared lock 7 1} OK + 2 db2 {exclusive lock 7 1} BUSY + 3 db {shared unlock 7 1} OK + 4 db2 {exclusive lock 7 1} OK + 5 db {shared lock 7 1} BUSY + 6 db {exclusive lock 7 1} BUSY + 7 db2 {exclusive unlock 7 1} OK + + 8 db {exclusive lock 0 8} OK + 9 db {exclusive unlock 0 8} OK + 10 db2 {exclusive lock 0 8} OK + 11 db2 {exclusive unlock 0 8} OK + + 12 db {shared lock 0 1} OK + 13 db2 {shared lock 0 1} OK + 14 db3 {shared lock 0 1} OK + 15 db3 {shared unlock 0 1} OK + 16 db3 {exclusive lock 0 1} BUSY + 17 db2 {shared unlock 0 1} OK + 18 db3 {exclusive lock 0 1} BUSY + 19 db {shared unlock 0 1} OK + 20 db3 {exclusive lock 0 1} OK + 21 db3 {exclusive unlock 0 1} OK + + 22 db {shared lock 3 1} OK + 23 db2 {exclusive lock 2 2} BUSY + 24 db {shared lock 2 1} OK + 25 db2 {exclusive lock 0 5} BUSY + 26 db2 {exclusive lock 0 4} BUSY + 27 db2 {exclusive lock 0 3} BUSY + 28 db {shared unlock 3 1} OK + 29 db2 {exclusive lock 2 2} BUSY + 28 db {shared unlock 2 1} OK + 29 db2 {exclusive lock 2 2} OK + 29 db2 {exclusive unlock 2 2} OK +} { + do_test 1.3.$tn [list vfs_shmlock $dbhandle main {*}$cmd] "SQLITE_$res" +} + +db close +db2 close +db3 close + +if {[permutation]=="unix-excl"} { + do_test 2.0 { + for {set i 0} {$i < 256} {incr i} { + sqlite3 db$i test.db + execsql { SELECT * FROM t1 } db$i + } + for {set i 0} {$i < 255} {incr i} { + set rc [vfs_shmlock db$i main shared lock 4 1] + if {$rc != "SQLITE_OK"} { error $rc } + } + + vfs_shmlock db255 main shared lock 4 1 + } {SQLITE_BUSY} + + do_test 2.1 { vfs_shmlock db255 main exclusive lock 4 1 } SQLITE_BUSY + do_test 2.2 { vfs_shmlock db0 main shared unlock 4 1 } SQLITE_OK + do_test 2.3 { vfs_shmlock db255 main shared lock 4 1 } SQLITE_OK + do_test 2.4 { vfs_shmlock db255 main shared unlock 4 1 } SQLITE_OK + do_test 2.5 { vfs_shmlock db255 main exclusive lock 4 1 } SQLITE_BUSY + + do_test 2.6 { + for {set i 1} {$i < 255} {incr i} { + set rc [vfs_shmlock db255 main exclusive lock 4 1] + if {$rc != "SQLITE_BUSY"} { error $rc } + set rc [vfs_shmlock db$i main shared unlock 4 1] + if {$rc != "SQLITE_OK"} { error $rc } + } + + vfs_shmlock db255 main exclusive lock 4 1 + } {SQLITE_OK} + + vfs_shmlock db255 main exclusive unlock 4 1 + + for {set i 0} {$i < 256} {incr i} { + db$i close + } +} + +sqlite3 db0 test.db +sqlite3 db1 test.db +do_test 3.1 { execsql { SELECT * FROM t1 } db0 } {1 2} +do_test 3.2 { execsql { SELECT * FROM t1 } db1 } {1 2} + +set L(0) {n n n n n n n n} +set L(1) {n n n n n n n n} +proc random_lock_test {idx} { + global L + set iSlot [expr int(rand()*8)] + if {[expr int(rand()*2)]} { + # Unlock operation + if {[lindex $L($idx) $iSlot]!="n"} { + vfs_shmlock db$idx main [lindex $L($idx) $iSlot] unlock $iSlot 1 + lset L($idx) $iSlot n + } + } else { + # Lock operation + if {[lindex $L($idx) $iSlot]=="n"} { + set locktype [lindex {e s} [expr int(rand()*2)]] + set n 1 + if {$locktype=="e"} { + for {set l $iSlot} {$l<8 && [lindex $L($idx) $l]=="n"} {incr l} {} + set n [expr int(rand()*($l-$iSlot))+1] + # puts "iSlot=$iSlot l=$l L=$L($idx)" + # puts "$iSlot $n" + } + set res [vfs_shmlock db$idx main $locktype lock $iSlot $n] + + set bBusy 0 + for {set i $iSlot} {$i<($iSlot+$n)} {incr i} { + set other [lindex $L([expr ($idx+1)%2]) $i] + if {($other!="n" && $locktype=="e")||($other=="e" && $locktype=="s")} { + if {$res != "SQLITE_BUSY"} { error "BUSY not detected" } + set bBusy 1 + break + } + } + + if {$bBusy==0} { + if {$res != "SQLITE_OK"} { error "BUSY false-positive" } + for {set i $iSlot} {$i<($iSlot+$n)} {incr i} { + lset L($idx) $i $locktype + } + } + } + } +} + +set nStep 100000 +for {set i 0} {$i < $nStep} {incr i} { + random_lock_test 0 + random_lock_test 1 +} + +db0 close +db1 close + +finish_test + + Index: test/wal.test ================================================================== --- test/wal.test +++ test/wal.test @@ -1295,55 +1295,57 @@ # the data is present and the database is not corrupt. # # At one point, SQLite was failing to grow the mapping of the wal-index # file in step 3 and the checkpoint was corrupting the database file. # -do_test wal-20.1 { - catch {db close} - forcedelete test.db test.db-wal test.db-journal - sqlite3 db test.db - execsql { - PRAGMA journal_mode = WAL; - CREATE TABLE t1(x); - INSERT INTO t1 VALUES(randomblob(900)); - SELECT count(*) FROM t1; - } -} {wal 1} -do_test wal-20.2 { - set ::buddy [launch_testfixture] - testfixture $::buddy { - sqlite3 db test.db - db transaction { db eval { - PRAGMA wal_autocheckpoint = 0; - INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */ - 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 t1 SELECT randomblob(900) FROM t1; /* 128 */ - INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 256 */ - INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 512 */ - INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 1024 */ - INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2048 */ - INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4096 */ - INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ - INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ - } } - } -} {0} -do_test wal-20.3 { - close $::buddy - execsql { PRAGMA wal_checkpoint } - execsql { SELECT count(*) FROM t1 } -} {16384} -do_test wal-20.4 { - db close - sqlite3 db test.db - execsql { SELECT count(*) FROM t1 } -} {16384} -integrity_check wal-20.5 +if {[permutation]!="unix-excl"} { + do_test wal-20.1 { + catch {db close} + forcedelete test.db test.db-wal test.db-journal + sqlite3 db test.db + execsql { + PRAGMA journal_mode = WAL; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(randomblob(900)); + SELECT count(*) FROM t1; + } + } {wal 1} + do_test wal-20.2 { + set ::buddy [launch_testfixture] + testfixture $::buddy { + sqlite3 db test.db + db transaction { db eval { + PRAGMA wal_autocheckpoint = 0; + INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */ + 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 t1 SELECT randomblob(900) FROM t1; /* 128 */ + INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 256 */ + INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 512 */ + INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 1024 */ + INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2048 */ + INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4096 */ + INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ + INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ + } } + } + } {0} + do_test wal-20.3 { + close $::buddy + execsql { PRAGMA wal_checkpoint } + execsql { SELECT count(*) FROM t1 } + } {16384} + do_test wal-20.4 { + db close + sqlite3 db test.db + execsql { SELECT count(*) FROM t1 } + } {16384} + integrity_check wal-20.5 +} catch { db2 close } catch { db close } do_test wal-21.1 {