From c2614b54df858ec0f561e372da9101d95497a1fe Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 2 Feb 2019 12:46:52 +0100 Subject: [PATCH] Move InnerConnection in its own module --- src/inner_connection.rs | 404 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 410 ++-------------------------------------- 2 files changed, 415 insertions(+), 399 deletions(-) create mode 100644 src/inner_connection.rs diff --git a/src/inner_connection.rs b/src/inner_connection.rs new file mode 100644 index 0000000..9842611 --- /dev/null +++ b/src/inner_connection.rs @@ -0,0 +1,404 @@ +use std::ffi::CString; +use std::mem; +use std::os::raw::c_int; +#[cfg(feature = "load_extension")] +use std::path::Path; +use std::ptr; +use std::str; +use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; +use std::sync::{Arc, Mutex, Once, ONCE_INIT}; + +use super::ffi; +use super::str_to_cstring; +use super::{Connection, InterruptHandle, OpenFlags, Result}; +use crate::error::{error_from_handle, error_from_sqlite_code, Error}; +use crate::raw_statement::RawStatement; +use crate::statement::Statement; +use crate::unlock_notify; +use crate::version::version_number; + +pub struct InnerConnection { + pub db: *mut ffi::sqlite3, + // It's unsafe to call `sqlite3_close` while another thread is performing + // a `sqlite3_interrupt`, and vice versa, so we take this mutex during + // those functions. This protects a copy of the `db` pointer (which is + // cleared on closing), however the main copy, `db`, is unprotected. + // Otherwise, a long running query would prevent calling interrupt, as + // interrupt would only acquire the lock after the query's completion. + interrupt_lock: Arc>, + #[cfg(feature = "hooks")] + pub free_commit_hook: Option, + #[cfg(feature = "hooks")] + pub free_rollback_hook: Option, + #[cfg(feature = "hooks")] + pub free_update_hook: Option, + owned: bool, +} + +impl InnerConnection { + #[cfg(not(feature = "hooks"))] + pub fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection { + InnerConnection { + db, + interrupt_lock: Arc::new(Mutex::new(db)), + owned, + } + } + + #[cfg(feature = "hooks")] + pub fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection { + InnerConnection { + db, + interrupt_lock: Arc::new(Mutex::new(db)), + free_commit_hook: None, + free_rollback_hook: None, + free_update_hook: None, + owned, + } + } + + pub fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result { + #[cfg(not(feature = "bundled"))] + ensure_valid_sqlite_version(); + ensure_safe_sqlite_threading_mode()?; + + // Replicate the check for sane open flags from SQLite, because the check in + // SQLite itself wasn't added until version 3.7.3. + debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02); + debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04); + debug_assert_eq!( + 1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, + 0x40 + ); + if (1 << (flags.bits & 0x7)) & 0x46 == 0 { + return Err(Error::SqliteFailure( + ffi::Error::new(ffi::SQLITE_MISUSE), + None, + )); + } + + unsafe { + let mut db: *mut ffi::sqlite3 = mem::uninitialized(); + let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); + if r != ffi::SQLITE_OK { + let e = if db.is_null() { + error_from_sqlite_code(r, None) + } else { + let e = error_from_handle(db, r); + ffi::sqlite3_close(db); + e + }; + + return Err(e); + } + let r = ffi::sqlite3_busy_timeout(db, 5000); + if r != ffi::SQLITE_OK { + let e = error_from_handle(db, r); + ffi::sqlite3_close(db); + return Err(e); + } + + // attempt to turn on extended results code; don't fail if we can't. + ffi::sqlite3_extended_result_codes(db, 1); + + Ok(InnerConnection::new(db, true)) + } + } + + pub fn db(&self) -> *mut ffi::sqlite3 { + self.db + } + + pub fn decode_result(&mut self, code: c_int) -> Result<()> { + InnerConnection::decode_result_raw(self.db(), code) + } + + fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> { + if code == ffi::SQLITE_OK { + Ok(()) + } else { + Err(error_from_handle(db, code)) + } + } + + pub fn close(&mut self) -> Result<()> { + if self.db.is_null() { + return Ok(()); + } + self.remove_hooks(); + let mut shared_handle = self.interrupt_lock.lock().unwrap(); + assert!( + !shared_handle.is_null(), + "Bug: Somehow interrupt_lock was cleared before the DB was closed" + ); + if !self.owned { + self.db = ptr::null_mut(); + return Ok(()); + } + unsafe { + let r = ffi::sqlite3_close(self.db); + // Need to use _raw because _guard has a reference out, and + // decode_result takes &mut self. + let r = InnerConnection::decode_result_raw(self.db, r); + if r.is_ok() { + *shared_handle = ptr::null_mut(); + self.db = ptr::null_mut(); + } + r + } + } + + pub fn get_interrupt_handle(&self) -> InterruptHandle { + InterruptHandle { + db_lock: Arc::clone(&self.interrupt_lock), + } + } + + pub fn execute_batch(&mut self, sql: &str) -> Result<()> { + let c_sql = str_to_cstring(sql)?; + unsafe { + let r = ffi::sqlite3_exec( + self.db(), + c_sql.as_ptr(), + None, + ptr::null_mut(), + ptr::null_mut(), + ); + self.decode_result(r) + } + } + + #[cfg(feature = "load_extension")] + pub fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> { + let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) }; + self.decode_result(r) + } + + #[cfg(feature = "load_extension")] + pub fn load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> Result<()> { + use std::os::raw::c_char; + + let dylib_str = super::path_to_cstring(dylib_path)?; + unsafe { + let mut errmsg: *mut c_char = mem::uninitialized(); + let r = if let Some(entry_point) = entry_point { + let c_entry = str_to_cstring(entry_point)?; + ffi::sqlite3_load_extension( + self.db, + dylib_str.as_ptr(), + c_entry.as_ptr(), + &mut errmsg, + ) + } else { + ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg) + }; + if r == ffi::SQLITE_OK { + Ok(()) + } else { + let message = super::errmsg_to_string(&*errmsg); + ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void); + Err(error_from_sqlite_code(r, Some(message))) + } + } + } + + pub fn last_insert_rowid(&self) -> i64 { + unsafe { ffi::sqlite3_last_insert_rowid(self.db()) } + } + + pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result> { + if sql.len() >= ::std::i32::MAX as usize { + return Err(error_from_sqlite_code(ffi::SQLITE_TOOBIG, None)); + } + let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() }; + let c_sql = str_to_cstring(sql)?; + let len_with_nul = (sql.len() + 1) as c_int; + let r = unsafe { + if cfg!(feature = "unlock_notify") { + let mut rc; + loop { + rc = ffi::sqlite3_prepare_v2( + self.db(), + c_sql.as_ptr(), + len_with_nul, + &mut c_stmt, + ptr::null_mut(), + ); + if !unlock_notify::is_locked(self.db, rc) { + break; + } + rc = unlock_notify::wait_for_unlock_notify(self.db); + if rc != ffi::SQLITE_OK { + break; + } + } + rc + } else { + ffi::sqlite3_prepare_v2( + self.db(), + c_sql.as_ptr(), + len_with_nul, + &mut c_stmt, + ptr::null_mut(), + ) + } + }; + self.decode_result(r) + .map(|_| Statement::new(conn, RawStatement::new(c_stmt))) + } + + pub fn changes(&mut self) -> usize { + unsafe { ffi::sqlite3_changes(self.db()) as usize } + } + + pub fn is_autocommit(&self) -> bool { + unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 } + } + + #[cfg(feature = "bundled")] // 3.8.6 + pub fn is_busy(&self) -> bool { + let db = self.db(); + unsafe { + let mut stmt = ffi::sqlite3_next_stmt(db, ptr::null_mut()); + while !stmt.is_null() { + if ffi::sqlite3_stmt_busy(stmt) != 0 { + return true; + } + stmt = ffi::sqlite3_next_stmt(db, stmt); + } + } + false + } + + #[cfg(not(feature = "hooks"))] + fn remove_hooks(&mut self) {} +} + +impl Drop for InnerConnection { + #[allow(unused_must_use)] + fn drop(&mut self) { + use std::thread::panicking; + + if let Err(e) = self.close() { + if panicking() { + eprintln!("Error while closing SQLite connection: {:?}", e); + } else { + panic!("Error while closing SQLite connection: {:?}", e); + } + } + } +} + +#[cfg(not(feature = "bundled"))] +static SQLITE_VERSION_CHECK: Once = ONCE_INIT; +#[cfg(not(feature = "bundled"))] +pub static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT; + +#[cfg(not(feature = "bundled"))] +fn ensure_valid_sqlite_version() { + use crate::version::version; + + SQLITE_VERSION_CHECK.call_once(|| { + let version_number = version_number(); + + // Check our hard floor. + if version_number < 3_006_008 { + panic!("rusqlite requires SQLite 3.6.8 or newer"); + } + + // Check that the major version number for runtime and buildtime match. + let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000; + let runtime_major = version_number / 1_000_000; + if buildtime_major != runtime_major { + panic!( + "rusqlite was built against SQLite {} but is running with SQLite {}", + str::from_utf8(ffi::SQLITE_VERSION).unwrap(), + version() + ); + } + + if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) { + return; + } + + // Check that the runtime version number is compatible with the version number + // we found at build-time. + if version_number < ffi::SQLITE_VERSION_NUMBER { + panic!( + "\ +rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either: +* Recompile rusqlite and link against the SQLite version you are using at runtime, or +* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this + means you're sure everything will work correctly even though the runtime version is older than + the version we found at build time.", + str::from_utf8(ffi::SQLITE_VERSION).unwrap(), + version() + ); + } + }); +} + +static SQLITE_INIT: Once = ONCE_INIT; +pub static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT; + +fn ensure_safe_sqlite_threading_mode() -> Result<()> { + // Ensure SQLite was compiled in thredsafe mode. + if unsafe { ffi::sqlite3_threadsafe() == 0 } { + return Err(Error::SqliteSingleThreadedMode); + } + + // Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, + // but it's possible someone configured it to be in Single-thread mode + // before calling into us. That would mean we're exposing an unsafe API via + // a safe one (in Rust terminology), which is no good. We have two options + // to protect against this, depending on the version of SQLite we're linked + // with: + // + // 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for + // the magic value 8. This isn't documented, but it's what SQLite + // returns for its mutex allocation function in Single-thread mode. + // 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the + // threading mode. The check we perform for >= 3.7.0 will segfault. + // Instead, we insist on being able to call sqlite3_config and + // sqlite3_initialize ourself, ensuring we know the threading + // mode. This will fail if someone else has already initialized SQLite + // even if they initialized it safely. That's not ideal either, which is + // why we expose bypass_sqlite_initialization above. + if version_number() >= 3_007_000 { + const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; + let is_singlethreaded = unsafe { + let mutex_ptr = ffi::sqlite3_mutex_alloc(0); + let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC; + ffi::sqlite3_mutex_free(mutex_ptr); + is_singlethreaded + }; + if is_singlethreaded { + Err(Error::SqliteSingleThreadedMode) + } else { + Ok(()) + } + } else { + SQLITE_INIT.call_once(|| { + if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) { + return; + } + + unsafe { + let msg = "\ +Could not ensure safe initialization of SQLite. +To fix this, either: +* Upgrade SQLite to at least version 3.7.0 +* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call + rusqlite::bypass_sqlite_initialization() prior to your first connection attempt."; + + if ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) != ffi::SQLITE_OK { + panic!(msg); + } + if ffi::sqlite3_initialize() != ffi::SQLITE_OK { + panic!(msg); + } + } + }); + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 7e7153a..9bb11a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,39 +72,31 @@ use std::convert; use std::default::Default; use std::ffi::{CStr, CString}; use std::fmt; -use std::mem; use std::os::raw::{c_char, c_int}; use std::path::{Path, PathBuf}; -use std::ptr; use std::result; use std::str; -use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; -use std::sync::{Arc, Mutex, Once, ONCE_INIT}; +use std::sync::atomic::Ordering; +use std::sync::{Arc, Mutex}; use crate::cache::StatementCache; -use crate::error::{error_from_handle, error_from_sqlite_code}; +use crate::inner_connection::{InnerConnection, BYPASS_SQLITE_INIT}; use crate::raw_statement::RawStatement; use crate::types::ValueRef; -pub use crate::types::ToSql; - -pub use crate::statement::{Statement, StatementStatus}; - -pub use crate::row::{AndThenRows, MappedRows, Row, RowIndex, Rows}; - -pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; - +pub use crate::cache::CachedStatement; pub use crate::error::Error; pub use crate::ffi::ErrorCode; - -pub use crate::cache::CachedStatement; -pub use crate::version::*; - #[cfg(feature = "hooks")] pub use crate::hooks::*; #[cfg(feature = "load_extension")] pub use crate::load_extension_guard::LoadExtensionGuard; +pub use crate::row::{AndThenRows, MappedRows, Row, RowIndex, Rows}; +pub use crate::statement::{Statement, StatementStatus}; +pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; +pub use crate::types::ToSql; +pub use crate::version::*; #[cfg(feature = "backup")] pub mod backup; @@ -120,6 +112,7 @@ mod error; pub mod functions; #[cfg(feature = "hooks")] mod hooks; +mod inner_connection; #[cfg(feature = "limits")] pub mod limits; #[cfg(feature = "load_extension")] @@ -720,24 +713,6 @@ impl fmt::Debug for Connection { } } -struct InnerConnection { - db: *mut ffi::sqlite3, - // It's unsafe to call `sqlite3_close` while another thread is performing - // a `sqlite3_interrupt`, and vice versa, so we take this mutex during - // those functions. This protects a copy of the `db` pointer (which is - // cleared on closing), however the main copy, `db`, is unprotected. - // Otherwise, a long running query would prevent calling interrupt, as - // interrupt would only acquire the lock after the query's completion. - interrupt_lock: Arc>, - #[cfg(feature = "hooks")] - free_commit_hook: Option, - #[cfg(feature = "hooks")] - free_rollback_hook: Option, - #[cfg(feature = "hooks")] - free_update_hook: Option, - owned: bool, -} - bitflags! { #[doc = "Flags for opening SQLite database connections."] #[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."] @@ -764,13 +739,6 @@ impl Default for OpenFlags { } } -static SQLITE_INIT: Once = ONCE_INIT; -#[cfg(not(feature = "bundled"))] -static SQLITE_VERSION_CHECK: Once = ONCE_INIT; -static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT; -#[cfg(not(feature = "bundled"))] -static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT; - /// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or /// later. If you are running against a SQLite older than that, rusqlite /// attempts to ensure safety by performing configuration and initialization of @@ -801,348 +769,7 @@ pub unsafe fn bypass_sqlite_initialization() { /// your first connection attempt. pub unsafe fn bypass_sqlite_version_check() { #[cfg(not(feature = "bundled"))] - BYPASS_VERSION_CHECK.store(true, Ordering::Relaxed); -} - -#[cfg(not(feature = "bundled"))] -fn ensure_valid_sqlite_version() { - SQLITE_VERSION_CHECK.call_once(|| { - let version_number = version_number(); - - // Check our hard floor. - if version_number < 3_006_008 { - panic!("rusqlite requires SQLite 3.6.8 or newer"); - } - - // Check that the major version number for runtime and buildtime match. - let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000; - let runtime_major = version_number / 1_000_000; - if buildtime_major != runtime_major { - panic!( - "rusqlite was built against SQLite {} but is running with SQLite {}", - str::from_utf8(ffi::SQLITE_VERSION).unwrap(), - version() - ); - } - - if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) { - return; - } - - // Check that the runtime version number is compatible with the version number - // we found at build-time. - if version_number < ffi::SQLITE_VERSION_NUMBER { - panic!( - "\ -rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either: -* Recompile rusqlite and link against the SQLite version you are using at runtime, or -* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this - means you're sure everything will work correctly even though the runtime version is older than - the version we found at build time.", - str::from_utf8(ffi::SQLITE_VERSION).unwrap(), - version() - ); - } - }); -} - -fn ensure_safe_sqlite_threading_mode() -> Result<()> { - // Ensure SQLite was compiled in thredsafe mode. - if unsafe { ffi::sqlite3_threadsafe() == 0 } { - return Err(Error::SqliteSingleThreadedMode); - } - - // Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, - // but it's possible someone configured it to be in Single-thread mode - // before calling into us. That would mean we're exposing an unsafe API via - // a safe one (in Rust terminology), which is no good. We have two options - // to protect against this, depending on the version of SQLite we're linked - // with: - // - // 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for - // the magic value 8. This isn't documented, but it's what SQLite - // returns for its mutex allocation function in Single-thread mode. - // 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the - // threading mode. The check we perform for >= 3.7.0 will segfault. - // Instead, we insist on being able to call sqlite3_config and - // sqlite3_initialize ourself, ensuring we know the threading - // mode. This will fail if someone else has already initialized SQLite - // even if they initialized it safely. That's not ideal either, which is - // why we expose bypass_sqlite_initialization above. - if version_number() >= 3_007_000 { - const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; - let is_singlethreaded = unsafe { - let mutex_ptr = ffi::sqlite3_mutex_alloc(0); - let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC; - ffi::sqlite3_mutex_free(mutex_ptr); - is_singlethreaded - }; - if is_singlethreaded { - Err(Error::SqliteSingleThreadedMode) - } else { - Ok(()) - } - } else { - SQLITE_INIT.call_once(|| { - if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) { - return; - } - - unsafe { - let msg = "\ -Could not ensure safe initialization of SQLite. -To fix this, either: -* Upgrade SQLite to at least version 3.7.0 -* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call - rusqlite::bypass_sqlite_initialization() prior to your first connection attempt."; - - if ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) != ffi::SQLITE_OK { - panic!(msg); - } - if ffi::sqlite3_initialize() != ffi::SQLITE_OK { - panic!(msg); - } - } - }); - Ok(()) - } -} - -impl InnerConnection { - #[cfg(not(feature = "hooks"))] - fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection { - InnerConnection { - db, - interrupt_lock: Arc::new(Mutex::new(db)), - owned, - } - } - - #[cfg(feature = "hooks")] - fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection { - InnerConnection { - db, - interrupt_lock: Arc::new(Mutex::new(db)), - free_commit_hook: None, - free_rollback_hook: None, - free_update_hook: None, - owned, - } - } - - fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result { - #[cfg(not(feature = "bundled"))] - ensure_valid_sqlite_version(); - ensure_safe_sqlite_threading_mode()?; - - // Replicate the check for sane open flags from SQLite, because the check in - // SQLite itself wasn't added until version 3.7.3. - debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02); - debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04); - debug_assert_eq!( - 1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, - 0x40 - ); - if (1 << (flags.bits & 0x7)) & 0x46 == 0 { - return Err(Error::SqliteFailure( - ffi::Error::new(ffi::SQLITE_MISUSE), - None, - )); - } - - unsafe { - let mut db: *mut ffi::sqlite3 = mem::uninitialized(); - let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); - if r != ffi::SQLITE_OK { - let e = if db.is_null() { - error_from_sqlite_code(r, None) - } else { - let e = error_from_handle(db, r); - ffi::sqlite3_close(db); - e - }; - - return Err(e); - } - let r = ffi::sqlite3_busy_timeout(db, 5000); - if r != ffi::SQLITE_OK { - let e = error_from_handle(db, r); - ffi::sqlite3_close(db); - return Err(e); - } - - // attempt to turn on extended results code; don't fail if we can't. - ffi::sqlite3_extended_result_codes(db, 1); - - Ok(InnerConnection::new(db, true)) - } - } - - fn db(&self) -> *mut ffi::sqlite3 { - self.db - } - - fn decode_result(&mut self, code: c_int) -> Result<()> { - InnerConnection::decode_result_raw(self.db(), code) - } - - fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> { - if code == ffi::SQLITE_OK { - Ok(()) - } else { - Err(error_from_handle(db, code)) - } - } - - fn close(&mut self) -> Result<()> { - if self.db.is_null() { - return Ok(()); - } - self.remove_hooks(); - let mut shared_handle = self.interrupt_lock.lock().unwrap(); - assert!( - !shared_handle.is_null(), - "Bug: Somehow interrupt_lock was cleared before the DB was closed" - ); - if !self.owned { - self.db = ptr::null_mut(); - return Ok(()); - } - unsafe { - let r = ffi::sqlite3_close(self.db); - // Need to use _raw because _guard has a reference out, and - // decode_result takes &mut self. - let r = InnerConnection::decode_result_raw(self.db, r); - if r.is_ok() { - *shared_handle = ptr::null_mut(); - self.db = ptr::null_mut(); - } - r - } - } - - fn get_interrupt_handle(&self) -> InterruptHandle { - InterruptHandle { - db_lock: Arc::clone(&self.interrupt_lock), - } - } - - fn execute_batch(&mut self, sql: &str) -> Result<()> { - let c_sql = str_to_cstring(sql)?; - unsafe { - let r = ffi::sqlite3_exec( - self.db(), - c_sql.as_ptr(), - None, - ptr::null_mut(), - ptr::null_mut(), - ); - self.decode_result(r) - } - } - - #[cfg(feature = "load_extension")] - fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> { - let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) }; - self.decode_result(r) - } - - #[cfg(feature = "load_extension")] - fn load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> Result<()> { - let dylib_str = path_to_cstring(dylib_path)?; - unsafe { - let mut errmsg: *mut c_char = mem::uninitialized(); - let r = if let Some(entry_point) = entry_point { - let c_entry = str_to_cstring(entry_point)?; - ffi::sqlite3_load_extension( - self.db, - dylib_str.as_ptr(), - c_entry.as_ptr(), - &mut errmsg, - ) - } else { - ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg) - }; - if r == ffi::SQLITE_OK { - Ok(()) - } else { - let message = errmsg_to_string(&*errmsg); - ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void); - Err(error_from_sqlite_code(r, Some(message))) - } - } - } - - fn last_insert_rowid(&self) -> i64 { - unsafe { ffi::sqlite3_last_insert_rowid(self.db()) } - } - - fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result> { - if sql.len() >= ::std::i32::MAX as usize { - return Err(error_from_sqlite_code(ffi::SQLITE_TOOBIG, None)); - } - let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() }; - let c_sql = str_to_cstring(sql)?; - let len_with_nul = (sql.len() + 1) as c_int; - let r = unsafe { - if cfg!(feature = "unlock_notify") { - let mut rc; - loop { - rc = ffi::sqlite3_prepare_v2( - self.db(), - c_sql.as_ptr(), - len_with_nul, - &mut c_stmt, - ptr::null_mut(), - ); - if !unlock_notify::is_locked(self.db, rc) { - break; - } - rc = unlock_notify::wait_for_unlock_notify(self.db); - if rc != ffi::SQLITE_OK { - break; - } - } - rc - } else { - ffi::sqlite3_prepare_v2( - self.db(), - c_sql.as_ptr(), - len_with_nul, - &mut c_stmt, - ptr::null_mut(), - ) - } - }; - self.decode_result(r) - .map(|_| Statement::new(conn, RawStatement::new(c_stmt))) - } - - fn changes(&mut self) -> usize { - unsafe { ffi::sqlite3_changes(self.db()) as usize } - } - - fn is_autocommit(&self) -> bool { - unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 } - } - - #[cfg(feature = "bundled")] // 3.8.6 - fn is_busy(&self) -> bool { - let db = self.db(); - unsafe { - let mut stmt = ffi::sqlite3_next_stmt(db, ptr::null_mut()); - while !stmt.is_null() { - if ffi::sqlite3_stmt_busy(stmt) != 0 { - return true; - } - stmt = ffi::sqlite3_next_stmt(db, stmt); - } - } - false - } - - #[cfg(not(feature = "hooks"))] - fn remove_hooks(&mut self) {} + inner_connection::BYPASS_VERSION_CHECK.store(true, Ordering::Relaxed); } /// Allows interrupting a long-running computation. @@ -1164,21 +791,6 @@ impl InterruptHandle { } } -impl Drop for InnerConnection { - #[allow(unused_must_use)] - fn drop(&mut self) { - use std::thread::panicking; - - if let Err(e) = self.close() { - if panicking() { - eprintln!("Error while closing SQLite connection: {:?}", e); - } else { - panic!("Error while closing SQLite connection: {:?}", e); - } - } - } -} - #[cfg(feature = "bundled")] // 3.7.10 unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option { let db_name = DatabaseName::Main.to_cstring().unwrap();