Merge pull request #456 from gwenn/from_handle

Introduce Connection::from_handle
This commit is contained in:
gwenn 2019-01-26 10:26:22 +01:00 committed by GitHub
commit b7d1e7e4ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -196,7 +196,7 @@ pub enum DatabaseName<'a> {
// Currently DatabaseName is only used by the backup and blob mods, so hide // Currently DatabaseName is only used by the backup and blob mods, so hide
// this (private) impl to avoid dead code warnings. // this (private) impl to avoid dead code warnings.
#[cfg(any(feature = "backup", feature = "blob", feature = "session"))] #[cfg(any(feature = "backup", feature = "blob", feature = "session", feature = "bundled"))]
impl<'a> DatabaseName<'a> { impl<'a> DatabaseName<'a> {
fn to_cstring(&self) -> Result<CString> { fn to_cstring(&self) -> Result<CString> {
use self::DatabaseName::{Attached, Main, Temp}; use self::DatabaseName::{Attached, Main, Temp};
@ -594,6 +594,20 @@ impl Connection {
self.db.borrow().db() self.db.borrow().db()
} }
/// Create a `Connection` from a raw handle.
///
/// The underlying SQLite database connection handle will not be closed when
/// the returned connection is dropped/closed.
pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result<Connection> {
let db_path = db_filename(db);
let db = InnerConnection::new(db, false);
Ok(Connection {
db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: db_path,
})
}
/// Get access to a handle that can be used to interrupt long running /// Get access to a handle that can be used to interrupt long running
/// queries from another thread. /// queries from another thread.
pub fn get_interrupt_handle(&self) -> InterruptHandle { pub fn get_interrupt_handle(&self) -> InterruptHandle {
@ -644,6 +658,7 @@ struct InnerConnection {
free_rollback_hook: Option<fn(*mut ::std::os::raw::c_void)>, free_rollback_hook: Option<fn(*mut ::std::os::raw::c_void)>,
#[cfg(feature = "hooks")] #[cfg(feature = "hooks")]
free_update_hook: Option<fn(*mut ::std::os::raw::c_void)>, free_update_hook: Option<fn(*mut ::std::os::raw::c_void)>,
owned: bool,
} }
bitflags! { bitflags! {
@ -818,21 +833,23 @@ To fix this, either:
impl InnerConnection { impl InnerConnection {
#[cfg(not(feature = "hooks"))] #[cfg(not(feature = "hooks"))]
fn new(db: *mut ffi::sqlite3) -> InnerConnection { fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
InnerConnection { InnerConnection {
db, db,
interrupt_lock: Arc::new(Mutex::new(db)), interrupt_lock: Arc::new(Mutex::new(db)),
owned,
} }
} }
#[cfg(feature = "hooks")] #[cfg(feature = "hooks")]
fn new(db: *mut ffi::sqlite3) -> InnerConnection { fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
InnerConnection { InnerConnection {
db, db,
interrupt_lock: Arc::new(Mutex::new(db)), interrupt_lock: Arc::new(Mutex::new(db)),
free_commit_hook: None, free_commit_hook: None,
free_rollback_hook: None, free_rollback_hook: None,
free_update_hook: None, free_update_hook: None,
owned,
} }
} }
@ -880,7 +897,7 @@ impl InnerConnection {
// attempt to turn on extended results code; don't fail if we can't. // attempt to turn on extended results code; don't fail if we can't.
ffi::sqlite3_extended_result_codes(db, 1); ffi::sqlite3_extended_result_codes(db, 1);
Ok(InnerConnection::new(db)) Ok(InnerConnection::new(db, true))
} }
} }
@ -910,6 +927,10 @@ impl InnerConnection {
!shared_handle.is_null(), !shared_handle.is_null(),
"Bug: Somehow interrupt_lock was cleared before the DB was closed" "Bug: Somehow interrupt_lock was cleared before the DB was closed"
); );
if !self.owned {
self.db = ptr::null_mut();
return Ok(());
}
unsafe { unsafe {
let r = ffi::sqlite3_close(self.db); let r = ffi::sqlite3_close(self.db);
// Need to use _raw because _guard has a reference out, and // Need to use _raw because _guard has a reference out, and
@ -1081,6 +1102,21 @@ impl Drop for InnerConnection {
} }
} }
#[cfg(feature = "bundled")] // 3.7.10
unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option<PathBuf> {
let db_name = DatabaseName::Main.to_cstring().unwrap();
let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr());
if db_filename.is_null() {
None
} else {
CStr::from_ptr(db_filename).to_str().ok().map(PathBuf::from)
}
}
#[cfg(not(feature = "bundled"))]
unsafe fn db_filename(_: *mut ffi::sqlite3) -> Option<PathBuf> {
None
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use self::tempdir::TempDir; use self::tempdir::TempDir;
@ -1618,6 +1654,17 @@ mod test {
} }
} }
#[test]
fn test_from_handle() {
let db = checked_memory_handle();
let handle = unsafe { db.handle() };
{
let db = unsafe { Connection::from_handle(handle) }.unwrap();
db.execute_batch("PRAGMA VACUUM").unwrap();
}
db.close().unwrap();
}
mod query_and_then_tests { mod query_and_then_tests {
use super::*; use super::*;