sql_middleware/sqlite/connection/
core.rs

1use std::fmt;
2use std::sync::Arc;
3
4use crate::middleware::SqlMiddlewareDbError;
5
6use crate::sqlite::config::{SharedSqliteConnection, SqlitePooledConnection};
7use tokio::sync::oneshot;
8
9/// Connection wrapper backed by a bb8 pooled `SQLite` connection.
10pub struct SqliteConnection {
11    pub(crate) conn: SqlitePooledConnection,
12    pub(crate) in_transaction: bool,
13}
14
15impl SqliteConnection {
16    pub(crate) fn new(conn: SqlitePooledConnection) -> Self {
17        Self {
18            conn,
19            in_transaction: false,
20        }
21    }
22
23    /// Run `func` on the pooled rusqlite connection while no other transaction is in flight.
24    ///
25    /// # Errors
26    /// Returns `SqlMiddlewareDbError::ExecutionError` if the connection is in a transaction or the closure returns an error.
27    pub async fn with_connection<F, R>(&self, func: F) -> Result<R, SqlMiddlewareDbError>
28    where
29        F: FnOnce(&mut rusqlite::Connection) -> Result<R, SqlMiddlewareDbError> + Send + 'static,
30        R: Send + 'static,
31    {
32        if self.in_transaction {
33            return Err(SqlMiddlewareDbError::ExecutionError(
34                "SQLite transaction in progress; operation not permitted (with connection)".into(),
35            ));
36        }
37        run_blocking(self.conn_handle(), func).await
38    }
39
40    pub(crate) fn conn_handle(&self) -> SharedSqliteConnection {
41        Arc::clone(&*self.conn)
42    }
43
44    pub(crate) fn ensure_not_in_tx(&self, ctx: &str) -> Result<(), SqlMiddlewareDbError> {
45        if self.in_transaction {
46            Err(SqlMiddlewareDbError::ExecutionError(format!(
47                "SQLite transaction in progress; operation not permitted ({ctx})"
48            )))
49        } else {
50            Ok(())
51        }
52    }
53}
54
55impl fmt::Debug for SqliteConnection {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_struct("SqliteConnection")
58            .field("conn", &self.conn)
59            .field("in_transaction", &self.in_transaction)
60            .finish()
61    }
62}
63
64pub(crate) async fn run_blocking<F, R>(
65    conn: SharedSqliteConnection,
66    func: F,
67) -> Result<R, SqlMiddlewareDbError>
68where
69    F: FnOnce(&mut rusqlite::Connection) -> Result<R, SqlMiddlewareDbError> + Send + 'static,
70    R: Send + 'static,
71{
72    let (tx, rx) = oneshot::channel();
73    conn.execute(move |conn| {
74        let _ = tx.send(func(conn));
75    })?;
76    rx.await.map_err(|e| {
77        SqlMiddlewareDbError::ExecutionError(format!("sqlite worker receive error: {e}"))
78    })?
79}
80
81/// Apply WAL pragmas to a pooled connection.
82///
83/// # Errors
84/// Returns `SqlMiddlewareDbError` if the PRAGMA statements cannot be executed.
85pub async fn apply_wal_pragmas(
86    conn: &mut SqlitePooledConnection,
87) -> Result<(), SqlMiddlewareDbError> {
88    let handle = Arc::clone(&*conn);
89    run_blocking(handle, |guard| {
90        guard
91            .execute_batch("PRAGMA journal_mode = WAL;")
92            .map_err(SqlMiddlewareDbError::SqliteError)
93    })
94    .await
95}