1mod aggregate;
4mod functions;
5pub mod helpers;
6mod ops;
7mod subquery;
8mod window;
9
10pub use aggregate::AggregateFunction;
12pub use functions::eval_function;
13pub use ops::{BinaryOp, UnaryOp};
14pub use subquery::{SubqueryCompareOp, SubqueryType};
15pub use window::{
16 FrameBound, FrameUnit, WindowFrame, WindowFunction, WindowFunctionType, WindowOrderByExpr,
17 WindowSortOrder,
18};
19
20use featherdb_core::{Error, Result, Value};
21use std::collections::HashMap;
22
23#[derive(Debug, Clone, PartialEq)]
25pub enum Expr {
26 Column {
28 table: Option<String>,
29 name: String,
30 index: Option<usize>,
31 },
32
33 Literal(Value),
35
36 Parameter { index: usize },
39
40 BinaryOp {
42 left: Box<Expr>,
43 op: BinaryOp,
44 right: Box<Expr>,
45 },
46
47 UnaryOp { op: UnaryOp, expr: Box<Expr> },
49
50 Function { name: String, args: Vec<Expr> },
52
53 Aggregate {
55 func: AggregateFunction,
56 arg: Option<Box<Expr>>,
57 distinct: bool,
58 },
59
60 Case {
62 operand: Option<Box<Expr>>,
63 when_clauses: Vec<(Expr, Expr)>,
64 else_result: Option<Box<Expr>>,
65 },
66
67 Between {
69 expr: Box<Expr>,
70 low: Box<Expr>,
71 high: Box<Expr>,
72 negated: bool,
73 },
74
75 InList {
77 expr: Box<Expr>,
78 list: Vec<Expr>,
79 negated: bool,
80 },
81
82 Like {
84 expr: Box<Expr>,
85 pattern: String,
86 negated: bool,
87 },
88
89 Wildcard,
91
92 QualifiedWildcard(String),
94
95 Window(WindowFunction),
97
98 ScalarSubquery {
101 subquery_id: usize,
103 correlated: bool,
105 },
106
107 Exists { subquery_id: usize, negated: bool },
110
111 InSubquery {
114 expr: Box<Expr>,
115 subquery_id: usize,
116 negated: bool,
117 },
118
119 AnySubquery {
122 expr: Box<Expr>,
123 op: SubqueryCompareOp,
124 subquery_id: usize,
125 },
126
127 AllSubquery {
130 expr: Box<Expr>,
131 op: SubqueryCompareOp,
132 subquery_id: usize,
133 },
134}
135
136impl Expr {
137 pub fn column(name: impl Into<String>) -> Self {
139 Expr::Column {
140 table: None,
141 name: name.into(),
142 index: None,
143 }
144 }
145
146 pub fn qualified_column(table: impl Into<String>, name: impl Into<String>) -> Self {
148 Expr::Column {
149 table: Some(table.into()),
150 name: name.into(),
151 index: None,
152 }
153 }
154
155 pub fn literal(value: impl Into<Value>) -> Self {
157 Expr::Literal(value.into())
158 }
159
160 pub fn binary(left: Expr, op: BinaryOp, right: Expr) -> Self {
162 Expr::BinaryOp {
163 left: Box::new(left),
164 op,
165 right: Box::new(right),
166 }
167 }
168
169 pub fn and(left: Expr, right: Expr) -> Self {
171 Expr::binary(left, BinaryOp::And, right)
172 }
173
174 pub fn or(left: Expr, right: Expr) -> Self {
176 Expr::binary(left, BinaryOp::Or, right)
177 }
178
179 pub fn parameter(index: usize) -> Self {
181 Expr::Parameter { index }
182 }
183
184 pub fn resolve_indices(&mut self, column_map: &HashMap<String, usize>) {
188 match self {
189 Expr::Column {
190 name, table, index, ..
191 } => {
192 if index.is_some() {
193 return; }
195 let resolved = if let Some(t) = table {
196 let key = format!("{}.{}", t, name);
197 column_map
198 .get(&key)
199 .or_else(|| column_map.get(name.as_str()))
200 .or_else(|| column_map.get(&key.to_ascii_lowercase()))
201 .or_else(|| column_map.get(&name.to_ascii_lowercase()))
202 .copied()
203 } else {
204 column_map
205 .get(name.as_str())
206 .or_else(|| {
207 column_map
208 .iter()
209 .find(|(k, _)| {
210 k.strip_suffix(name.as_str())
211 .is_some_and(|prefix| prefix.ends_with('.'))
212 })
213 .map(|(_, v)| v)
214 })
215 .or_else(|| column_map.get(&name.to_ascii_lowercase()))
216 .copied()
217 };
218 *index = resolved;
219 }
220 Expr::BinaryOp { left, right, .. } => {
221 left.resolve_indices(column_map);
222 right.resolve_indices(column_map);
223 }
224 Expr::UnaryOp { expr, .. } => expr.resolve_indices(column_map),
225 Expr::Function { args, .. } => {
226 for arg in args {
227 arg.resolve_indices(column_map);
228 }
229 }
230 Expr::Aggregate { arg: Some(e), .. } => e.resolve_indices(column_map),
231 Expr::Case {
232 operand,
233 when_clauses,
234 else_result,
235 } => {
236 if let Some(e) = operand {
237 e.resolve_indices(column_map);
238 }
239 for (w, t) in when_clauses {
240 w.resolve_indices(column_map);
241 t.resolve_indices(column_map);
242 }
243 if let Some(e) = else_result {
244 e.resolve_indices(column_map);
245 }
246 }
247 Expr::Between {
248 expr, low, high, ..
249 } => {
250 expr.resolve_indices(column_map);
251 low.resolve_indices(column_map);
252 high.resolve_indices(column_map);
253 }
254 Expr::InList { expr, list, .. } => {
255 expr.resolve_indices(column_map);
256 for e in list {
257 e.resolve_indices(column_map);
258 }
259 }
260 Expr::Like { expr, .. } => expr.resolve_indices(column_map),
261 Expr::InSubquery { expr, .. } => expr.resolve_indices(column_map),
262 Expr::AnySubquery { expr, .. } => expr.resolve_indices(column_map),
263 Expr::AllSubquery { expr, .. } => expr.resolve_indices(column_map),
264 _ => {} }
266 }
267
268 pub fn referenced_columns(&self, out: &mut Vec<usize>) {
270 match self {
271 Expr::Column { index: Some(i), .. } => out.push(*i),
272 Expr::BinaryOp { left, right, .. } => {
273 left.referenced_columns(out);
274 right.referenced_columns(out);
275 }
276 Expr::UnaryOp { expr, .. } => expr.referenced_columns(out),
277 Expr::Function { args, .. } => {
278 for arg in args {
279 arg.referenced_columns(out);
280 }
281 }
282 Expr::Aggregate { arg: Some(e), .. } => e.referenced_columns(out),
283 Expr::Case {
284 operand,
285 when_clauses,
286 else_result,
287 } => {
288 if let Some(e) = operand {
289 e.referenced_columns(out);
290 }
291 for (w, t) in when_clauses {
292 w.referenced_columns(out);
293 t.referenced_columns(out);
294 }
295 if let Some(e) = else_result {
296 e.referenced_columns(out);
297 }
298 }
299 Expr::Between {
300 expr, low, high, ..
301 } => {
302 expr.referenced_columns(out);
303 low.referenced_columns(out);
304 high.referenced_columns(out);
305 }
306 Expr::InList { expr, list, .. } => {
307 expr.referenced_columns(out);
308 for e in list {
309 e.referenced_columns(out);
310 }
311 }
312 Expr::Like { expr, .. } => expr.referenced_columns(out),
313 Expr::InSubquery { expr, .. } => expr.referenced_columns(out),
314 Expr::AnySubquery { expr, .. } => expr.referenced_columns(out),
315 Expr::AllSubquery { expr, .. } => expr.referenced_columns(out),
316 _ => {} }
318 }
319
320 pub fn eval(&self, row: &[Value], column_map: &HashMap<String, usize>) -> Result<Value> {
322 match self {
323 Expr::Column { name, index, table } => {
324 if let Some(idx) = index {
325 Ok(row.get(*idx).cloned().unwrap_or(Value::Null))
326 } else if let Some(t) = table {
327 let key = format!("{}.{}", t, name);
329 let idx = column_map
330 .get(&key)
331 .or_else(|| column_map.get(name.as_str()))
332 .or_else(|| column_map.get(&key.to_ascii_lowercase()))
333 .or_else(|| column_map.get(&name.to_ascii_lowercase()));
334 match idx {
335 Some(&i) => Ok(row.get(i).cloned().unwrap_or(Value::Null)),
336 None => Err(Error::ColumnNotFound {
337 column: name.clone(),
338 table: t.clone(),
339 suggestion: None,
340 }),
341 }
342 } else {
343 let idx = column_map
347 .get(name.as_str())
348 .or_else(|| {
349 column_map
350 .iter()
351 .find(|(k, _)| {
352 k.strip_suffix(name.as_str())
353 .is_some_and(|prefix| prefix.ends_with('.'))
354 })
355 .map(|(_, v)| v)
356 })
357 .or_else(|| {
358 let lower = name.to_ascii_lowercase();
359 column_map.get(lower.as_str()).or_else(|| {
360 column_map
361 .iter()
362 .find(|(k, _)| {
363 k.to_ascii_lowercase()
364 .strip_suffix(lower.as_str())
365 .is_some_and(|prefix| prefix.ends_with('.'))
366 })
367 .map(|(_, v)| v)
368 })
369 });
370 match idx {
371 Some(&i) => Ok(row.get(i).cloned().unwrap_or(Value::Null)),
372 None => Err(Error::ColumnNotFound {
373 column: name.clone(),
374 table: String::new(),
375 suggestion: None,
376 }),
377 }
378 }
379 }
380
381 Expr::Literal(v) => Ok(v.clone()),
382
383 Expr::BinaryOp { left, op, right } => {
384 let l = left.eval(row, column_map)?;
385 let r = right.eval(row, column_map)?;
386 op.eval(&l, &r)
387 }
388
389 Expr::UnaryOp { op, expr } => {
390 let v = expr.eval(row, column_map)?;
391 op.eval(&v)
392 }
393
394 Expr::Case {
395 operand,
396 when_clauses,
397 else_result,
398 } => {
399 let operand_val = operand
400 .as_ref()
401 .map(|e| e.eval(row, column_map))
402 .transpose()?;
403
404 for (when_expr, then_expr) in when_clauses {
405 let when_val = when_expr.eval(row, column_map)?;
406 let matches = if let Some(ref op_val) = operand_val {
407 when_val == *op_val
408 } else {
409 when_val.as_bool().unwrap_or(false)
410 };
411
412 if matches {
413 return then_expr.eval(row, column_map);
414 }
415 }
416
417 if let Some(else_expr) = else_result {
418 else_expr.eval(row, column_map)
419 } else {
420 Ok(Value::Null)
421 }
422 }
423
424 Expr::Between {
425 expr,
426 low,
427 high,
428 negated,
429 } => {
430 let v = expr.eval(row, column_map)?;
431 let l = low.eval(row, column_map)?;
432 let h = high.eval(row, column_map)?;
433
434 let in_range = v >= l && v <= h;
435 Ok(Value::Boolean(if *negated { !in_range } else { in_range }))
436 }
437
438 Expr::InList {
439 expr,
440 list,
441 negated,
442 } => {
443 let v = expr.eval(row, column_map)?;
444 let mut found = false;
445
446 for item in list {
447 let item_val = item.eval(row, column_map)?;
448 if v == item_val {
449 found = true;
450 break;
451 }
452 }
453
454 Ok(Value::Boolean(if *negated { !found } else { found }))
455 }
456
457 Expr::Like {
458 expr,
459 pattern,
460 negated,
461 } => {
462 let v = expr.eval(row, column_map)?;
463 let matches = match v {
464 Value::Text(s) => helpers::like_match(&s, pattern),
465 _ => false,
466 };
467 Ok(Value::Boolean(if *negated { !matches } else { matches }))
468 }
469
470 Expr::Function { name, args } => {
471 let arg_values: Vec<Value> = args
472 .iter()
473 .map(|a| a.eval(row, column_map))
474 .collect::<Result<_>>()?;
475
476 eval_function(name, &arg_values)
477 }
478
479 Expr::Aggregate { .. } => {
480 Err(Error::Internal(
482 "Aggregate should be handled by executor".into(),
483 ))
484 }
485
486 Expr::Parameter { index } => {
487 Err(Error::Internal(format!(
489 "Parameter ${} was not substituted before execution",
490 index + 1
491 )))
492 }
493
494 Expr::Wildcard | Expr::QualifiedWildcard(_) => {
495 Err(Error::Internal("Wildcard should be expanded".into()))
496 }
497
498 Expr::Window(_) => {
499 Err(Error::Internal(
501 "Window function should be handled by executor".into(),
502 ))
503 }
504
505 Expr::ScalarSubquery { .. } => {
506 Err(Error::Internal(
508 "Scalar subquery should be handled by executor".into(),
509 ))
510 }
511
512 Expr::Exists { .. } => {
513 Err(Error::Internal(
515 "EXISTS subquery should be handled by executor".into(),
516 ))
517 }
518
519 Expr::InSubquery { .. } => {
520 Err(Error::Internal(
522 "IN subquery should be handled by executor".into(),
523 ))
524 }
525
526 Expr::AnySubquery { .. } => {
527 Err(Error::Internal(
529 "ANY subquery should be handled by executor".into(),
530 ))
531 }
532
533 Expr::AllSubquery { .. } => {
534 Err(Error::Internal(
536 "ALL subquery should be handled by executor".into(),
537 ))
538 }
539 }
540 }
541
542 pub fn contains_aggregate(&self) -> bool {
544 match self {
545 Expr::Aggregate { .. } => true,
546 Expr::BinaryOp { left, right, .. } => {
547 left.contains_aggregate() || right.contains_aggregate()
548 }
549 Expr::UnaryOp { expr, .. } => expr.contains_aggregate(),
550 Expr::Function { args, .. } => args.iter().any(|a| a.contains_aggregate()),
551 Expr::Case {
552 operand,
553 when_clauses,
554 else_result,
555 } => {
556 operand
557 .as_ref()
558 .map(|e| e.contains_aggregate())
559 .unwrap_or(false)
560 || when_clauses
561 .iter()
562 .any(|(w, t)| w.contains_aggregate() || t.contains_aggregate())
563 || else_result
564 .as_ref()
565 .map(|e| e.contains_aggregate())
566 .unwrap_or(false)
567 }
568 Expr::Between {
569 expr, low, high, ..
570 } => expr.contains_aggregate() || low.contains_aggregate() || high.contains_aggregate(),
571 Expr::InList { expr, list, .. } => {
572 expr.contains_aggregate() || list.iter().any(|e| e.contains_aggregate())
573 }
574 Expr::Like { expr, .. } => expr.contains_aggregate(),
575 Expr::InSubquery { expr, .. } => expr.contains_aggregate(),
576 Expr::AnySubquery { expr, .. } => expr.contains_aggregate(),
577 Expr::AllSubquery { expr, .. } => expr.contains_aggregate(),
578 _ => false,
579 }
580 }
581
582 pub fn collect_column_references(&self) -> Vec<(Option<String>, String)> {
585 let mut refs = Vec::new();
586 self.collect_column_references_recursive(&mut refs);
587 refs
588 }
589
590 fn collect_column_references_recursive(&self, refs: &mut Vec<(Option<String>, String)>) {
591 match self {
592 Expr::Column { table, name, .. } => {
593 refs.push((table.clone(), name.clone()));
594 }
595 Expr::BinaryOp { left, right, .. } => {
596 left.collect_column_references_recursive(refs);
597 right.collect_column_references_recursive(refs);
598 }
599 Expr::UnaryOp { expr, .. } => {
600 expr.collect_column_references_recursive(refs);
601 }
602 Expr::Function { args, .. } => {
603 for arg in args {
604 arg.collect_column_references_recursive(refs);
605 }
606 }
607 Expr::Aggregate { arg: Some(e), .. } => {
608 e.collect_column_references_recursive(refs);
609 }
610 Expr::Aggregate { arg: None, .. } => {}
611 Expr::Case {
612 operand,
613 when_clauses,
614 else_result,
615 } => {
616 if let Some(e) = operand {
617 e.collect_column_references_recursive(refs);
618 }
619 for (when, then) in when_clauses {
620 when.collect_column_references_recursive(refs);
621 then.collect_column_references_recursive(refs);
622 }
623 if let Some(e) = else_result {
624 e.collect_column_references_recursive(refs);
625 }
626 }
627 Expr::Between {
628 expr, low, high, ..
629 } => {
630 expr.collect_column_references_recursive(refs);
631 low.collect_column_references_recursive(refs);
632 high.collect_column_references_recursive(refs);
633 }
634 Expr::InList { expr, list, .. } => {
635 expr.collect_column_references_recursive(refs);
636 for item in list {
637 item.collect_column_references_recursive(refs);
638 }
639 }
640 Expr::Like { expr, .. } => {
641 expr.collect_column_references_recursive(refs);
642 }
643 Expr::InSubquery { expr, .. } => {
644 expr.collect_column_references_recursive(refs);
645 }
646 Expr::AnySubquery { expr, .. } => {
647 expr.collect_column_references_recursive(refs);
648 }
649 Expr::AllSubquery { expr, .. } => {
650 expr.collect_column_references_recursive(refs);
651 }
652 Expr::Window(window) => {
653 for expr in &window.partition_by {
654 expr.collect_column_references_recursive(refs);
655 }
656 for order_expr in &window.order_by {
657 order_expr.expr.collect_column_references_recursive(refs);
658 }
659 match &window.function {
661 WindowFunctionType::Lag { expr, default, .. }
662 | WindowFunctionType::Lead { expr, default, .. } => {
663 expr.collect_column_references_recursive(refs);
664 if let Some(d) = default {
665 d.collect_column_references_recursive(refs);
666 }
667 }
668 WindowFunctionType::FirstValue(e)
669 | WindowFunctionType::LastValue(e)
670 | WindowFunctionType::Sum(e)
671 | WindowFunctionType::Avg(e)
672 | WindowFunctionType::Min(e)
673 | WindowFunctionType::Max(e) => {
674 e.collect_column_references_recursive(refs);
675 }
676 WindowFunctionType::NthValue(e, _) => {
677 e.collect_column_references_recursive(refs);
678 }
679 WindowFunctionType::Count(Some(e)) => {
680 e.collect_column_references_recursive(refs);
681 }
682 _ => {}
683 }
684 }
685 _ => {}
687 }
688 }
689
690 pub fn contains_window_function(&self) -> bool {
692 match self {
693 Expr::Window(_) => true,
694 Expr::BinaryOp { left, right, .. } => {
695 left.contains_window_function() || right.contains_window_function()
696 }
697 Expr::UnaryOp { expr, .. } => expr.contains_window_function(),
698 Expr::Function { args, .. } => args.iter().any(|a| a.contains_window_function()),
699 Expr::Case {
700 operand,
701 when_clauses,
702 else_result,
703 } => {
704 operand
705 .as_ref()
706 .map(|e| e.contains_window_function())
707 .unwrap_or(false)
708 || when_clauses
709 .iter()
710 .any(|(w, t)| w.contains_window_function() || t.contains_window_function())
711 || else_result
712 .as_ref()
713 .map(|e| e.contains_window_function())
714 .unwrap_or(false)
715 }
716 Expr::Between {
717 expr, low, high, ..
718 } => {
719 expr.contains_window_function()
720 || low.contains_window_function()
721 || high.contains_window_function()
722 }
723 Expr::InList { expr, list, .. } => {
724 expr.contains_window_function() || list.iter().any(|e| e.contains_window_function())
725 }
726 Expr::Like { expr, .. } => expr.contains_window_function(),
727 Expr::InSubquery { expr, .. } => expr.contains_window_function(),
728 Expr::AnySubquery { expr, .. } => expr.contains_window_function(),
729 Expr::AllSubquery { expr, .. } => expr.contains_window_function(),
730 _ => false,
731 }
732 }
733
734 pub fn contains_subquery(&self) -> bool {
736 match self {
737 Expr::ScalarSubquery { .. }
738 | Expr::Exists { .. }
739 | Expr::InSubquery { .. }
740 | Expr::AnySubquery { .. }
741 | Expr::AllSubquery { .. } => true,
742 Expr::BinaryOp { left, right, .. } => {
743 left.contains_subquery() || right.contains_subquery()
744 }
745 Expr::UnaryOp { expr, .. } => expr.contains_subquery(),
746 Expr::Function { args, .. } => args.iter().any(|a| a.contains_subquery()),
747 Expr::Case {
748 operand,
749 when_clauses,
750 else_result,
751 } => {
752 operand
753 .as_ref()
754 .map(|e| e.contains_subquery())
755 .unwrap_or(false)
756 || when_clauses
757 .iter()
758 .any(|(w, t)| w.contains_subquery() || t.contains_subquery())
759 || else_result
760 .as_ref()
761 .map(|e| e.contains_subquery())
762 .unwrap_or(false)
763 }
764 Expr::Between {
765 expr, low, high, ..
766 } => expr.contains_subquery() || low.contains_subquery() || high.contains_subquery(),
767 Expr::InList { expr, list, .. } => {
768 expr.contains_subquery() || list.iter().any(|e| e.contains_subquery())
769 }
770 Expr::Like { expr, .. } => expr.contains_subquery(),
771 _ => false,
772 }
773 }
774}
775
776#[cfg(test)]
777mod tests {
778 use super::*;
779
780 #[test]
781 fn test_expr_column() {
782 let expr = Expr::column("name");
783 assert!(matches!(expr, Expr::Column { .. }));
784 }
785
786 #[test]
787 fn test_expr_qualified_column() {
788 let expr = Expr::qualified_column("users", "name");
789 match expr {
790 Expr::Column { table, name, .. } => {
791 assert_eq!(table, Some("users".to_string()));
792 assert_eq!(name, "name");
793 }
794 _ => panic!("Expected Column variant"),
795 }
796 }
797
798 #[test]
799 fn test_expr_literal() {
800 let expr = Expr::literal(Value::Integer(42));
801 assert_eq!(expr, Expr::Literal(Value::Integer(42)));
802 }
803
804 #[test]
805 fn test_expr_binary() {
806 let expr = Expr::binary(
807 Expr::literal(Value::Integer(5)),
808 BinaryOp::Add,
809 Expr::literal(Value::Integer(3)),
810 );
811 assert!(matches!(expr, Expr::BinaryOp { .. }));
812 }
813
814 #[test]
815 fn test_expr_and() {
816 let expr = Expr::and(
817 Expr::literal(Value::Boolean(true)),
818 Expr::literal(Value::Boolean(false)),
819 );
820 match expr {
821 Expr::BinaryOp { op, .. } => assert_eq!(op, BinaryOp::And),
822 _ => panic!("Expected BinaryOp"),
823 }
824 }
825
826 #[test]
827 fn test_expr_or() {
828 let expr = Expr::or(
829 Expr::literal(Value::Boolean(true)),
830 Expr::literal(Value::Boolean(false)),
831 );
832 match expr {
833 Expr::BinaryOp { op, .. } => assert_eq!(op, BinaryOp::Or),
834 _ => panic!("Expected BinaryOp"),
835 }
836 }
837
838 #[test]
839 fn test_expr_parameter() {
840 let expr = Expr::parameter(0);
841 assert_eq!(expr, Expr::Parameter { index: 0 });
842 }
843
844 #[test]
845 fn test_expr_eval_literal() {
846 let expr = Expr::literal(Value::Integer(42));
847 let row = vec![];
848 let column_map = HashMap::new();
849 let result = expr.eval(&row, &column_map).unwrap();
850 assert_eq!(result, Value::Integer(42));
851 }
852
853 #[test]
854 fn test_expr_eval_binary_op() {
855 let expr = Expr::binary(
856 Expr::literal(Value::Integer(5)),
857 BinaryOp::Add,
858 Expr::literal(Value::Integer(3)),
859 );
860 let row = vec![];
861 let column_map = HashMap::new();
862 let result = expr.eval(&row, &column_map).unwrap();
863 assert_eq!(result, Value::Integer(8));
864 }
865
866 #[test]
867 fn test_contains_aggregate() {
868 let agg_expr = Expr::Aggregate {
869 func: AggregateFunction::Count,
870 arg: None,
871 distinct: false,
872 };
873 assert!(agg_expr.contains_aggregate());
874
875 let non_agg = Expr::literal(Value::Integer(42));
876 assert!(!non_agg.contains_aggregate());
877 }
878
879 #[test]
880 fn test_contains_window_function() {
881 let window_expr = Expr::Window(WindowFunction {
882 function: WindowFunctionType::RowNumber,
883 partition_by: vec![],
884 order_by: vec![],
885 frame: None,
886 });
887 assert!(window_expr.contains_window_function());
888
889 let non_window = Expr::literal(Value::Integer(42));
890 assert!(!non_window.contains_window_function());
891 }
892
893 #[test]
894 fn test_contains_subquery() {
895 let subquery_expr = Expr::ScalarSubquery {
896 subquery_id: 0,
897 correlated: false,
898 };
899 assert!(subquery_expr.contains_subquery());
900
901 let non_subquery = Expr::literal(Value::Integer(42));
902 assert!(!non_subquery.contains_subquery());
903 }
904
905 #[test]
906 fn test_collect_column_references() {
907 let expr = Expr::BinaryOp {
908 left: Box::new(Expr::Column {
909 table: Some("users".to_string()),
910 name: "age".to_string(),
911 index: None,
912 }),
913 op: BinaryOp::Gt,
914 right: Box::new(Expr::Column {
915 table: None,
916 name: "salary".to_string(),
917 index: None,
918 }),
919 };
920
921 let refs = expr.collect_column_references();
922 assert_eq!(refs.len(), 2);
923 assert!(refs.contains(&(Some("users".to_string()), "age".to_string())));
924 assert!(refs.contains(&(None, "salary".to_string())));
925 }
926
927 #[test]
932 fn test_eval_column_lookup() {
933 let row = vec![
934 Value::Integer(1),
935 Value::Text("Alice".into()),
936 Value::Integer(30),
937 ];
938 let col_map: HashMap<String, usize> = [("id", 0), ("name", 1), ("age", 2)]
939 .iter()
940 .map(|(k, v)| (k.to_string(), *v))
941 .collect();
942
943 let expr = Expr::column("name");
944 let result = expr.eval(&row, &col_map).unwrap();
945 assert_eq!(result, Value::Text("Alice".into()));
946
947 let expr = Expr::column("age");
948 let result = expr.eval(&row, &col_map).unwrap();
949 assert_eq!(result, Value::Integer(30));
950 }
951
952 #[test]
953 fn test_eval_qualified_column() {
954 let row = vec![Value::Integer(100)];
955 let mut col_map = HashMap::new();
956 col_map.insert("users.salary".to_string(), 0);
957
958 let expr = Expr::qualified_column("users", "salary");
959 let result = expr.eval(&row, &col_map).unwrap();
960 assert_eq!(result, Value::Integer(100));
961 }
962
963 #[test]
964 fn test_eval_column_not_found() {
965 let row = vec![Value::Integer(1)];
966 let col_map = HashMap::new();
967
968 let expr = Expr::column("missing");
969 let result = expr.eval(&row, &col_map);
970 assert!(result.is_err());
971 assert!(matches!(result.unwrap_err(), Error::ColumnNotFound { .. }));
972 }
973
974 #[test]
975 fn test_eval_between_inclusive() {
976 let row = vec![];
977 let col_map = HashMap::new();
978
979 let expr = Expr::Between {
981 expr: Box::new(Expr::literal(Value::Integer(5))),
982 low: Box::new(Expr::literal(Value::Integer(5))),
983 high: Box::new(Expr::literal(Value::Integer(10))),
984 negated: false,
985 };
986 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
987
988 let expr = Expr::Between {
990 expr: Box::new(Expr::literal(Value::Integer(10))),
991 low: Box::new(Expr::literal(Value::Integer(5))),
992 high: Box::new(Expr::literal(Value::Integer(10))),
993 negated: false,
994 };
995 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
996
997 let expr = Expr::Between {
999 expr: Box::new(Expr::literal(Value::Integer(7))),
1000 low: Box::new(Expr::literal(Value::Integer(5))),
1001 high: Box::new(Expr::literal(Value::Integer(10))),
1002 negated: false,
1003 };
1004 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1005
1006 let expr = Expr::Between {
1008 expr: Box::new(Expr::literal(Value::Integer(3))),
1009 low: Box::new(Expr::literal(Value::Integer(5))),
1010 high: Box::new(Expr::literal(Value::Integer(10))),
1011 negated: false,
1012 };
1013 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1014
1015 let expr = Expr::Between {
1017 expr: Box::new(Expr::literal(Value::Integer(15))),
1018 low: Box::new(Expr::literal(Value::Integer(5))),
1019 high: Box::new(Expr::literal(Value::Integer(10))),
1020 negated: false,
1021 };
1022 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1023 }
1024
1025 #[test]
1026 fn test_eval_between_negated() {
1027 let row = vec![];
1028 let col_map = HashMap::new();
1029
1030 let expr = Expr::Between {
1032 expr: Box::new(Expr::literal(Value::Integer(7))),
1033 low: Box::new(Expr::literal(Value::Integer(5))),
1034 high: Box::new(Expr::literal(Value::Integer(10))),
1035 negated: true,
1036 };
1037 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1038
1039 let expr = Expr::Between {
1041 expr: Box::new(Expr::literal(Value::Integer(15))),
1042 low: Box::new(Expr::literal(Value::Integer(5))),
1043 high: Box::new(Expr::literal(Value::Integer(10))),
1044 negated: true,
1045 };
1046 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1047 }
1048
1049 #[test]
1050 fn test_eval_in_list_found() {
1051 let row = vec![];
1052 let col_map = HashMap::new();
1053
1054 let expr = Expr::InList {
1055 expr: Box::new(Expr::literal(Value::Integer(5))),
1056 list: vec![
1057 Expr::literal(Value::Integer(1)),
1058 Expr::literal(Value::Integer(5)),
1059 Expr::literal(Value::Integer(10)),
1060 ],
1061 negated: false,
1062 };
1063 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1064 }
1065
1066 #[test]
1067 fn test_eval_in_list_not_found() {
1068 let row = vec![];
1069 let col_map = HashMap::new();
1070
1071 let expr = Expr::InList {
1072 expr: Box::new(Expr::literal(Value::Integer(7))),
1073 list: vec![
1074 Expr::literal(Value::Integer(1)),
1075 Expr::literal(Value::Integer(5)),
1076 Expr::literal(Value::Integer(10)),
1077 ],
1078 negated: false,
1079 };
1080 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1081 }
1082
1083 #[test]
1084 fn test_eval_in_list_negated() {
1085 let row = vec![];
1086 let col_map = HashMap::new();
1087
1088 let expr = Expr::InList {
1090 expr: Box::new(Expr::literal(Value::Integer(7))),
1091 list: vec![
1092 Expr::literal(Value::Integer(1)),
1093 Expr::literal(Value::Integer(5)),
1094 Expr::literal(Value::Integer(10)),
1095 ],
1096 negated: true,
1097 };
1098 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1099
1100 let expr = Expr::InList {
1102 expr: Box::new(Expr::literal(Value::Integer(5))),
1103 list: vec![
1104 Expr::literal(Value::Integer(1)),
1105 Expr::literal(Value::Integer(5)),
1106 Expr::literal(Value::Integer(10)),
1107 ],
1108 negated: true,
1109 };
1110 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1111 }
1112
1113 #[test]
1114 fn test_eval_in_list_with_null() {
1115 let row = vec![];
1116 let col_map = HashMap::new();
1117
1118 let expr = Expr::InList {
1120 expr: Box::new(Expr::literal(Value::Null)),
1121 list: vec![
1122 Expr::literal(Value::Integer(1)),
1123 Expr::literal(Value::Null),
1124 Expr::literal(Value::Integer(5)),
1125 ],
1126 negated: false,
1127 };
1128 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1130
1131 let expr = Expr::InList {
1133 expr: Box::new(Expr::literal(Value::Integer(5))),
1134 list: vec![Expr::literal(Value::Null), Expr::literal(Value::Integer(5))],
1135 negated: false,
1136 };
1137 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1138 }
1139
1140 #[test]
1141 fn test_eval_like_percent() {
1142 let row = vec![];
1143 let col_map = HashMap::new();
1144
1145 let expr = Expr::Like {
1147 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1148 pattern: "h%".to_string(),
1149 negated: false,
1150 };
1151 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1152
1153 let expr = Expr::Like {
1155 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1156 pattern: "%lo".to_string(),
1157 negated: false,
1158 };
1159 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1160
1161 let expr = Expr::Like {
1163 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1164 pattern: "%ll%".to_string(),
1165 negated: false,
1166 };
1167 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1168
1169 let expr = Expr::Like {
1171 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1172 pattern: "%world%".to_string(),
1173 negated: false,
1174 };
1175 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1176 }
1177
1178 #[test]
1179 fn test_eval_like_underscore() {
1180 let row = vec![];
1181 let col_map = HashMap::new();
1182
1183 let expr = Expr::Like {
1185 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1186 pattern: "h_llo".to_string(),
1187 negated: false,
1188 };
1189 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1190
1191 let expr = Expr::Like {
1193 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1194 pattern: "h__lo".to_string(),
1195 negated: false,
1196 };
1197 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1198
1199 let expr = Expr::Like {
1201 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1202 pattern: "h_lo".to_string(),
1203 negated: false,
1204 };
1205 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1206 }
1207
1208 #[test]
1209 fn test_eval_like_exact() {
1210 let row = vec![];
1211 let col_map = HashMap::new();
1212
1213 let expr = Expr::Like {
1215 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1216 pattern: "hello".to_string(),
1217 negated: false,
1218 };
1219 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1220
1221 let expr = Expr::Like {
1223 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1224 pattern: "world".to_string(),
1225 negated: false,
1226 };
1227 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1228 }
1229
1230 #[test]
1231 fn test_eval_like_negated() {
1232 let row = vec![];
1233 let col_map = HashMap::new();
1234
1235 let expr = Expr::Like {
1237 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1238 pattern: "h%".to_string(),
1239 negated: true,
1240 };
1241 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1242
1243 let expr = Expr::Like {
1245 expr: Box::new(Expr::literal(Value::Text("hello".into()))),
1246 pattern: "%world%".to_string(),
1247 negated: true,
1248 };
1249 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(true));
1250 }
1251
1252 #[test]
1253 fn test_eval_like_non_text() {
1254 let row = vec![];
1255 let col_map = HashMap::new();
1256
1257 let expr = Expr::Like {
1259 expr: Box::new(Expr::literal(Value::Integer(123))),
1260 pattern: "1%".to_string(),
1261 negated: false,
1262 };
1263 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Boolean(false));
1264 }
1265
1266 #[test]
1267 fn test_eval_case_simple() {
1268 let row = vec![];
1269 let col_map = HashMap::new();
1270
1271 let expr = Expr::Case {
1273 operand: None,
1274 when_clauses: vec![
1275 (
1276 Expr::literal(Value::Boolean(false)),
1277 Expr::literal(Value::Text("first".into())),
1278 ),
1279 (
1280 Expr::literal(Value::Boolean(true)),
1281 Expr::literal(Value::Text("second".into())),
1282 ),
1283 ],
1284 else_result: Some(Box::new(Expr::literal(Value::Text("else".into())))),
1285 };
1286 assert_eq!(
1287 expr.eval(&row, &col_map).unwrap(),
1288 Value::Text("second".into())
1289 );
1290 }
1291
1292 #[test]
1293 fn test_eval_case_else() {
1294 let row = vec![];
1295 let col_map = HashMap::new();
1296
1297 let expr = Expr::Case {
1299 operand: None,
1300 when_clauses: vec![
1301 (
1302 Expr::literal(Value::Boolean(false)),
1303 Expr::literal(Value::Text("first".into())),
1304 ),
1305 (
1306 Expr::literal(Value::Boolean(false)),
1307 Expr::literal(Value::Text("second".into())),
1308 ),
1309 ],
1310 else_result: Some(Box::new(Expr::literal(Value::Text("else".into())))),
1311 };
1312 assert_eq!(
1313 expr.eval(&row, &col_map).unwrap(),
1314 Value::Text("else".into())
1315 );
1316 }
1317
1318 #[test]
1319 fn test_eval_case_no_else() {
1320 let row = vec![];
1321 let col_map = HashMap::new();
1322
1323 let expr = Expr::Case {
1325 operand: None,
1326 when_clauses: vec![
1327 (
1328 Expr::literal(Value::Boolean(false)),
1329 Expr::literal(Value::Text("first".into())),
1330 ),
1331 (
1332 Expr::literal(Value::Boolean(false)),
1333 Expr::literal(Value::Text("second".into())),
1334 ),
1335 ],
1336 else_result: None,
1337 };
1338 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Null);
1339 }
1340
1341 #[test]
1342 fn test_eval_case_with_operand() {
1343 let row = vec![];
1344 let col_map = HashMap::new();
1345
1346 let expr = Expr::Case {
1348 operand: Some(Box::new(Expr::literal(Value::Integer(2)))),
1349 when_clauses: vec![
1350 (
1351 Expr::literal(Value::Integer(1)),
1352 Expr::literal(Value::Text("one".into())),
1353 ),
1354 (
1355 Expr::literal(Value::Integer(2)),
1356 Expr::literal(Value::Text("two".into())),
1357 ),
1358 (
1359 Expr::literal(Value::Integer(3)),
1360 Expr::literal(Value::Text("three".into())),
1361 ),
1362 ],
1363 else_result: Some(Box::new(Expr::literal(Value::Text("other".into())))),
1364 };
1365 assert_eq!(
1366 expr.eval(&row, &col_map).unwrap(),
1367 Value::Text("two".into())
1368 );
1369
1370 let expr = Expr::Case {
1372 operand: Some(Box::new(Expr::literal(Value::Integer(5)))),
1373 when_clauses: vec![
1374 (
1375 Expr::literal(Value::Integer(1)),
1376 Expr::literal(Value::Text("one".into())),
1377 ),
1378 (
1379 Expr::literal(Value::Integer(2)),
1380 Expr::literal(Value::Text("two".into())),
1381 ),
1382 ],
1383 else_result: Some(Box::new(Expr::literal(Value::Text("other".into())))),
1384 };
1385 assert_eq!(
1386 expr.eval(&row, &col_map).unwrap(),
1387 Value::Text("other".into())
1388 );
1389 }
1390
1391 #[test]
1392 fn test_eval_binary_null_arithmetic() {
1393 let row = vec![];
1394 let col_map = HashMap::new();
1395
1396 let expr = Expr::binary(
1398 Expr::literal(Value::Null),
1399 BinaryOp::Add,
1400 Expr::literal(Value::Integer(1)),
1401 );
1402 let result = expr.eval(&row, &col_map).unwrap();
1403 assert_eq!(result, Value::Null);
1404
1405 let expr = Expr::binary(
1407 Expr::literal(Value::Integer(5)),
1408 BinaryOp::Add,
1409 Expr::literal(Value::Null),
1410 );
1411 let result = expr.eval(&row, &col_map).unwrap();
1412 assert_eq!(result, Value::Null);
1413
1414 let expr = Expr::binary(
1416 Expr::literal(Value::Null),
1417 BinaryOp::Mul,
1418 Expr::literal(Value::Integer(5)),
1419 );
1420 let result = expr.eval(&row, &col_map).unwrap();
1421 assert_eq!(result, Value::Null);
1422 }
1423
1424 #[test]
1425 fn test_eval_nested_binary() {
1426 let row = vec![];
1427 let col_map = HashMap::new();
1428
1429 let expr = Expr::binary(
1431 Expr::binary(
1432 Expr::literal(Value::Integer(1)),
1433 BinaryOp::Add,
1434 Expr::literal(Value::Integer(2)),
1435 ),
1436 BinaryOp::Mul,
1437 Expr::literal(Value::Integer(3)),
1438 );
1439 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Integer(9));
1440
1441 let expr = Expr::binary(
1443 Expr::literal(Value::Integer(10)),
1444 BinaryOp::Sub,
1445 Expr::binary(
1446 Expr::literal(Value::Integer(3)),
1447 BinaryOp::Add,
1448 Expr::literal(Value::Integer(2)),
1449 ),
1450 );
1451 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Integer(5));
1452 }
1453
1454 #[test]
1455 fn test_eval_function_upper() {
1456 let row = vec![];
1457 let col_map = HashMap::new();
1458
1459 let expr = Expr::Function {
1460 name: "UPPER".to_string(),
1461 args: vec![Expr::literal(Value::Text("hello".into()))],
1462 };
1463 assert_eq!(
1464 expr.eval(&row, &col_map).unwrap(),
1465 Value::Text("HELLO".into())
1466 );
1467 }
1468
1469 #[test]
1470 fn test_eval_function_lower() {
1471 let row = vec![];
1472 let col_map = HashMap::new();
1473
1474 let expr = Expr::Function {
1475 name: "LOWER".to_string(),
1476 args: vec![Expr::literal(Value::Text("HELLO".into()))],
1477 };
1478 assert_eq!(
1479 expr.eval(&row, &col_map).unwrap(),
1480 Value::Text("hello".into())
1481 );
1482 }
1483
1484 #[test]
1485 fn test_eval_function_length() {
1486 let row = vec![];
1487 let col_map = HashMap::new();
1488
1489 let expr = Expr::Function {
1490 name: "LENGTH".to_string(),
1491 args: vec![Expr::literal(Value::Text("hello".into()))],
1492 };
1493 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Integer(5));
1494 }
1495
1496 #[test]
1497 fn test_eval_function_coalesce() {
1498 let row = vec![];
1499 let col_map = HashMap::new();
1500
1501 let expr = Expr::Function {
1503 name: "COALESCE".to_string(),
1504 args: vec![
1505 Expr::literal(Value::Null),
1506 Expr::literal(Value::Integer(5)),
1507 Expr::literal(Value::Integer(10)),
1508 ],
1509 };
1510 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Integer(5));
1511
1512 let expr = Expr::Function {
1514 name: "COALESCE".to_string(),
1515 args: vec![Expr::literal(Value::Null), Expr::literal(Value::Null)],
1516 };
1517 assert_eq!(expr.eval(&row, &col_map).unwrap(), Value::Null);
1518 }
1519
1520 #[test]
1521 fn test_eval_function_with_column() {
1522 let row = vec![Value::Text("world".into())];
1523 let mut col_map = HashMap::new();
1524 col_map.insert("greeting".to_string(), 0);
1525
1526 let expr = Expr::Function {
1528 name: "UPPER".to_string(),
1529 args: vec![Expr::column("greeting")],
1530 };
1531 assert_eq!(
1532 expr.eval(&row, &col_map).unwrap(),
1533 Value::Text("WORLD".into())
1534 );
1535 }
1536}