diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/attach.c | 7 | ||||
-rw-r--r-- | src/main.c | 14 | ||||
-rw-r--r-- | src/os_unix.c | 15 | ||||
-rw-r--r-- | src/os_win.c | 19 | ||||
-rw-r--r-- | src/sqlite.h.in | 21 | ||||
-rw-r--r-- | src/sqliteInt.h | 1 | ||||
-rw-r--r-- | src/test1.c | 23 |
7 files changed, 85 insertions, 15 deletions
diff --git a/src/attach.c b/src/attach.c index 9f23dce1e..3ade237b5 100644 --- a/src/attach.c +++ b/src/attach.c @@ -221,6 +221,13 @@ static void attachFunc( sqlite3BtreeEnterAll(db); db->init.iDb = 0; db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( db->setlkFlags & SQLITE_SETLK_BLOCK_ON_CONNECT ){ + int val = 1; + sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pNew->pBt)); + sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, &val); + } +#endif if( !REOPEN_AS_MEMDB(db) ){ rc = sqlite3Init(db, &zErrDyn); } diff --git a/src/main.c b/src/main.c index c1799455f..8cd0303c4 100644 --- a/src/main.c +++ b/src/main.c @@ -1830,13 +1830,25 @@ int sqlite3_busy_timeout(sqlite3 *db, int ms){ /* ** Set the setlk timeout value. */ -int sqlite3_setlk_timeout(sqlite3 *db, int ms){ +int sqlite3_setlk_timeout(sqlite3 *db, int ms, int flags){ + int iDb; + int bBOC = ((flags & SQLITE_SETLK_BLOCK_ON_CONNECT) ? 1 : 0); #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif if( ms<-1 ) return SQLITE_RANGE; #ifdef SQLITE_ENABLE_SETLK_TIMEOUT db->setlkTimeout = ms; + db->setlkFlags = flags; + sqlite3BtreeEnterAll(db); + for(iDb=0; iDb<db->nDb; iDb++){ + Btree *pBt = db->aDb[iDb].pBt; + if( pBt ){ + sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt)); + sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC); + } + } + sqlite3BtreeLeaveAll(db); #endif return SQLITE_OK; } diff --git a/src/os_unix.c b/src/os_unix.c index 58e495210..7f92c5234 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -284,6 +284,7 @@ struct unixFile { #endif #ifdef SQLITE_ENABLE_SETLK_TIMEOUT unsigned iBusyTimeout; /* Wait this many millisec on locks */ + int bBlockOnConnect; /* True to block for SHARED locks */ #endif #if OS_VXWORKS struct vxworksFileId *pId; /* Unique file ID */ @@ -1677,6 +1678,13 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ rc = 0; } }else{ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( pFile->bBlockOnConnect && pLock->l_type==F_RDLCK + && pLock->l_start==SHARED_FIRST && pLock->l_len==SHARED_SIZE + ){ + rc = osFcntl(pFile->h, F_SETLKW, pLock); + }else +#endif rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); } return rc; @@ -4049,7 +4057,12 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ *(int*)pArg = iOld; return SQLITE_OK; } -#endif + case SQLITE_FCNTL_BLOCK_ON_CONNECT: { + int iNew = *(int*)pArg; + pFile->bBlockOnConnect = iNew; + return SQLITE_OK; + } +#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; diff --git a/src/os_win.c b/src/os_win.c index 751ba2148..f7a295aa9 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -289,6 +289,7 @@ struct winFile { #endif #ifdef SQLITE_ENABLE_SETLK_TIMEOUT DWORD iBusyTimeout; /* Wait this many millisec on locks */ + int bBlockOnConnect; #endif }; @@ -3356,8 +3357,9 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ ** Different API routines are called depending on whether or not this ** is Win9x or WinNT. */ -static int winGetReadLock(winFile *pFile){ +static int winGetReadLock(winFile *pFile, int bBlock){ int res; + DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0); OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); if( osIsNT() ){ #if SQLITE_OS_WINCE @@ -3367,7 +3369,7 @@ static int winGetReadLock(winFile *pFile){ */ res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); #else - res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, + res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0, SHARED_SIZE, 0); #endif } @@ -3376,7 +3378,7 @@ static int winGetReadLock(winFile *pFile){ int lk; sqlite3_randomness(sizeof(lk), &lk); pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS&mask, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); } #endif @@ -3510,7 +3512,7 @@ static int winLock(sqlite3_file *id, int locktype){ */ if( locktype==SHARED_LOCK && res ){ assert( pFile->locktype==NO_LOCK ); - res = winGetReadLock(pFile); + res = winGetReadLock(pFile, pFile->bBlockOnConnect); if( res ){ newLocktype = SHARED_LOCK; }else{ @@ -3548,7 +3550,7 @@ static int winLock(sqlite3_file *id, int locktype){ newLocktype = EXCLUSIVE_LOCK; }else{ lastErrno = osGetLastError(); - winGetReadLock(pFile); + winGetReadLock(pFile, 0); } } @@ -3853,7 +3855,12 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ *(int*)pArg = iOld; return SQLITE_OK; } -#endif + case SQLITE_FCNTL_BLOCK_ON_CONNECT: { + int iNew = *(int*)pArg; + pFile->bBlockOnConnect = iNew; + return SQLITE_OK; + } +#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ } OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 68474ab71..d053eb7d7 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -1163,6 +1163,11 @@ struct sqlite3_io_methods { ** the value that M is to be set to. Before returning, the 32-bit signed ** integer is overwritten with the previous value of M. ** +** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]] +** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the +** VFS to block when taking SHARED locks. This is used to implement the +** functionality associated with SQLITE_SETLK_BLOCK_ON_CONNECT. +** ** <li>[[SQLITE_FCNTL_DATA_VERSION]] ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to ** a database file. The argument is a pointer to a 32-bit unsigned integer. @@ -1259,6 +1264,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 #define SQLITE_FCNTL_NULL_IO 43 +#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -2921,8 +2927,21 @@ int sqlite3_busy_timeout(sqlite3*, int ms); ** values, this function sets only the setlk-timeout value. Therefore, ** to configure separate busy-timeout and setlk-timeout values for a single ** database handle, call sqlite3_busy_timeout() followed by this function. +** +** Whenever the number of connections to a wal mode database falls from +** 1 to 0, the last connection takes an exclusive lock on the database, +** then checkpoints and deletes the wal file. While it is doing this, any +** new connection that tries to read from the database fails with an +** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is +** passed to this API, the new connection blocks until the exclusive lock +** has been released. +*/ +int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); + +/* +** CAPI3REF: Flags for sqlite3_setlk_timeout() */ -int sqlite3_setlk_timeout(sqlite3*, int ms); +#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 /* ** CAPI3REF: Convenience Routines For Running Queries diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 631bb2a24..f56625cef 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1746,6 +1746,7 @@ struct sqlite3 { int busyTimeout; /* Busy handler timeout, in msec */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT int setlkTimeout; /* Blocking lock timeout, in msec. -1 -> inf. */ + int setlkFlags; /* Flags passed to setlk_timeout() */ #endif int nSavepoint; /* Number of non-transaction savepoints */ int nStatement; /* Number of nested statement-transactions */ diff --git a/src/test1.c b/src/test1.c index fb7607f10..a0ca93d10 100644 --- a/src/test1.c +++ b/src/test1.c @@ -5940,7 +5940,7 @@ static int SQLITE_TCLAPI test_busy_timeout( } /* -** Usage: sqlite3_setlk_timeout DB MS +** Usage: sqlite3_setlk_timeout ?-blockonconnect? DB MS ** ** Set the setlk timeout. */ @@ -5952,14 +5952,25 @@ static int SQLITE_TCLAPI test_setlk_timeout( ){ int rc, ms; sqlite3 *db; - if( argc!=3 ){ + int bBlockOnConnect = 0; + + if( argc==4 ){ + const char *zArg = argv[1]; + int nArg = strlen(zArg); + if( nArg>=2 && nArg<=15 && memcmp(zArg, "-blockonconnect", nArg)==0 ){ + bBlockOnConnect = 1; + } + } + if( argc!=(3+bBlockOnConnect) ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " DB", 0); + " ?-blockonconnect? DB MS", 0); return TCL_ERROR; } - if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; - if( Tcl_GetInt(interp, argv[2], &ms) ) return TCL_ERROR; - rc = sqlite3_setlk_timeout(db, ms); + if( getDbPointer(interp, argv[argc-2], &db) ) return TCL_ERROR; + if( Tcl_GetInt(interp, argv[argc-1], &ms) ) return TCL_ERROR; + rc = sqlite3_setlk_timeout( + db, ms, (bBlockOnConnect ? SQLITE_SETLK_BLOCK_ON_CONNECT : 0) + ); Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_OK; } |