]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the rbu_exclusive_checkpoint query parameter to RBU. reuse-schema-3.36
authordrh <>
Sat, 6 Nov 2021 11:58:43 +0000 (11:58 +0000)
committerdrh <>
Sat, 6 Nov 2021 11:58:43 +0000 (11:58 +0000)
FossilOrigin-Name: c15806a45ab435639a1cd5b365939e8075d2873a322cd161a8d2aa5bf414dbd6

ext/rbu/rbuexlock.test [new file with mode: 0644]
ext/rbu/sqlite3rbu.c
manifest
manifest.uuid

diff --git a/ext/rbu/rbuexlock.test b/ext/rbu/rbuexlock.test
new file mode 100644 (file)
index 0000000..eddcdc1
--- /dev/null
@@ -0,0 +1,207 @@
+# 2021 November 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.
+#
+#***********************************************************************
+#
+
+source [file join [file dirname [info script]] rbu_common.tcl]
+set ::testprefix rbuexlock
+
+db close
+sqlite3_shutdown
+sqlite3_config_uri 1
+
+# Create a simple RBU database. That expects to write to a table:
+#
+#   CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+#
+proc create_rbu {filename} {
+  forcedelete $filename
+  sqlite3 rbu1 $filename  
+  rbu1 eval {
+    CREATE TABLE data_t1(a, b, c, rbu_control);
+    INSERT INTO data_t1 VALUES(10, random(), random(), 0);
+    INSERT INTO data_t1 VALUES(20, random(), random(), 0);
+    INSERT INTO data_t1 VALUES(30, random(), random(), 0);
+    INSERT INTO data_t1 VALUES(40, random(), random(), 0);
+    INSERT INTO data_t1 VALUES(50, random(), random(), 0);
+    INSERT INTO data_t1 VALUES(60, random(), random(), 0);
+    INSERT INTO data_t1 VALUES(70, random(), random(), 0);
+    INSERT INTO data_t1 VALUES(80, random(), random(), 0);
+  }
+  rbu1 close
+  return $filename
+}
+
+reset_db
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a PRIMARY KEY, b INT, c INT);
+  CREATE INDEX t1b ON t1(b);
+  CREATE INDEX t1c ON t1(c);
+  INSERT INTO t1 VALUES(1, 2, 3);
+}
+create_rbu rbu1.db
+
+do_test 1.1.0 {
+  sqlite3rbu rbu file:test.db?rbu_exclusive_checkpoint=1 rbu1.db
+  rbu step
+} SQLITE_OK
+do_catchsql_test 1.1.1 { SELECT * FROM t1 } {0 {1 2 3}}
+
+do_test 1.2.0 {
+  for {set ii 0} {$ii < 10} {incr ii} {
+    rbu step
+  }
+  rbu step
+} SQLITE_OK
+do_catchsql_test 1.2.1 { SELECT * FROM t1 } {0 {1 2 3}}
+do_test 1.2.2 {
+  db eval {PRAGMA journal_mode}
+} {delete}
+
+do_test 1.3.0 {
+  while {[file exists test.db-wal]==0} {
+    rbu step
+  }
+} {}
+do_catchsql_test 1.3.1 { SELECT * FROM t1 } {1 {database is locked}}
+do_test 1.3.2 {
+  db eval {PRAGMA journal_mode}
+} {delete}
+
+
+do_test 1.4.0 {
+  rbu step
+} SQLITE_OK
+do_catchsql_test 1.4.1 { SELECT * FROM t1 } {1 {database is locked}}
+do_test 1.4.2 {
+  db eval {PRAGMA journal_mode}
+} {delete}
+
+
+rbu close
+
+do_test 1.5.0 {
+  file exists test.db-wal
+} {1}
+do_test 1.5.1 {
+  sqlite3rbu rbu file:test.db?rbu_exclusive_checkpoint=1 rbu1.db
+  file exists test.db-wal
+} 1
+do_catchsql_test 1.5.2 { SELECT * FROM t1 } {1 {database is locked}}
+do_test 1.5.2 {
+  db eval {PRAGMA journal_mode}
+} {delete}
+
+
+do_test 1.6.0 {
+  rbu step
+} SQLITE_OK
+do_catchsql_test 1.6.1 { SELECT * FROM t1 } {1 {database is locked}}
+do_test 1.6.2 {
+  db eval {PRAGMA journal_mode}
+} {delete}
+
+do_test 1.7.0 {
+  while {[rbu step]=="SQLITE_OK"} {}
+  rbu close
+} SQLITE_DONE
+do_catchsql_test 1.7.2 { SELECT count(*) FROM t1 } {0 9}
+do_test 1.7.2 {
+  db eval {PRAGMA journal_mode}
+} {delete}
+
+reset_db
+do_execsql_test 2.0 {
+  CREATE TABLE t1(a PRIMARY KEY, b INT, c INT);
+  CREATE INDEX t1b ON t1(b);
+  CREATE INDEX t1c ON t1(c);
+  INSERT INTO t1 VALUES(1, 2, 3);
+}
+create_rbu rbu1.db
+
+do_test 2.1.0 {
+  sqlite3rbu rbu file:test.db?rbu_exclusive_checkpoint=0 rbu1.db
+  rbu step
+} SQLITE_OK
+do_catchsql_test 2.1.1 { SELECT * FROM t1 } {0 {1 2 3}}
+
+do_test 2.2.0 {
+  for {set ii 0} {$ii < 10} {incr ii} {
+    rbu step
+  }
+  rbu step
+} SQLITE_OK
+do_catchsql_test 2.2.1 { SELECT * FROM t1 } {0 {1 2 3}}
+
+do_test 2.3.0 {
+  while {[file exists test.db-wal]==0} {
+    rbu step
+  }
+} {}
+do_test 2.3.1 {
+  llength [db eval {SELECT * FROM t1}]
+} {27}
+do_test 2.3.2 {
+  db eval {PRAGMA journal_mode}
+} {wal}
+
+do_test 2.4.0 {
+  rbu step
+} SQLITE_OK
+do_test 2.4.1 {
+  llength [db eval  {SELECT * FROM t1}]
+} {27}
+do_test 2.4.2 {
+  db eval {PRAGMA journal_mode}
+} {wal}
+
+rbu close
+
+do_test 2.5.0 {
+  db eval {PRAGMA journal_mode}
+} {wal}
+do_execsql_test 2.5.1 {
+  DELETE FROM t1;
+} {}
+
+create_rbu rbu1.db
+do_test 3.1.0 {
+  sqlite3rbu rbu file:test.db?rbu_exclusive_checkpoint=0 rbu1.db
+  rbu step
+} SQLITE_ERROR
+
+do_test 3.1.1 {
+  set rc [catch {rbu close} msg]
+  lappend rc $msg
+} {1 {SQLITE_ERROR - cannot update wal mode database}}
+db eval {PRAGMA journal_mode=DELETE}
+
+create_rbu rbu1.db
+do_test 3.2.0 {
+  sqlite3rbu rbu file:test.db?rbu_exclusive_checkpoint=0 rbu1.db
+  rbu step
+} SQLITE_OK
+
+do_test 3.3.1 {
+  set rc [catch {rbu close} msg]
+  lappend rc $msg
+} {0 SQLITE_OK}
+
+db close
+create_rbu rbu1.db
+do_test 3.4.0 {
+  sqlite3rbu rbu file:test.db?rbu_exclusive_checkpoint=0 rbu1.db
+  rbu step
+} SQLITE_OK
+rbu close
+
+
+finish_test
index a0ecc558eac63ec7e4825821b0a985a8257b7bf7..2285b7f6ed04933aff5cbf428c5d1831fcd51eb4 100644 (file)
 # define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;}
 #endif
 
