rusqlite/src/cache.rs

352 lines
9.9 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
2018-10-31 03:11:35 +08:00
use crate::raw_statement::RawStatement;
2018-10-31 03:13:41 +08:00
use crate::{Connection, Result, Statement};
use lru_cache::LruCache;
2018-08-11 18:48:21 +08:00
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
2015-08-02 18:07:49 +08:00
2016-05-18 01:11:25 +08:00
impl Connection {
2018-08-17 00:29:46 +08:00
/// 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.
2016-05-18 01:11:25 +08:00
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert_new_people(conn: &Connection) -> Result<()> {
/// {
2018-10-31 03:11:35 +08:00
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?;
/// stmt.execute(&["Joe Smith"])?;
2016-05-18 01:11:25 +08:00
/// }
/// {
/// // This will return the same underlying SQLite statement handle without
/// // having to prepare it again.
2018-10-31 03:11:35 +08:00
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?;
/// stmt.execute(&["Bob Jones"])?;
2016-05-18 01:11:25 +08:00
/// }
/// Ok(())
/// }
/// ```
///
/// # Failure
///
2018-08-17 00:29:46 +08:00
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails.
2019-02-02 18:08:04 +08:00
pub fn prepare_cached(&self, sql: &str) -> Result<CachedStatement<'_>> {
2016-05-20 04:03:01 +08:00
self.cache.get(self, sql)
2016-05-18 01:11:25 +08:00
}
2018-08-17 00:29:46 +08:00
/// Set the maximum number of cached prepared statements this connection
/// will hold. By default, a connection will hold a relatively small
/// number of cached statements. If you need more, or know that you
/// will not use cached statements, you
/// can set the capacity manually using this method.
pub fn set_prepared_statement_cache_capacity(&self, capacity: usize) {
self.cache.set_capacity(capacity)
}
/// Remove/finalize all prepared statements currently in the cache.
pub fn flush_prepared_statement_cache(&self) {
self.cache.flush()
}
2016-05-18 01:11:25 +08:00
}
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(RefCell<LruCache<String, RawStatement>>);
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.
2016-05-18 01:13:51 +08:00
pub struct CachedStatement<'conn> {
stmt: Option<Statement<'conn>>,
cache: &'conn StatementCache,
}
2016-05-18 01:13:51 +08:00
impl<'conn> Deref for CachedStatement<'conn> {
type Target = Statement<'conn>;
2015-12-18 03:33:34 +08:00
2016-05-18 01:13:51 +08:00
fn deref(&self) -> &Statement<'conn> {
2015-12-18 03:33:34 +08:00
self.stmt.as_ref().unwrap()
}
}
2016-05-18 01:13:51 +08:00
impl<'conn> DerefMut for CachedStatement<'conn> {
fn deref_mut(&mut self) -> &mut Statement<'conn> {
2015-12-18 03:33:34 +08:00
self.stmt.as_mut().unwrap()
}
}
2019-02-03 18:02:38 +08:00
impl Drop for CachedStatement<'_> {
#[allow(unused_must_use)]
fn drop(&mut self) {
if let Some(stmt) = self.stmt.take() {
self.cache.cache_stmt(stmt.into());
}
}
}
2019-02-03 18:02:38 +08:00
impl CachedStatement<'_> {
fn new<'conn>(stmt: Statement<'conn>, cache: &'conn StatementCache) -> CachedStatement<'conn> {
2015-12-18 03:33:34 +08:00
CachedStatement {
stmt: Some(stmt),
2018-05-05 01:55:55 +08:00
cache,
2015-12-18 03:33:34 +08:00
}
}
2018-08-17 00:29:46 +08:00
/// Discard the statement, preventing it from being returned to its
/// `Connection`'s collection of cached statements.
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(RefCell::new(LruCache::new(capacity)))
2015-08-02 18:07:49 +08:00
}
fn set_capacity(&self, capacity: usize) {
self.0.borrow_mut().set_capacity(capacity)
}
// 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.
//
// # Failure
//
2018-08-17 00:29:46 +08:00
// Will return `Err` if no cached statement can be found and the underlying
// SQLite prepare call fails.
2018-08-11 18:48:21 +08:00
fn get<'conn>(
&'conn self,
conn: &'conn Connection,
sql: &str,
) -> Result<CachedStatement<'conn>> {
let mut cache = self.0.borrow_mut();
let stmt = match cache.remove(sql.trim()) {
Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
None => 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) {
if stmt.is_null() {
return;
}
let mut cache = self.0.borrow_mut();
stmt.clear_bindings();
let sql = String::from_utf8_lossy(stmt.sql().unwrap().to_bytes())
2018-08-11 18:48:21 +08:00
.trim()
.to_string();
cache.insert(sql, stmt);
2015-08-02 18:07:49 +08:00
}
fn flush(&self) {
let mut cache = self.0.borrow_mut();
cache.clear()
}
2015-08-02 18:07:49 +08:00
}
#[cfg(test)]
mod test {
use super::StatementCache;
2018-10-31 03:11:35 +08:00
use crate::{Connection, NO_PARAMS};
2019-03-20 03:45:04 +08:00
use fallible_iterator::FallibleIterator;
2015-08-02 18:07:49 +08:00
2016-05-18 01:11:25 +08:00
impl StatementCache {
fn clear(&self) {
self.0.borrow_mut().clear();
2016-05-18 01:11:25 +08:00
}
fn len(&self) -> usize {
self.0.borrow().len()
2016-05-18 01:11:25 +08:00
}
fn capacity(&self) -> usize {
self.0.borrow().capacity()
2016-05-18 01:11:25 +08:00
}
}
#[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,
stmt.query_row(NO_PARAMS, |r| r.get::<_, i64>(0)).unwrap()
);
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,
stmt.query_row(NO_PARAMS, |r| r.get::<_, i64>(0)).unwrap()
);
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_set_capacity() {
let db = Connection::open_in_memory().unwrap();
let cache = &db.cache;
let sql = "PRAGMA schema_version";
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(0, cache.len());
assert_eq!(
0,
stmt.query_row(NO_PARAMS, |r| r.get::<_, i64>(0)).unwrap()
);
}
assert_eq!(1, cache.len());
db.set_prepared_statement_cache_capacity(0);
assert_eq!(0, cache.len());
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(0, cache.len());
assert_eq!(
0,
stmt.query_row(NO_PARAMS, |r| r.get::<_, i64>(0)).unwrap()
);
}
assert_eq!(0, cache.len());
db.set_prepared_statement_cache_capacity(8);
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(0, cache.len());
assert_eq!(
0,
stmt.query_row(NO_PARAMS, |r| r.get::<_, i64>(0)).unwrap()
);
}
assert_eq!(1, cache.len());
}
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,
stmt.query_row(NO_PARAMS, |r| r.get::<_, i64>(0)).unwrap()
);
stmt.discard();
2015-12-20 00:01:06 +08:00
}
assert_eq!(0, cache.len());
}
#[test]
fn test_ddl() {
let db = Connection::open_in_memory().unwrap();
2018-08-11 18:48:21 +08:00
db.execute_batch(
r#"
CREATE TABLE foo (x INT);
INSERT INTO foo VALUES (1);
2018-08-11 18:48:21 +08:00
"#,
2018-10-28 15:51:02 +08:00
)
.unwrap();
let sql = "SELECT * FROM foo";
{
let mut stmt = db.prepare_cached(sql).unwrap();
2018-08-11 18:48:21 +08:00
assert_eq!(
2019-03-10 19:58:20 +08:00
Ok(Some(1i32)),
2019-03-20 03:45:04 +08:00
stmt.query(NO_PARAMS).unwrap().map(|r| r.get(0)).next()
2018-08-11 18:48:21 +08:00
);
}
2018-08-11 18:48:21 +08:00
db.execute_batch(
r#"
ALTER TABLE foo ADD COLUMN y INT;
UPDATE foo SET y = 2;
2018-08-11 18:48:21 +08:00
"#,
2018-10-28 15:51:02 +08:00
)
.unwrap();
{
let mut stmt = db.prepare_cached(sql).unwrap();
2018-08-11 18:48:21 +08:00
assert_eq!(
2019-03-10 19:58:20 +08:00
Ok(Some((1i32, 2i32))),
2019-03-20 03:45:04 +08:00
stmt.query(NO_PARAMS)
.unwrap()
.map(|r| Ok((r.get(0)?, r.get(1)?)))
2018-08-11 18:48:21 +08:00
.next()
);
}
}
#[test]
fn test_connection_close() {
let conn = Connection::open_in_memory().unwrap();
2018-08-11 18:48:21 +08:00
conn.prepare_cached("SELECT * FROM sqlite_master;").unwrap();
conn.close().expect("connection not closed");
}
#[test]
fn test_cache_key() {
let db = Connection::open_in_memory().unwrap();
let cache = &db.cache;
assert_eq!(0, cache.len());
//let sql = " PRAGMA schema_version; -- comment";
let sql = "PRAGMA schema_version; ";
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(0, cache.len());
assert_eq!(
0,
stmt.query_row(NO_PARAMS, |r| r.get::<_, i64>(0)).unwrap()
);
}
assert_eq!(1, cache.len());
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(0, cache.len());
assert_eq!(
0,
stmt.query_row(NO_PARAMS, |r| r.get::<_, i64>(0)).unwrap()
);
}
assert_eq!(1, cache.len());
}
#[test]
fn test_empty_stmt() {
let conn = Connection::open_in_memory().unwrap();
conn.prepare_cached("").unwrap();
}
2015-08-02 18:07:49 +08:00
}