use std::{
cell::RefCell,
collections::HashMap,
hash::{Hash, Hasher},
rc::Rc,
};
use ahash::AHasher;
use lru::LruCache;
use crate::{errors::ExecutorError, schema::CombinedSchema, select::WindowFunctionKey};
pub struct CombinedExpressionEvaluator<'a> {
pub(super) schema: &'a CombinedSchema,
pub(super) database: Option<&'a vibesql_storage::Database>,
pub(super) outer_row: Option<&'a vibesql_storage::Row>,
pub(super) outer_schema: Option<&'a CombinedSchema>,
pub(super) outer_rows: Option<&'a [vibesql_storage::Row]>,
pub(super) outer_context: Option<&'a CombinedExpressionEvaluator<'a>>,
pub(super) window_mapping: Option<&'a HashMap<WindowFunctionKey, usize>>,
pub(super) procedural_context: Option<&'a crate::procedural::ExecutionContext>,
pub(super) trigger_context: Option<&'a crate::trigger_execution::TriggerContext<'a>>,
pub(super) cte_context: Option<&'a HashMap<String, crate::select::cte::CteResult>>,
column_cache: RefCell<HashMap<u64, usize>>,
pub(super) subquery_cache: Rc<RefCell<LruCache<u64, Vec<vibesql_storage::Row>>>>,
pub(super) depth: usize,
pub(super) cse_cache: Rc<RefCell<LruCache<u64, vibesql_types::SqlValue>>>,
pub(super) enable_cse: bool,
pub(super) correlation_cache: Rc<RefCell<HashMap<usize, bool>>>,
pub(super) subquery_hash_cache: Rc<RefCell<HashMap<usize, u64>>>,
}
impl<'a> CombinedExpressionEvaluator<'a> {
#[allow(dead_code)]
pub(crate) fn new(schema: &'a CombinedSchema) -> Self {
CombinedExpressionEvaluator {
schema,
database: None,
outer_row: None,
outer_schema: None,
outer_rows: None,
outer_context: None,
window_mapping: None,
procedural_context: None,
trigger_context: None,
cte_context: None,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: None,
outer_schema: None,
outer_rows: None,
outer_context: None,
window_mapping: None,
procedural_context: None,
trigger_context: None,
cte_context: None,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_and_outer_context(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
outer_row: &'a vibesql_storage::Row,
outer_schema: &'a CombinedSchema,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: Some(outer_row),
outer_schema: Some(outer_schema),
outer_rows: None,
outer_context: None,
window_mapping: None,
procedural_context: None,
trigger_context: None,
cte_context: None,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_and_windows(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
window_mapping: &'a HashMap<WindowFunctionKey, usize>,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: None,
outer_schema: None,
outer_rows: None,
outer_context: None,
window_mapping: Some(window_mapping),
procedural_context: None,
trigger_context: None,
cte_context: None,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_outer_and_windows(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
outer_row: &'a vibesql_storage::Row,
outer_schema: &'a CombinedSchema,
window_mapping: &'a HashMap<WindowFunctionKey, usize>,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: Some(outer_row),
outer_schema: Some(outer_schema),
outer_rows: None,
outer_context: None,
window_mapping: Some(window_mapping),
procedural_context: None,
trigger_context: None,
cte_context: None,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_and_windows_and_cte(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
window_mapping: &'a HashMap<WindowFunctionKey, usize>,
cte_context: &'a HashMap<String, crate::select::cte::CteResult>,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: None,
outer_schema: None,
outer_rows: None,
outer_context: None,
window_mapping: Some(window_mapping),
procedural_context: None,
trigger_context: None,
cte_context: Some(cte_context),
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_and_procedural_context(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
procedural_context: &'a crate::procedural::ExecutionContext,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: None,
outer_schema: None,
outer_rows: None,
outer_context: None,
window_mapping: None,
procedural_context: Some(procedural_context),
trigger_context: None,
cte_context: None,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_and_trigger_context(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
trigger_context: &'a crate::trigger_execution::TriggerContext<'a>,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: None,
outer_schema: None,
outer_rows: None,
outer_context: None,
window_mapping: None,
procedural_context: None,
trigger_context: Some(trigger_context),
cte_context: None,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_and_cte(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
cte_context: &'a HashMap<String, crate::select::cte::CteResult>,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: None,
outer_schema: None,
outer_rows: None,
outer_context: None,
window_mapping: None,
procedural_context: None,
trigger_context: None,
cte_context: Some(cte_context),
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_and_outer_context_and_cte(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
outer_row: &'a vibesql_storage::Row,
outer_schema: &'a CombinedSchema,
cte_context: &'a HashMap<String, crate::select::cte::CteResult>,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: Some(outer_row),
outer_schema: Some(outer_schema),
outer_rows: None,
outer_context: None,
window_mapping: None,
procedural_context: None,
trigger_context: None,
cte_context: Some(cte_context),
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_database_and_procedural_context_and_cte(
schema: &'a CombinedSchema,
database: &'a vibesql_storage::Database,
procedural_context: &'a crate::procedural::ExecutionContext,
cte_context: &'a HashMap<String, crate::select::cte::CteResult>,
) -> Self {
CombinedExpressionEvaluator {
schema,
database: Some(database),
outer_row: None,
outer_schema: None,
outer_rows: None,
outer_context: None,
window_mapping: None,
procedural_context: Some(procedural_context),
trigger_context: None,
cte_context: Some(cte_context),
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: super::caching::is_cse_enabled(),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
pub(crate) fn with_cte_context(
mut self,
cte_context: &'a HashMap<String, crate::select::cte::CteResult>,
) -> Self {
self.cte_context = Some(cte_context);
self
}
pub(crate) fn clear_cse_cache(&self) {
self.cse_cache.borrow_mut().clear();
}
pub fn set_outer_rows(&mut self, outer_rows: &'a [vibesql_storage::Row]) {
self.outer_rows = Some(outer_rows);
}
pub(crate) fn get_outer_rows(&self) -> Option<&'a [vibesql_storage::Row]> {
self.outer_rows
}
pub(crate) fn get_outer_schema(&self) -> Option<&'a CombinedSchema> {
self.outer_schema
}
pub(crate) fn get_schema(&self) -> &'a CombinedSchema {
self.schema
}
#[inline]
fn column_cache_key(table: Option<&str>, column: &str) -> u64 {
let mut hasher = AHasher::default();
table.hash(&mut hasher);
column.hash(&mut hasher);
hasher.finish()
}
pub(crate) fn get_column_index_cached(
&self,
table: Option<&str>,
column: &str,
) -> Option<usize> {
let key = Self::column_cache_key(table, column);
if let Some(&idx) = self.column_cache.borrow().get(&key) {
return Some(idx);
}
if let Some(idx) = self.schema.get_column_index(table, column) {
self.column_cache.borrow_mut().insert(key, idx);
Some(idx)
} else {
None
}
}
pub(super) fn with_incremented_depth<F, T>(&self, f: F) -> Result<T, ExecutorError>
where
F: FnOnce(&Self) -> Result<T, ExecutorError>,
{
let evaluator = CombinedExpressionEvaluator {
schema: self.schema,
database: self.database,
outer_row: self.outer_row,
outer_schema: self.outer_schema,
outer_rows: self.outer_rows,
outer_context: self.outer_context,
window_mapping: self.window_mapping,
procedural_context: self.procedural_context,
trigger_context: self.trigger_context,
cte_context: self.cte_context,
column_cache: RefCell::new(self.column_cache.borrow().clone()),
subquery_cache: self.subquery_cache.clone(),
depth: self.depth + 1,
cse_cache: self.cse_cache.clone(),
enable_cse: self.enable_cse,
correlation_cache: self.correlation_cache.clone(),
subquery_hash_cache: self.subquery_hash_cache.clone(),
};
f(&evaluator)
}
pub fn clone_for_new_expression(&self) -> Self {
CombinedExpressionEvaluator {
schema: self.schema,
database: self.database,
outer_row: self.outer_row,
outer_schema: self.outer_schema,
outer_rows: self.outer_rows,
outer_context: self.outer_context,
window_mapping: self.window_mapping,
procedural_context: self.procedural_context,
trigger_context: self.trigger_context,
cte_context: self.cte_context,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: self.subquery_cache.clone(),
depth: self.depth,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
enable_cse: self.enable_cse,
correlation_cache: self.correlation_cache.clone(),
subquery_hash_cache: self.subquery_hash_cache.clone(),
}
}
pub(crate) fn schema(&self) -> &'a CombinedSchema {
self.schema
}
pub(crate) fn database(&self) -> Option<&'a vibesql_storage::Database> {
self.database
}
#[inline]
pub(crate) fn is_correlated_cached(&self, subquery: &vibesql_ast::SelectStmt) -> bool {
let ptr = subquery as *const _ as usize;
if let Some(&is_correlated) = self.correlation_cache.borrow().get(&ptr) {
return is_correlated;
}
let is_correlated =
crate::optimizer::subquery_rewrite::correlation::is_correlated(subquery);
self.correlation_cache.borrow_mut().insert(ptr, is_correlated);
is_correlated
}
#[inline]
pub(crate) fn compute_subquery_hash_cached(&self, subquery: &vibesql_ast::SelectStmt) -> u64 {
let ptr = subquery as *const _ as usize;
if let Some(&hash) = self.subquery_hash_cache.borrow().get(&ptr) {
return hash;
}
let hash = super::caching::compute_subquery_hash(subquery);
self.subquery_hash_cache.borrow_mut().insert(ptr, hash);
hash
}
#[cfg(feature = "parallel")]
pub(crate) fn get_parallel_components(&self) -> super::parallel::ParallelComponents<'a> {
(
self.schema,
self.database,
self.outer_row,
self.outer_schema,
self.window_mapping,
self.cte_context,
self.enable_cse,
)
}
#[cfg(feature = "parallel")]
pub(crate) fn from_parallel_components(
schema: &'a CombinedSchema,
database: Option<&'a vibesql_storage::Database>,
outer_row: Option<&'a vibesql_storage::Row>,
outer_schema: Option<&'a CombinedSchema>,
window_mapping: Option<&'a HashMap<WindowFunctionKey, usize>>,
cte_context: Option<
&'a std::collections::HashMap<String, super::super::select::cte::CteResult>,
>,
enable_cse: bool,
) -> Self {
CombinedExpressionEvaluator {
schema,
database,
outer_row,
outer_schema,
outer_rows: None,
outer_context: None,
window_mapping,
procedural_context: None,
trigger_context: None,
cte_context,
column_cache: RefCell::new(HashMap::new()),
subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
depth: 0,
cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
correlation_cache: Rc::new(RefCell::new(HashMap::new())),
subquery_hash_cache: Rc::new(RefCell::new(HashMap::new())),
enable_cse,
}
}
}