1use vibesql_types::{SqlValue, TypeAffinity};
4
5use super::super::core::ExpressionEvaluator;
6use crate::errors::ExecutorError;
7
8fn format_float_for_text_comparison(n: f64) -> String {
14 if n.is_nan() {
15 return "NaN".to_string();
16 }
17 if n.is_infinite() {
18 return if n > 0.0 {
19 "Inf".to_string()
20 } else {
21 "-Inf".to_string()
22 };
23 }
24
25 let s = n.to_string();
27
28 if !s.contains('.') && !s.contains('e') && !s.contains('E') {
31 format!("{}.0", s)
32 } else {
33 s
34 }
35}
36
37impl ExpressionEvaluator<'_> {
38 pub(super) fn get_expression_affinity(
48 &self,
49 expr: &vibesql_ast::Expression,
50 ) -> Option<TypeAffinity> {
51 match expr {
52 vibesql_ast::Expression::ColumnRef(col_id) => {
53 let column_name = col_id.column_canonical();
55 if let Some(col_idx) = self.schema.get_column_index(column_name) {
56 let col_schema = &self.schema.columns[col_idx];
57 Some(col_schema.data_type.sqlite_affinity())
58 } else {
59 Some(TypeAffinity::None)
61 }
62 }
63 vibesql_ast::Expression::Collate { expr, .. } => self.get_expression_affinity(expr),
65 _ => None,
67 }
68 }
69
70 pub(super) fn get_expression_collation(
83 &self,
84 expr: &vibesql_ast::Expression,
85 ) -> Option<String> {
86 match expr {
87 vibesql_ast::Expression::Collate { collation, .. } => Some(collation.clone()),
89 vibesql_ast::Expression::ColumnRef(col_id) => {
91 let column_name = col_id.column_canonical();
92 if let Some(col_idx) = self.schema.get_column_index(column_name) {
93 self.schema.columns[col_idx].collation.clone()
94 } else {
95 None
96 }
97 }
98 _ => None,
100 }
101 }
102
103 pub(super) fn is_numeric_literal(&self, expr: &vibesql_ast::Expression) -> bool {
105 match expr {
106 vibesql_ast::Expression::Literal(val) => {
107 matches!(
108 val,
109 SqlValue::Integer(_)
110 | SqlValue::Smallint(_)
111 | SqlValue::Bigint(_)
112 | SqlValue::Unsigned(_)
113 | SqlValue::Float(_)
114 | SqlValue::Real(_)
115 | SqlValue::Double(_)
116 | SqlValue::Numeric(_)
117 )
118 }
119 _ => false,
120 }
121 }
122
123 pub(super) fn is_string_literal(&self, expr: &vibesql_ast::Expression) -> bool {
125 match expr {
126 vibesql_ast::Expression::Literal(val) => {
127 matches!(val, SqlValue::Varchar(_) | SqlValue::Character(_))
128 }
129 _ => false,
130 }
131 }
132
133 fn try_coerce_string_to_numeric(val: &SqlValue) -> SqlValue {
137 match val {
138 SqlValue::Varchar(s) | SqlValue::Character(s) => {
139 let trimmed = s.trim();
140 if let Ok(n) = trimmed.parse::<i64>() {
142 return SqlValue::Integer(n);
143 }
144 if let Ok(n) = trimmed.parse::<f64>() {
146 return SqlValue::Double(n);
147 }
148 val.clone()
150 }
151 _ => val.clone(),
152 }
153 }
154
155 pub(super) fn apply_affinity_for_comparison(
163 &self,
164 left_expr: &vibesql_ast::Expression,
165 left_val: SqlValue,
166 right_expr: &vibesql_ast::Expression,
167 right_val: SqlValue,
168 ) -> (SqlValue, SqlValue) {
169 let left_affinity = self.get_expression_affinity(left_expr);
170 let right_affinity = self.get_expression_affinity(right_expr);
171
172 if left_affinity == Some(TypeAffinity::Text) && self.is_numeric_literal(right_expr) {
177 let right_as_text = match &right_val {
178 SqlValue::Integer(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
179 SqlValue::Smallint(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
180 SqlValue::Bigint(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
181 SqlValue::Unsigned(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
182 SqlValue::Float(n) => SqlValue::Varchar(arcstr::ArcStr::from(
183 format_float_for_text_comparison(*n as f64),
184 )),
185 SqlValue::Real(n) => SqlValue::Varchar(arcstr::ArcStr::from(
186 format_float_for_text_comparison(*n),
187 )),
188 SqlValue::Double(n) => SqlValue::Varchar(arcstr::ArcStr::from(
189 format_float_for_text_comparison(*n),
190 )),
191 SqlValue::Numeric(n) => SqlValue::Varchar(arcstr::ArcStr::from(
192 format_float_for_text_comparison(*n),
193 )),
194 _ => right_val,
195 };
196 return (left_val, right_as_text);
197 }
198
199 if right_affinity == Some(TypeAffinity::Text) && self.is_numeric_literal(left_expr) {
202 let left_as_text = match &left_val {
203 SqlValue::Integer(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
204 SqlValue::Smallint(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
205 SqlValue::Bigint(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
206 SqlValue::Unsigned(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
207 SqlValue::Float(n) => SqlValue::Varchar(arcstr::ArcStr::from(
208 format_float_for_text_comparison(*n as f64),
209 )),
210 SqlValue::Real(n) => SqlValue::Varchar(arcstr::ArcStr::from(
211 format_float_for_text_comparison(*n),
212 )),
213 SqlValue::Double(n) => SqlValue::Varchar(arcstr::ArcStr::from(
214 format_float_for_text_comparison(*n),
215 )),
216 SqlValue::Numeric(n) => SqlValue::Varchar(arcstr::ArcStr::from(
217 format_float_for_text_comparison(*n),
218 )),
219 _ => left_val,
220 };
221 return (left_as_text, right_val);
222 }
223
224 let is_left_numeric_affinity = matches!(
228 left_affinity,
229 Some(TypeAffinity::Numeric) | Some(TypeAffinity::Integer) | Some(TypeAffinity::Real)
230 );
231 if is_left_numeric_affinity && self.is_string_literal(right_expr) {
232 let right_coerced = Self::try_coerce_string_to_numeric(&right_val);
233 return (left_val, right_coerced);
234 }
235
236 let is_right_numeric_affinity = matches!(
239 right_affinity,
240 Some(TypeAffinity::Numeric) | Some(TypeAffinity::Integer) | Some(TypeAffinity::Real)
241 );
242 if is_right_numeric_affinity && self.is_string_literal(left_expr) {
243 let left_coerced = Self::try_coerce_string_to_numeric(&left_val);
244 return (left_coerced, right_val);
245 }
246
247 let is_right_non_numeric_affinity = matches!(
252 right_affinity,
253 Some(TypeAffinity::None) | Some(TypeAffinity::Text) | None
254 );
255 if is_left_numeric_affinity
256 && is_right_non_numeric_affinity
257 && matches!(right_val, SqlValue::Varchar(_) | SqlValue::Character(_))
258 {
259 let right_coerced = Self::try_coerce_string_to_numeric(&right_val);
260 return (left_val, right_coerced);
261 }
262
263 let is_left_non_numeric_affinity = matches!(
266 left_affinity,
267 Some(TypeAffinity::None) | Some(TypeAffinity::Text) | None
268 );
269 if is_right_numeric_affinity
270 && is_left_non_numeric_affinity
271 && matches!(left_val, SqlValue::Varchar(_) | SqlValue::Character(_))
272 {
273 let left_coerced = Self::try_coerce_string_to_numeric(&left_val);
274 return (left_coerced, right_val);
275 }
276
277 (left_val, right_val)
284 }
285
286 pub(super) fn apply_affinity_for_in_comparison(
293 &self,
294 left_expr: &vibesql_ast::Expression,
295 left_val: SqlValue,
296 right_expr: &vibesql_ast::Expression,
297 right_val: SqlValue,
298 ) -> (SqlValue, SqlValue) {
299 let left_affinity = self.get_expression_affinity(left_expr);
300 let right_affinity = self.get_expression_affinity(right_expr);
301
302 if left_affinity == Some(TypeAffinity::Text) && self.is_numeric_literal(right_expr) {
304 let right_as_text = match &right_val {
305 SqlValue::Integer(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
306 SqlValue::Smallint(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
307 SqlValue::Bigint(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
308 SqlValue::Unsigned(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
309 SqlValue::Float(n) => SqlValue::Varchar(arcstr::ArcStr::from(
310 format_float_for_text_comparison(*n as f64),
311 )),
312 SqlValue::Real(n) => SqlValue::Varchar(arcstr::ArcStr::from(
313 format_float_for_text_comparison(*n),
314 )),
315 SqlValue::Double(n) => SqlValue::Varchar(arcstr::ArcStr::from(
316 format_float_for_text_comparison(*n),
317 )),
318 SqlValue::Numeric(n) => SqlValue::Varchar(arcstr::ArcStr::from(
319 format_float_for_text_comparison(*n),
320 )),
321 _ => right_val,
322 };
323 return (left_val, right_as_text);
324 }
325
326 if right_affinity == Some(TypeAffinity::Text) && self.is_numeric_literal(left_expr) {
328 let left_as_text = match &left_val {
329 SqlValue::Integer(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
330 SqlValue::Smallint(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
331 SqlValue::Bigint(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
332 SqlValue::Unsigned(n) => SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())),
333 SqlValue::Float(n) => SqlValue::Varchar(arcstr::ArcStr::from(
334 format_float_for_text_comparison(*n as f64),
335 )),
336 SqlValue::Real(n) => SqlValue::Varchar(arcstr::ArcStr::from(
337 format_float_for_text_comparison(*n),
338 )),
339 SqlValue::Double(n) => SqlValue::Varchar(arcstr::ArcStr::from(
340 format_float_for_text_comparison(*n),
341 )),
342 SqlValue::Numeric(n) => SqlValue::Varchar(arcstr::ArcStr::from(
343 format_float_for_text_comparison(*n),
344 )),
345 _ => left_val,
346 };
347 return (left_as_text, right_val);
348 }
349
350 if left_affinity == Some(TypeAffinity::Real) && self.is_string_literal(right_expr) {
353 let right_coerced = Self::try_coerce_string_to_numeric(&right_val);
354 return (left_val, right_coerced);
355 }
356
357 if right_affinity == Some(TypeAffinity::Real) && self.is_string_literal(left_expr) {
359 let left_coerced = Self::try_coerce_string_to_numeric(&left_val);
360 return (left_coerced, right_val);
361 }
362
363 (left_val, right_val)
366 }
367
368 #[inline]
370 pub fn eval(
371 &self,
372 expr: &vibesql_ast::Expression,
373 row: &vibesql_storage::Row,
374 ) -> Result<vibesql_types::SqlValue, ExecutorError> {
375 if self.depth >= crate::limits::MAX_EXPRESSION_DEPTH {
377 return Err(ExecutorError::ExpressionDepthExceeded {
378 depth: self.depth,
379 max_depth: crate::limits::MAX_EXPRESSION_DEPTH,
380 });
381 }
382
383 if self.enable_cse
385 && super::super::expression_hash::ExpressionHasher::is_deterministic(expr)
386 {
387 let hash = super::super::expression_hash::ExpressionHasher::hash(expr);
388
389 if let Some(cached) = self.cse_cache.borrow_mut().get(&hash) {
391 return Ok(cached.clone());
392 }
393
394 let result = self.with_incremented_depth(|evaluator| evaluator.eval_impl(expr, row))?;
396 self.cse_cache.borrow_mut().put(hash, result.clone());
397 return Ok(result);
398 }
399
400 self.with_incremented_depth(|evaluator| evaluator.eval_impl(expr, row))
402 }
403
404 #[inline]
406 fn eval_impl(
407 &self,
408 expr: &vibesql_ast::Expression,
409 row: &vibesql_storage::Row,
410 ) -> Result<vibesql_types::SqlValue, ExecutorError> {
411 match expr {
412 vibesql_ast::Expression::Literal(val) => Ok(val.clone()),
414
415 vibesql_ast::Expression::Default => Err(ExecutorError::UnsupportedExpression(
417 "DEFAULT keyword is only valid in INSERT VALUES and UPDATE SET clauses".to_string(),
418 )),
419
420 vibesql_ast::Expression::DuplicateKeyValue { .. } => Err(ExecutorError::UnsupportedExpression(
422 "VALUES() function is only valid in ON DUPLICATE KEY UPDATE clauses".to_string(),
423 )),
424
425 vibesql_ast::Expression::ColumnRef(col_id) => {
427 self.eval_column_ref(col_id.schema_canonical(), col_id.table_canonical(), col_id.column_canonical(), row)
428 }
429
430 vibesql_ast::Expression::BinaryOp { left, op, right } => {
432 match op {
434 vibesql_ast::BinaryOperator::And => {
435 let left_val = self.eval(left, row)?;
436 match left_val {
438 SqlValue::Boolean(false) => Ok(SqlValue::Boolean(false)),
439 _ => {
445 let right_val = self.eval(right, row)?;
446
447 if matches!(left_val, SqlValue::Null)
449 && matches!(right_val, SqlValue::Boolean(false))
450 {
451 return Ok(SqlValue::Boolean(false));
452 }
453
454 self.eval_binary_op(&left_val, op, &right_val)
455 }
456 }
457 }
458 vibesql_ast::BinaryOperator::Or => {
459 let left_val = self.eval(left, row)?;
460 match left_val {
462 SqlValue::Boolean(true) => Ok(SqlValue::Boolean(true)),
463 _ => {
469 let right_val = self.eval(right, row)?;
470
471 if matches!(left_val, SqlValue::Null)
473 && matches!(right_val, SqlValue::Boolean(true))
474 {
475 return Ok(SqlValue::Boolean(true));
476 }
477
478 self.eval_binary_op(&left_val, op, &right_val)
479 }
480 }
481 }
482 _ => {
484 let is_comparison = matches!(
486 op,
487 vibesql_ast::BinaryOperator::Equal
488 | vibesql_ast::BinaryOperator::NotEqual
489 | vibesql_ast::BinaryOperator::LessThan
490 | vibesql_ast::BinaryOperator::LessThanOrEqual
491 | vibesql_ast::BinaryOperator::GreaterThan
492 | vibesql_ast::BinaryOperator::GreaterThanOrEqual
493 );
494
495 if is_comparison {
499 if let (
500 vibesql_ast::Expression::RowValueConstructor(left_values),
501 vibesql_ast::Expression::RowValueConstructor(right_values),
502 ) = (left.as_ref(), right.as_ref())
503 {
504 return self.eval_row_value_comparison(left_values, op, right_values, row);
505 }
506 }
507
508 let collation = if is_comparison {
512 self.get_expression_collation(left)
513 .or_else(|| self.get_expression_collation(right))
514 } else {
515 None
516 };
517
518 let left_val = self.eval(left, row)?;
519 let right_val = self.eval(right, row)?;
520
521 let (left_val, right_val) = if let Some(ref collation_name) = collation {
523 let collation_lower = collation_name.to_lowercase();
524 if collation_lower == "nocase" {
525 let left_transformed = match &left_val {
527 SqlValue::Varchar(s) => SqlValue::Varchar(arcstr::ArcStr::from(s.to_uppercase())),
528 SqlValue::Character(s) => SqlValue::Character(arcstr::ArcStr::from(s.to_uppercase())),
529 other => other.clone(),
530 };
531 let right_transformed = match &right_val {
532 SqlValue::Varchar(s) => SqlValue::Varchar(arcstr::ArcStr::from(s.to_uppercase())),
533 SqlValue::Character(s) => SqlValue::Character(arcstr::ArcStr::from(s.to_uppercase())),
534 other => other.clone(),
535 };
536 (left_transformed, right_transformed)
537 } else if collation_lower == "rtrim" {
538 let left_transformed = match &left_val {
540 SqlValue::Varchar(s) => SqlValue::Varchar(arcstr::ArcStr::from(s.trim_end())),
541 SqlValue::Character(s) => SqlValue::Character(arcstr::ArcStr::from(s.trim_end())),
542 other => other.clone(),
543 };
544 let right_transformed = match &right_val {
545 SqlValue::Varchar(s) => SqlValue::Varchar(arcstr::ArcStr::from(s.trim_end())),
546 SqlValue::Character(s) => SqlValue::Character(arcstr::ArcStr::from(s.trim_end())),
547 other => other.clone(),
548 };
549 (left_transformed, right_transformed)
550 } else {
551 (left_val, right_val)
553 }
554 } else {
555 (left_val, right_val)
556 };
557
558 let (left_val, right_val) = if is_comparison {
561 self.apply_affinity_for_comparison(left, left_val, right, right_val)
562 } else {
563 (left_val, right_val)
564 };
565
566 self.eval_binary_op(&left_val, op, &right_val)
567 }
568 }
569 }
570
571 vibesql_ast::Expression::Case { operand, when_clauses, else_result } => {
573 self.eval_case(operand, when_clauses, else_result, row)
574 }
575
576 vibesql_ast::Expression::In { expr, subquery, negated } => {
578 self.eval_in_subquery(expr, subquery, *negated, row)
579 }
580
581 vibesql_ast::Expression::ScalarSubquery(subquery) => self.eval_scalar_subquery(subquery, row),
583
584 vibesql_ast::Expression::Between { expr, low, high, negated, symmetric } => {
586 self.eval_between(expr, low, high, *negated, *symmetric, row)
587 }
588
589 vibesql_ast::Expression::Cast { expr, data_type } => self.eval_cast(expr, data_type, row),
591
592 vibesql_ast::Expression::Position { substring, string, character_unit: _ } => {
594 self.eval_position(substring, string, row)
595 }
596
597 vibesql_ast::Expression::Trim { position, removal_char, string } => {
599 self.eval_trim(position, removal_char, string, row)
600 }
601
602 vibesql_ast::Expression::Extract { field, expr } => {
604 self.eval_extract(field, expr, row)
605 }
606
607 vibesql_ast::Expression::Like { expr, pattern, negated, escape } => {
609 self.eval_like(expr, pattern, escape, *negated, row)
610 }
611
612 vibesql_ast::Expression::Glob { expr, pattern, negated, .. } => {
614 self.eval_glob(expr, pattern, *negated, row)
615 }
616
617 vibesql_ast::Expression::InList { expr, values, negated } => {
619 self.eval_in_list(expr, values, *negated, row)
620 }
621
622 vibesql_ast::Expression::Exists { subquery, negated } => {
624 self.eval_exists(subquery, *negated, row)
625 }
626
627 vibesql_ast::Expression::QuantifiedComparison { expr, op, quantifier, subquery } => {
629 self.eval_quantified(expr, op, quantifier, subquery, row)
630 }
631
632 vibesql_ast::Expression::Function { name, args, character_unit } => {
634 self.eval_function(name.display(), args, character_unit, row)
635 }
636
637 vibesql_ast::Expression::CurrentDate => {
639 let sql_mode = self.database.map(|db| db.sql_mode()).unwrap_or_default();
640 super::super::functions::eval_scalar_function("CURRENT_DATE", &[], &None, &sql_mode)
641 }
642 vibesql_ast::Expression::CurrentTime { precision: _ } => {
643 let sql_mode = self.database.map(|db| db.sql_mode()).unwrap_or_default();
646 super::super::functions::eval_scalar_function("CURRENT_TIME", &[], &None, &sql_mode)
647 }
648 vibesql_ast::Expression::CurrentTimestamp { precision: _ } => {
649 let sql_mode = self.database.map(|db| db.sql_mode()).unwrap_or_default();
652 super::super::functions::eval_scalar_function("CURRENT_TIMESTAMP", &[], &None, &sql_mode)
653 }
654
655 vibesql_ast::Expression::Interval {
657 value,
658 unit,
659 leading_precision: _,
660 fractional_precision: _,
661 } => {
662 let interval_value = self.eval(value, row)?;
664
665 let unit_str = Self::interval_unit_to_string(unit);
667
668 let interval_str = format!("{} {}", interval_value, unit_str);
671 Ok(SqlValue::Interval(vibesql_types::Interval::new(
672 interval_str,
673 )))
674 }
675
676 vibesql_ast::Expression::Wildcard => Err(ExecutorError::UnsupportedExpression(
678 "Wildcard (*) not supported in expressions".to_string(),
679 )),
680
681 vibesql_ast::Expression::UnaryOp { op, expr } => {
683 let val = self.eval(expr, row)?;
684 super::operators::eval_unary_op(op, &val)
685 }
686
687 vibesql_ast::Expression::IsNull { expr, negated } => {
688 let value = self.eval(expr, row)?;
689 let is_null = matches!(value, SqlValue::Null);
690 let result = if *negated { !is_null } else { is_null };
691 Ok(SqlValue::Boolean(result))
692 }
693
694 vibesql_ast::Expression::IsDistinctFrom { left, right, negated } => {
695 if let (
699 vibesql_ast::Expression::RowValueConstructor(left_exprs),
700 vibesql_ast::Expression::RowValueConstructor(right_exprs),
701 ) = (left.as_ref(), right.as_ref())
702 {
703 return self.eval_row_value_is_distinct(left_exprs, right_exprs, *negated, row);
704 }
705
706 let left_val = self.eval(left, row)?;
707 let right_val = self.eval(right, row)?;
708 let is_distinct = super::super::core::values_are_distinct(&left_val, &right_val);
709 let result = if *negated { !is_distinct } else { is_distinct };
710 Ok(SqlValue::Boolean(result))
711 }
712
713 vibesql_ast::Expression::IsTruthValue { expr, truth_value, negated } => {
714 let val = self.eval(expr, row)?;
715 let result = match truth_value {
726 vibesql_ast::TruthValue::True => match &val {
727 SqlValue::Boolean(true) => true,
728 SqlValue::Integer(n) => *n != 0,
729 SqlValue::Bigint(n) => *n != 0,
730 SqlValue::Smallint(n) => *n != 0,
731 _ => false,
732 },
733 vibesql_ast::TruthValue::False => matches!(
734 val,
735 SqlValue::Boolean(false)
736 | SqlValue::Integer(0)
737 | SqlValue::Bigint(0)
738 | SqlValue::Smallint(0)
739 ),
740 vibesql_ast::TruthValue::Unknown => matches!(val, SqlValue::Null),
741 };
742 let final_result = if *negated { !result } else { result };
743 Ok(SqlValue::Boolean(final_result))
744 }
745
746 vibesql_ast::Expression::WindowFunction { function, .. } => {
747 let function_name = match function {
750 vibesql_ast::WindowFunctionSpec::Aggregate { name, .. }
751 | vibesql_ast::WindowFunctionSpec::Ranking { name, .. }
752 | vibesql_ast::WindowFunctionSpec::Value { name, .. } => name.to_string(),
753 };
754 Err(ExecutorError::MisuseOfWindowFunction { function_name })
755 }
756
757 vibesql_ast::Expression::AggregateFunction { name, .. } => {
758 Err(ExecutorError::MisuseOfAggregateContext { function_name: name.to_string() })
763 }
764
765 vibesql_ast::Expression::NextValue { sequence_name } => {
784 Err(ExecutorError::UnsupportedExpression(format!(
785 "NEXT VALUE FOR {} - Sequence expressions not yet implemented. \
786 Requires sequence catalog infrastructure (CREATE SEQUENCE support), \
787 sequence state management, and mutable catalog access. \
788 Use auto-incrementing primary keys or generate values in application code instead.",
789 sequence_name
790 )))
791 }
792
793 vibesql_ast::Expression::MatchAgainst { columns, search_modifier, mode } => {
794 self.eval_match_against(columns, search_modifier, mode, row)
795 }
796
797 vibesql_ast::Expression::PseudoVariable { pseudo_table, column } => {
799 if let Some(ctx) = self.trigger_context {
801 ctx.resolve_pseudo_var(*pseudo_table, column)
802 } else {
803 Err(ExecutorError::UnsupportedExpression(
806 format!(
807 "Pseudo-variable {}.{} is only valid within trigger bodies",
808 match pseudo_table {
809 vibesql_ast::PseudoTable::Old => "OLD",
810 vibesql_ast::PseudoTable::New => "NEW",
811 },
812 column
813 )
814 ))
815 }
816 }
817
818 vibesql_ast::Expression::SessionVariable { name } => {
820 if let Some(db) = self.database {
822 if let Some(value) = db.get_session_variable(name) {
824 Ok(value.clone())
825 } else {
826 Ok(SqlValue::Null)
828 }
829 } else {
830 Err(ExecutorError::UnsupportedExpression(
832 format!("Session variable @@{} cannot be evaluated without database context", name)
833 ))
834 }
835 }
836
837 vibesql_ast::Expression::Placeholder(idx) => {
839 Err(ExecutorError::UnsupportedExpression(
840 format!("Unbound placeholder ?{} - placeholders must be bound to values before execution", idx)
841 ))
842 }
843
844 vibesql_ast::Expression::NumberedPlaceholder(idx) => {
846 Err(ExecutorError::UnsupportedExpression(
847 format!("Unbound numbered placeholder ${} - placeholders must be bound to values before execution", idx)
848 ))
849 }
850
851 vibesql_ast::Expression::NamedPlaceholder(name) => {
853 Err(ExecutorError::UnsupportedExpression(
854 format!("Unbound named placeholder :{} - placeholders must be bound to values before execution", name)
855 ))
856 }
857
858 vibesql_ast::Expression::Conjunction(children) => {
860 let mut result = SqlValue::Boolean(true);
861 for child in children {
862 let val = self.eval(child, row)?;
863 match val {
864 SqlValue::Boolean(false) => return Ok(SqlValue::Boolean(false)),
865 SqlValue::Null => result = SqlValue::Null,
866 SqlValue::Boolean(true) => {}
867 _ => return Err(ExecutorError::TypeError(
868 format!("Conjunction requires boolean operands, got {:?}", val)
869 )),
870 }
871 }
872 Ok(result)
873 }
874
875 vibesql_ast::Expression::Disjunction(children) => {
877 let mut result = SqlValue::Boolean(false);
878 for child in children {
879 let val = self.eval(child, row)?;
880 match val {
881 SqlValue::Boolean(true) => return Ok(SqlValue::Boolean(true)),
882 SqlValue::Null => result = SqlValue::Null,
883 SqlValue::Boolean(false) => {}
884 _ => return Err(ExecutorError::TypeError(
885 format!("Disjunction requires boolean operands, got {:?}", val)
886 )),
887 }
888 }
889 Ok(result)
890 }
891
892 vibesql_ast::Expression::RowValueConstructor(_) => Err(ExecutorError::UnsupportedExpression(
896 "Row value constructors are only supported in comparison expressions".to_string(),
897 )),
898
899 vibesql_ast::Expression::Collate { expr, .. } => self.eval(expr, row),
902 }
903 }
904
905 fn eval_match_against(
907 &self,
908 columns: &[String],
909 search_modifier: &vibesql_ast::Expression,
910 mode: &vibesql_ast::FulltextMode,
911 row: &vibesql_storage::Row,
912 ) -> Result<vibesql_types::SqlValue, ExecutorError> {
913 let search_value = self.eval(search_modifier, row)?;
915 let search_string: arcstr::ArcStr = match search_value {
916 SqlValue::Varchar(s) | SqlValue::Character(s) => s,
917 SqlValue::Null => return Ok(SqlValue::Boolean(false)),
918 other => arcstr::ArcStr::from(other.to_string().as_str()),
919 };
920
921 let mut text_values: Vec<arcstr::ArcStr> = Vec::new();
923 for column_name in columns {
924 match self.eval_column_ref(None, None, column_name, row) {
925 Ok(SqlValue::Varchar(s)) | Ok(SqlValue::Character(s)) => text_values.push(s),
926 Ok(SqlValue::Null) => {
927 text_values.push(arcstr::ArcStr::from(""));
929 }
930 Ok(other) => text_values.push(arcstr::ArcStr::from(other.to_string().as_str())),
931 Err(_) => {
932 return Ok(SqlValue::Boolean(false));
934 }
935 }
936 }
937
938 let result = super::fulltext::eval_match_against(&search_string, &text_values, mode)?;
940 Ok(SqlValue::Boolean(result))
941 }
942
943 #[inline]
945 fn eval_column_ref(
946 &self,
947 schema_qualifier: Option<&str>,
948 table_qualifier: Option<&str>,
949 column: &str,
950 row: &vibesql_storage::Row,
951 ) -> Result<vibesql_types::SqlValue, ExecutorError> {
952 if let Some(schema) = schema_qualifier {
958 let schema_lower = schema.to_lowercase();
959 if schema_lower != "main" {
960 return Err(ExecutorError::ColumnNotFound {
962 column_name: format!("{}.{}.{}", schema, table_qualifier.unwrap_or(""), column),
963 table_name: self.schema.name.clone(),
964 searched_tables: vec![self.schema.name.clone()],
965 available_columns: self.schema.columns.iter().map(|c| c.name.clone()).collect(),
966 });
967 }
968 }
970
971 if column == "*" {
974 return Ok(vibesql_types::SqlValue::Null);
975 }
976
977 let column_lower = column.to_lowercase();
982 if column_lower == "rowid" || column_lower == "_rowid_" || column_lower == "oid" {
983 if self.schema.get_column_index(column).is_none() {
985 if self.schema.without_rowid {
987 return Err(ExecutorError::ColumnNotFound {
988 column_name: column.to_string(),
989 table_name: self.schema.name.clone(),
990 searched_tables: vec![self.schema.name.clone()],
991 available_columns: self
992 .schema
993 .columns
994 .iter()
995 .map(|c| c.name.clone())
996 .collect(),
997 });
998 }
999
1000 if let Some(ipk_col_idx) = self.schema.rowid_alias_column {
1004 return row
1005 .get(ipk_col_idx)
1006 .cloned()
1007 .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: ipk_col_idx });
1008 }
1009
1010 if let Some(row_id) = row.get_row_id_for_table(table_qualifier) {
1013 return Ok(vibesql_types::SqlValue::Bigint(row_id as i64));
1014 }
1015 if let Some(row_index) = self.row_index {
1017 return Ok(vibesql_types::SqlValue::Bigint(row_index as i64));
1018 }
1019 return Ok(vibesql_types::SqlValue::Null);
1022 }
1023 }
1024
1025 if table_qualifier.is_none() {
1029 if let Some(proc_ctx) = self.procedural_context {
1030 if let Some(value) = proc_ctx.get_value(column) {
1032 return Ok(value.clone());
1033 }
1034 }
1035 }
1036
1037 let mut searched_tables = Vec::new();
1039 let mut available_columns = Vec::new();
1040
1041 if let Some(qualifier) = table_qualifier {
1043 let qualifier_lower = qualifier.to_lowercase();
1044 let inner_name_lower = self.schema.name.to_lowercase();
1045
1046 let alias_lower = self.table_alias.as_ref().map(|a| a.to_lowercase());
1048 let matches_alias = alias_lower.as_ref().is_some_and(|a| a == &qualifier_lower);
1049
1050 if qualifier_lower == inner_name_lower || matches_alias {
1052 searched_tables.push(self.schema.name.clone());
1054 if let Some(col_index) = self.schema.get_column_index(column) {
1055 return row
1056 .get(col_index)
1057 .cloned()
1058 .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: col_index });
1059 }
1060 } else if let Some(outer_schema) = self.outer_schema {
1061 let outer_name_lower = outer_schema.name.to_lowercase();
1062
1063 if qualifier_lower == outer_name_lower {
1065 if let Some(outer_row) = self.outer_row {
1067 searched_tables.push(outer_schema.name.clone());
1068 if let Some(col_index) = outer_schema.get_column_index(column) {
1069 return outer_row
1070 .get(col_index)
1071 .cloned()
1072 .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: col_index });
1073 }
1074 }
1075 } else {
1076 let mut known_tables = vec![self.schema.name.clone()];
1078 if let Some(ref alias) = self.table_alias {
1079 known_tables.push(alias.clone());
1080 }
1081 known_tables.push(outer_schema.name.clone());
1082
1083 return Err(ExecutorError::InvalidTableQualifier {
1084 qualifier: qualifier.to_string(),
1085 column: column.to_string(),
1086 available_tables: known_tables,
1087 });
1088 }
1089 } else {
1090 let mut known_tables = vec![self.schema.name.clone()];
1092 if let Some(ref alias) = self.table_alias {
1093 known_tables.push(alias.clone());
1094 }
1095 return Err(ExecutorError::InvalidTableQualifier {
1096 qualifier: qualifier.to_string(),
1097 column: column.to_string(),
1098 available_tables: known_tables,
1099 });
1100 }
1101
1102 available_columns.extend(self.schema.columns.iter().map(|c| c.name.clone()));
1104 if let Some(outer_schema) = self.outer_schema {
1105 available_columns.extend(outer_schema.columns.iter().map(|c| c.name.clone()));
1106 }
1107
1108 return Err(ExecutorError::ColumnNotFound {
1109 column_name: column.to_string(),
1110 table_name: qualifier.to_string(),
1111 searched_tables,
1112 available_columns,
1113 });
1114 }
1115
1116 searched_tables.push(self.schema.name.clone());
1119 if let Some(col_index) = self.schema.get_column_index(column) {
1120 return row
1121 .get(col_index)
1122 .cloned()
1123 .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: col_index });
1124 }
1125
1126 if let (Some(outer_row), Some(outer_schema)) = (self.outer_row, self.outer_schema) {
1128 searched_tables.push(outer_schema.name.clone());
1129 if let Some(col_index) = outer_schema.get_column_index(column) {
1130 return outer_row
1131 .get(col_index)
1132 .cloned()
1133 .ok_or(ExecutorError::ColumnIndexOutOfBounds { index: col_index });
1134 }
1135 }
1136
1137 available_columns.extend(self.schema.columns.iter().map(|c| c.name.clone()));
1139 if let Some(outer_schema) = self.outer_schema {
1140 available_columns.extend(outer_schema.columns.iter().map(|c| c.name.clone()));
1141 }
1142
1143 Err(ExecutorError::ColumnNotFound {
1145 column_name: column.to_string(),
1146 table_name: table_qualifier.unwrap_or("unknown").to_string(),
1147 searched_tables,
1148 available_columns,
1149 })
1150 }
1151
1152 fn interval_unit_to_string(unit: &vibesql_ast::IntervalUnit) -> String {
1154 use vibesql_ast::IntervalUnit;
1155 match unit {
1156 IntervalUnit::Microsecond => "MICROSECOND",
1157 IntervalUnit::Second => "SECOND",
1158 IntervalUnit::Minute => "MINUTE",
1159 IntervalUnit::Hour => "HOUR",
1160 IntervalUnit::Day => "DAY",
1161 IntervalUnit::Week => "WEEK",
1162 IntervalUnit::Month => "MONTH",
1163 IntervalUnit::Quarter => "QUARTER",
1164 IntervalUnit::Year => "YEAR",
1165 IntervalUnit::SecondMicrosecond => "SECOND_MICROSECOND",
1166 IntervalUnit::MinuteMicrosecond => "MINUTE_MICROSECOND",
1167 IntervalUnit::MinuteSecond => "MINUTE_SECOND",
1168 IntervalUnit::HourMicrosecond => "HOUR_MICROSECOND",
1169 IntervalUnit::HourSecond => "HOUR_SECOND",
1170 IntervalUnit::HourMinute => "HOUR_MINUTE",
1171 IntervalUnit::DayMicrosecond => "DAY_MICROSECOND",
1172 IntervalUnit::DaySecond => "DAY_SECOND",
1173 IntervalUnit::DayMinute => "DAY_MINUTE",
1174 IntervalUnit::DayHour => "DAY_HOUR",
1175 IntervalUnit::YearMonth => "YEAR_MONTH",
1176 }
1177 .to_string()
1178 }
1179
1180 pub(super) fn eval_row_value_comparison(
1191 &self,
1192 left_exprs: &[vibesql_ast::Expression],
1193 op: &vibesql_ast::BinaryOperator,
1194 right_exprs: &[vibesql_ast::Expression],
1195 row: &vibesql_storage::Row,
1196 ) -> Result<SqlValue, ExecutorError> {
1197 if left_exprs.len() != right_exprs.len() {
1199 return Err(ExecutorError::UnsupportedExpression(format!(
1200 "Row value constructor size mismatch: left has {} elements, right has {}",
1201 left_exprs.len(),
1202 right_exprs.len()
1203 )));
1204 }
1205
1206 if left_exprs.is_empty() {
1208 return Err(ExecutorError::UnsupportedExpression(
1209 "Empty row value constructors are not allowed".to_string(),
1210 ));
1211 }
1212
1213 let mut left_values = Vec::with_capacity(left_exprs.len());
1215 let mut right_values = Vec::with_capacity(right_exprs.len());
1216
1217 for (left_expr, right_expr) in left_exprs.iter().zip(right_exprs.iter()) {
1218 left_values.push(self.eval(left_expr, row)?);
1219 right_values.push(self.eval(right_expr, row)?);
1220 }
1221
1222 match op {
1224 vibesql_ast::BinaryOperator::Equal => {
1225 let mut has_null = false;
1228 for (left_val, right_val) in left_values.iter().zip(right_values.iter()) {
1229 let cmp_result = self.eval_binary_op(left_val, op, right_val)?;
1230 match cmp_result {
1231 SqlValue::Boolean(false) => return Ok(SqlValue::Boolean(false)),
1232 SqlValue::Null => has_null = true,
1233 SqlValue::Boolean(true) => {}
1234 _ => {
1235 return Err(ExecutorError::TypeError(format!(
1236 "Comparison returned non-boolean: {:?}",
1237 cmp_result
1238 )))
1239 }
1240 }
1241 }
1242 if has_null {
1243 Ok(SqlValue::Null)
1244 } else {
1245 Ok(SqlValue::Boolean(true))
1246 }
1247 }
1248
1249 vibesql_ast::BinaryOperator::NotEqual => {
1250 let mut has_null = false;
1254 for (left_val, right_val) in left_values.iter().zip(right_values.iter()) {
1255 let eq_result =
1256 self.eval_binary_op(left_val, &vibesql_ast::BinaryOperator::Equal, right_val)?;
1257 match eq_result {
1258 SqlValue::Boolean(false) => return Ok(SqlValue::Boolean(true)), SqlValue::Null => has_null = true,
1260 SqlValue::Boolean(true) => {} _ => {
1262 return Err(ExecutorError::TypeError(format!(
1263 "Comparison returned non-boolean: {:?}",
1264 eq_result
1265 )))
1266 }
1267 }
1268 }
1269 if has_null {
1270 Ok(SqlValue::Null)
1271 } else {
1272 Ok(SqlValue::Boolean(false)) }
1274 }
1275
1276 vibesql_ast::BinaryOperator::LessThan
1277 | vibesql_ast::BinaryOperator::LessThanOrEqual
1278 | vibesql_ast::BinaryOperator::GreaterThan
1279 | vibesql_ast::BinaryOperator::GreaterThanOrEqual => {
1280 self.eval_row_value_ordering(&left_values, op, &right_values)
1283 }
1284
1285 _ => Err(ExecutorError::UnsupportedExpression(format!(
1286 "Unsupported operator for row value comparison: {:?}",
1287 op
1288 ))),
1289 }
1290 }
1291
1292 fn eval_row_value_ordering(
1300 &self,
1301 left_values: &[SqlValue],
1302 op: &vibesql_ast::BinaryOperator,
1303 right_values: &[SqlValue],
1304 ) -> Result<SqlValue, ExecutorError> {
1305 let is_less = matches!(
1306 op,
1307 vibesql_ast::BinaryOperator::LessThan | vibesql_ast::BinaryOperator::LessThanOrEqual
1308 );
1309 let is_or_equal = matches!(
1310 op,
1311 vibesql_ast::BinaryOperator::LessThanOrEqual
1312 | vibesql_ast::BinaryOperator::GreaterThanOrEqual
1313 );
1314
1315 let strict_op = if is_less {
1317 vibesql_ast::BinaryOperator::LessThan
1318 } else {
1319 vibesql_ast::BinaryOperator::GreaterThan
1320 };
1321
1322 let eq_op = vibesql_ast::BinaryOperator::Equal;
1323
1324 let mut has_null = false;
1326
1327 for i in 0..left_values.len() {
1329 let left_val = &left_values[i];
1330 let right_val = &right_values[i];
1331
1332 let strict_result = self.eval_binary_op(left_val, &strict_op, right_val)?;
1334 match strict_result {
1335 SqlValue::Boolean(true) => {
1336 return Ok(SqlValue::Boolean(true));
1338 }
1339 SqlValue::Null => {
1340 has_null = true;
1342 }
1343 SqlValue::Boolean(false) => {
1344 let eq_result = self.eval_binary_op(left_val, &eq_op, right_val)?;
1346 match eq_result {
1347 SqlValue::Boolean(true) => {
1348 }
1350 SqlValue::Boolean(false) => {
1351 return Ok(SqlValue::Boolean(false));
1354 }
1355 SqlValue::Null => {
1356 has_null = true;
1357 }
1358 _ => {
1359 return Err(ExecutorError::TypeError(format!(
1360 "Comparison returned non-boolean: {:?}",
1361 eq_result
1362 )))
1363 }
1364 }
1365 }
1366 _ => {
1367 return Err(ExecutorError::TypeError(format!(
1368 "Comparison returned non-boolean: {:?}",
1369 strict_result
1370 )))
1371 }
1372 }
1373 }
1374
1375 if has_null {
1377 Ok(SqlValue::Null)
1379 } else if is_or_equal {
1380 Ok(SqlValue::Boolean(true))
1382 } else {
1383 Ok(SqlValue::Boolean(false))
1385 }
1386 }
1387
1388 fn eval_row_value_is_distinct(
1398 &self,
1399 left_exprs: &[vibesql_ast::Expression],
1400 right_exprs: &[vibesql_ast::Expression],
1401 negated: bool,
1402 row: &vibesql_storage::Row,
1403 ) -> Result<SqlValue, ExecutorError> {
1404 if left_exprs.len() != right_exprs.len() {
1406 return Err(ExecutorError::UnsupportedExpression(format!(
1407 "Row value constructor size mismatch: left has {} elements, right has {}",
1408 left_exprs.len(),
1409 right_exprs.len()
1410 )));
1411 }
1412
1413 if left_exprs.is_empty() {
1415 return Err(ExecutorError::UnsupportedExpression(
1416 "Empty row value constructors are not allowed".to_string(),
1417 ));
1418 }
1419
1420 for (left_expr, right_expr) in left_exprs.iter().zip(right_exprs.iter()) {
1426 let left_val = self.eval(left_expr, row)?;
1427 let right_val = self.eval(right_expr, row)?;
1428
1429 let is_distinct = super::super::core::values_are_distinct(&left_val, &right_val);
1430
1431 if negated {
1432 if is_distinct {
1435 return Ok(SqlValue::Boolean(false));
1436 }
1437 } else {
1438 if is_distinct {
1440 return Ok(SqlValue::Boolean(true));
1441 }
1442 }
1443 }
1444
1445 Ok(SqlValue::Boolean(negated))
1449 }
1450}