mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Experimental change to allow clients to block when taking a SHARED lock to connect to a wal mode database.
FossilOrigin-Name: d2d6a000fb9bf8097e0ce9979685408d183be3ab785ceeb11ec1f97a81a83e41
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
14
src/main.c
14
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
19
src/os_win.c
19
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));
|
||||
|
@ -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 sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags for sqlite3_setlk_timeout()
|
||||
*/
|
||||
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
|
||||
|
||||
/*
|
||||
** CAPI3REF: Convenience Routines For Running Queries
|
||||
|
@ -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 */
|
||||
|
23
src/test1.c
23
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user