]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental change to allow clients to block when taking a SHARED lock to connect...
authordan <Dan Kennedy>
Mon, 10 Feb 2025 20:46:14 +0000 (20:46 +0000)
committerdan <Dan Kennedy>
Mon, 10 Feb 2025 20:46:14 +0000 (20:46 +0000)
FossilOrigin-Name: d2d6a000fb9bf8097e0ce9979685408d183be3ab785ceeb11ec1f97a81a83e41

manifest
manifest.uuid
src/attach.c
src/main.c
src/os_unix.c
src/os_win.c
src/sqlite.h.in
src/sqliteInt.h
src/test1.c
test/lock_common.tcl
test/walsetlk3.test [new file with mode: 0644]

index fd640c9b3578f0e08bd7aa0010d9d9156b9d5e3d..32d9f7e9f46db98751c4dd2b2e8f9cf0303b305b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sproblem\scausing\sthe\swrite-lock\sto\sbe\sheld\swhen\sit\sshould\snot\sbe\sin\ssome\scircumstances\sfollowing\sa\sSEH\sexception.
-D 2025-01-30T17:04:28.336
+C Experimental\schange\sto\sallow\sclients\sto\sblock\swhen\staking\sa\sSHARED\slock\sto\sconnect\sto\sa\swal\smode\sdatabase.
+D 2025-02-10T20:46:14.679
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
@@ -711,7 +711,7 @@ F sqlite3.1 acdff36db796e2d00225b911d3047d580cd136547298435426ce9d40347973cc
 F sqlite3.pc.in 0977c03a4da7c4204bd60e784a0efb8d51a190448aba78a4e973fe7192bdaf03
 F src/alter.c aa93e37e4a36a0525bbb2a2aeda20d2018f0aa995542c7dc658e031375e3f532
 F src/analyze.c 9a8b67239d899ac12289db5db3f5bfe7f7a0ad1277f80f87ead1d048085876eb
-F src/attach.c f35bb8cc1fcdde8f6815a7ef09ae413bcac71821d530796800ba24b3c7da1e80
+F src/attach.c f541c1fb59e56a1c8ba6b023d5e1844448b499fac669d2cbca18be7b6e7d4998
 F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
 F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
 F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
@@ -740,7 +740,7 @@ F src/insert.c f8d1a0f8ee258411009c6b7f2d93170e351bd19f5ad89d57e1180644297cbe70
 F src/json.c 68a98c020c22127f2d65f08855f7fc7460ff352a6ce0b543d8931dde83319c22
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
-F src/main.c a54dfdbfee90c8696e473c8fcd5ca1159aa56c096643a0983cc3f0ba4ad45baa
+F src/main.c ad702f1ad48cd82f700b3e1b28b523154488874655cbd39bc9336d0bb2e8f728
 F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@@ -761,8 +761,8 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
 F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
-F src/os_unix.c 5d0d1788ffa3a43bfc47502e4baa36cf8507e478cd182298fc4974a7cca0954f
-F src/os_win.c 79a1b02f483ec82ab8fb1b00cc003bff8a3c44bf8d7ea074992a4e0dda9488df
+F src/os_unix.c e15305b49518d2e2cd405d14ede027a7ca52679d4d31b25635fb88d545211feb
+F src/os_win.c 48c536ebd662a4b39623fffba41d95234854ddfbea4865d03fef1f51c29da187
 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
 F src/pager.c 2fdd489447aa6bb0f672973bacb801ced92225ca9a1c874ed9b856d2741dc54e
 F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
@@ -779,16 +779,16 @@ F src/resolve.c c8a5372b97b2a2e972a280676f06ddb5b74e885d3b1f5ce383f839907b57ef68
 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
 F src/select.c 83e88fbb36f89b6703b348777491619554f0fd6f917c9fdf51e4c2e9cda6c04e
 F src/shell.c.in ee54de10e9bd5572f689a6bc0c8e6fa58a8870e1670978ded44412d2715fd908
-F src/sqlite.h.in 6958f27ef2f789bb724c2be8ee6edf5b2200e74bf638d2e58bdae9cfc6a55d5e
+F src/sqlite.h.in 78a34c8c6564ae43b16322bac5e843167ef97f5084c9c663684f67576ddcbcb2
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
-F src/sqliteInt.h d718c8517bd0659e1d4239642fa4ff7e85e39bc32a2e33a18b470b362c485f63
+F src/sqliteInt.h b6e49765628a6b74e2c47a4e19f5245fa226ef32c6abc1b82849af8107a6b042
 F src/sqliteLimit.h 1bbdbf72bd0411d003267ffebc59a262f061df5653027a75627d03f48ca30523
 F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
 F src/tclsqlite.c 90441d3cc16f966a23499d9096a3d2d971e5e8fddb4d1413b096b79c2b2cff07
 F src/tclsqlite.h 65e2c761446e1c9fa0342b7d2612a703483643c8b6a316d12a65b745a4727395
