use lru::LruCache;
use oxisql_core::OxiSqlError;
use sqlparser::ast::Statement;
use std::num::NonZeroUsize;
use std::sync::Mutex;
use crate::{parse_with_dialect, SqlDialect};
pub struct ParseCache {
inner: Mutex<LruCache<(String, SqlDialect), Vec<Statement>>>,
}
impl ParseCache {
pub fn new(capacity: usize) -> Self {
let cap = NonZeroUsize::new(capacity.max(1)).unwrap_or(NonZeroUsize::MIN);
Self {
inner: Mutex::new(LruCache::new(cap)),
}
}
pub fn parse(&self, sql: &str, dialect: SqlDialect) -> Result<Vec<Statement>, OxiSqlError> {
let key = (sql.to_string(), dialect);
{
let mut cache = self
.inner
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if let Some(stmts) = cache.get(&key) {
return Ok(stmts.clone());
}
}
let result = parse_with_dialect(sql, key.1)?;
{
let mut cache = self
.inner
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
cache.put(key, result.clone());
}
Ok(result)
}
pub fn len(&self) -> usize {
self.inner
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&self) {
self.inner
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.clear();
}
}
impl std::fmt::Debug for ParseCache {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let len = self.len();
write!(f, "ParseCache(len={len})")
}
}