vibesql_executor/evaluator/expressions/
eval.rs

1//! Main evaluation entry point and basic expression types
2
3use vibesql_types::SqlValue;
4
5use super::super::core::ExpressionEvaluator;
6use crate::errors::ExecutorError;
7
8impl ExpressionEvaluator<'_> {
9    /// Evaluate an expression in the context of a row
10    #[inline]
11    pub fn eval(
12        &self,
13        expr: &vibesql_ast::Expression,
14        row: &vibesql_storage::Row,
15    ) -> Result<vibesql_types::SqlValue, ExecutorError> {
16        // Check depth limit to prevent stack overflow from deeply nested expressions
17        if self.depth >= crate::limits::MAX_EXPRESSION_DEPTH {
18            return Err(ExecutorError::ExpressionDepthExceeded {
19                depth: self.depth,
20                max_depth: crate::limits::MAX_EXPRESSION_DEPTH,
21            });
22        }
23
24        // CSE: Check cache if enabled and expression is deterministic
25        if self.enable_cse
26            && super::super::expression_hash::ExpressionHasher::is_deterministic(expr)
27        {
28            let hash = super::super::expression_hash::ExpressionHasher::hash(expr);
29
30            // Check cache (get requires mut borrow to update LRU order)
31            if let Some(cached) = self.cse_cache.borrow_mut().get(&hash) {
32                return Ok(cached.clone());
33            }
34
35            // Evaluate with depth increment and cache result
36            let result = self.with_incremented_depth(|evaluator| evaluator.eval_impl(expr, row))?;
37            self.cse_cache.borrow_mut().put(hash, result.clone());
38            return Ok(result);
39        }
40
41        // Non-cached path: increment depth and evaluate
42        self.with_incremented_depth(|evaluator| evaluator.eval_impl(expr, row))
43    }
44
45    /// Internal implementation of eval with depth already incremented
46    #[inline]
47    fn eval_impl(
48        &self,
49        expr: &vibesql_ast::Expression,
50        row: &vibesql_storage::Row,
51    ) -> Result<vibesql_types::SqlValue, ExecutorError> {
52        match expr {
53            // Literals - just return the value
54            vibesql_ast::Expression::Literal(val) => Ok(val.clone()),
55
56            // DEFAULT keyword - not allowed in SELECT/WHERE expressions
57            vibesql_ast::Expression::Default => Err(ExecutorError::UnsupportedExpression(
58                "DEFAULT keyword is only valid in INSERT VALUES and UPDATE SET clauses".to_string(),
59            )),
60
61            // VALUES() function - not allowed in SELECT/WHERE expressions
62            vibesql_ast::Expression::DuplicateKeyValue { .. } => Err(ExecutorError::UnsupportedExpression(
63                "VALUES() function is only valid in ON DUPLICATE KEY UPDATE clauses".to_string(),
64            )),
65
66            // Column reference - look up column index and get value from row
67            vibesql_ast::Expression::ColumnRef { table, column } => {
68                self.eval_column_ref(table.as_deref(), column, row)
69            }
70
71            // Binary operations
72            vibesql_ast::Expression::BinaryOp { left, op, right } => {
73                // Short-circuit evaluation for AND/OR operators
74                match op {
75                    vibesql_ast::BinaryOperator::And => {
76                        let left_val = self.eval(left, row)?;
77                        // Short-circuit: if left is false, return false immediately
78                        match left_val {
79                            SqlValue::Boolean(false) => Ok(SqlValue::Boolean(false)),
80                            // For NULL and TRUE, must evaluate right side
81                            // SQL three-valued logic:
82                            // - NULL AND FALSE = FALSE (not NULL!)
83                            // - NULL AND TRUE = NULL
84                            // - TRUE AND x = x
85                            _ => {
86                                let right_val = self.eval(right, row)?;
87
88                                // Special case: NULL AND FALSE = FALSE
89                                if matches!(left_val, SqlValue::Null)
90                                    && matches!(right_val, SqlValue::Boolean(false))
91                                {
92                                    return Ok(SqlValue::Boolean(false));
93                                }
94
95                                self.eval_binary_op(&left_val, op, &right_val)
96                            }
97                        }
98                    }
99                    vibesql_ast::BinaryOperator::Or => {
100                        let left_val = self.eval(left, row)?;
101                        // Short-circuit: if left is true, return true immediately
102                        match left_val {
103                            SqlValue::Boolean(true) => Ok(SqlValue::Boolean(true)),
104                            // For NULL and FALSE, must evaluate right side
105                            // SQL three-valued logic:
106                            // - NULL OR TRUE = TRUE (not NULL!)
107                            // - NULL OR FALSE = NULL
108                            // - FALSE OR x = x
109                            _ => {
110                                let right_val = self.eval(right, row)?;
111
112                                // Special case: NULL OR TRUE = TRUE
113                                if matches!(left_val, SqlValue::Null)
114                                    && matches!(right_val, SqlValue::Boolean(true))
115                                {
116                                    return Ok(SqlValue::Boolean(true));
117                                }
118
119                                self.eval_binary_op(&left_val, op, &right_val)
120                            }
121                        }
122                    }
123                    // For all other operators, evaluate both sides as before
124                    _ => {
125                        let left_val = self.eval(left, row)?;
126                        let right_val = self.eval(right, row)?;
127                        self.eval_binary_op(&left_val, op, &right_val)
128                    }
129                }
130            }
131
132            // CASE expression
133            vibesql_ast::Expression::Case { operand, when_clauses, else_result } => {
134                self.eval_case(operand, when_clauses, else_result, row)
135            }
136
137            // IN operator with subquery
138            vibesql_ast::Expression::In { expr, subquery, negated } => {
139                self.eval_in_subquery(expr, subquery, *negated, row)
140            }
141
142            // Scalar subquery
143            vibesql_ast::Expression::ScalarSubquery(subquery) => self.eval_scalar_subquery(subquery, row),
144
145            // BETWEEN predicate
146            vibesql_ast::Expression::Between { expr, low, high, negated, symmetric } => {
147                self.eval_between(expr, low, high, *negated, *symmetric, row)
148            }
149
150            // CAST expression
151            vibesql_ast::Expression::Cast { expr, data_type } => self.eval_cast(expr, data_type, row),
152
153            // POSITION expression
154            vibesql_ast::Expression::Position { substring, string, character_unit: _ } => {
155                self.eval_position(substring, string, row)
156            }
157
158            // TRIM expression
159            vibesql_ast::Expression::Trim { position, removal_char, string } => {
160                self.eval_trim(position, removal_char, string, row)
161            }
162
163            // EXTRACT expression
164            vibesql_ast::Expression::Extract { field, expr } => {
165                self.eval_extract(field, expr, row)
166            }
167
168            // LIKE pattern matching
169            vibesql_ast::Expression::Like { expr, pattern, negated } => {
170                self.eval_like(expr, pattern, *negated, row)
171            }
172
173            // IN list (value list)
174            vibesql_ast::Expression::InList { expr, values, negated } => {
175                self.eval_in_list(expr, values, *negated, row)
176            }
177
178            // EXISTS predicate
179            vibesql_ast::Expression::Exists { subquery, negated } => {
180                self.eval_exists(subquery, *negated, row)
181            }
182
183            // Quantified comparison (ALL/ANY/SOME)
184            vibesql_ast::Expression::QuantifiedComparison { expr, op, quantifier, subquery } => {
185                self.eval_quantified(expr, op, quantifier, subquery, row)
186            }
187
188            // Function call
189            vibesql_ast::Expression::Function { name, args, character_unit } => {
190                self.eval_function(name, args, character_unit, row)
191            }
192
193            // Current date/time functions
194            vibesql_ast::Expression::CurrentDate => {
195                let sql_mode = self.database.map(|db| db.sql_mode()).unwrap_or_default();
196                super::super::functions::eval_scalar_function("CURRENT_DATE", &[], &None, &sql_mode)
197            }
198            vibesql_ast::Expression::CurrentTime { precision: _ } => {
199                // For now, ignore precision and call existing function
200                // Phase 2 will implement precision-aware formatting
201                let sql_mode = self.database.map(|db| db.sql_mode()).unwrap_or_default();
202                super::super::functions::eval_scalar_function("CURRENT_TIME", &[], &None, &sql_mode)
203            }
204            vibesql_ast::Expression::CurrentTimestamp { precision: _ } => {
205                // For now, ignore precision and call existing function
206                // Phase 2 will implement precision-aware formatting
207                let sql_mode = self.database.map(|db| db.sql_mode()).unwrap_or_default();
208                super::super::functions::eval_scalar_function("CURRENT_TIMESTAMP", &[], &None, &sql_mode)
209            }
210
211            // INTERVAL expression
212            vibesql_ast::Expression::Interval {
213                value,
214                unit,
215                leading_precision: _,
216                fractional_precision: _,
217            } => {
218                // Evaluate the value expression (typically a string literal like '5')
219                let interval_value = self.eval(value, row)?;
220
221                // Convert unit to string for the Interval type
222                let unit_str = Self::interval_unit_to_string(unit);
223
224                // Create an Interval SqlValue
225                // The format is "value unit" (e.g., "5 DAY", "1-6 YEAR TO MONTH")
226                let interval_str = format!("{} {}", interval_value, unit_str);
227                Ok(SqlValue::Interval(vibesql_types::Interval::new(
228                    interval_str,
229                )))
230            }
231
232            // Unsupported expressions
233            vibesql_ast::Expression::Wildcard => Err(ExecutorError::UnsupportedExpression(
234                "Wildcard (*) not supported in expressions".to_string(),
235            )),
236
237            // Unary operations
238            vibesql_ast::Expression::UnaryOp { op, expr } => {
239                let val = self.eval(expr, row)?;
240                super::operators::eval_unary_op(op, &val)
241            }
242
243            vibesql_ast::Expression::IsNull { expr, negated } => {
244                let value = self.eval(expr, row)?;
245                let is_null = matches!(value, SqlValue::Null);
246                let result = if *negated { !is_null } else { is_null };
247                Ok(SqlValue::Boolean(result))
248            }
249
250            vibesql_ast::Expression::WindowFunction { .. } => Err(ExecutorError::UnsupportedExpression(
251                "Window functions should be evaluated separately".to_string(),
252            )),
253
254            vibesql_ast::Expression::AggregateFunction { .. } => Err(ExecutorError::UnsupportedExpression(
255                "Aggregate functions should be evaluated in aggregation context".to_string(),
256            )),
257
258            // NEXT VALUE FOR sequence expression
259            // TODO: Implement proper sequence evaluation
260            //
261            // Requirements for implementation:
262            // 1. Sequence catalog objects (CREATE SEQUENCE, DROP SEQUENCE, etc.)
263            // 2. Sequence state storage (current value, increment, min/max, cycle, etc.)
264            // 3. Mutable access to catalog to advance sequences (architectural change)
265            // 4. Thread-safe sequence value generation
266            //
267            // Current architecture has immutable database references in evaluator.
268            // Possible solutions:
269            // 1. Use RefCell<Sequence> or Arc<Mutex<Sequence>> for interior mutability
270            // 2. Handle NEXT VALUE FOR at statement execution level (INSERT/SELECT)
271            // 3. Change evaluator to accept mutable database reference
272            // 4. Use a separate sequence manager with thread-safe state
273            //
274            // Note: Parser and AST support already exists (Expression::NextValue).
275            // See SQL:1999 Section 6.13 for sequence expression specification.
276            vibesql_ast::Expression::NextValue { sequence_name } => {
277                Err(ExecutorError::UnsupportedExpression(format!(
278                    "NEXT VALUE FOR {} - Sequence expressions not yet implemented. \
279                    Requires sequence catalog infrastructure (CREATE SEQUENCE support), \
280                    sequence state management, and mutable catalog access. \
281                    Use auto-incrementing primary keys or generate values in application code instead.",
282                    sequence_name
283                )))
284            }
285
286            vibesql_ast::Expression::MatchAgainst { columns, search_modifier, mode } => {
287                self.eval_match_against(columns, search_modifier, mode, row)
288            }
289
290            // Pseudo-variable (OLD.column, NEW.column in triggers)
291            vibesql_ast::Expression::PseudoVariable { pseudo_table, column } => {
292                // Resolve pseudo-variable using trigger context
293                if let Some(ctx) = self.trigger_context {
294                    ctx.resolve_pseudo_var(*pseudo_table, column)
295                } else {
296                    // This expression type is only valid in trigger context
297                    // Return an error if encountered outside triggers
298                    Err(ExecutorError::UnsupportedExpression(
299                        format!(
300                            "Pseudo-variable {}.{} is only valid within trigger bodies",
301                            match pseudo_table {
302                                vibesql_ast::PseudoTable::Old => "OLD",
303                                vibesql_ast::PseudoTable::New => "NEW",
304                            },
305                            column
306                        )
307                    ))
308                }
309            }
310
311            // Session variable (@@sql_mode, @@version, etc.)
312            vibesql_ast::Expression::SessionVariable { name } => {
313                // Get session variable from database metadata
314                if let Some(db) = self.database {
315                    // Get the session variable value from the database metadata
316                    if let Some(value) = db.get_session_variable(name) {
317                        Ok(value.clone())
318                    } else {
319                        // Variable not found - return NULL (MySQL behavior)
320                        Ok(SqlValue::Null)
321                    }
322                } else {
323                    // No database context available
324                    Err(ExecutorError::UnsupportedExpression(
325                        format!("Session variable @@{} cannot be evaluated without database context", name)
326                    ))
327                }
328            }
329
330            // Placeholder (?) - must be bound before evaluation
331            vibesql_ast::Expression::Placeholder(idx) => {
332                Err(ExecutorError::UnsupportedExpression(
333                    format!("Unbound placeholder ?{} - placeholders must be bound to values before execution", idx)
334                ))
335            }
336
337            // Numbered placeholder ($1, $2, etc.) - must be bound before evaluation
338            vibesql_ast::Expression::NumberedPlaceholder(idx) => {
339                Err(ExecutorError::UnsupportedExpression(
340                    format!("Unbound numbered placeholder ${} - placeholders must be bound to values before execution", idx)
341                ))
342            }
343
344            // Named placeholder (:name) - must be bound before evaluation
345            vibesql_ast::Expression::NamedPlaceholder(name) => {
346                Err(ExecutorError::UnsupportedExpression(
347                    format!("Unbound named placeholder :{} - placeholders must be bound to values before execution", name)
348                ))
349            }
350
351            // Conjunction (AND) - evaluate all children with short-circuit
352            vibesql_ast::Expression::Conjunction(children) => {
353                let mut result = SqlValue::Boolean(true);
354                for child in children {
355                    let val = self.eval(child, row)?;
356                    match val {
357                        SqlValue::Boolean(false) => return Ok(SqlValue::Boolean(false)),
358                        SqlValue::Null => result = SqlValue::Null,
359                        SqlValue::Boolean(true) => {}
360                        _ => return Err(ExecutorError::TypeError(
361                            format!("Conjunction requires boolean operands, got {:?}", val)
362                        )),
363                    }
364                }
365                Ok(result)
366            }
367
368            // Disjunction (OR) - evaluate all children with short-circuit
369            vibesql_ast::Expression::Disjunction(children) => {
370                let mut result = SqlValue::Boolean(false);
371                for child in children {
372                    let val = self.eval(child, row)?;
373                    match val {
374                        SqlValue::Boolean(true) => return Ok(SqlValue::Boolean(true)),
375                        SqlValue::Null => result = SqlValue::Null,
376                        SqlValue::Boolean(false) => {}
377                        _ => return Err(ExecutorError::TypeError(
378                            format!("Disjunction requires boolean operands, got {:?}", val)
379                        )),
380                    }
381                }
382                Ok(result)
383            }
384        }
385    }
386
387    /// Evaluate a MATCH...AGAINST full-text search expression
388    fn eval_match_against(
389        &self,
390        columns: &[String],
391        search_modifier: &vibesql_ast::Expression,
392        mode: &vibesql_ast::FulltextMode,
393        row: &vibesql_storage::Row,
394    ) -> Result<vibesql_types::SqlValue, ExecutorError> {
395        // Evaluate the search string
396        let search_value = self.eval(search_modifier, row)?;
397        let search_string: arcstr::ArcStr = match search_value {
398            SqlValue::Varchar(s) | SqlValue::Character(s) => s,
399            SqlValue::Null => return Ok(SqlValue::Boolean(false)),
400            other => arcstr::ArcStr::from(other.to_string().as_str()),
401        };
402
403        // Collect text values from the specified columns
404        let mut text_values: Vec<arcstr::ArcStr> = Vec::new();
405        for column_name in columns {
406            match self.eval_column_ref(None, column_name, row) {
407                Ok(SqlValue::Varchar(s)) | Ok(SqlValue::Character(s)) => text_values.push(s),
408                Ok(SqlValue::Null) => {
409                    // NULL values are treated as empty strings in MATCH
410                    text_values.push(arcstr::ArcStr::from(""));
411                }
412                Ok(other) => text_values.push(arcstr::ArcStr::from(other.to_string().as_str())),
413                Err(_) => {
414                    // Column not found - return false for this match
415                    return Ok(SqlValue::Boolean(false));
416                }
417            }
418        }
419
420        // Perform full-text search
421        let result = super::fulltext::eval_match_against(&search_string, &text_values, mode)?;
422        Ok(SqlValue::Boolean(result))
423    }
424
425    /// Evaluate column reference
426    #[inline]
427    fn eval_column_ref(
428        &self,
429        table_qualifier: Option<&str>,
430        column: &str,
431        row: &vibesql_storage::Row,
432    ) -> Result<vibesql_types::SqlValue, ExecutorError> {
433        // Special case: "*" is a wildcard used in COUNT(*) and is not a real column
434        // Return NULL here - the actual COUNT(*) logic handles this specially
435        if column == "*" {
436            return Ok(vibesql_types::SqlValue::Null);
437        }
438
439        // Check procedural context first (variables/parameters take precedence over table columns)
440        // This is only checked when there's no table qualifier, as variables don't have table prefixes
441        if table_qualifier.is_none() {
442            if let Some(proc_ctx) = self.procedural_context {
443                // Try to get value from procedural context (checks variables then parameters)
444                if let Some(value) = proc_ctx.get_value(column) {
445                    return Ok(value.clone());
446                }
447            }
448        }
449
450        // Track which tables we searched for better error messages
451        let mut searched_tables = Vec::new();
452        let mut available_columns = Vec::new();
453
454        // If table qualifier is provided, validate it matches a known schema
455        if let Some(qualifier) = table_qualifier {
456            let qualifier_lower = qualifier.to_lowercase();
457            let inner_name_lower = self.schema.name.to_lowercase();
458
459            // Check if qualifier matches inner schema
460            if qualifier_lower == inner_name_lower {
461                // Qualifier matches inner schema - search only there
462                searched_tables.push(self.schema.name.clone());
463                if let Some(col_index) = self.schema.get_column_index(column) {
464                    return row
465                        .get(col_index)
466                        .cloned()
467                        .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: col_index });
468                }
469            } else if let Some(outer_schema) = self.outer_schema {
470                let outer_name_lower = outer_schema.name.to_lowercase();
471
472                // Check if qualifier matches outer schema
473                if qualifier_lower == outer_name_lower {
474                    // Qualifier matches outer schema - search only there
475                    if let Some(outer_row) = self.outer_row {
476                        searched_tables.push(outer_schema.name.clone());
477                        if let Some(col_index) = outer_schema.get_column_index(column) {
478                            return outer_row
479                                .get(col_index)
480                                .cloned()
481                                .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: col_index });
482                        }
483                    }
484                } else {
485                    // Qualifier doesn't match any known schema
486                    let mut known_tables = vec![self.schema.name.clone()];
487                    known_tables.push(outer_schema.name.clone());
488
489                    return Err(ExecutorError::InvalidTableQualifier {
490                        qualifier: qualifier.to_string(),
491                        column: column.to_string(),
492                        available_tables: known_tables,
493                    });
494                }
495            } else {
496                // No outer schema and qualifier doesn't match inner schema
497                return Err(ExecutorError::InvalidTableQualifier {
498                    qualifier: qualifier.to_string(),
499                    column: column.to_string(),
500                    available_tables: vec![self.schema.name.clone()],
501                });
502            }
503
504            // If we get here, qualifier was valid but column wasn't found
505            available_columns.extend(self.schema.columns.iter().map(|c| c.name.clone()));
506            if let Some(outer_schema) = self.outer_schema {
507                available_columns.extend(outer_schema.columns.iter().map(|c| c.name.clone()));
508            }
509
510            return Err(ExecutorError::ColumnNotFound {
511                column_name: column.to_string(),
512                table_name: qualifier.to_string(),
513                searched_tables,
514                available_columns,
515            });
516        }
517
518        // No qualifier provided - use original search logic (inner first, then outer)
519        // Try to resolve in inner schema first
520        searched_tables.push(self.schema.name.clone());
521        if let Some(col_index) = self.schema.get_column_index(column) {
522            return row
523                .get(col_index)
524                .cloned()
525                .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: col_index });
526        }
527
528        // If not found in inner schema and outer context exists, try outer schema
529        if let (Some(outer_row), Some(outer_schema)) = (self.outer_row, self.outer_schema) {
530            searched_tables.push(outer_schema.name.clone());
531            if let Some(col_index) = outer_schema.get_column_index(column) {
532                return outer_row
533                    .get(col_index)
534                    .cloned()
535                    .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: col_index });
536            }
537        }
538
539        // Column not found - collect available columns for suggestions
540        available_columns.extend(self.schema.columns.iter().map(|c| c.name.clone()));
541        if let Some(outer_schema) = self.outer_schema {
542            available_columns.extend(outer_schema.columns.iter().map(|c| c.name.clone()));
543        }
544
545        // Column not found in either schema
546        Err(ExecutorError::ColumnNotFound {
547            column_name: column.to_string(),
548            table_name: table_qualifier.unwrap_or("unknown").to_string(),
549            searched_tables,
550            available_columns,
551        })
552    }
553
554    /// Convert IntervalUnit to string representation for Interval SqlValue
555    fn interval_unit_to_string(unit: &vibesql_ast::IntervalUnit) -> String {
556        use vibesql_ast::IntervalUnit;
557        match unit {
558            IntervalUnit::Microsecond => "MICROSECOND",
559            IntervalUnit::Second => "SECOND",
560            IntervalUnit::Minute => "MINUTE",
561            IntervalUnit::Hour => "HOUR",
562            IntervalUnit::Day => "DAY",
563            IntervalUnit::Week => "WEEK",
564            IntervalUnit::Month => "MONTH",
565            IntervalUnit::Quarter => "QUARTER",
566            IntervalUnit::Year => "YEAR",
567            IntervalUnit::SecondMicrosecond => "SECOND_MICROSECOND",
568            IntervalUnit::MinuteMicrosecond => "MINUTE_MICROSECOND",
569            IntervalUnit::MinuteSecond => "MINUTE_SECOND",
570            IntervalUnit::HourMicrosecond => "HOUR_MICROSECOND",
571            IntervalUnit::HourSecond => "HOUR_SECOND",
572            IntervalUnit::HourMinute => "HOUR_MINUTE",
573            IntervalUnit::DayMicrosecond => "DAY_MICROSECOND",
574            IntervalUnit::DaySecond => "DAY_SECOND",
575            IntervalUnit::DayMinute => "DAY_MINUTE",
576            IntervalUnit::DayHour => "DAY_HOUR",
577            IntervalUnit::YearMonth => "YEAR_MONTH",
578        }
579        .to_string()
580    }
581}