-F src/test1.c 64df9d5ad54521eb3c103f1253bf33ceddbc5fa931658d3701027a2ac86eb5e9
+F src/test1.c 1be101915405d022cef33bf7a13bdc0753015785de409a028b393fd4b8c7fc9b
 F src/test2.c 7ebc518e6735939d8979273a6f7b1d9b5702babf059f6ad62499f7f60a9eb9a3
 F src/test3.c e7573aa0f78ee4e070a4bc8c3493941c1aa64d5c66d4825c74c0f055451f432b
 F src/test4.c 13e57ae7ec7a959ee180970aef09deed141252fe9bb07c61054f0dfa4f1dfd5d
@@ -1413,7 +1413,7 @@ F test/lock4.test 27143363eda1622f03c133efc8db808fc331afd973486cb571ea71cd717d37
 F test/lock5.test 583cae05992af0f66607286917f7d5f8aed3b6053c52df5994efb98f2a8fdbaf
 F test/lock6.test ad5b387a3a8096afd3c68a55b9535056431b0cf5
 F test/lock7.test 49f1eaff1cdc491cc5dee3669f3c671d9f172431
-F test/lock_common.tcl 2f3f7f2e9637f93ccf609df48ef5b27a50278b6b1cd752b445d52262e5841413
+F test/lock_common.tcl f33b7fbc275be25a6f2863b4cc8af35278e24d127a3f734825477bf223b05ffe
 F test/lookaside.test 5a828e7256f1ee4da8e1bdaa03373a3ccdb0f1ff98dfa82e9b76cb41a45b1083
 F test/main.test e8752d76233b1c8906cd2c98ad920dba868bd63c87d51d8a2ea5e9cba55dd496
 F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
@@ -2023,6 +2023,7 @@ F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cf
 F test/walseh1.test bae700eb99519b6d5cd3f893c04759accc5a59c391d4189fe4dd6995a533442b
 F test/walsetlk.test 9c5b92f9a20252540fedf9ffa6ee3d1b8af08ea4b80d0144d9b88e6c0c1de80d
 F test/walsetlk2.test 5ae8662a28c013e8df2ce975f9e3577a7f239aeb4622bb8d4d0ca8e16c0c132e
+F test/walsetlk3.test 520633cb23561e960d3100b23f9da692f0cce2ffd650d72e2d06163e6c2d228b
 F test/walshared.test 42e3808582504878af237ea02c42ca793e8a0efaa19df7df26ac573370dbc7a3
 F test/walslow.test 0c51843836c9dcf40a5ac05aa781bfb977b396ee2c872d92bd48b79d5dd9aa23
 F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747
@@ -2203,8 +2204,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
 F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 62009565d2f2a2c4d347e1da0d5b4ad43056742df47fd6ddb92e62f53a2b57f1
-R 1ce6d89df3820633838b6629306f6da1
+P 7eb5accb7cf937fc967dcd86da0af813fb18a2697348bd231fbefd3c09b930ab
+R a7261f9e35c03d513b32b9d4fcb3d611
 U dan
-Z ed92d59f35c33d93aa0b00d0344b6f87
+Z c73a12f9f64486e78d8e69c08a89db7b
 # Remove this line to create a well-formed Fossil manifest.
index 240cfee2384bbd32e6191a5cd447632e58b4da48..27fe52162fcccf4578242e07a73e621e7d320c65 100644 (file)
@@ -1 +1 @@
-7eb5accb7cf937fc967dcd86da0af813fb18a2697348bd231fbefd3c09b930ab
+d2d6a000fb9bf8097e0ce9979685408d183be3ab785ceeb11ec1f97a81a83e41
index 9f23dce1ed3fb2b2e0ef8b47e46c3d3b3bc4dd00..3ade237b5c3d81fbe18345364140b7f523150952 100644 (file)
@@ -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);
     }
index c1799455fa005122420d63a313e903c4fb699f26..8cd0303c4d10fbf573836bd0a31b14958810f05f 100644 (file)
@@ -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;
 }
