mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 19:41:37 +08:00
Merge pull request #105 from jgallagher/check-sqlite-threading-mode
Add test and check for SQLite being in single-threaded mode
This commit is contained in:
commit
94492ebef3
@ -36,3 +36,6 @@ version = "0.3.0"
|
||||
[[test]]
|
||||
name = "config_log"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "deny_single_threaded_sqlite_config"
|
||||
|
@ -6,6 +6,9 @@
|
||||
`SqliteFailure` cases (which still include the error code but also include a Rust-friendlier
|
||||
enum as well), and rusqlite-level errors are captured in other cases. Because of this change,
|
||||
`SqliteError` no longer implements `PartialEq`.
|
||||
* BREAKING CHANGE: When opening a new detection, rusqlite now detects if SQLite was compiled or
|
||||
configured for single-threaded use only; if it was, connection attempts will fail. If this
|
||||
affects you, please open an issue.
|
||||
* BREAKING CHANGE: `SqliteTransactionDeferred`, `SqliteTransactionImmediate`, and
|
||||
`SqliteTransactionExclusive` are no longer exported. Instead, use
|
||||
`TransactionBehavior::Deferred`, `TransactionBehavior::Immediate`, and
|
||||
|
@ -14,6 +14,10 @@ pub enum Error {
|
||||
/// An error from an underlying SQLite call.
|
||||
SqliteFailure(ffi::Error, Option<String>),
|
||||
|
||||
/// Error reported when attempting to open a connection when SQLite was configured to
|
||||
/// allow single-threaded use only.
|
||||
SqliteSingleThreadedMode,
|
||||
|
||||
/// An error case available for implementors of the `FromSql` trait.
|
||||
FromSqlConversionFailure(Box<error::Error + Send + Sync>),
|
||||
|
||||
@ -77,6 +81,7 @@ impl fmt::Display for Error {
|
||||
match self {
|
||||
&Error::SqliteFailure(ref err, None) => err.fmt(f),
|
||||
&Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s),
|
||||
&Error::SqliteSingleThreadedMode => write!(f, "SQLite was compiled or configured for single-threaded use only"),
|
||||
&Error::FromSqlConversionFailure(ref err) => err.fmt(f),
|
||||
&Error::Utf8Error(ref err) => err.fmt(f),
|
||||
&Error::NulError(ref err) => err.fmt(f),
|
||||
@ -101,6 +106,7 @@ impl error::Error for Error {
|
||||
match self {
|
||||
&Error::SqliteFailure(ref err, None) => err.description(),
|
||||
&Error::SqliteFailure(_, Some(ref s)) => s,
|
||||
&Error::SqliteSingleThreadedMode => "SQLite was compiled or configured for single-threaded use only",
|
||||
&Error::FromSqlConversionFailure(ref err) => err.description(),
|
||||
&Error::Utf8Error(ref err) => err.description(),
|
||||
&Error::InvalidParameterName(_) => "invalid parameter name",
|
||||
@ -122,6 +128,7 @@ impl error::Error for Error {
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match self {
|
||||
&Error::SqliteFailure(ref err, _) => Some(err),
|
||||
&Error::SqliteSingleThreadedMode => None,
|
||||
&Error::FromSqlConversionFailure(ref err) => Some(&**err),
|
||||
&Error::Utf8Error(ref err) => Some(err),
|
||||
&Error::NulError(ref err) => Some(err),
|
||||
|
24
src/lib.rs
24
src/lib.rs
@ -546,6 +546,30 @@ impl InnerConnection {
|
||||
flags: OpenFlags)
|
||||
-> Result<InnerConnection> {
|
||||
unsafe {
|
||||
// Before opening the database, we need to check that SQLite hasn't been
|
||||
// compiled or configured to be in single-threaded mode. If it has, we're
|
||||
// exposing a very unsafe API to Rust, so refuse to open connections at all.
|
||||
// Unfortunately, the check for this is quite gross. sqlite3_threadsafe() only
|
||||
// returns how SQLite was _compiled_; there is no public API to check whether
|
||||
// someone called sqlite3_config() to set single-threaded mode. We can cheat
|
||||
// by trying to allocate a mutex, though; in single-threaded mode due to
|
||||
// compilation settings, the magic value 8 is returned (see the definition of
|
||||
// sqlite3_mutex_alloc at https://github.com/mackyle/sqlite/blob/master/src/mutex.h);
|
||||
// in single-threaded mode due to sqlite3_config(), the magic value 8 is also
|
||||
// returned (see the definition of noopMutexAlloc at
|
||||
// https://github.com/mackyle/sqlite/blob/master/src/mutex_noop.c).
|
||||
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
|
||||
let mutex_ptr = ffi::sqlite3_mutex_alloc(0);
|
||||
let is_singlethreaded = if mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
ffi::sqlite3_mutex_free(mutex_ptr);
|
||||
if is_singlethreaded {
|
||||
return Err(Error::SqliteSingleThreadedMode);
|
||||
}
|
||||
|
||||
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 {
|
||||
|
20
tests/deny_single_threaded_sqlite_config.rs
Normal file
20
tests/deny_single_threaded_sqlite_config.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Ensure we reject connections when SQLite is in single-threaded mode, as it
|
||||
//! would violate safety if multiple Rust threads tried to use connections.
|
||||
|
||||
extern crate rusqlite;
|
||||
extern crate libsqlite3_sys as ffi;
|
||||
|
||||
use rusqlite::Connection;
|
||||
|
||||
#[test]
|
||||
fn test_error_when_singlethread_mode() {
|
||||
// put SQLite into single-threaded mode
|
||||
unsafe {
|
||||
// 1 == SQLITE_CONFIG_SINGLETHREAD
|
||||
assert_eq!(ffi::sqlite3_config(1), ffi::SQLITE_OK);
|
||||
println!("{}", ffi::sqlite3_mutex_alloc(0) as u64);
|
||||
}
|
||||
|
||||
let result = Connection::open_in_memory();
|
||||
assert!(result.is_err());
|
||||
}
|
Loading…
Reference in New Issue
Block a user