mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-24 08:05:02 +01:00
Add Cache to SQL stmt
The cache will store the placeholders and if possible the RAW SQL with position placeholders, to avoid repetitive computations
This commit is contained in:
@@ -28,3 +28,4 @@ serde.workspace = true
|
|||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
lightning-invoice.workspace = true
|
lightning-invoice.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
once_cell.workspace = true
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
//! Stataments mod
|
//! Stataments mod
|
||||||
use std::sync::Arc;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use cdk_common::database::Error;
|
use cdk_common::database::Error;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::database::DatabaseExecutor;
|
use crate::database::DatabaseExecutor;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
@@ -140,9 +142,14 @@ pub fn split_sql_parts(input: &str) -> Result<Vec<SqlPart>, SqlParseError> {
|
|||||||
Ok(parts)
|
Ok(parts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Cache = HashMap<String, (Vec<SqlPart>, Option<Arc<str>>)>;
|
||||||
|
|
||||||
/// Sql message
|
/// Sql message
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Statement {
|
pub struct Statement {
|
||||||
|
cache: Arc<RwLock<Cache>>,
|
||||||
|
cached_sql: Option<Arc<str>>,
|
||||||
|
sql: Option<String>,
|
||||||
/// The SQL statement
|
/// The SQL statement
|
||||||
pub parts: Vec<SqlPart>,
|
pub parts: Vec<SqlPart>,
|
||||||
/// The expected response type
|
/// The expected response type
|
||||||
@@ -151,11 +158,35 @@ pub struct Statement {
|
|||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
/// Creates a new statement
|
/// Creates a new statement
|
||||||
pub fn new(sql: &str) -> Result<Self, SqlParseError> {
|
fn new(sql: &str, cache: Arc<RwLock<Cache>>) -> Result<Self, SqlParseError> {
|
||||||
Ok(Self {
|
let parsed = cache
|
||||||
parts: split_sql_parts(sql)?,
|
.read()
|
||||||
..Default::default()
|
.map(|cache| cache.get(sql).cloned())
|
||||||
})
|
.ok()
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
if let Some((parts, cached_sql)) = parsed {
|
||||||
|
Ok(Self {
|
||||||
|
parts,
|
||||||
|
cached_sql,
|
||||||
|
sql: None,
|
||||||
|
cache,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let parts = split_sql_parts(sql)?;
|
||||||
|
|
||||||
|
let _ = cache.write().map(|mut cache| {
|
||||||
|
cache.insert(sql.to_owned(), (parts.clone(), None));
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
parts,
|
||||||
|
sql: Some(sql.to_owned()),
|
||||||
|
cache,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Statement into a SQL statement and the list of placeholders
|
/// Convert Statement into a SQL statement and the list of placeholders
|
||||||
@@ -164,7 +195,29 @@ impl Statement {
|
|||||||
/// to be more widely supported, although it can be reimplemented with other formats since part
|
/// to be more widely supported, although it can be reimplemented with other formats since part
|
||||||
/// is public
|
/// is public
|
||||||
pub fn to_sql(self) -> Result<(String, Vec<Value>), Error> {
|
pub fn to_sql(self) -> Result<(String, Vec<Value>), Error> {
|
||||||
|
if let Some(cached_sql) = self.cached_sql {
|
||||||
|
let sql = cached_sql.to_string();
|
||||||
|
let values = self
|
||||||
|
.parts
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| match x {
|
||||||
|
SqlPart::Placeholder(name, value) => {
|
||||||
|
match value.ok_or(Error::MissingPlaceholder(name.to_string()))? {
|
||||||
|
PlaceholderValue::Value(value) => Ok(vec![value]),
|
||||||
|
PlaceholderValue::Set(values) => Ok(values),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SqlPart::Raw(_) => Ok(vec![]),
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, Error>>()?
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
return Ok((sql, values));
|
||||||
|
}
|
||||||
|
|
||||||
let mut placeholder_values = Vec::new();
|
let mut placeholder_values = Vec::new();
|
||||||
|
let mut can_be_cached = true;
|
||||||
let sql = self
|
let sql = self
|
||||||
.parts
|
.parts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -176,6 +229,7 @@ impl Statement {
|
|||||||
Ok::<_, Error>(format!("${}", placeholder_values.len()))
|
Ok::<_, Error>(format!("${}", placeholder_values.len()))
|
||||||
}
|
}
|
||||||
PlaceholderValue::Set(mut values) => {
|
PlaceholderValue::Set(mut values) => {
|
||||||
|
can_be_cached = false;
|
||||||
let start_size = placeholder_values.len();
|
let start_size = placeholder_values.len();
|
||||||
placeholder_values.append(&mut values);
|
placeholder_values.append(&mut values);
|
||||||
let placeholders = (start_size + 1..=placeholder_values.len())
|
let placeholders = (start_size + 1..=placeholder_values.len())
|
||||||
@@ -191,6 +245,16 @@ impl Statement {
|
|||||||
.collect::<Result<Vec<String>, _>>()?
|
.collect::<Result<Vec<String>, _>>()?
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|
||||||
|
if can_be_cached {
|
||||||
|
if let Some(original_sql) = self.sql {
|
||||||
|
let _ = self.cache.write().map(|mut cache| {
|
||||||
|
if let Some((_, cached_sql)) = cache.get_mut(&original_sql) {
|
||||||
|
*cached_sql = Some(sql.clone().into());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok((sql, placeholder_values))
|
Ok((sql, placeholder_values))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,5 +352,6 @@ impl Statement {
|
|||||||
/// Creates a new query statement
|
/// Creates a new query statement
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn query(sql: &str) -> Result<Statement, Error> {
|
pub fn query(sql: &str) -> Result<Statement, Error> {
|
||||||
Statement::new(sql).map_err(|e| Error::Database(Box::new(e)))
|
static CACHE: Lazy<Arc<RwLock<Cache>>> = Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
|
||||||
|
Statement::new(sql, CACHE.clone()).map_err(|e| Error::Database(Box::new(e)))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user