From 3c15eb0218d0e239f5095af1ba6ae9ea018303cc Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Tue, 17 May 2016 12:11:25 -0500 Subject: [PATCH] Add Connection::prepare_cached. --- src/cache.rs | 76 +++++++++++++++++++++++++++++++++++++--------------- src/lib.rs | 7 +++++ 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/src/cache.rs b/src/cache.rs index 8d7ac06..452dcd3 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -6,6 +6,38 @@ use std::ops::{Deref, DerefMut}; use {Result, Connection, Statement}; use raw_statement::RawStatement; +impl Connection { + /// Prepare a SQL statement for execution, returning a previously prepared (but + /// not currently in-use) statement if one is available. The returned statement + /// will be cached for reuse by future calls to `prepare_cached` once it is + /// dropped. + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn insert_new_people(conn: &Connection) -> Result<()> { + /// { + /// let mut stmt = try!(conn.prepare_cached("INSERT INTO People (name) VALUES (?)")); + /// try!(stmt.execute(&[&"Joe Smith"])); + /// } + /// { + /// // This will return the same underlying SQLite statement handle without + /// // having to prepare it again. + /// let mut stmt = try!(conn.prepare_cached("INSERT INTO People (name) VALUES (?)")); + /// try!(stmt.execute(&[&"Bob Jones"])); + /// } + /// Ok(()) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the + /// underlying SQLite call fails. + pub fn prepare_cached<'a>(&'a self, sql: &str) -> Result> { + self.cache.get(&self, sql) + } +} + /// Prepared statements LRU cache. #[derive(Debug)] pub struct StatementCache { @@ -93,21 +125,6 @@ impl StatementCache { stmt.clear_bindings(); cache.push_front(stmt) } - - /// Flush the prepared statement cache - pub fn clear(&self) { - self.cache.borrow_mut().clear(); - } - - /// Return current cache size. - pub fn len(&self) -> usize { - self.cache.borrow().len() - } - - /// Return maximum cache size. - pub fn capacity(&self) -> usize { - self.cache.borrow().capacity() - } } #[cfg(test)] @@ -115,16 +132,31 @@ mod test { use Connection; use super::StatementCache; + impl StatementCache { + fn clear(&self) { + self.cache.borrow_mut().clear(); + } + + fn len(&self) -> usize { + self.cache.borrow().len() + } + + fn capacity(&self) -> usize { + self.cache.borrow().capacity() + } + } + #[test] fn test_cache() { let db = Connection::open_in_memory().unwrap(); - let cache = StatementCache::with_capacity(15); + let cache = &db.cache; + let initial_capacity = cache.capacity(); assert_eq!(0, cache.len()); - assert_eq!(15, cache.capacity()); + assert!(initial_capacity > 0); let sql = "PRAGMA schema_version"; { - let mut stmt = cache.get(&db, sql).unwrap(); + let mut stmt = db.prepare_cached(sql).unwrap(); assert_eq!(0, cache.len()); assert_eq!(0, stmt.query(&[]).unwrap().get_expected_row().unwrap().get::(0)); @@ -132,7 +164,7 @@ mod test { assert_eq!(1, cache.len()); { - let mut stmt = cache.get(&db, sql).unwrap(); + let mut stmt = db.prepare_cached(sql).unwrap(); assert_eq!(0, cache.len()); assert_eq!(0, stmt.query(&[]).unwrap().get_expected_row().unwrap().get::(0)); @@ -141,17 +173,17 @@ mod test { cache.clear(); assert_eq!(0, cache.len()); - assert_eq!(15, cache.capacity()); + assert_eq!(initial_capacity, cache.capacity()); } #[test] fn test_discard() { let db = Connection::open_in_memory().unwrap(); - let cache = StatementCache::with_capacity(15); + let cache = &db.cache; let sql = "PRAGMA schema_version"; { - let mut stmt = cache.get(&db, sql).unwrap(); + let mut stmt = db.prepare_cached(sql).unwrap(); assert_eq!(0, cache.len()); assert_eq!(0, stmt.query(&[]).unwrap().get_expected_row().unwrap().get::(0)); diff --git a/src/lib.rs b/src/lib.rs index 8889deb..dbebe55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,7 @@ use libc::{c_int, c_char}; use types::{ToSql, FromSql}; use error::{error_from_sqlite_code, error_from_handle}; use raw_statement::RawStatement; +use cache::StatementCache; pub use transaction::{SqliteTransaction, Transaction, TransactionBehavior}; pub use error::{SqliteError, Error}; @@ -98,6 +99,9 @@ mod raw_statement; #[cfg(feature = "functions")]pub mod functions; #[cfg(feature = "blob")]pub mod blob; +// Number of cached prepared statements we'll hold on to. +const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16; + /// Old name for `Result`. `SqliteResult` is deprecated. pub type SqliteResult = Result; @@ -150,6 +154,7 @@ pub type SqliteConnection = Connection; /// A connection to a SQLite database. pub struct Connection { db: RefCell, + cache: StatementCache, path: Option, } @@ -194,6 +199,7 @@ impl Connection { InnerConnection::open_with_flags(&c_path, flags).map(|db| { Connection { db: RefCell::new(db), + cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), path: Some(path.as_ref().to_path_buf()), } }) @@ -212,6 +218,7 @@ impl Connection { InnerConnection::open_with_flags(&c_memory, flags).map(|db| { Connection { db: RefCell::new(db), + cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), path: None, } })