deadpool_sqlite/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![deny(
4    nonstandard_style,
5    rust_2018_idioms,
6    rustdoc::broken_intra_doc_links,
7    rustdoc::private_intra_doc_links
8)]
9#![forbid(non_ascii_idents, unsafe_code)]
10#![warn(
11    deprecated_in_future,
12    missing_copy_implementations,
13    missing_debug_implementations,
14    missing_docs,
15    unreachable_pub,
16    unused_import_braces,
17    unused_labels,
18    unused_lifetimes,
19    unused_qualifications,
20    unused_results
21)]
22#![allow(clippy::uninlined_format_args)]
23
24mod config;
25
26use std::sync::atomic::{AtomicUsize, Ordering};
27
28use deadpool::managed::{self, RecycleError};
29use deadpool_sync::SyncWrapper;
30
31pub use deadpool::managed::reexports::*;
32pub use deadpool_sync::reexports::*;
33pub use rusqlite;
34
35deadpool::managed_reexports!(
36    "rusqlite",
37    Manager,
38    managed::Object<Manager>,
39    rusqlite::Error,
40    ConfigError
41);
42
43pub use self::config::{Config, ConfigError};
44
45/// Type alias for [`Object`]
46pub type Connection = Object;
47
48/// [`Manager`] for creating and recycling SQLite [`Connection`]s.
49///
50/// [`Manager`]: managed::Manager
51#[derive(Debug)]
52pub struct Manager {
53    config: Config,
54    recycle_count: AtomicUsize,
55    runtime: Runtime,
56}
57
58impl Manager {
59    /// Creates a new [`Manager`] using the given [`Config`] backed by the
60    /// specified [`Runtime`].
61    #[must_use]
62    pub fn from_config(config: &Config, runtime: Runtime) -> Self {
63        Self {
64            config: config.clone(),
65            recycle_count: AtomicUsize::new(0),
66            runtime,
67        }
68    }
69}
70
71impl managed::Manager for Manager {
72    type Type = SyncWrapper<rusqlite::Connection>;
73    type Error = rusqlite::Error;
74
75    async fn create(&self) -> Result<Self::Type, Self::Error> {
76        let path = self.config.path.clone();
77        SyncWrapper::new(self.runtime, move || rusqlite::Connection::open(path)).await
78    }
79
80    async fn recycle(
81        &self,
82        conn: &mut Self::Type,
83        _: &Metrics,
84    ) -> managed::RecycleResult<Self::Error> {
85        if conn.is_mutex_poisoned() {
86            return Err(RecycleError::Message(
87                "Mutex is poisoned. Connection is considered unusable.".into(),
88            ));
89        }
90        let recycle_count = self.recycle_count.fetch_add(1, Ordering::Relaxed);
91        let n: usize = conn
92            .interact(move |conn| conn.query_row("SELECT $1", [recycle_count], |row| row.get(0)))
93            .await
94            .map_err(|e| RecycleError::message(format!("{}", e)))??;
95        if n == recycle_count {
96            Ok(())
97        } else {
98            Err(RecycleError::message("Recycle count mismatch"))
99        }
100    }
101}