bb8_sqlite/
lib.rs

1//! rusqlite support for the `bb8` connection pool. Note that in-memory
2//! databases aren't supported, since they are always per-connection, and
3//! therefore don't make sense in a pool environment.
4#![deny(missing_docs, missing_debug_implementations)]
5
6use std::{
7    path::{Path, PathBuf},
8    sync::Arc,
9};
10
11use async_trait::async_trait;
12use bb8::ManageConnection;
13use rusqlite::{Connection, OpenFlags};
14
15
16#[cfg(test)]
17mod tests;
18
19/// A `bb8::ManageConnection` implementation for `rusqlite::Connection`
20/// instances.
21#[derive(Clone, Debug)]
22pub struct RusqliteConnectionManager(Arc<ConnectionOptions>);
23
24#[derive(Debug)]
25struct ConnectionOptions {
26    mode: OpenMode,
27    path: PathBuf,
28}
29
30#[derive(Debug)]
31enum OpenMode {
32    Plain,
33    WithFlags {
34        flags: rusqlite::OpenFlags,
35    },
36    WithFlagsAndVFS {
37        flags: rusqlite::OpenFlags,
38        vfs: String,
39    },
40}
41
42/// Error wraps errors from both rusqlite and tokio.
43#[derive(thiserror::Error, Debug)]
44pub enum Error {
45    /// A rusqlite error.
46    #[error("rusqlite error")]
47    Rusqlite(#[from] rusqlite::Error),
48
49    /// A tokio join handle error.
50    #[error("tokio join error")]
51    TokioJoin(#[from] tokio::task::JoinError),
52}
53
54impl RusqliteConnectionManager {
55    /// Analogous to `rusqlite::Connection::open()`.
56    pub fn new<P: AsRef<Path>>(path: P) -> Self {
57        Self(Arc::new(ConnectionOptions {
58            mode: OpenMode::Plain,
59            path: path.as_ref().into(),
60        }))
61    }
62
63    /// Analogous to `rusqlite::Connection::open_with_flags()`.
64    pub fn new_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Self {
65        Self(Arc::new(ConnectionOptions {
66            mode: OpenMode::WithFlags { flags },
67            path: path.as_ref().into(),
68        }))
69    }
70
71    /// Analogous to `rusqlite::Connection::open_with_flags_and_vfs()`.
72    pub fn new_with_flags_and_vfs<P: AsRef<Path>>(path: P, flags: OpenFlags, vfs: &str) -> Self {
73        Self(Arc::new(ConnectionOptions {
74            mode: OpenMode::WithFlagsAndVFS {
75                flags,
76                vfs: vfs.into(),
77            },
78            path: path.as_ref().into(),
79        }))
80    }
81}
82
83#[async_trait]
84impl ManageConnection for RusqliteConnectionManager {
85    type Connection = Connection;
86    type Error = Error;
87
88    async fn connect(&self) -> Result<Self::Connection, Self::Error> {
89        let options = self.0.clone();
90
91        // Technically, we don't need to use spawn_blocking() here, but doing so
92        // means we won't inadvertantly block this task for any length of time,
93        // since rusqlite is inherently synchronous.
94        Ok(tokio::task::spawn_blocking(move || match &options.mode {
95            OpenMode::Plain => rusqlite::Connection::open(&options.path),
96            OpenMode::WithFlags { flags } => {
97                rusqlite::Connection::open_with_flags(&options.path, *flags)
98            }
99            OpenMode::WithFlagsAndVFS { flags, vfs } => {
100                rusqlite::Connection::open_with_flags_and_vfs(&options.path, *flags, vfs)
101            }
102        })
103        .await??)
104    }
105
106    async fn is_valid(
107        &self,
108        conn: &mut Self::Connection 
109    ) -> Result<(), Self::Error> {
110        // Matching bb8-postgres, we'll try to run a trivial query here. Using
111        // block_in_place() gives better behaviour if the SQLite call blocks for
112        // some reason, but means that we depend on the tokio multi-threaded
113        // runtime being active. (We can't use spawn_blocking() here because
114        // Connection isn't Sync.)
115        tokio::task::block_in_place(|| conn.execute("SELECT 1", []))?;
116        Ok(())
117    }
118
119    fn has_broken(&self, _conn: &mut Self::Connection) -> bool {
120        // There's no real concept of a "broken" connection in SQLite: if the
121        // handle is still open, then we're good. (And we know the handle is
122        // still open, because Connection::close() consumes the Connection, in
123        // which case we're definitely not here.)
124        false
125    }
126}