r2d2_sqlite/lib.rs
1#![deny(warnings)]
2//! # Sqlite support for the `r2d2` connection pool.
3//!
4//! Library crate: [r2d2-sqlite](https://crates.io/crates/r2d2-sqlite/)
5//!
6//! Integrated with: [r2d2](https://crates.io/crates/r2d2)
7//! and [rusqlite](https://crates.io/crates/rusqlite)
8//!
9//! ## Example
10//!
11//! ```rust,no_run
12//! extern crate r2d2;
13//! extern crate r2d2_sqlite;
14//! extern crate rusqlite;
15//!
16//! use std::thread;
17//! use r2d2_sqlite::SqliteConnectionManager;
18//! use rusqlite::params;
19//!
20//! fn main() {
21//! let manager = SqliteConnectionManager::file("file.db");
22//! let pool = r2d2::Pool::new(manager).unwrap();
23//! pool.get()
24//! .unwrap()
25//! .execute("CREATE TABLE IF NOT EXISTS foo (bar INTEGER)", params![])
26//! .unwrap();
27//!
28//! (0..10)
29//! .map(|i| {
30//! let pool = pool.clone();
31//! thread::spawn(move || {
32//! let conn = pool.get().unwrap();
33//! conn.execute("INSERT INTO foo (bar) VALUES (?)", &[&i])
34//! .unwrap();
35//! })
36//! })
37//! .collect::<Vec<_>>()
38//! .into_iter()
39//! .map(thread::JoinHandle::join)
40//! .collect::<Result<_, _>>()
41//! .unwrap()
42//! }
43//! ```
44use rusqlite::{Connection, Error, OpenFlags};
45use std::fmt;
46use std::path::{Path, PathBuf};
47pub use rusqlite;
48
49#[derive(Debug)]
50enum Source {
51 File(PathBuf),
52 Memory,
53}
54
55type InitFn = dyn Fn(&mut Connection) -> Result<(), rusqlite::Error> + Send + Sync + 'static;
56
57/// An `r2d2::ManageConnection` for `rusqlite::Connection`s.
58pub struct SqliteConnectionManager {
59 source: Source,
60 flags: OpenFlags,
61 init: Option<Box<InitFn>>,
62}
63
64impl fmt::Debug for SqliteConnectionManager {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 let mut builder = f.debug_struct("SqliteConnectionManager");
67 let _ = builder.field("source", &self.source);
68 let _ = builder.field("flags", &self.source);
69 let _ = builder.field("init", &self.init.as_ref().map(|_| "InitFn"));
70 builder.finish()
71 }
72}
73
74impl SqliteConnectionManager {
75 /// Creates a new `SqliteConnectionManager` from file.
76 ///
77 /// See `rusqlite::Connection::open`
78 pub fn file<P: AsRef<Path>>(path: P) -> Self {
79 Self {
80 source: Source::File(path.as_ref().to_path_buf()),
81 flags: OpenFlags::default(),
82 init: None,
83 }
84 }
85
86 /// Creates a new `SqliteConnectionManager` from memory.
87 pub fn memory() -> Self {
88 Self {
89 source: Source::Memory,
90 flags: OpenFlags::default(),
91 init: None,
92 }
93 }
94
95 /// Converts `SqliteConnectionManager` into one that sets OpenFlags upon
96 /// connection creation.
97 ///
98 /// See `rustqlite::OpenFlags` for a list of available flags.
99 pub fn with_flags(self, flags: OpenFlags) -> Self {
100 Self { flags, ..self }
101 }
102
103 /// Converts `SqliteConnectionManager` into one that calls an initialization
104 /// function upon connection creation. Could be used to set PRAGMAs, for
105 /// example.
106 ///
107 /// ### Example
108 ///
109 /// Make a `SqliteConnectionManager` that sets the `foreign_keys` pragma to
110 /// true for every connection.
111 ///
112 /// ```rust,no_run
113 /// # use r2d2_sqlite::{SqliteConnectionManager};
114 /// let manager = SqliteConnectionManager::file("app.db")
115 /// .with_init(|c| c.execute_batch("PRAGMA foreign_keys=1;"));
116 /// ```
117 pub fn with_init<F>(self, init: F) -> Self
118 where
119 F: Fn(&mut Connection) -> Result<(), rusqlite::Error> + Send + Sync + 'static,
120 {
121 let init: Option<Box<InitFn>> = Some(Box::new(init));
122 Self { init, ..self }
123 }
124}
125
126impl r2d2::ManageConnection for SqliteConnectionManager {
127 type Connection = Connection;
128 type Error = rusqlite::Error;
129
130 fn connect(&self) -> Result<Connection, Error> {
131 match self.source {
132 Source::File(ref path) => Connection::open_with_flags(path, self.flags),
133 Source::Memory => Connection::open_in_memory_with_flags(self.flags),
134 }
135 .map_err(Into::into)
136 .and_then(|mut c| match self.init {
137 None => Ok(c),
138 Some(ref init) => init(&mut c).map(|_| c),
139 })
140 }
141
142 fn is_valid(&self, conn: &mut Connection) -> Result<(), Error> {
143 conn.execute_batch("").map_err(Into::into)
144 }
145
146 fn has_broken(&self, _: &mut Connection) -> bool {
147 false
148 }
149}