Просмотр исходного кода

Add Cache to SQL stmt

The cache will store the placeholders and if possible the RAW SQL with position
placeholders, to avoid repetitive computations
Cesar Rodas 11 месяцев назад
Родитель
Сommit
034af74013
2 измененных файлов с 73 добавлено и 7 удалено
  1. 1 0
      crates/cdk-sql-common/Cargo.toml
  2. 72 7
      crates/cdk-sql-common/src/stmt.rs

+ 1 - 0
crates/cdk-sql-common/Cargo.toml

@@ -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

+ 72 - 7
crates/cdk-sql-common/src/stmt.rs

@@ -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> {
-        Ok(Self {
-            parts: split_sql_parts(sql)?,
-            ..Default::default()
-        })
+    fn new(sql: &str, cache: Arc<RwLock<Cache>>) -> Result<Self, SqlParseError> {
+        let parsed = cache
+            .read()
+            .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)))
 }
 }