diff options
Diffstat (limited to 'third_party/rust/rusqlite/src/inner_connection.rs')
-rw-r--r-- | third_party/rust/rusqlite/src/inner_connection.rs | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/third_party/rust/rusqlite/src/inner_connection.rs b/third_party/rust/rusqlite/src/inner_connection.rs new file mode 100644 index 0000000000..dd786fed0f --- /dev/null +++ b/third_party/rust/rusqlite/src/inner_connection.rs @@ -0,0 +1,430 @@ +use std::ffi::CStr; +use std::os::raw::{c_char, c_int}; +#[cfg(feature = "load_extension")] +use std::path::Path; +use std::ptr; +use std::str; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; + +use super::ffi; +use super::str_for_sqlite; +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<Mutex<*mut ffi::sqlite3>>, + #[cfg(feature = "hooks")] + pub free_commit_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>, + #[cfg(feature = "hooks")] + pub free_rollback_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>, + #[cfg(feature = "hooks")] + pub free_update_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>, + owned: bool, +} + +impl InnerConnection { + #[allow(clippy::mutex_atomic)] + pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection { + InnerConnection { + db, + interrupt_lock: Arc::new(Mutex::new(db)), + #[cfg(feature = "hooks")] + free_commit_hook: None, + #[cfg(feature = "hooks")] + free_rollback_hook: None, + #[cfg(feature = "hooks")] + free_update_hook: None, + owned, + } + } + + pub fn open_with_flags( + c_path: &CStr, + flags: OpenFlags, + vfs: Option<&CStr>, + ) -> Result<InnerConnection> { + #[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, + )); + } + + let z_vfs = match vfs { + Some(c_vfs) => c_vfs.as_ptr(), + None => ptr::null(), + }; + + unsafe { + let mut db: *mut ffi::sqlite3 = ptr::null_mut(); + let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), z_vfs); + if r != ffi::SQLITE_OK { + let e = if db.is_null() { + error_from_sqlite_code(r, Some(c_path.to_string_lossy().to_string())) + } else { + let mut e = error_from_handle(db, r); + if let Error::SqliteFailure( + ffi::Error { + code: ffi::ErrorCode::CannotOpen, + .. + }, + Some(msg), + ) = e + { + e = Error::SqliteFailure( + ffi::Error::new(r), + Some(format!("{}: {}", msg, c_path.to_string_lossy())), + ); + } + ffi::sqlite3_close(db); + e + }; + + return Err(e); + } + + // attempt to turn on extended results code; don't fail if we can't. + ffi::sqlite3_extended_result_codes(db, 1); + + 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); + } + + Ok(InnerConnection::new(db, true)) + } + } + + pub fn db(&self) -> *mut ffi::sqlite3 { + self.db + } + + pub fn decode_result(&mut self, code: c_int) -> Result<()> { + unsafe { InnerConnection::decode_result_raw(self.db(), code) } + } + + unsafe 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)) + } + } + + #[allow(clippy::mutex_atomic)] + 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), + } + } + + #[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<()> { + let dylib_str = super::path_to_cstring(dylib_path)?; + unsafe { + let mut errmsg: *mut c_char = ptr::null_mut(); + let r = if let Some(entry_point) = entry_point { + let c_entry = crate::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<Statement<'a>> { + let mut c_stmt = ptr::null_mut(); + let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?; + let mut c_tail = ptr::null(); + let r = unsafe { + if cfg!(feature = "unlock_notify") { + let mut rc; + loop { + rc = ffi::sqlite3_prepare_v2( + self.db(), + c_sql, + len, + &mut c_stmt as *mut *mut ffi::sqlite3_stmt, + &mut c_tail as *mut *const c_char, + ); + 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, + len, + &mut c_stmt as *mut *mut ffi::sqlite3_stmt, + &mut c_tail as *mut *const c_char, + ) + } + }; + // If there is an error, *ppStmt is set to NULL. + self.decode_result(r)?; + // If the input text contains no SQL (if the input is an empty string or a + // comment) then *ppStmt is set to NULL. + let c_stmt: *mut ffi::sqlite3_stmt = c_stmt; + let c_tail: *const c_char = c_tail; + let tail = if c_tail.is_null() { + 0 + } else { + // TODO nightly feature ptr_offset_from #41079 + let n = (c_tail as isize) - (c_sql as isize); + if n <= 0 || n >= len as isize { + 0 + } else { + n as usize + } + }; + Ok(Statement::new(conn, unsafe { + RawStatement::new(c_stmt, tail) + })) + } + + 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 = "modern_sqlite")] // 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: std::sync::Once = std::sync::Once::new(); +#[cfg(not(feature = "bundled"))] +pub static BYPASS_VERSION_CHECK: AtomicBool = AtomicBool::new(false); + +#[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() + ); + } + }); +} + +#[cfg(not(any(target_arch = "wasm32")))] +static SQLITE_INIT: std::sync::Once = std::sync::Once::new(); + +pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false); + +// threading mode checks are not necessary (and do not work) on target +// platforms that do not have threading (such as webassembly) +#[cfg(any(target_arch = "wasm32"))] +fn ensure_safe_sqlite_threading_mode() -> Result<()> { + Ok(()) +} + +#[cfg(not(any(target_arch = "wasm32")))] +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(()) + } +} |