2015-08-08 22:33:08 +08:00
|
|
|
//! Prepared statements cache for faster execution.
|
2015-08-02 18:07:49 +08:00
|
|
|
|
2015-12-17 03:10:31 +08:00
|
|
|
use std::cell::RefCell;
|
2015-12-18 03:02:49 +08:00
|
|
|
use std::collections::VecDeque;
|
2015-12-18 03:33:34 +08:00
|
|
|
use std::ops::{Deref, DerefMut};
|
2015-12-13 18:23:54 +08:00
|
|
|
use {Result, Connection, Statement};
|
2015-08-02 18:07:49 +08:00
|
|
|
|
2015-08-08 22:33:08 +08:00
|
|
|
/// Prepared statements cache.
|
2015-08-02 18:07:49 +08:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct StatementCache<'conn> {
|
2015-12-17 02:42:03 +08:00
|
|
|
conn: &'conn Connection,
|
2015-12-19 03:18:46 +08:00
|
|
|
cache: RefCell<VecDeque<Statement<'conn>>>, // back = LRU
|
2015-08-02 18:07:49 +08:00
|
|
|
}
|
|
|
|
|
2015-12-19 23:49:11 +08:00
|
|
|
pub struct CachedStatement<'c: 's, 's> {
|
|
|
|
stmt: Option<Statement<'c>>,
|
|
|
|
cache: &'s StatementCache<'c>,
|
2015-12-18 03:02:49 +08:00
|
|
|
pub cacheable: bool,
|
2015-12-17 02:42:03 +08:00
|
|
|
}
|
|
|
|
|
2015-12-19 23:49:11 +08:00
|
|
|
impl<'c, 's> Deref for CachedStatement<'c, 's> {
|
|
|
|
type Target = Statement<'c>;
|
2015-12-18 03:33:34 +08:00
|
|
|
|
2015-12-19 23:49:11 +08:00
|
|
|
fn deref(&self) -> &Statement<'c> {
|
2015-12-18 03:33:34 +08:00
|
|
|
self.stmt.as_ref().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-19 23:49:11 +08:00
|
|
|
impl<'c, 's> DerefMut for CachedStatement<'c, 's> {
|
|
|
|
fn deref_mut(&mut self) -> &mut Statement<'c> {
|
2015-12-18 03:33:34 +08:00
|
|
|
self.stmt.as_mut().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-19 23:49:11 +08:00
|
|
|
impl<'c, 's> Drop for CachedStatement<'c, 's> {
|
2015-12-17 02:42:03 +08:00
|
|
|
#[allow(unused_must_use)]
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if self.cacheable {
|
2015-12-19 03:18:46 +08:00
|
|
|
self.cache.release(self.stmt.take().unwrap());
|
2015-12-17 02:42:03 +08:00
|
|
|
} else {
|
2015-12-18 03:02:49 +08:00
|
|
|
self.stmt.take().unwrap().finalize();
|
2015-12-17 02:42:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-19 23:49:11 +08:00
|
|
|
impl<'c, 's> CachedStatement<'c, 's> {
|
|
|
|
fn new(stmt: Statement<'c>, cache: &'s StatementCache<'c>) -> CachedStatement<'c, 's> {
|
2015-12-18 03:33:34 +08:00
|
|
|
CachedStatement {
|
|
|
|
stmt: Some(stmt),
|
|
|
|
cache: cache,
|
|
|
|
cacheable: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-02 18:07:49 +08:00
|
|
|
impl<'conn> StatementCache<'conn> {
|
2015-08-08 22:33:08 +08:00
|
|
|
/// Create a statement cache.
|
2015-12-13 18:23:54 +08:00
|
|
|
pub fn new(conn: &'conn Connection, capacity: usize) -> StatementCache<'conn> {
|
|
|
|
StatementCache {
|
|
|
|
conn: conn,
|
2015-12-19 03:18:46 +08:00
|
|
|
cache: RefCell::new(VecDeque::with_capacity(capacity)),
|
2015-12-13 18:23:54 +08:00
|
|
|
}
|
2015-08-02 18:07:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Search the cache for a prepared-statement object that implements `sql`.
|
|
|
|
// If no such prepared-statement can be found, allocate and prepare a new one.
|
2015-12-07 02:57:20 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// Will return `Err` if no cached statement can be found and the underlying SQLite prepare call fails.
|
2015-12-19 23:49:11 +08:00
|
|
|
pub fn get<'s>(&'s self, sql: &str) -> Result<CachedStatement<'conn, 's>> {
|
2015-12-19 03:18:46 +08:00
|
|
|
let stmt = match self.cache.borrow().iter().rposition(|entry| entry.eq(sql)) {
|
|
|
|
Some(index) => Ok(self.cache.borrow_mut().swap_remove_front(index).unwrap()), // FIXME Not LRU compliant
|
2015-12-13 18:23:54 +08:00
|
|
|
_ => self.conn.prepare(sql),
|
2015-12-18 03:33:34 +08:00
|
|
|
};
|
2015-12-19 03:18:46 +08:00
|
|
|
stmt.map(|stmt| CachedStatement::new(stmt, self))
|
2015-08-02 18:07:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// If `discard` is true, then the statement is deleted immediately.
|
|
|
|
/// Otherwise it is added to the LRU list and may be returned
|
|
|
|
/// by a subsequent call to `get()`.
|
2015-12-07 02:57:20 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// 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.
|
2015-12-19 03:18:46 +08:00
|
|
|
fn release(&self, mut stmt: Statement<'conn>) {
|
|
|
|
if self.cache.borrow().capacity() == self.cache.borrow().len() {
|
2015-12-18 03:33:34 +08:00
|
|
|
// is full
|
2015-12-19 03:18:46 +08:00
|
|
|
self.cache.borrow_mut().pop_back(); // LRU dropped
|
2015-08-02 18:07:49 +08:00
|
|
|
}
|
2015-12-07 02:57:20 +08:00
|
|
|
stmt.reset_if_needed();
|
2015-12-16 04:49:59 +08:00
|
|
|
stmt.clear_bindings();
|
2015-12-19 03:18:46 +08:00
|
|
|
self.cache.borrow_mut().push_front(stmt)
|
2015-08-02 18:07:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Flush the prepared statement cache
|
2015-12-19 03:18:46 +08:00
|
|
|
pub fn clear(&self) {
|
|
|
|
self.cache.borrow_mut().clear();
|
2015-08-02 18:07:49 +08:00
|
|
|
}
|
|
|
|
|
2015-12-16 04:49:59 +08:00
|
|
|
/// Return current cache size.
|
|
|
|
pub fn len(&self) -> usize {
|
2015-12-19 03:18:46 +08:00
|
|
|
self.cache.borrow().len()
|
2015-12-16 04:49:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return maximum cache size.
|
|
|
|
pub fn capacity(&self) -> usize {
|
2015-12-19 03:18:46 +08:00
|
|
|
self.cache.borrow().capacity()
|
2015-08-02 18:07:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2015-12-13 18:23:54 +08:00
|
|
|
use Connection;
|
2015-08-02 18:07:49 +08:00
|
|
|
use super::StatementCache;
|
|
|
|
|
2015-12-13 18:23:54 +08:00
|
|
|
#[test]
|
2015-08-02 18:07:49 +08:00
|
|
|
fn test_cache() {
|
2015-12-13 18:23:54 +08:00
|
|
|
let db = Connection::open_in_memory().unwrap();
|
2015-12-18 03:02:49 +08:00
|
|
|
let mut cache = StatementCache::new(&db, 15);
|
2015-12-16 04:49:59 +08:00
|
|
|
assert_eq!(0, cache.len());
|
2015-12-18 03:02:49 +08:00
|
|
|
assert_eq!(15, cache.capacity());
|
2015-12-16 04:49:59 +08:00
|
|
|
|
2015-08-02 18:07:49 +08:00
|
|
|
let sql = "PRAGMA schema_version";
|
2015-12-19 03:18:46 +08:00
|
|
|
{
|
|
|
|
let mut stmt = cache.get(sql).unwrap();
|
|
|
|
assert_eq!(0, cache.len());
|
|
|
|
assert_eq!(0,
|
|
|
|
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i64>(0));
|
|
|
|
}
|
2015-12-16 04:49:59 +08:00
|
|
|
assert_eq!(1, cache.len());
|
|
|
|
|
2015-12-19 03:18:46 +08:00
|
|
|
{
|
|
|
|
let mut stmt = cache.get(sql).unwrap();
|
|
|
|
assert_eq!(0, cache.len());
|
|
|
|
assert_eq!(0,
|
|
|
|
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i64>(0));
|
|
|
|
}
|
2015-12-18 03:02:49 +08:00
|
|
|
assert_eq!(1, cache.len());
|
2015-12-16 04:49:59 +08:00
|
|
|
|
|
|
|
cache.clear();
|
|
|
|
assert_eq!(0, cache.len());
|
2015-12-18 03:02:49 +08:00
|
|
|
assert_eq!(15, cache.capacity());
|
2015-08-02 18:07:49 +08:00
|
|
|
}
|
|
|
|
}
|