diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs index 8d9533e34a..8635a897b3 100644 --- a/crates/dbx-core/src/connection.rs +++ b/crates/dbx-core/src/connection.rs @@ -529,7 +529,9 @@ impl AppState { .cloned() .collect(); for key in keys_to_remove { - conns.remove(&key); + if let Some(PoolKind::DuckDb(con)) = conns.remove(&key) { + crate::db::duckdb_driver::close_connection(con); + } } } diff --git a/crates/dbx-core/src/db/duckdb_driver.rs b/crates/dbx-core/src/db/duckdb_driver.rs index cf3b6238c6..5d2bfeb8d0 100644 --- a/crates/dbx-core/src/db/duckdb_driver.rs +++ b/crates/dbx-core/src/db/duckdb_driver.rs @@ -54,6 +54,30 @@ pub fn is_memory_database_path(path: &str) -> bool { path.trim().eq_ignore_ascii_case(":memory:") } +/// Closes a DuckDB connection, releasing the file lock. +/// +/// Unlike relying on Drop, this calls `duckdb_disconnect` synchronously +/// so the file handle is released before this function returns. +/// On Windows this prevents "file already in use" errors when reconnecting. +pub fn close_connection(con: Arc>) { + match Arc::try_unwrap(con) { + Ok(mutex) => { + match mutex.into_inner() { + Ok(conn) => { + let _ = conn.close(); + } + Err(poisoned) => { + let _ = poisoned.into_inner().close(); + } + } + } + Err(_) => { + // Arc still referenced elsewhere (e.g. running queries); + // the last holder will drop and close the connection. + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs index 905411922a..7b916a5528 100644 --- a/src-tauri/src/commands/connection.rs +++ b/src-tauri/src/commands/connection.rs @@ -269,8 +269,9 @@ pub async fn test_connection(state: State<'_, Arc>, config: Connection if state.duckdb_existing_pool_is_usable_for_config(&config).await? { Ok("Connection successful".to_string()) } else { - db::duckdb_driver::connect_path(&expand_tilde(&config.host)) - .map(|_| "Connection successful".to_string()) + let con = db::duckdb_driver::connect_path(&expand_tilde(&config.host))?; + dbx_core::db::duckdb_driver::close_connection(con); + Ok("Connection successful".to_string()) } } DatabaseType::MongoDb => { @@ -474,7 +475,9 @@ pub async fn disconnect_db(state: State<'_, Arc>, connection_id: Strin PoolKind::Postgres(p) => p.close(), PoolKind::Sqlite(_) => {} PoolKind::Redis(_) => {} - PoolKind::DuckDb(_) => {} + PoolKind::DuckDb(con) => { + dbx_core::db::duckdb_driver::close_connection(con); + } PoolKind::MongoDb(_) => {} PoolKind::ClickHouse(_) => {} PoolKind::SqlServer(_) => {}