use super::{RawConnection, SqliteConnection};
use crate::connection::instrumentation::DynInstrumentation;
use crate::connection::statement_cache::StatementCache;
use crate::connection::*;
use crate::expression::QueryMetadata;
use crate::prelude::{ConnectionResult, QueryResult};
use crate::query_builder::{Query, QueryFragment, QueryId};
use crate::sqlite::Sqlite;
use core::mem::ManuallyDrop;
#[allow(missing_debug_implementations)]
pub struct BorrowedSqliteConnection {
inner: ManuallyDrop<SqliteConnection>,
}
impl Drop for BorrowedSqliteConnection {
#[allow(unsafe_code)] fn drop(&mut self) {
let conn = unsafe { core::mem::ManuallyDrop::take(&mut self.inner) };
let SqliteConnection {
statement_cache,
raw_connection,
transaction_state: _,
metadata_lookup: (),
instrumentation,
} = conn;
let mut raw_connection = core::mem::ManuallyDrop::new(raw_connection);
raw_connection.reset_registered_hooks();
drop(statement_cache);
drop(instrumentation);
}
}
impl SimpleConnection for BorrowedSqliteConnection {
fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
SqliteConnection::batch_execute(&mut self.inner, query)
}
}
impl Connection for BorrowedSqliteConnection {
type Backend = Sqlite;
type TransactionManager = AnsiTransactionManager;
fn establish(_database_url: &str) -> ConnectionResult<Self> {
Err(crate::ConnectionError::BadConnection(
"Cannot create a pinned sqlite connection".into(),
))
}
fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
where
T: QueryFragment<Self::Backend> + QueryId,
{
SqliteConnection::execute_returning_count(&mut self.inner, source)
}
fn transaction_state(
&mut self,
) -> &mut <Self::TransactionManager as TransactionManager<Self>>::TransactionStateData {
SqliteConnection::transaction_state(&mut self.inner)
}
fn instrumentation(&mut self) -> &mut dyn Instrumentation {
SqliteConnection::instrumentation(&mut self.inner)
}
fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
SqliteConnection::set_instrumentation(&mut self.inner, instrumentation);
}
fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
SqliteConnection::set_prepared_statement_cache_size(&mut self.inner, size)
}
}
impl LoadConnection for BorrowedSqliteConnection {
type Cursor<'conn, 'query>
= <SqliteConnection as LoadConnection>::Cursor<'conn, 'query>
where
Self: 'conn;
type Row<'conn, 'query>
= <SqliteConnection as LoadConnection>::Row<'conn, 'query>
where
Self: 'conn;
fn load<'conn, 'query, T>(
&'conn mut self,
source: T,
) -> QueryResult<Self::Cursor<'conn, 'query>>
where
T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
Self::Backend: QueryMetadata<T::SqlType>,
{
SqliteConnection::load(&mut self.inner, source)
}
}
impl ConnectionSealed for BorrowedSqliteConnection {}
impl BorrowedSqliteConnection {
pub(super) fn new(conn: RawConnection) -> Self {
BorrowedSqliteConnection {
inner: core::mem::ManuallyDrop::new(SqliteConnection {
statement_cache: StatementCache::new(),
raw_connection: conn,
transaction_state: AnsiTransactionManager::default(),
metadata_lookup: (),
instrumentation: DynInstrumentation::default_instrumentation(),
}),
}
}
#[allow(unsafe_code)] pub unsafe fn inner_mut(&mut self) -> &mut SqliteConnection {
&mut self.inner
}
}
#[cfg(all(
test,
not(any(miri, all(target_family = "wasm", target_os = "unknown")))
))]
mod tests {
use super::*;
use crate::connection::SimpleConnection;
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
#[diesel_test_helper::test]
fn wal_hook_survives_its_own_call() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.db");
let conn = &mut SqliteConnection::establish(path.to_str().unwrap()).unwrap();
conn.batch_execute("PRAGMA journal_mode = WAL; CREATE TABLE borrowed_wal (x)")
.unwrap();
let calls = Arc::new(AtomicU32::new(0));
let c2 = calls.clone();
conn.on_wal(move |_conn, _db_name, _n_pages| {
c2.fetch_add(1, Ordering::Relaxed);
});
conn.batch_execute("INSERT INTO borrowed_wal VALUES (1)")
.unwrap();
conn.batch_execute("INSERT INTO borrowed_wal VALUES (2)")
.unwrap();
assert_eq!(calls.load(Ordering::Relaxed), 2);
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
#[diesel_test_helper::test]
#[allow(unsafe_code)]
fn hooks_registered_in_wal_callback_are_freed() {
use crate::sqlite::CommitDecision;
struct Token(Arc<AtomicU32>);
impl Drop for Token {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.db");
let conn = &mut SqliteConnection::establish(path.to_str().unwrap()).unwrap();
conn.batch_execute("PRAGMA journal_mode = WAL; CREATE TABLE borrowed_wal_hooks (x)")
.unwrap();
let dropped = Arc::new(AtomicU32::new(0));
let inner_wal_calls = Arc::new(AtomicU32::new(0));
let (d, w) = (dropped.clone(), inner_wal_calls.clone());
conn.on_wal(move |conn, _db_name, _n_pages| {
let conn = unsafe { conn.inner_mut() };
let t = Token(d.clone());
conn.on_commit(move || {
let _ = &t;
CommitDecision::Proceed
});
let (t, w) = (Token(d.clone()), w.clone());
conn.on_wal(move |_conn, _, _| {
let _ = &t;
w.fetch_add(1, Ordering::SeqCst);
});
conn.batch_execute("INSERT INTO borrowed_wal_hooks VALUES (0)")
.unwrap();
});
conn.batch_execute("INSERT INTO borrowed_wal_hooks VALUES (1)")
.unwrap();
assert_eq!(inner_wal_calls.load(Ordering::SeqCst), 1);
assert_eq!(dropped.load(Ordering::SeqCst), 2);
}
}