aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/attach.c7
-rw-r--r--src/main.c14
-rw-r--r--src/os_unix.c15
-rw-r--r--src/os_win.c19
-rw-r--r--src/sqlite.h.in21
-rw-r--r--src/sqliteInt.h1
-rw-r--r--src/test1.c23
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;
}