diff --git a/core/lib.rs b/core/lib.rs index 615c1f0c..d1e867cb 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -118,8 +118,8 @@ pub struct Database { maybe_shared_wal: RwLock>>>, is_empty: Arc, init_lock: Arc>, - open_flags: OpenFlags, + wal_checkpoint_disabled: Cell, } unsafe impl Send for Database {} @@ -211,6 +211,7 @@ impl Database { open_flags: flags, is_empty: Arc::new(AtomicUsize::new(is_empty)), init_lock: Arc::new(Mutex::new(())), + wal_checkpoint_disabled: Cell::new(false), }; let db = Arc::new(db); @@ -674,7 +675,8 @@ impl Connection { /// If the WAL size is over the checkpoint threshold, it will checkpoint the WAL to /// the database file and then fsync the database file. pub fn cacheflush(&self) -> Result { - self.pager.cacheflush() + self.pager + .cacheflush(self._db.wal_checkpoint_disabled.get()) } pub fn clear_page_cache(&self) -> Result<()> { @@ -683,12 +685,18 @@ impl Connection { } pub fn checkpoint(&self) -> Result { - self.pager.wal_checkpoint() + self.pager + .wal_checkpoint(self._db.wal_checkpoint_disabled.get()) } /// Close a connection and checkpoint. pub fn close(&self) -> Result<()> { - self.pager.checkpoint_shutdown() + self.pager + .checkpoint_shutdown(self._db.wal_checkpoint_disabled.get()) + } + + pub fn wal_disable_checkpoint(&self) { + self._db.wal_checkpoint_disabled.set(true); } pub fn last_insert_rowid(&self) -> i64 { diff --git a/core/storage/btree.rs b/core/storage/btree.rs index daffe818..c650d791 100644 --- a/core/storage/btree.rs +++ b/core/storage/btree.rs @@ -7057,7 +7057,7 @@ mod tests { ) .unwrap(); loop { - match pager.end_tx(false, false, &conn).unwrap() { + match pager.end_tx(false, false, &conn, false).unwrap() { crate::PagerCacheflushStatus::Done(_) => break, crate::PagerCacheflushStatus::IO => { pager.io.run_once().unwrap(); @@ -7183,7 +7183,7 @@ mod tests { .unwrap(); cursor.move_to_root(); loop { - match pager.end_tx(false, false, &conn).unwrap() { + match pager.end_tx(false, false, &conn, false).unwrap() { crate::PagerCacheflushStatus::Done(_) => break, crate::PagerCacheflushStatus::IO => { pager.io.run_once().unwrap(); diff --git a/core/storage/pager.rs b/core/storage/pager.rs index 802d11f5..008c154e 100644 --- a/core/storage/pager.rs +++ b/core/storage/pager.rs @@ -636,6 +636,7 @@ impl Pager { rollback: bool, change_schema: bool, connection: &Connection, + wal_checkpoint_disabled: bool, ) -> Result { tracing::trace!("end_tx(rollback={})", rollback); if rollback { @@ -643,7 +644,7 @@ impl Pager { self.wal.borrow().end_read_tx()?; return Ok(PagerCacheflushStatus::Done(PagerCacheflushResult::Rollback)); } - let cacheflush_status = self.cacheflush()?; + let cacheflush_status = self.cacheflush(wal_checkpoint_disabled)?; match cacheflush_status { PagerCacheflushStatus::IO => Ok(PagerCacheflushStatus::IO), PagerCacheflushStatus::Done(_) => { @@ -758,7 +759,7 @@ impl Pager { /// In the base case, it will write the dirty pages to the WAL and then fsync the WAL. /// If the WAL size is over the checkpoint threshold, it will checkpoint the WAL to /// the database file and then fsync the database file. - pub fn cacheflush(&self) -> Result { + pub fn cacheflush(&self, wal_checkpoint_disabled: bool) -> Result { let mut checkpoint_result = CheckpointResult::default(); loop { let state = self.flush_info.borrow().state; @@ -804,7 +805,7 @@ impl Pager { return Ok(PagerCacheflushStatus::IO); } - if !self.wal.borrow().should_checkpoint() { + if wal_checkpoint_disabled || !self.wal.borrow().should_checkpoint() { self.flush_info.borrow_mut().state = FlushState::Start; return Ok(PagerCacheflushStatus::Done( PagerCacheflushResult::WalWritten, @@ -912,7 +913,7 @@ impl Pager { .expect("Failed to clear page cache"); } - pub fn checkpoint_shutdown(&self) -> Result<()> { + pub fn checkpoint_shutdown(&self, wal_checkpoint_disabled: bool) -> Result<()> { let mut attempts = 0; { let mut wal = self.wal.borrow_mut(); @@ -927,11 +928,17 @@ impl Pager { attempts += 1; } } - self.wal_checkpoint()?; + self.wal_checkpoint(wal_checkpoint_disabled)?; Ok(()) } - pub fn wal_checkpoint(&self) -> Result { + pub fn wal_checkpoint(&self, wal_checkpoint_disabled: bool) -> Result { + if wal_checkpoint_disabled { + return Ok(CheckpointResult { + num_wal_frames: 0, + num_checkpointed_frames: 0, + }); + } let checkpoint_result: CheckpointResult; loop { match self.wal.borrow_mut().checkpoint( diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 502164cb..383c4a82 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -469,7 +469,8 @@ impl Program { rollback: bool, change_schema: bool, ) -> Result { - let cacheflush_status = pager.end_tx(rollback, change_schema, connection)?; + let cacheflush_status = + pager.end_tx(rollback, change_schema, connection, connection._db.wal_checkpoint_disabled.get())?; match cacheflush_status { PagerCacheflushStatus::Done(_) => { if self.change_cnt_on { diff --git a/sqlite3/README.md b/sqlite3/README.md index e58ea8c4..b5b044b0 100644 --- a/sqlite3/README.md +++ b/sqlite3/README.md @@ -1,14 +1,14 @@ -# SQLite3 Implementation for Limbo +# SQLite3 Implementation for Turso -This directory contains a Rust implementation of the SQLite3 C API. The implementation serves as a compatibility layer between SQLite's C API and Limbo's native Rust database implementation. +This directory contains a Rust implementation of the SQLite3 C API. The implementation serves as a compatibility layer between SQLite's C API and Turso's native Rust database implementation. ## Purpose -This implementation provides SQLite3 API compatibility for Limbo, allowing existing applications that use SQLite to work with Limbo without modification. The code: +This implementation provides SQLite3 API compatibility for Turso, allowing existing applications that use SQLite to work with Turso without modification. The code: 1. Implements the SQLite3 C API functions in Rust 2. Translates between C and Rust data structures -3. Maps SQLite operations to equivalent Limbo operations +3. Maps SQLite operations to equivalent Turso operations 4. Maintains API compatibility with SQLite version 3.42.0 ## Testing Strategy diff --git a/sqlite3/src/lib.rs b/sqlite3/src/lib.rs index 331eaa5c..5fcbfbd4 100644 --- a/sqlite3/src/lib.rs +++ b/sqlite3/src/lib.rs @@ -1188,6 +1188,17 @@ pub unsafe extern "C" fn libsql_wal_get_frame( } } +#[no_mangle] +pub unsafe extern "C" fn libsql_wal_disable_checkpoint(db: *mut sqlite3) -> ffi::c_int { + if db.is_null() { + return SQLITE_MISUSE; + } + let db: &mut sqlite3 = &mut *db; + let db = db.inner.lock().unwrap(); + db.conn.wal_disable_checkpoint(); + SQLITE_OK +} + fn sqlite3_safety_check_sick_or_ok(db: &sqlite3Inner) -> bool { match db.e_open_state { SQLITE_STATE_SICK | SQLITE_STATE_OPEN | SQLITE_STATE_BUSY => true,