1 //! Prepared statements cache for faster execution.
2 
3 use crate::raw_statement::RawStatement;
4 use crate::{Connection, Result, Statement};
5 use hashlink::LruCache;
6 use std::cell::RefCell;
7 use std::ops::{Deref, DerefMut};
8 use std::sync::Arc;
9 
10 impl Connection {
11     /// Prepare a SQL statement for execution, returning a previously prepared
12     /// (but not currently in-use) statement if one is available. The
13     /// returned statement will be cached for reuse by future calls to
14     /// [`prepare_cached`](Connection::prepare_cached) once it is dropped.
15     ///
16     /// ```rust,no_run
17     /// # use rusqlite::{Connection, Result};
18     /// fn insert_new_people(conn: &Connection) -> Result<()> {
19     ///     {
20     ///         let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?;
21     ///         stmt.execute(["Joe Smith"])?;
22     ///     }
23     ///     {
24     ///         // This will return the same underlying SQLite statement handle without
25     ///         // having to prepare it again.
26     ///         let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?;
27     ///         stmt.execute(["Bob Jones"])?;
28     ///     }
29     ///     Ok(())
30     /// }
31     /// ```
32     ///
33     /// # Failure
34     ///
35     /// Will return `Err` if `sql` cannot be converted to a C-compatible string
36     /// or if the underlying SQLite call fails.
37     #[inline]
prepare_cached(&self, sql: &str) -> Result<CachedStatement<'_>>38     pub fn prepare_cached(&self, sql: &str) -> Result<CachedStatement<'_>> {
39         self.cache.get(self, sql)
40     }
41 
42     /// Set the maximum number of cached prepared statements this connection
43     /// will hold. By default, a connection will hold a relatively small
44     /// number of cached statements. If you need more, or know that you
45     /// will not use cached statements, you
46     /// can set the capacity manually using this method.
47     #[inline]
set_prepared_statement_cache_capacity(&self, capacity: usize)48     pub fn set_prepared_statement_cache_capacity(&self, capacity: usize) {
49         self.cache.set_capacity(capacity);
50     }
51 
52     /// Remove/finalize all prepared statements currently in the cache.
53     #[inline]
flush_prepared_statement_cache(&self)54     pub fn flush_prepared_statement_cache(&self) {
55         self.cache.flush();
56     }
57 }
58 
59 /// Prepared statements LRU cache.
60 // #[derive(Debug)] // FIXME: https://github.com/kyren/hashlink/pull/4
61 pub struct StatementCache(RefCell<LruCache<Arc<str>, RawStatement>>);
62 
63 #[allow(clippy::non_send_fields_in_send_ty)]
64 unsafe impl Send for StatementCache {}
65 
66 /// Cacheable statement.
67 ///
68 /// Statement will return automatically to the cache by default.
69 /// If you want the statement to be discarded, call
70 /// [`discard()`](CachedStatement::discard) on it.
71 pub struct CachedStatement<'conn> {
72     stmt: Option<Statement<'conn>>,
73     cache: &'conn StatementCache,
74 }
75 
76 impl<'conn> Deref for CachedStatement<'conn> {
77     type Target = Statement<'conn>;
78 
79     #[inline]
deref(&self) -> &Statement<'conn>80     fn deref(&self) -> &Statement<'conn> {
81         self.stmt.as_ref().unwrap()
82     }
83 }
84 
85 impl<'conn> DerefMut for CachedStatement<'conn> {
86     #[inline]
deref_mut(&mut self) -> &mut Statement<'conn>87     fn deref_mut(&mut self) -> &mut Statement<'conn> {
88         self.stmt.as_mut().unwrap()
89     }
90 }
91 
92 impl Drop for CachedStatement<'_> {
93     #[allow(unused_must_use)]
94     #[inline]
drop(&mut self)95     fn drop(&mut self) {
96         if let Some(stmt) = self.stmt.take() {
97             self.cache.cache_stmt(unsafe { stmt.into_raw() });
98         }
99     }
100 }
101 
102 impl CachedStatement<'_> {
103     #[inline]
new<'conn>(stmt: Statement<'conn>, cache: &'conn StatementCache) -> CachedStatement<'conn>104     fn new<'conn>(stmt: Statement<'conn>, cache: &'conn StatementCache) -> CachedStatement<'conn> {
105         CachedStatement {
106             stmt: Some(stmt),
107             cache,
108         }
109     }
110 
111     /// Discard the statement, preventing it from being returned to its
112     /// [`Connection`]'s collection of cached statements.
113     #[inline]
discard(mut self)114     pub fn discard(mut self) {
115         self.stmt = None;
116     }
117 }
118 
119 impl StatementCache {
120     /// Create a statement cache.
121     #[inline]
with_capacity(capacity: usize) -> StatementCache122     pub fn with_capacity(capacity: usize) -> StatementCache {
123         StatementCache(RefCell::new(LruCache::new(capacity)))
124     }
125 
126     #[inline]
set_capacity(&self, capacity: usize)127     fn set_capacity(&self, capacity: usize) {
128         self.0.borrow_mut().set_capacity(capacity);
129     }
130 
131     // Search the cache for a prepared-statement object that implements `sql`.
132     // If no such prepared-statement can be found, allocate and prepare a new one.
133     //
134     // # Failure
135     //
136     // Will return `Err` if no cached statement can be found and the underlying
137     // SQLite prepare call fails.
get<'conn>( &'conn self, conn: &'conn Connection, sql: &str, ) -> Result<CachedStatement<'conn>>138     fn get<'conn>(
139         &'conn self,
140         conn: &'conn Connection,
141         sql: &str,
142     ) -> Result<CachedStatement<'conn>> {
143         let trimmed = sql.trim();
144         let mut cache = self.0.borrow_mut();
145         let stmt = match cache.remove(trimmed) {
146             Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
147             None => conn.prepare(trimmed),
148         };
149         stmt.map(|mut stmt| {
150             stmt.stmt.set_statement_cache_key(trimmed);
151             CachedStatement::new(stmt, self)
152         })
153     }
154 
155     // Return a statement to the cache.
cache_stmt(&self, stmt: RawStatement)156     fn cache_stmt(&self, stmt: RawStatement) {
157         if stmt.is_null() {
158             return;
159         }
160         let mut cache = self.0.borrow_mut();
161         stmt.clear_bindings();
162         if let Some(sql) = stmt.statement_cache_key() {
163             cache.insert(sql, stmt);
164         } else {
165             debug_assert!(
166                 false,
167                 "bug in statement cache code, statement returned to cache that without key"
168             );
169         }
170     }
171 
172     #[inline]
flush(&self)173     fn flush(&self) {
174         let mut cache = self.0.borrow_mut();
175         cache.clear();
176     }
177 }
178 
179 #[cfg(test)]
180 mod test {
181     use super::StatementCache;
182     use crate::{Connection, Result};
183     use fallible_iterator::FallibleIterator;
184 
185     impl StatementCache {
clear(&self)186         fn clear(&self) {
187             self.0.borrow_mut().clear();
188         }
189 
len(&self) -> usize190         fn len(&self) -> usize {
191             self.0.borrow().len()
192         }
193 
capacity(&self) -> usize194         fn capacity(&self) -> usize {
195             self.0.borrow().capacity()
196         }
197     }
198 
199     #[test]
test_cache() -> Result<()>200     fn test_cache() -> Result<()> {
201         let db = Connection::open_in_memory()?;
202         let cache = &db.cache;
203         let initial_capacity = cache.capacity();
204         assert_eq!(0, cache.len());
205         assert!(initial_capacity > 0);
206 
207         let sql = "PRAGMA schema_version";
208         {
209             let mut stmt = db.prepare_cached(sql)?;
210             assert_eq!(0, cache.len());
211             assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?);
212         }
213         assert_eq!(1, cache.len());
214 
215         {
216             let mut stmt = db.prepare_cached(sql)?;
217             assert_eq!(0, cache.len());
218             assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?);
219         }
220         assert_eq!(1, cache.len());
221 
222         cache.clear();
223         assert_eq!(0, cache.len());
224         assert_eq!(initial_capacity, cache.capacity());
225         Ok(())
226     }
227 
228     #[test]
test_set_capacity() -> Result<()>229     fn test_set_capacity() -> Result<()> {
230         let db = Connection::open_in_memory()?;
231         let cache = &db.cache;
232 
233         let sql = "PRAGMA schema_version";
234         {
235             let mut stmt = db.prepare_cached(sql)?;
236             assert_eq!(0, cache.len());
237             assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?);
238         }
239         assert_eq!(1, cache.len());
240 
241         db.set_prepared_statement_cache_capacity(0);
242         assert_eq!(0, cache.len());
243 
244         {
245             let mut stmt = db.prepare_cached(sql)?;
246             assert_eq!(0, cache.len());
247             assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?);
248         }
249         assert_eq!(0, cache.len());
250 
251         db.set_prepared_statement_cache_capacity(8);
252         {
253             let mut stmt = db.prepare_cached(sql)?;
254             assert_eq!(0, cache.len());
255             assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?);
256         }
257         assert_eq!(1, cache.len());
258         Ok(())
259     }
260 
261     #[test]
test_discard() -> Result<()>262     fn test_discard() -> Result<()> {
263         let db = Connection::open_in_memory()?;
264         let cache = &db.cache;
265 
266         let sql = "PRAGMA schema_version";
267         {
268             let mut stmt = db.prepare_cached(sql)?;
269             assert_eq!(0, cache.len());
270             assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?);
271             stmt.discard();
272         }
273         assert_eq!(0, cache.len());
274         Ok(())
275     }
276 
277     #[test]
test_ddl() -> Result<()>278     fn test_ddl() -> Result<()> {
279         let db = Connection::open_in_memory()?;
280         db.execute_batch(
281             r#"
282             CREATE TABLE foo (x INT);
283             INSERT INTO foo VALUES (1);
284         "#,
285         )?;
286 
287         let sql = "SELECT * FROM foo";
288 
289         {
290             let mut stmt = db.prepare_cached(sql)?;
291             assert_eq!(Ok(Some(1i32)), stmt.query([])?.map(|r| r.get(0)).next());
292         }
293 
294         db.execute_batch(
295             r#"
296             ALTER TABLE foo ADD COLUMN y INT;
297             UPDATE foo SET y = 2;
298         "#,
299         )?;
300 
301         {
302             let mut stmt = db.prepare_cached(sql)?;
303             assert_eq!(
304                 Ok(Some((1i32, 2i32))),
305                 stmt.query([])?.map(|r| Ok((r.get(0)?, r.get(1)?))).next()
306             );
307         }
308         Ok(())
309     }
310 
311     #[test]
test_connection_close() -> Result<()>312     fn test_connection_close() -> Result<()> {
313         let conn = Connection::open_in_memory()?;
314         conn.prepare_cached("SELECT * FROM sqlite_master;")?;
315 
316         conn.close().expect("connection not closed");
317         Ok(())
318     }
319 
320     #[test]
test_cache_key() -> Result<()>321     fn test_cache_key() -> Result<()> {
322         let db = Connection::open_in_memory()?;
323         let cache = &db.cache;
324         assert_eq!(0, cache.len());
325 
326         //let sql = " PRAGMA schema_version; -- comment";
327         let sql = "PRAGMA schema_version; ";
328         {
329             let mut stmt = db.prepare_cached(sql)?;
330             assert_eq!(0, cache.len());
331             assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?);
332         }
333         assert_eq!(1, cache.len());
334 
335         {
336             let mut stmt = db.prepare_cached(sql)?;
337             assert_eq!(0, cache.len());
338             assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?);
339         }
340         assert_eq!(1, cache.len());
341         Ok(())
342     }
343 
344     #[test]
test_empty_stmt() -> Result<()>345     fn test_empty_stmt() -> Result<()> {
346         let conn = Connection::open_in_memory()?;
347         conn.prepare_cached("")?;
348         Ok(())
349     }
350 }
351