From 91107d364a012d3f2b630e490e03ae5ca169f185 Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Tue, 8 Jul 2025 15:17:15 +0200 Subject: [PATCH 1/4] only close connection in case of reference count is 1 Due to how `execute` is implemented, it returns a `Connection` clone which internally shares a turso_core::Connection with every other Connection. Since `execute` returns `Connection` and immediatly it is dropped, it will close connection, checkpoint and leave database in weird state. --- bindings/python/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 6606c8dd0..eb56d429d 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -296,9 +296,11 @@ impl Connection { impl Drop for Connection { fn drop(&mut self) { - self.conn - .close() - .expect("Failed to drop (close) connection"); + if Arc::strong_count(&self.conn) == 1 { + self.conn + .close() + .expect("Failed to drop (close) connection"); + } } } From 8909e198ae5c38a07b9ddfe3b606e1046fae2c20 Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Tue, 8 Jul 2025 15:19:04 +0200 Subject: [PATCH 2/4] set closed flag for connection to detect force zombies Let's make sure we don't keep using a connection after it was dropped. In case of executing a query that was closed we will try to rollback and return early. --- core/lib.rs | 4 ++++ core/vdbe/mod.rs | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/core/lib.rs b/core/lib.rs index 7f63d1b3c..1c8cad1e2 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -280,6 +280,7 @@ impl Database { readonly: Cell::new(false), wal_checkpoint_disabled: Cell::new(false), capture_data_changes: RefCell::new(CaptureDataChangesMode::Off), + closed: RefCell::new(false), }); if let Err(e) = conn.register_builtins() { return Err(LimboError::ExtensionError(e)); @@ -333,6 +334,7 @@ impl Database { readonly: Cell::new(false), wal_checkpoint_disabled: Cell::new(false), capture_data_changes: RefCell::new(CaptureDataChangesMode::Off), + closed: RefCell::new(false), }); if let Err(e) = conn.register_builtins() { @@ -487,6 +489,7 @@ pub struct Connection { readonly: Cell, wal_checkpoint_disabled: Cell, capture_data_changes: RefCell, + closed: RefCell, } impl Connection { @@ -739,6 +742,7 @@ impl Connection { /// Close a connection and checkpoint. pub fn close(&self) -> Result<()> { + self.closed.replace(true); self.pager .checkpoint_shutdown(self.wal_checkpoint_disabled.get()) } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 3750fcfec..8e3c4427d 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -380,6 +380,14 @@ impl Program { pager: Rc, ) -> Result { loop { + if *self.connection.closed.borrow() { + // Connection is closed for whatever reason, rollback the transaction. + let state = self.connection.transaction_state.get(); + if let TransactionState::Write { schema_did_change } = state { + pager.rollback(schema_did_change, &self.connection)? + } + return Err(LimboError::InternalError("Connection closed".to_string())); + } if state.is_interrupted() { return Ok(StepResult::Interrupt); } From 5319af8fd801ae004e8900dce1e4c1b1dd5755f5 Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Tue, 8 Jul 2025 15:55:50 +0200 Subject: [PATCH 3/4] set closed to cell --- core/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/lib.rs b/core/lib.rs index 1c8cad1e2..98c150e6c 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -280,7 +280,7 @@ impl Database { readonly: Cell::new(false), wal_checkpoint_disabled: Cell::new(false), capture_data_changes: RefCell::new(CaptureDataChangesMode::Off), - closed: RefCell::new(false), + closed: Cell::new(false), }); if let Err(e) = conn.register_builtins() { return Err(LimboError::ExtensionError(e)); @@ -334,7 +334,7 @@ impl Database { readonly: Cell::new(false), wal_checkpoint_disabled: Cell::new(false), capture_data_changes: RefCell::new(CaptureDataChangesMode::Off), - closed: RefCell::new(false), + closed: Cell::new(false), }); if let Err(e) = conn.register_builtins() { @@ -489,7 +489,7 @@ pub struct Connection { readonly: Cell, wal_checkpoint_disabled: Cell, capture_data_changes: RefCell, - closed: RefCell, + closed: Cell, } impl Connection { @@ -742,7 +742,7 @@ impl Connection { /// Close a connection and checkpoint. pub fn close(&self) -> Result<()> { - self.closed.replace(true); + self.closed.set(true); self.pager .checkpoint_shutdown(self.wal_checkpoint_disabled.get()) } From ef41c19542703afc9567f9fc5b23355d438f3f60 Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Tue, 8 Jul 2025 15:58:11 +0200 Subject: [PATCH 4/4] assert is not closed already --- core/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/lib.rs b/core/lib.rs index 98c150e6c..6e2fb82a7 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -742,6 +742,7 @@ impl Connection { /// Close a connection and checkpoint. pub fn close(&self) -> Result<()> { + turso_assert!(!self.closed.get(), "Connection already closed"); self.closed.set(true); self.pager .checkpoint_shutdown(self.wal_checkpoint_disabled.get())