vibesql_executor/evaluator/
single.rs

1//! Single-table expression evaluator
2//!
3//! This module provides the ExpressionEvaluator for evaluating expressions
4//! in the context of a single table schema.
5
6use std::{cell::RefCell, rc::Rc};
7
8use lru::LruCache;
9
10use crate::errors::ExecutorError;
11
12/// Evaluates expressions in the context of a row
13pub struct ExpressionEvaluator<'a> {
14    pub(super) schema: &'a vibesql_catalog::TableSchema,
15    pub(super) outer_row: Option<&'a vibesql_storage::Row>,
16    pub(super) outer_schema: Option<&'a vibesql_catalog::TableSchema>,
17    pub(super) database: Option<&'a vibesql_storage::Database>,
18    /// Trigger context for OLD/NEW pseudo-variable resolution
19    pub(super) trigger_context: Option<&'a crate::trigger_execution::TriggerContext<'a>>,
20    /// Procedural context for stored procedure/function variable resolution
21    pub(super) procedural_context: Option<&'a crate::procedural::ExecutionContext>,
22    /// CTE context for WITH clause support in UPDATE/DELETE subqueries
23    pub(super) cte_context: Option<&'a std::collections::HashMap<String, crate::select::cte::CteResult>>,
24    /// Current depth in expression tree (for preventing stack overflow)
25    pub(super) depth: usize,
26    /// CSE cache for common sub-expression elimination with LRU eviction (shared via Rc across
27    /// depth levels)
28    pub(super) cse_cache: Rc<RefCell<LruCache<u64, vibesql_types::SqlValue>>>,
29    /// Whether CSE is enabled (can be disabled for debugging)
30    pub(super) enable_cse: bool,
31    /// Cache for non-correlated subquery results with LRU eviction (key = subquery hash, value =
32    /// result rows) Shared via Rc across child evaluators within a single statement execution.
33    pub(super) subquery_cache: Rc<RefCell<LruCache<u64, Vec<vibesql_storage::Row>>>>,
34    /// Row index for ROWID pseudo-column support (SQLite compatibility)
35    /// When set, ROWID/_rowid_/oid column references will return this value
36    pub(super) row_index: Option<u64>,
37    /// Table alias for UPDATE/DELETE statements (SQLite extension: UPDATE t1 AS xyz)
38    /// When set, column references qualified with this alias will resolve to the schema
39    pub(super) table_alias: Option<String>,
40}
41
42impl<'a> ExpressionEvaluator<'a> {
43    /// Create a new expression evaluator for a given schema
44    pub fn new(schema: &'a vibesql_catalog::TableSchema) -> Self {
45        ExpressionEvaluator {
46            schema,
47            outer_row: None,
48            outer_schema: None,
49            database: None,
50            trigger_context: None,
51            procedural_context: None,
52            cte_context: None,
53            depth: 0,
54            cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
55            enable_cse: super::caching::is_cse_enabled(),
56            subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
57            row_index: None,
58            table_alias: None,
59        }
60    }
61
62    /// Create a new expression evaluator with outer query context for correlated subqueries
63    pub fn with_outer_context(
64        schema: &'a vibesql_catalog::TableSchema,
65        outer_row: &'a vibesql_storage::Row,
66        outer_schema: &'a vibesql_catalog::TableSchema,
67    ) -> Self {
68        ExpressionEvaluator {
69            schema,
70            outer_row: Some(outer_row),
71            outer_schema: Some(outer_schema),
72            database: None,
73            trigger_context: None,
74            procedural_context: None,
75            cte_context: None,
76            depth: 0,
77            cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
78            enable_cse: super::caching::is_cse_enabled(),
79            subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
80            row_index: None,
81            table_alias: None,
82        }
83    }
84
85    /// Create a new expression evaluator with database reference for subqueries
86    pub fn with_database(
87        schema: &'a vibesql_catalog::TableSchema,
88        database: &'a vibesql_storage::Database,
89    ) -> Self {
90        ExpressionEvaluator {
91            schema,
92            outer_row: None,
93            outer_schema: None,
94            database: Some(database),
95            trigger_context: None,
96            procedural_context: None,
97            cte_context: None,
98            depth: 0,
99            cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
100            enable_cse: super::caching::is_cse_enabled(),
101            subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
102            row_index: None,
103            table_alias: None,
104        }
105    }
106
107    /// Create a new expression evaluator with trigger context for OLD/NEW pseudo-variables
108    pub fn with_trigger_context(
109        schema: &'a vibesql_catalog::TableSchema,
110        database: &'a vibesql_storage::Database,
111        trigger_context: &'a crate::trigger_execution::TriggerContext<'a>,
112    ) -> Self {
113        ExpressionEvaluator {
114            schema,
115            outer_row: None,
116            outer_schema: None,
117            database: Some(database),
118            trigger_context: Some(trigger_context),
119            procedural_context: None,
120            cte_context: None,
121            depth: 0,
122            cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
123            enable_cse: super::caching::is_cse_enabled(),
124            subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
125            row_index: None,
126            table_alias: None,
127        }
128    }
129
130    /// Create a new expression evaluator with database and outer context (for correlated
131    /// subqueries)
132    pub fn with_database_and_outer_context(
133        schema: &'a vibesql_catalog::TableSchema,
134        database: &'a vibesql_storage::Database,
135        outer_row: &'a vibesql_storage::Row,
136        outer_schema: &'a vibesql_catalog::TableSchema,
137    ) -> Self {
138        ExpressionEvaluator {
139            schema,
140            outer_row: Some(outer_row),
141            outer_schema: Some(outer_schema),
142            database: Some(database),
143            trigger_context: None,
144            procedural_context: None,
145            cte_context: None,
146            depth: 0,
147            cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
148            enable_cse: super::caching::is_cse_enabled(),
149            subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
150            row_index: None,
151            table_alias: None,
152        }
153    }
154
155    /// Create a new expression evaluator with procedural context for stored procedure/function
156    /// execution
157    pub fn with_procedural_context(
158        schema: &'a vibesql_catalog::TableSchema,
159        database: &'a vibesql_storage::Database,
160        procedural_context: &'a crate::procedural::ExecutionContext,
161    ) -> Self {
162        ExpressionEvaluator {
163            schema,
164            outer_row: None,
165            outer_schema: None,
166            database: Some(database),
167            trigger_context: None,
168            procedural_context: Some(procedural_context),
169            cte_context: None,
170            depth: 0,
171            cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
172            enable_cse: super::caching::is_cse_enabled(),
173            subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
174            row_index: None,
175            table_alias: None,
176        }
177    }
178
179    /// Create a new expression evaluator with database and CTE context
180    /// Used for UPDATE/DELETE statements with WITH clauses
181    pub fn with_database_and_cte(
182        schema: &'a vibesql_catalog::TableSchema,
183        database: &'a vibesql_storage::Database,
184        cte_context: &'a std::collections::HashMap<String, crate::select::cte::CteResult>,
185    ) -> Self {
186        ExpressionEvaluator {
187            schema,
188            outer_row: None,
189            outer_schema: None,
190            database: Some(database),
191            trigger_context: None,
192            procedural_context: None,
193            cte_context: Some(cte_context),
194            depth: 0,
195            cse_cache: Rc::new(RefCell::new(super::caching::create_cse_cache())),
196            enable_cse: super::caching::is_cse_enabled(),
197            subquery_cache: Rc::new(RefCell::new(super::caching::create_subquery_cache())),
198            row_index: None,
199            table_alias: None,
200        }
201    }
202
203    /// Set the row index for ROWID pseudo-column support
204    ///
205    /// When set, references to ROWID, _rowid_, or oid columns will return
206    /// this value as a Bigint. This provides SQLite compatibility.
207    pub fn set_row_index(&mut self, index: u64) {
208        self.row_index = Some(index);
209    }
210
211    /// Clear the row index (typically between row evaluations)
212    pub fn clear_row_index(&mut self) {
213        self.row_index = None;
214    }
215
216    /// Set the table alias for UPDATE/DELETE statements
217    ///
218    /// When set, column references qualified with this alias will resolve to the schema.
219    /// SQLite extension: UPDATE t1 AS xyz SET ... WHERE xyz.column = ...
220    pub fn set_table_alias(&mut self, alias: String) {
221        self.table_alias = Some(alias);
222    }
223
224    /// Evaluate a binary operation
225    pub(crate) fn eval_binary_op(
226        &self,
227        left: &vibesql_types::SqlValue,
228        op: &vibesql_ast::BinaryOperator,
229        right: &vibesql_types::SqlValue,
230    ) -> Result<vibesql_types::SqlValue, ExecutorError> {
231        // Extract SQL mode from database, default to MySQL if not available
232        let sql_mode = self.database.map(|db| db.sql_mode()).unwrap_or_default();
233
234        super::core::eval_binary_op_static(left, op, right, sql_mode)
235    }
236
237    /// Clear the CSE cache
238    /// Should be called before evaluating expressions for a new row in multi-row contexts
239    pub fn clear_cse_cache(&self) {
240        self.cse_cache.borrow_mut().clear();
241    }
242
243    /// Static version of eval_binary_op for shared logic
244    ///
245    /// Delegates to the core module's implementation.
246    pub(crate) fn eval_binary_op_static(
247        left: &vibesql_types::SqlValue,
248        op: &vibesql_ast::BinaryOperator,
249        right: &vibesql_types::SqlValue,
250        sql_mode: vibesql_types::SqlMode,
251    ) -> Result<vibesql_types::SqlValue, ExecutorError> {
252        super::core::eval_binary_op_static(left, op, right, sql_mode)
253    }
254
255    /// Static version of eval_between for constant folding during optimization
256    ///
257    /// Delegates to the core module's implementation.
258    pub(crate) fn eval_between_static(
259        expr_val: &vibesql_types::SqlValue,
260        low_val: &vibesql_types::SqlValue,
261        high_val: &vibesql_types::SqlValue,
262        negated: bool,
263        symmetric: bool,
264        sql_mode: vibesql_types::SqlMode,
265    ) -> Result<vibesql_types::SqlValue, ExecutorError> {
266        super::core::eval_between_static(expr_val, low_val, high_val, negated, symmetric, sql_mode)
267    }
268
269    /// Compare two SQL values for equality in simple CASE expressions
270    ///
271    /// Delegates to the core module's implementation.
272    pub(crate) fn values_are_equal(
273        left: &vibesql_types::SqlValue,
274        right: &vibesql_types::SqlValue,
275    ) -> bool {
276        super::core::values_are_equal(left, right)
277    }
278
279    /// Compare two SQL values using IS DISTINCT FROM semantics (SQL:1999)
280    ///
281    /// Delegates to the core module's implementation.
282    pub(crate) fn values_are_distinct(
283        left: &vibesql_types::SqlValue,
284        right: &vibesql_types::SqlValue,
285    ) -> bool {
286        super::core::values_are_distinct(left, right)
287    }
288
289    /// Helper to execute a closure with incremented depth
290    pub(super) fn with_incremented_depth<F, T>(&self, f: F) -> Result<T, ExecutorError>
291    where
292        F: FnOnce(&Self) -> Result<T, ExecutorError>,
293    {
294        // Create a new evaluator with incremented depth
295        // Share the CSE cache and subquery cache across depth levels for consistent caching
296        let evaluator = ExpressionEvaluator {
297            schema: self.schema,
298            outer_row: self.outer_row,
299            outer_schema: self.outer_schema,
300            database: self.database,
301            trigger_context: self.trigger_context,
302            procedural_context: self.procedural_context,
303            cte_context: self.cte_context,
304            depth: self.depth + 1,
305            cse_cache: self.cse_cache.clone(),
306            enable_cse: self.enable_cse,
307            subquery_cache: self.subquery_cache.clone(),
308            row_index: self.row_index,
309            table_alias: self.table_alias.clone(),
310        };
311        f(&evaluator)
312    }
313}