index 58e4952100903c3b2c57d48f088901969e49c10b..7f92c5234de8fd580e528fe56deab3a08a8689ae 100644 (file)
@@ -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;
index 751ba21481925a8e216ce9f36a3548c96dceccbc..f7a295aa9b4b4f9ee87ad359f8eca7eb7010a47a 100644 (file)
@@ -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));
index 68474ab71cdfc3e62cac7d3483bc93b8dcd990f0..d053eb7d7e67f00e986ef305e78a36b14fdf3949 100644 (file)
@@ -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
index 631bb2a249531f11a59b957ee7cb995b90545100..f56625cef0e67b0bcc1ca41b4e306da5da7d4ef0 100644 (file)
@@ -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  */
index fb7607f1015cdfddc2499e9cf0a6c0103d7dde5f..a0ca93d10c6ab2ad4a32fcae0c527cdbfcb4e118 100644 (file)
@@ -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;
 }
index 3e1821bab0b5d4de72ff8f75afbc3313104ce482..9504a8c04656ad4cb8bf3650f338a2772d59b850 100644 (file)
@@ -145,6 +145,7 @@ proc testfixture_nb_cb {varname chan} {
   }
 
   if { $line == "OVER" } {
+    global $varname
     set $varname [lindex $::tfnb($chan) 1]
     unset ::tfnb($chan)
     close $chan
diff --git a/test/walsetlk3.test b/test/walsetlk3.test
new file mode 100644 (file)
index 0000000..cbd2f72
--- /dev/null
@@ -0,0 +1,130 @@
+# 2020 May 06
+#
+# 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.
+#
+#***********************************************************************
+#
+# TESTRUNNER: slow
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+set testprefix walsetlk3
+
+ifcapable !wal {finish_test ; return }
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(x, y);
+  PRAGMA journal_mode = wal;
+  INSERT INTO t1 VALUES(1, 2);
+  INSERT INTO t1 VALUES(3, 4);
+} {wal}
+
+db close
+
+proc sql_block_on_close {sql} {
+  testfixture_nb done [string map [list %SQL% $sql] {
+    testvfs tvfs
+    tvfs script xWrite
+    tvfs filter xWrite
+  
+    set ::delay_done 0
+    proc xWrite {method fname args} {
+      if {[file tail $fname]=="test.db" && $::delay_done==0} {
+        after 3000
+        set ::delay_done 1
+      }
+      return 0
+    }
+  
+    sqlite3 db test.db -vfs tvfs
+    db eval {%SQL%}
+    db close
+  }]
+}
+
+# Start a second process that writes to the db, then blocks within the
+# [db close] holding an EXCLUSIVE on the db in order to checkpoint and 
+# delete the wal file. Then try to read the db.
+#
+# Without the SQLITE_SETLK_BLOCK_ON_CONNECT flag, this should fail with
+# SQLITE_BUSY.
+#
+sql_block_on_close {
+  INSERT INTO t1 VALUES(5, 6);
+  INSERT INTO t1 VALUES(7, 8);
+}
+after 500
+sqlite3 db test.db
+sqlite3_setlk_timeout db 2000
+do_catchsql_test 1.1 {
+  SELECT * FROM t1
+} {1 {database is locked}}
+
+vwait ::done
+
+# But with SQLITE_SETLK_BLOCK_ON_CONNECT flag, it should succeed.
+#
+sql_block_on_close {
+  INSERT INTO t1 VALUES(9, 10);
+  INSERT INTO t1 VALUES(11, 12);
+}
+after 500
+sqlite3 db test.db
+sqlite3_setlk_timeout -block db 2000
+do_catchsql_test 1.2 {
+  SELECT * FROM t1
+} {0 {1 2 3 4 5 6 7 8 9 10 11 12}}
+
+vwait ::done
+
+#-------------------------------------------------------------------------
+# Check that the SQLITE_SETLK_BLOCK_ON_CONNECT does not cause connections
+# to block when taking a SHARED lock on a rollback mode database.
+#
+reset_db
+do_execsql_test 2.1 {
+  CREATE TABLE x1(a);
+  INSERT INTO x1 VALUES(1), (2), (3);
+}
+
+proc sql_block_on_write {sql} {
+  testfixture_nb done [string map [list %SQL% $sql] {
+    sqlite3 db test.db 
+    db eval "BEGIN EXCLUSIVE"
+    db eval {%SQL%}
+    after 3000
+    db eval COMMIT
+    db close
+  }]
+}
+
+db close
+sql_block_on_write {
+  INSERT INTO x1 VALUES(4);
+}
+
+after 500
+
+sqlite3 db test.db
+sqlite3_setlk_timeout -block db 2000
+
+do_catchsql_test 2.2 {
+  SELECT * FROM x1
+} {1 {database is locked}}
+
+vwait ::done
+after 500
+
+do_catchsql_test 2.3 {
+  SELECT * FROM x1
+} {0 {1 2 3 4}}
+
+finish_test
+