+/*
+** Name of the URI option that causes RBU to take an exclusive lock as
+** part of the incremental checkpoint operation.
+*/
+#define RBU_EXCLUSIVE_CHECKPOINT "rbu_exclusive_checkpoint"
+
+
 /*
 ** The rbu_state table is used to save the state of a partially applied
 ** update so that it can be resumed later. The table consists of integer
@@ -2754,13 +2761,19 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
 /*
 ** Open the database handle and attach the RBU database as "rbu". If an
 ** error occurs, leave an error code and message in the RBU handle.
+**
+** If argument dbMain is not NULL, then it is a database handle already
+** open on the target database. Use this handle instead of opening a new
+** one.
 */
-static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
+static void rbuOpenDatabase(sqlite3rbu *p, sqlite3 *dbMain, int *pbRetry){
   assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
   assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );
+  assert( dbMain==0 || rbuIsVacuum(p)==0 );
 
   /* Open the RBU database */
   p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
+  p->dbMain = dbMain;
 
   if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
     sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
@@ -3126,15 +3139,31 @@ static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){
 
 
 /*
-** Take an EXCLUSIVE lock on the database file.
+** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if
+** successful, or an SQLite error code otherwise.
 */
-static void rbuLockDatabase(sqlite3rbu *p){
-  sqlite3_file *pReal = p->pTargetFd->pReal;
-  assert( p->rc==SQLITE_OK );
-  p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED);
-  if( p->rc==SQLITE_OK ){
-    p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE);
+static int rbuLockDatabase(sqlite3 *db){
+  int rc = SQLITE_OK;
+  sqlite3_file *fd = 0;
+  sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd);
+
+  if( fd->pMethods ){
+    rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED);
+    if( rc==SQLITE_OK ){
+      rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE);
+    }
   }
