rusqlite/src/inner_connection.rs

446 lines
16 KiB
Rust
Raw Normal View History

2020-04-14 17:04:19 +08:00
use std::ffi::CStr;
use std::os::raw::{c_char, c_int};
2019-02-02 19:46:52 +08:00
#[cfg(feature = "load_extension")]
use std::path::Path;
use std::ptr;
use std::str;
2019-02-09 13:42:33 +08:00
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
2019-02-02 19:46:52 +08:00
use super::ffi;
2020-04-14 17:04:19 +08:00
use super::str_for_sqlite;
2019-02-02 19:46:52 +08:00
use super::{Connection, InterruptHandle, OpenFlags, Result};
2022-03-14 00:31:07 +08:00
use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error};
2019-02-02 19:46:52 +08:00
use crate::raw_statement::RawStatement;
use crate::statement::Statement;
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)>,
2019-02-02 19:46:52 +08:00
#[cfg(feature = "hooks")]
pub free_rollback_hook: Option<unsafe fn(*mut std::os::raw::c_void)>,
2019-02-02 19:46:52 +08:00
#[cfg(feature = "hooks")]
pub free_update_hook: Option<unsafe fn(*mut std::os::raw::c_void)>,
2020-10-25 18:58:47 +08:00
#[cfg(feature = "hooks")]
pub progress_handler: Option<Box<dyn FnMut() -> bool + Send>>,
#[cfg(feature = "hooks")]
pub authorizer: Option<crate::hooks::BoxedAuthorizer>,
2019-02-02 19:46:52 +08:00
owned: bool,
}
unsafe impl Send for InnerConnection {}
2019-02-02 19:46:52 +08:00
impl InnerConnection {
#[allow(clippy::mutex_atomic)]
#[inline]
pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
2019-02-02 19:46:52 +08:00
InnerConnection {
db,
interrupt_lock: Arc::new(Mutex::new(db)),
#[cfg(feature = "hooks")]
2019-02-02 19:46:52 +08:00
free_commit_hook: None,
#[cfg(feature = "hooks")]
2019-02-02 19:46:52 +08:00
free_rollback_hook: None,
#[cfg(feature = "hooks")]
2019-02-02 19:46:52 +08:00
free_update_hook: None,
2020-10-25 18:58:47 +08:00
#[cfg(feature = "hooks")]
progress_handler: None,
#[cfg(feature = "hooks")]
authorizer: None,
2019-02-02 19:46:52 +08:00
owned,
}
}
2020-02-09 19:08:25 +08:00
pub fn open_with_flags(
2020-04-14 17:04:19 +08:00
c_path: &CStr,
2020-02-09 19:08:25 +08:00
flags: OpenFlags,
2020-04-14 17:04:19 +08:00
vfs: Option<&CStr>,
2020-02-09 19:08:25 +08:00
) -> Result<InnerConnection> {
2019-02-02 19:46:52 +08:00
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,
));
}
2020-02-09 19:08:25 +08:00
let z_vfs = match vfs {
2019-06-17 17:42:54 +08:00
Some(c_vfs) => c_vfs.as_ptr(),
2020-02-09 19:08:25 +08:00
None => ptr::null(),
};
2019-06-17 17:42:54 +08:00
2019-02-02 19:46:52 +08:00
unsafe {
2020-04-07 10:53:55 +08:00
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);
2019-02-02 19:46:52 +08:00
if r != ffi::SQLITE_OK {
let e = if db.is_null() {
error_from_sqlite_code(r, Some(c_path.to_string_lossy().to_string()))
2019-02-02 19:46:52 +08:00
} else {
let mut e = error_from_handle(db, r);
if let Error::SqliteFailure(
ffi::Error {
code: ffi::ErrorCode::CannotOpen,
2019-11-03 18:19:07 +08:00
..
},
Some(msg),
) = e
{
e = Error::SqliteFailure(
ffi::Error::new(r),
2022-10-29 01:02:49 +08:00
Some(format!("{msg}: {}", c_path.to_string_lossy())),
);
}
2019-02-02 19:46:52 +08:00
ffi::sqlite3_close(db);
e
};
return Err(e);
}
2020-05-16 17:01:30 +08:00
// attempt to turn on extended results code; don't fail if we can't.
ffi::sqlite3_extended_result_codes(db, 1);
2019-02-02 19:46:52 +08:00
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))
}
}
#[inline]
2019-02-02 19:46:52 +08:00
pub fn db(&self) -> *mut ffi::sqlite3 {
self.db
}
#[inline]
pub fn decode_result(&self, code: c_int) -> Result<()> {
unsafe { InnerConnection::decode_result_raw(self.db(), code) }
2019-02-02 19:46:52 +08:00
}
#[inline]
unsafe fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> {
2019-02-02 19:46:52 +08:00
if code == ffi::SQLITE_OK {
Ok(())
} else {
Err(error_from_handle(db, code))
}
}
#[allow(clippy::mutex_atomic)]
2019-02-02 19:46:52 +08:00
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
}
}
#[inline]
2019-02-02 19:46:52 +08:00
pub fn get_interrupt_handle(&self) -> InterruptHandle {
InterruptHandle {
db_lock: Arc::clone(&self.interrupt_lock),
}
}
#[inline]
2019-02-02 19:46:52 +08:00
#[cfg(feature = "load_extension")]
2021-09-06 17:49:29 +08:00
pub unsafe fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> {
let r = ffi::sqlite3_enable_load_extension(self.db, onoff);
2019-02-02 19:46:52 +08:00
self.decode_result(r)
}
#[cfg(feature = "load_extension")]
2021-09-06 17:49:29 +08:00
pub unsafe fn load_extension(
&self,
dylib_path: &Path,
entry_point: Option<&str>,
) -> Result<()> {
2019-02-02 19:46:52 +08:00
let dylib_str = super::path_to_cstring(dylib_path)?;
2021-09-06 17:49:29 +08:00
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.cast::<std::os::raw::c_void>());
2021-09-06 17:49:29 +08:00
Err(error_from_sqlite_code(r, Some(message)))
2019-02-02 19:46:52 +08:00
}
}
#[inline]
2019-02-02 19:46:52 +08:00
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();
// TODO sqlite3_prepare_v3 (https://sqlite.org/c3ref/c_prepare_normalize.html) // 3.20.0, #728
2022-01-05 12:23:32 +08:00
#[cfg(not(feature = "unlock_notify"))]
2019-02-02 19:46:52 +08:00
let r = unsafe {
2022-01-05 12:23:32 +08:00
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,
)
};
#[cfg(feature = "unlock_notify")]
let r = unsafe {
use crate::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,
2022-01-05 12:23:32 +08:00
);
if !unlock_notify::is_locked(self.db, rc) {
break;
}
rc = unlock_notify::wait_for_unlock_notify(self.db);
if rc != ffi::SQLITE_OK {
break;
}
2019-02-02 19:46:52 +08:00
}
2022-01-05 12:23:32 +08:00
rc
2019-02-02 19:46:52 +08:00
};
// If there is an error, *ppStmt is set to NULL.
2022-03-14 00:31:07 +08:00
if r != ffi::SQLITE_OK {
2022-04-21 21:14:08 +08:00
return Err(unsafe { error_with_offset(self.db, r, sql) });
2022-03-14 00:31:07 +08:00
}
// 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 {
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)
}))
2019-02-02 19:46:52 +08:00
}
#[inline]
pub fn changes(&self) -> u64 {
#[cfg(not(feature = "modern_sqlite"))]
unsafe {
ffi::sqlite3_changes(self.db()) as u64
}
#[cfg(feature = "modern_sqlite")] // 3.37.0
unsafe {
ffi::sqlite3_changes64(self.db()) as u64
}
2019-02-02 19:46:52 +08:00
}
#[inline]
2019-02-02 19:46:52 +08:00
pub fn is_autocommit(&self) -> bool {
unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 }
}
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
}
pub fn cache_flush(&mut self) -> Result<()> {
2021-07-28 00:17:07 +08:00
crate::error::check(unsafe { ffi::sqlite3_db_cacheflush(self.db()) })
}
2019-02-02 19:46:52 +08:00
#[cfg(not(feature = "hooks"))]
#[inline]
2019-02-02 19:46:52 +08:00
fn remove_hooks(&mut self) {}
pub fn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool> {
let name = db_name.as_cstring()?;
let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) };
match r {
0 => Ok(false),
1 => Ok(true),
-1 => Err(Error::SqliteFailure(
ffi::Error::new(ffi::SQLITE_MISUSE),
2022-10-29 01:02:49 +08:00
Some(format!("{db_name:?} is not the name of a database")),
)),
_ => Err(error_from_sqlite_code(
r,
Some("Unexpected result".to_owned()),
)),
}
}
#[cfg(feature = "modern_sqlite")] // 3.37.0
pub fn txn_state(
&self,
db_name: Option<super::DatabaseName<'_>>,
) -> Result<super::transaction::TransactionState> {
let r = if let Some(ref name) = db_name {
let name = name.as_cstring()?;
unsafe { ffi::sqlite3_txn_state(self.db, name.as_ptr()) }
} else {
unsafe { ffi::sqlite3_txn_state(self.db, ptr::null()) }
};
match r {
0 => Ok(super::transaction::TransactionState::None),
1 => Ok(super::transaction::TransactionState::Read),
2 => Ok(super::transaction::TransactionState::Write),
-1 => Err(Error::SqliteFailure(
ffi::Error::new(ffi::SQLITE_MISUSE),
2022-10-29 01:02:49 +08:00
Some(format!("{db_name:?} is not the name of a valid schema")),
)),
_ => Err(error_from_sqlite_code(
r,
Some("Unexpected result".to_owned()),
)),
}
}
#[inline]
#[cfg(feature = "release_memory")]
pub fn release_memory(&self) -> Result<()> {
self.decode_result(unsafe { ffi::sqlite3_db_release_memory(self.db) })
}
2019-02-02 19:46:52 +08:00
}
impl Drop for InnerConnection {
#[allow(unused_must_use)]
#[inline]
2019-02-02 19:46:52 +08:00
fn drop(&mut self) {
self.close();
2019-02-02 19:46:52 +08:00
}
}
#[cfg(not(any(target_arch = "wasm32")))]
static SQLITE_INIT: std::sync::Once = std::sync::Once::new();
2019-02-09 13:42:33 +08:00
pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false);
2019-02-02 19:46:52 +08:00
// 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")))]
2019-02-02 19:46:52 +08:00
fn ensure_safe_sqlite_threading_mode() -> Result<()> {
// Ensure SQLite was compiled in threadsafe mode.
2019-02-02 19:46:52 +08:00
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 {
2021-10-02 02:09:48 +08:00
assert!(ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) == ffi::SQLITE_OK && ffi::sqlite3_initialize() == ffi::SQLITE_OK,
2021-03-03 10:19:11 +08:00
"Could not ensure safe initialization of SQLite.\n\
To fix this, either:\n\
* Upgrade SQLite to at least version 3.7.0\n\
* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call\n\
rusqlite::bypass_sqlite_initialization() prior to your first connection attempt."
);
2019-02-02 19:46:52 +08:00
}
});
Ok(())
}
}