1use vibesql_types::SqlValue;
4
5use super::super::core::ExpressionEvaluator;
6use crate::errors::ExecutorError;
7
8impl ExpressionEvaluator<'_> {
9 #[inline]
11 pub fn eval(
12 &self,
13 expr: &vibesql_ast::Expression,
14 row: &vibesql_storage::Row,
15 ) -> Result<vibesql_types::SqlValue, ExecutorError> {
16 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 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 if let Some(cached) = self.cse_cache.borrow_mut().get(&hash) {
32 return Ok(cached.clone());
33 }
34
35 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 self.with_incremented_depth(|evaluator| evaluator.eval_impl(expr, row))
43 }
44
45 #[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 vibesql_ast::Expression::Literal(val) => Ok(val.clone()),
55
56 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 vibesql_ast::Expression::DuplicateKeyValue { .. } => Err(ExecutorError::UnsupportedExpression(
63 "VALUES() function is only valid in ON DUPLICATE KEY UPDATE clauses".to_string(),
64 )),
65
66 vibesql_ast::Expression::ColumnRef { table, column } => {
68 self.eval_column_ref(table.as_deref(), column, row)
69 }
70
71 vibesql_ast::Expression::BinaryOp { left, op, right } => {
73 match op {
75 vibesql_ast::BinaryOperator::And => {
76 let left_val = self.eval(left, row)?;
77 match left_val {
79 SqlValue::Boolean(false) => Ok(SqlValue::Boolean(false)),
80 _ => {
86 let right_val = self.eval(right, row)?;
87
88 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 match left_val {
103 SqlValue::Boolean(true) => Ok(SqlValue::Boolean(true)),
104 _ => {
110 let right_val = self.eval(right, row)?;
111
112 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 _ => {
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 vibesql_ast::Expression::Case { operand, when_clauses, else_result } => {
134 self.eval_case(operand, when_clauses, else_result, row)
135 }
136
137 vibesql_ast::Expression::In { expr, subquery, negated } => {
139 self.eval_in_subquery(expr, subquery, *negated, row)
140 }
141
142 vibesql_ast::Expression::ScalarSubquery(subquery) => self.eval_scalar_subquery(subquery, row),
144
145 vibesql_ast::Expression::Between { expr, low, high, negated, symmetric } => {
147 self.eval_between(expr, low, high, *negated, *symmetric, row)
148 }
149
150 vibesql_ast::Expression::Cast { expr, data_type } => self.eval_cast(expr, data_type, row),
152
153 vibesql_ast::Expression::Position { substring, string, character_unit: _ } => {
155 self.eval_position(substring, string, row)
156 }
157
158 vibesql_ast::Expression::Trim { position, removal_char, string } => {
160 self.eval_trim(position, removal_char, string, row)
161 }
162
163 vibesql_ast::Expression::Extract { field, expr } => {
165 self.eval_extract(field, expr, row)
166 }
167
168 vibesql_ast::Expression::Like { expr, pattern, negated } => {
170 self.eval_like(expr, pattern, *negated, row)
171 }
172
173 vibesql_ast::Expression::InList { expr, values, negated } => {
175 self.eval_in_list(expr, values, *negated, row)
176 }
177
178 vibesql_ast::Expression::Exists { subquery, negated } => {
180 self.eval_exists(subquery, *negated, row)
181 }
182
183 vibesql_ast::Expression::QuantifiedComparison { expr, op, quantifier, subquery } => {
185 self.eval_quantified(expr, op, quantifier, subquery, row)
186 }
187
188 vibesql_ast::Expression::Function { name, args, character_unit } => {
190 self.eval_function(name, args, character_unit, row)
191 }
192
193 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 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 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 vibesql_ast::Expression::Interval {
213 value,
214 unit,
215 leading_precision: _,
216 fractional_precision: _,
217 } => {
218 let interval_value = self.eval(value, row)?;
220
221 let unit_str = Self::interval_unit_to_string(unit);
223
224 let interval_str = format!("{} {}", interval_value, unit_str);
227 Ok(SqlValue::Interval(vibesql_types::Interval::new(
228 interval_str,
229 )))
230 }
231
232 vibesql_ast::Expression::Wildcard => Err(ExecutorError::UnsupportedExpression(
234 "Wildcard (*) not supported in expressions".to_string(),
235 )),
236
237 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 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 vibesql_ast::Expression::PseudoVariable { pseudo_table, column } => {
292 if let Some(ctx) = self.trigger_context {
294 ctx.resolve_pseudo_var(*pseudo_table, column)
295 } else {
296 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 vibesql_ast::Expression::SessionVariable { name } => {
313 if let Some(db) = self.database {
315 if let Some(value) = db.get_session_variable(name) {
317 Ok(value.clone())
318 } else {
319 Ok(SqlValue::Null)
321 }
322 } else {
323 Err(ExecutorError::UnsupportedExpression(
325 format!("Session variable @@{} cannot be evaluated without database context", name)
326 ))
327 }
328 }
329
330 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 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 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 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 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 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 let search_value = self.eval(search_modifier, row)?;
397 let search_string = match search_value {
398 SqlValue::Varchar(s) | SqlValue::Character(s) => s,
399 SqlValue::Null => return Ok(SqlValue::Boolean(false)),
400 other => other.to_string(),
401 };
402
403 let mut text_values = 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 text_values.push(String::new());
411 }
412 Ok(other) => text_values.push(other.to_string()),
413 Err(_) => {
414 return Ok(SqlValue::Boolean(false));
416 }
417 }
418 }
419
420 let result = super::fulltext::eval_match_against(&search_string, &text_values, mode)?;
422 Ok(SqlValue::Boolean(result))
423 }
424
425 #[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 if column == "*" {
436 return Ok(vibesql_types::SqlValue::Null);
437 }
438
439 if table_qualifier.is_none() {
442 if let Some(proc_ctx) = self.procedural_context {
443 if let Some(value) = proc_ctx.get_value(column) {
445 return Ok(value.clone());
446 }
447 }
448 }
449
450 let mut searched_tables = Vec::new();
452 let mut available_columns = Vec::new();
453
454 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 if qualifier_lower == inner_name_lower {
461 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 if qualifier_lower == outer_name_lower {
474 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 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 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 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 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 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 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 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 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}