Cache param count and make statement cache more effective

This commit is contained in:
Thom Chiovoloni 2020-04-16 08:22:47 -07:00 committed by Thom Chiovoloni
parent 6485b324d7
commit a776f460e8
3 changed files with 62 additions and 9 deletions

View File

@ -5,6 +5,7 @@ use crate::{Connection, Result, Statement};
use lru_cache::LruCache; use lru_cache::LruCache;
use std::cell::RefCell; use std::cell::RefCell;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::Arc;
impl Connection { impl Connection {
/// Prepare a SQL statement for execution, returning a previously prepared /// Prepare a SQL statement for execution, returning a previously prepared
@ -54,7 +55,7 @@ impl Connection {
/// Prepared statements LRU cache. /// Prepared statements LRU cache.
#[derive(Debug)] #[derive(Debug)]
pub struct StatementCache(RefCell<LruCache<String, RawStatement>>); pub struct StatementCache(RefCell<LruCache<Arc<str>, RawStatement>>);
/// Cacheable statement. /// Cacheable statement.
/// ///
@ -125,12 +126,16 @@ impl StatementCache {
conn: &'conn Connection, conn: &'conn Connection,
sql: &str, sql: &str,
) -> Result<CachedStatement<'conn>> { ) -> Result<CachedStatement<'conn>> {
let trimmed = sql.trim();
let mut cache = self.0.borrow_mut(); let mut cache = self.0.borrow_mut();
let stmt = match cache.remove(sql.trim()) { let stmt = match cache.remove(trimmed) {
Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)), Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
None => conn.prepare(sql), None => conn.prepare(trimmed),
}; };
stmt.map(|stmt| CachedStatement::new(stmt, self)) stmt.map(|mut stmt| {
stmt.stmt.set_statement_cache_key(trimmed);
CachedStatement::new(stmt, self)
})
} }
// Return a statement to the cache. // Return a statement to the cache.
@ -140,10 +145,14 @@ impl StatementCache {
} }
let mut cache = self.0.borrow_mut(); let mut cache = self.0.borrow_mut();
stmt.clear_bindings(); stmt.clear_bindings();
let sql = String::from_utf8_lossy(stmt.sql().unwrap().to_bytes()) if let Some(sql) = stmt.statement_cache_key() {
.trim()
.to_string();
cache.insert(sql, stmt); cache.insert(sql, stmt);
} else {
debug_assert!(
false,
"bug in statement cache code, statement returned to cache that without key"
);
}
} }
fn flush(&self) { fn flush(&self) {

View File

@ -3,16 +3,32 @@ use super::unlock_notify;
use super::StatementStatus; use super::StatementStatus;
#[cfg(feature = "modern_sqlite")] #[cfg(feature = "modern_sqlite")]
use crate::util::SqliteMallocString; use crate::util::SqliteMallocString;
use std::cell::Cell;
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::c_int; use std::os::raw::c_int;
use std::ptr; use std::ptr;
use std::sync::Arc;
// Private newtype for raw sqlite3_stmts that finalize themselves when dropped. // Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
#[derive(Debug)] #[derive(Debug)]
pub struct RawStatement { pub struct RawStatement {
ptr: *mut ffi::sqlite3_stmt, ptr: *mut ffi::sqlite3_stmt,
tail: bool, tail: bool,
// Cached indices of named parameters, computed on the fly.
cache: crate::util::ParamIndexCache, cache: crate::util::ParamIndexCache,
// Cached count of named parameters, computed on first use.
bind_parameter_count: Cell<Option<usize>>,
// Cached SQL (trimmed) that we use as the key when we're in the statement
// cache. This is None for statements which didn't come from the statement
// cache.
//
// This is probably the same as `self.sql()` in most cases, but we don't
// care either way -- It's a better cache key as it is anyway since it's the
// actual source we got from rust.
//
// One example of a case where the result of `sqlite_sql` and the value in
// `statement_cache_key` might differ is if the statement has a `tail`.
statement_cache_key: Option<Arc<str>>,
} }
impl RawStatement { impl RawStatement {
@ -20,7 +36,9 @@ impl RawStatement {
RawStatement { RawStatement {
ptr: stmt, ptr: stmt,
tail, tail,
bind_parameter_count: Cell::new(None),
cache: Default::default(), cache: Default::default(),
statement_cache_key: None,
} }
} }
@ -28,11 +46,20 @@ impl RawStatement {
self.ptr.is_null() self.ptr.is_null()
} }
pub(crate) fn set_statement_cache_key(&mut self, p: impl Into<Arc<str>>) {
self.statement_cache_key = Some(p.into());
}
pub(crate) fn statement_cache_key(&self) -> Option<Arc<str>> {
self.statement_cache_key.clone()
}
pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt { pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
self.ptr self.ptr
} }
pub fn column_count(&self) -> usize { pub fn column_count(&self) -> usize {
// Note: Can't cache this as it changes if the schema is altered.
unsafe { ffi::sqlite3_column_count(self.ptr) as usize } unsafe { ffi::sqlite3_column_count(self.ptr) as usize }
} }
@ -94,7 +121,9 @@ impl RawStatement {
} }
pub fn bind_parameter_count(&self) -> usize { pub fn bind_parameter_count(&self) -> usize {
unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize } crate::util::get_cached(&self.bind_parameter_count, || unsafe {
ffi::sqlite3_bind_parameter_count(self.ptr) as usize
})
} }
pub fn bind_parameter_index(&self, name: &str) -> Option<usize> { pub fn bind_parameter_index(&self, name: &str) -> Option<usize> {

View File

@ -9,3 +9,18 @@ pub(crate) use small_cstr::SmallCString;
mod sqlite_string; mod sqlite_string;
#[cfg(any(feature = "modern_sqlite", feature = "vtab"))] #[cfg(any(feature = "modern_sqlite", feature = "vtab"))]
pub(crate) use sqlite_string::SqliteMallocString; pub(crate) use sqlite_string::SqliteMallocString;
#[inline]
pub(crate) fn get_cached<T, F>(cache: &std::cell::Cell<Option<T>>, lookup: F) -> T
where
T: Copy,
F: FnOnce() -> T,
{
if let Some(v) = cache.get() {
v
} else {
let cb = lookup();
cache.set(Some(cb));
cb
}
}