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