rusqlite/src/cache.rs

195 lines
5.8 KiB
Rust
Raw Normal View History

2015-08-08 22:33:08 +08:00
//! Prepared statements cache for faster execution.
2015-08-02 18:07:49 +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};
use {Result, Connection, Statement};
use raw_statement::RawStatement;
2015-08-02 18:07:49 +08:00
2016-05-18 01:11:25 +08:00
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<CachedStatement<'a, 'a>> {
self.cache.get(&self, sql)
}
}
2015-12-20 00:14:06 +08:00
/// Prepared statements LRU cache.
2015-08-02 18:07:49 +08:00
#[derive(Debug)]
pub struct StatementCache {
cache: RefCell<VecDeque<RawStatement>>, // back = LRU
2015-08-02 18:07:49 +08:00
}
2015-12-20 00:14:06 +08:00
/// Cacheable statement.
///
/// Statement will return automatically to the cache by default.
/// If you want the statement to be discarded, call `discard()` on it.
pub struct CachedStatement<'c: 's, 's> {
stmt: Option<Statement<'c>>,
cache: &'s StatementCache,
}
impl<'c, 's> Deref for CachedStatement<'c, 's> {
type Target = Statement<'c>;
2015-12-18 03:33:34 +08:00
fn deref(&self) -> &Statement<'c> {
2015-12-18 03:33:34 +08:00
self.stmt.as_ref().unwrap()
}
}
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()
}
}
impl<'c, 's> Drop for CachedStatement<'c, 's> {
#[allow(unused_must_use)]
fn drop(&mut self) {
if let Some(stmt) = self.stmt.take() {
self.cache.cache_stmt(stmt.into());
}
}
}
impl<'c, 's> CachedStatement<'c, 's> {
fn new(stmt: Statement<'c>, cache: &'s StatementCache) -> CachedStatement<'c, 's> {
2015-12-18 03:33:34 +08:00
CachedStatement {
stmt: Some(stmt),
cache: cache,
}
}
pub fn discard(mut self) {
self.stmt = None;
}
2015-12-18 03:33:34 +08:00
}
impl StatementCache {
2015-08-08 22:33:08 +08:00
/// Create a statement cache.
pub fn with_capacity(capacity: usize) -> StatementCache {
StatementCache {
2015-12-19 03:18:46 +08:00
cache: RefCell::new(VecDeque::with_capacity(capacity)),
}
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.
pub fn get<'conn, 's>(&'s self, conn: &'conn Connection, sql: &str) -> Result<CachedStatement<'conn, 's>> {
2015-12-19 23:56:41 +08:00
let mut cache = self.cache.borrow_mut();
let stmt = match cache.iter().rposition(|entry| entry.sql().to_bytes().eq(sql.as_bytes())) {
Some(index) => {
let raw_stmt = cache.swap_remove_front(index).unwrap(); // FIXME Not LRU compliant
Ok(Statement::new(conn, raw_stmt))
}
_ => 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
}
// Return a statement to the cache.
fn cache_stmt(&self, stmt: RawStatement) {
2015-12-19 23:56:41 +08:00
let mut cache = self.cache.borrow_mut();
if cache.capacity() == cache.len() {
2015-12-18 03:33:34 +08:00
// is full
2015-12-19 23:56:41 +08:00
cache.pop_back(); // LRU dropped
2015-08-02 18:07:49 +08:00
}
stmt.clear_bindings();
2015-12-19 23:56:41 +08:00
cache.push_front(stmt)
2015-08-02 18:07:49 +08:00
}
}
#[cfg(test)]
mod test {
use Connection;
2015-08-02 18:07:49 +08:00
use super::StatementCache;
2016-05-18 01:11:25 +08:00
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]
2015-08-02 18:07:49 +08:00
fn test_cache() {
let db = Connection::open_in_memory().unwrap();
2016-05-18 01:11:25 +08:00
let cache = &db.cache;
let initial_capacity = cache.capacity();
assert_eq!(0, cache.len());
2016-05-18 01:11:25 +08:00
assert!(initial_capacity > 0);
2015-08-02 18:07:49 +08:00
let sql = "PRAGMA schema_version";
2015-12-19 03:18:46 +08:00
{
2016-05-18 01:11:25 +08:00
let mut stmt = db.prepare_cached(sql).unwrap();
2015-12-19 03:18:46 +08:00
assert_eq!(0, cache.len());
assert_eq!(0,
2016-01-08 00:20:42 +08:00
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32,i64>(0));
2015-12-19 03:18:46 +08:00
}
assert_eq!(1, cache.len());
2015-12-19 03:18:46 +08:00
{
2016-05-18 01:11:25 +08:00
let mut stmt = db.prepare_cached(sql).unwrap();
2015-12-19 03:18:46 +08:00
assert_eq!(0, cache.len());
assert_eq!(0,
2016-01-08 00:20:42 +08:00
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32,i64>(0));
2015-12-19 03:18:46 +08:00
}
2015-12-18 03:02:49 +08:00
assert_eq!(1, cache.len());
cache.clear();
assert_eq!(0, cache.len());
2016-05-18 01:11:25 +08:00
assert_eq!(initial_capacity, cache.capacity());
2015-08-02 18:07:49 +08:00
}
2015-12-20 00:01:06 +08:00
#[test]
fn test_discard() {
2015-12-20 00:01:06 +08:00
let db = Connection::open_in_memory().unwrap();
2016-05-18 01:11:25 +08:00
let cache = &db.cache;
2015-12-20 00:01:06 +08:00
let sql = "PRAGMA schema_version";
{
2016-05-18 01:11:25 +08:00
let mut stmt = db.prepare_cached(sql).unwrap();
2015-12-20 00:01:06 +08:00
assert_eq!(0, cache.len());
assert_eq!(0,
2016-01-08 00:20:42 +08:00
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32,i64>(0));
stmt.discard();
2015-12-20 00:01:06 +08:00
}
assert_eq!(0, cache.len());
}
2015-08-02 18:07:49 +08:00
}