+  return rc;
+}
+
+/*
+** Return true if the database handle passed as the only argument
+** was opened with the rbu_exclusive_checkpoint=1 URI parameter
+** specified. Or false otherwise.
+*/
+static int rbuExclusiveCheckpoint(sqlite3 *db){
+  const char *zUri = sqlite3_db_filename(db, 0);
+  return sqlite3_uri_boolean(zUri, RBU_EXCLUSIVE_CHECKPOINT, 0);
 }
 
 #if defined(_WIN32_WCE)
@@ -3192,18 +3221,24 @@ static void rbuMoveOalFile(sqlite3rbu *p){
     ** In order to ensure that there are no database readers, an EXCLUSIVE
     ** lock is obtained here before the *-oal is moved to *-wal.
     */
-    rbuLockDatabase(p);
-    if( p->rc==SQLITE_OK ){
-      rbuFileSuffix3(zBase, zWal);
-      rbuFileSuffix3(zBase, zOal);
+    sqlite3 *dbMain = 0;
+    rbuFileSuffix3(zBase, zWal);
+    rbuFileSuffix3(zBase, zOal);
+
+    /* Re-open the databases. */
+    rbuObjIterFinalize(&p->objiter);
+    sqlite3_close(p->dbRbu);
+    sqlite3_close(p->dbMain);
+    p->dbMain = 0;
+    p->dbRbu = 0;
 
-      /* Re-open the databases. */
-      rbuObjIterFinalize(&p->objiter);
-      sqlite3_close(p->dbRbu);
-      sqlite3_close(p->dbMain);
-      p->dbMain = 0;
-      p->dbRbu = 0;
+    dbMain = rbuOpenDbhandle(p, p->zTarget, 1);
+    if( dbMain ){
+      assert( p->rc==SQLITE_OK );
+      p->rc = rbuLockDatabase(dbMain);
+    }
 
+    if( p->rc==SQLITE_OK ){
 #if defined(_WIN32_WCE)
       {
         LPWSTR zWideOal;
@@ -3230,11 +3265,19 @@ static void rbuMoveOalFile(sqlite3rbu *p){
 #else
       p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK;
 #endif
+    }
 
-      if( p->rc==SQLITE_OK ){
-        rbuOpenDatabase(p, 0);
-        rbuSetupCheckpoint(p, 0);
-      }
+    if( p->rc!=SQLITE_OK 
+     || rbuIsVacuum(p) 
+     || rbuExclusiveCheckpoint(dbMain)==0 
+    ){
+      sqlite3_close(dbMain);
+      dbMain = 0;
+    }
+
+    if( p->rc==SQLITE_OK ){
+      rbuOpenDatabase(p, dbMain, 0);
+      rbuSetupCheckpoint(p, 0);
     }
   }
 
@@ -3985,9 +4028,9 @@ static sqlite3rbu *openRbuHandle(
       ** If this is the case, it will have been checkpointed and deleted
       ** when the handle was closed and a second attempt to open the 
       ** database may succeed.  */
-      rbuOpenDatabase(p, &bRetry);
+      rbuOpenDatabase(p, 0, &bRetry);
       if( bRetry ){
-        rbuOpenDatabase(p, 0);
+        rbuOpenDatabase(p, 0, 0);
       }
     }
 
@@ -4082,6 +4125,14 @@ static sqlite3rbu *openRbuHandle(
       }else if( p->eStage==RBU_STAGE_MOVE ){
         /* no-op */
       }else if( p->eStage==RBU_STAGE_CKPT ){
+        if( !rbuIsVacuum(p) && rbuExclusiveCheckpoint(p->dbMain) ){
+          /* If the rbu_exclusive_checkpoint=1 URI parameter was specified
+          ** and an incremental checkpoint is being resumed, attempt an
+          ** exclusive lock on the db file. If this fails, so be it.  */
+          p->eStage = RBU_STAGE_DONE;
+          rbuLockDatabase(p->dbMain);
+          p->eStage = RBU_STAGE_CKPT;
+        }
         rbuSetupCheckpoint(p, pState);
       }else if( p->eStage==RBU_STAGE_DONE ){
         p->rc = SQLITE_DONE;
@@ -4119,7 +4170,6 @@ sqlite3rbu *sqlite3rbu_open(
   const char *zState
 ){
   if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); }
-  /* TODO: Check that zTarget and zRbu are non-NULL */
   return openRbuHandle(zTarget, zRbu, zState);
 }
 
index 942a2d82b67783e73e831b73e2c8f4bebfc7d434..28e3acd76018d5e1e7c1a2f313a19ec192fcf22a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\san\sRBU\sproblem\swith\srestarting\san\supdate\safter\sthe\s*-oal\sfile\sis\salready\slarger\sthan\s4GiB.
-D 2021-10-26T10:16:17.186
+C Add\sthe\srbu_exclusive_checkpoint\squery\sparameter\sto\sRBU.
+D 2021-11-06T11:58:43.160
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -362,6 +362,7 @@ F ext/rbu/rbucrash.test 000981a1fe8a6e4d9a684232f6a129e66a3ef595f5ed74655e2f9c68
 F ext/rbu/rbucrash2.test efa143cc94228eb0266d3f1abfbee60a5838a84cef7cc3fcb8c145b74d96fd41
 F ext/rbu/rbudiff.test abe895a8d479e4d33acb40e244e3d8e2cd25f55a18dfa8b9f83e13d00073f600
 F ext/rbu/rbudor.test e3e8623926012f43eebe51fedf06a102df2640750d971596b052495f2536db20
+F ext/rbu/rbuexlock.test 4634a5526d02bf80b0c563f95774bd5af5783e3219ddeb30e413753c9a65510c
 F ext/rbu/rbuexpr.test 10d0420537c3bc7666e576d72adeffe7e86cfbb00dcc30aa9ce096c042415190
 F ext/rbu/rbufault.test 2d7f567b79d558f6e093c58808cab4354f8a174e3802f69e7790a9689b3c09f8
 F ext/rbu/rbufault2.test c81327a3ac2c385b9b954db3644d4e0df93eeebfc3de9f1f29975a1e73fd3d0c
@@ -380,7 +381,7 @@ F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697
 F ext/rbu/rbuvacuum2.test b8e5b51dc8b2c0153373d024c0936be3f66f9234acbd6d0baab0869d56b14e6b
 F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
 F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
-F ext/rbu/sqlite3rbu.c 6141b8068ba2dbef1e42e6fba69322b61add8ed6bdd810c4fd9d59e09852ba6d
+F ext/rbu/sqlite3rbu.c b5428ff119d0e1c43d9c68983c44653c41f74cfab180cc535e278006e4be640f
 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
@@ -1928,11 +1929,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 792215204d887ecb8b1a0d65773d272b84606c9867b52d94701039de5c8bb12c
-Q +56869f54f31186231a4467f10208bcc08d9edae23c6e21831d6d3dff47da51de
-R 872e4b94293046bb5438e7587fecb54b
-T *branch * reuse-schema-3.36
-T *sym-reuse-schema-3.36 *
-T -sym-reuse-schema *
+P 0e79584a2b822594e2d7582f122ecba13531f1a593a4efa2330c40864b8be884
+Q +c2d33ea9d81975b27fe157d698033e7c01569cc0aa6178b7f0c43afbfba4a9e2
+R 5e30dc33fafe75fad73a0f20e0b06e27
 U drh
-Z f12a570fc3bf139c8d528d3c420510af
+Z 45dfdd0d574b76daae0944118994c832
index b67422234ba9a887572bc505b25361d3c506d0c5..8a86c79b396d8e44d527429d90bf5c8056f976aa 100644 (file)
@@ -1 +1 @@
-0e79584a2b822594e2d7582f122ecba13531f1a593a4efa2330c40864b8be884
\ No newline at end of file
+c15806a45ab435639a1cd5b365939e8075d2873a322cd161a8d2aa5bf414dbd6
\ No newline at end of file