Replace LruCache by VecDeque.

This commit is contained in:
Gwenael Treguier 2015-12-17 20:02:49 +01:00
parent ff02213b53
commit 109c26fea4
3 changed files with 26 additions and 39 deletions

View File

@ -16,7 +16,7 @@ name = "rusqlite"
load_extension = ["libsqlite3-sys/load_extension"] load_extension = ["libsqlite3-sys/load_extension"]
backup = [] backup = []
blob = [] blob = []
cache = ["lru-cache"] cache = []
functions = [] functions = []
trace = [] trace = []
@ -34,10 +34,6 @@ regex = "~0.1.41"
path = "libsqlite3-sys" path = "libsqlite3-sys"
version = "0.3.0" version = "0.3.0"
[dependencies.lru-cache]
version = "~0.0.4"
optional = true
[[test]] [[test]]
name = "config_log" name = "config_log"
harness = false harness = false

View File

@ -1,21 +1,18 @@
//! Prepared statements cache for faster execution. //! Prepared statements cache for faster execution.
extern crate lru_cache;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque;
use {Result, Connection, Statement}; use {Result, Connection, Statement};
use self::lru_cache::LruCache;
/// Prepared statements cache. /// Prepared statements cache.
///
/// FIXME limitation: the same SQL can be cached only once...
#[derive(Debug)] #[derive(Debug)]
pub struct StatementCache<'conn> { pub struct StatementCache<'conn> {
conn: &'conn Connection, conn: &'conn Connection,
cache: LruCache<String, Statement<'conn>>, cache: VecDeque<Statement<'conn>>, // back = LRU
} }
pub struct CachedStatement<'conn> { pub struct CachedStatement<'conn> {
stmt: Statement<'conn>, stmt: Option<Statement<'conn>>,
cache: RefCell<StatementCache<'conn>>, cache: RefCell<StatementCache<'conn>>,
pub cacheable: bool, pub cacheable: bool,
} }
@ -24,10 +21,9 @@ impl<'conn> Drop for CachedStatement<'conn> {
#[allow(unused_must_use)] #[allow(unused_must_use)]
fn drop(&mut self) { fn drop(&mut self) {
if self.cacheable { if self.cacheable {
// FIXME: cannot move out of type `cache::CachedStatement<'conn>`, which defines the `Drop` trait [E0509] self.cache.borrow_mut().release(self.stmt.take().unwrap());
//self.cache.borrow_mut().release(self.stmt, false);
} else { } else {
self.stmt.finalize_(); self.stmt.take().unwrap().finalize();
} }
} }
} }
@ -37,7 +33,7 @@ impl<'conn> StatementCache<'conn> {
pub fn new(conn: &'conn Connection, capacity: usize) -> StatementCache<'conn> { pub fn new(conn: &'conn Connection, capacity: usize) -> StatementCache<'conn> {
StatementCache { StatementCache {
conn: conn, conn: conn,
cache: LruCache::new(capacity), cache: VecDeque::with_capacity(capacity),
} }
} }
@ -48,9 +44,8 @@ impl<'conn> StatementCache<'conn> {
/// ///
/// Will return `Err` if no cached statement can be found and the underlying SQLite prepare call fails. /// Will return `Err` if no cached statement can be found and the underlying SQLite prepare call fails.
pub fn get(&mut self, sql: &str) -> Result<Statement<'conn>> { pub fn get(&mut self, sql: &str) -> Result<Statement<'conn>> {
let stmt = self.cache.remove(sql); match self.cache.iter().rposition(|entry| entry.eq(sql)) {
match stmt { Some(index) => Ok(self.cache.swap_remove_front(index).unwrap()), // FIXME Not LRU compliant
Some(stmt) => Ok(stmt),
_ => self.conn.prepare(sql), _ => self.conn.prepare(sql),
} }
} }
@ -63,13 +58,13 @@ impl<'conn> StatementCache<'conn> {
/// ///
/// Will return `Err` if `stmt` (or the already cached statement implementing the same SQL) statement is `discard`ed /// Will return `Err` if `stmt` (or the already cached statement implementing the same SQL) statement is `discard`ed
/// and the underlying SQLite finalize call fails. /// and the underlying SQLite finalize call fails.
pub fn release(&mut self, mut stmt: Statement<'conn>, discard: bool) -> Result<()> { fn release(&mut self, mut stmt: Statement<'conn>) {
if discard { if self.cache.capacity() == self.cache.len() { // is full
return stmt.finalize(); self.cache.pop_back(); // LRU dropped
} }
stmt.reset_if_needed(); stmt.reset_if_needed();
stmt.clear_bindings(); stmt.clear_bindings();
self.cache.insert(stmt.sql(), stmt).map_or(Ok(()), |stmt| stmt.finalize()) self.cache.push_front(stmt)
} }
/// Flush the prepared statement cache /// Flush the prepared statement cache
@ -86,11 +81,6 @@ impl<'conn> StatementCache<'conn> {
pub fn capacity(&self) -> usize { pub fn capacity(&self) -> usize {
self.cache.capacity() self.cache.capacity()
} }
/// Set the maximum number of cached statements.
pub fn set_capacity(&mut self, capacity: usize) {
self.cache.set_capacity(capacity);
}
} }
#[cfg(test)] #[cfg(test)]
@ -101,29 +91,31 @@ mod test {
#[test] #[test]
fn test_cache() { fn test_cache() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
let mut cache = StatementCache::new(&db, 10); let mut cache = StatementCache::new(&db, 15);
assert_eq!(0, cache.len()); assert_eq!(0, cache.len());
assert_eq!(10, cache.capacity()); assert_eq!(15, cache.capacity());
let sql = "PRAGMA schema_version"; let sql = "PRAGMA schema_version";
let mut stmt = cache.get(sql).unwrap(); let mut stmt = cache.get(sql).unwrap();
assert_eq!(0, cache.len()); assert_eq!(0, cache.len());
assert_eq!(0, stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i64>(0)); assert_eq!(0,
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i64>(0));
// println!("NEW {:?}", stmt); // println!("NEW {:?}", stmt);
cache.release(stmt, false).unwrap(); cache.release(stmt);
assert_eq!(1, cache.len()); assert_eq!(1, cache.len());
stmt = cache.get(sql).unwrap(); stmt = cache.get(sql).unwrap();
assert_eq!(0, cache.len()); assert_eq!(0, cache.len());
assert_eq!(0, stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i64>(0)); assert_eq!(0,
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i64>(0));
// println!("CACHED {:?}", stmt); // println!("CACHED {:?}", stmt);
cache.release(stmt, true).unwrap(); cache.release(stmt);
assert_eq!(0, cache.len()); assert_eq!(1, cache.len());
cache.clear(); cache.clear();
assert_eq!(0, cache.len()); assert_eq!(0, cache.len());
assert_eq!(10, cache.capacity()); assert_eq!(15, cache.capacity());
} }
} }

View File

@ -937,11 +937,10 @@ impl<'conn> Statement<'conn> {
} }
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
fn sql(&self) -> String { fn eq(&self, sql: &str) -> bool {
unsafe { unsafe {
let c_slice = CStr::from_ptr(ffi::sqlite3_sql(self.stmt)).to_bytes(); let c_slice = CStr::from_ptr(ffi::sqlite3_sql(self.stmt)).to_bytes();
let utf8_str = str::from_utf8(c_slice); str::from_utf8(c_slice).unwrap().eq(sql)
utf8_str.unwrap().to_string()
} }
} }