1use anyhow::{anyhow, Result};
2use std::sync::Arc;
3use std::time::Instant;
4use tracing::{debug, info};
5
6use crate::config::config::BehaviorConfig;
7use crate::data::arithmetic_evaluator::ArithmeticEvaluator;
8use crate::data::data_view::DataView;
9use crate::data::datatable::{DataColumn, DataRow, DataTable, DataValue};
10use crate::data::recursive_where_evaluator::RecursiveWhereEvaluator;
11use crate::sql::aggregates::contains_aggregate;
12use crate::sql::recursive_parser::{
13 OrderByColumn, Parser, SelectItem, SelectStatement, SortDirection, SqlExpression,
14};
15
16pub struct QueryEngine {
18 case_insensitive: bool,
19 date_notation: String,
20 behavior_config: Option<BehaviorConfig>,
21}
22
23impl Default for QueryEngine {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29impl QueryEngine {
30 #[must_use]
31 pub fn new() -> Self {
32 Self {
33 case_insensitive: false,
34 date_notation: "us".to_string(),
35 behavior_config: None,
36 }
37 }
38
39 #[must_use]
40 pub fn with_behavior_config(config: BehaviorConfig) -> Self {
41 let case_insensitive = config.case_insensitive_default;
42 let date_notation = config.default_date_notation.clone();
43 Self {
44 case_insensitive,
45 date_notation,
46 behavior_config: Some(config),
47 }
48 }
49
50 #[must_use]
51 pub fn with_date_notation(date_notation: String) -> Self {
52 Self {
53 case_insensitive: false,
54 date_notation,
55 behavior_config: None,
56 }
57 }
58
59 #[must_use]
60 pub fn with_case_insensitive(case_insensitive: bool) -> Self {
61 Self {
62 case_insensitive,
63 date_notation: "us".to_string(),
64 behavior_config: None,
65 }
66 }
67
68 #[must_use]
69 pub fn with_case_insensitive_and_date_notation(
70 case_insensitive: bool,
71 date_notation: String,
72 ) -> Self {
73 Self {
74 case_insensitive,
75 date_notation,
76 behavior_config: None,
77 }
78 }
79
80 fn find_similar_column(&self, table: &DataTable, name: &str) -> Option<String> {
82 let columns = table.column_names();
83 let mut best_match: Option<(String, usize)> = None;
84
85 for col in columns {
86 let distance = self.edit_distance(&col.to_lowercase(), &name.to_lowercase());
87 let max_distance = if name.len() > 10 { 3 } else { 2 };
90 if distance <= max_distance {
91 match &best_match {
92 None => best_match = Some((col, distance)),
93 Some((_, best_dist)) if distance < *best_dist => {
94 best_match = Some((col, distance));
95 }
96 _ => {}
97 }
98 }
99 }
100
101 best_match.map(|(name, _)| name)
102 }
103
104 fn edit_distance(&self, s1: &str, s2: &str) -> usize {
106 let len1 = s1.len();
107 let len2 = s2.len();
108 let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
109
110 for i in 0..=len1 {
111 matrix[i][0] = i;
112 }
113 for j in 0..=len2 {
114 matrix[0][j] = j;
115 }
116
117 for (i, c1) in s1.chars().enumerate() {
118 for (j, c2) in s2.chars().enumerate() {
119 let cost = usize::from(c1 != c2);
120 matrix[i + 1][j + 1] = std::cmp::min(
121 matrix[i][j + 1] + 1, std::cmp::min(
123 matrix[i + 1][j] + 1, matrix[i][j] + cost, ),
126 );
127 }
128 }
129
130 matrix[len1][len2]
131 }
132
133 pub fn execute(&self, table: Arc<DataTable>, sql: &str) -> Result<DataView> {
135 let start_time = Instant::now();
136
137 let parse_start = Instant::now();
139 let mut parser = Parser::new(sql);
140 let statement = parser
141 .parse()
142 .map_err(|e| anyhow::anyhow!("Parse error: {}", e))?;
143 let parse_duration = parse_start.elapsed();
144
145 let build_start = Instant::now();
147 let result = self.build_view(table, statement)?;
148 let build_duration = build_start.elapsed();
149
150 let total_duration = start_time.elapsed();
151 info!(
152 "Query execution complete: parse={:?}, build={:?}, total={:?}, rows={}",
153 parse_duration,
154 build_duration,
155 total_duration,
156 result.row_count()
157 );
158
159 Ok(result)
160 }
161
162 fn build_view(&self, table: Arc<DataTable>, statement: SelectStatement) -> Result<DataView> {
164 debug!(
165 "QueryEngine::build_view - select_items: {:?}",
166 statement.select_items
167 );
168 debug!(
169 "QueryEngine::build_view - where_clause: {:?}",
170 statement.where_clause
171 );
172
173 let mut visible_rows: Vec<usize> = (0..table.row_count()).collect();
175
176 if let Some(where_clause) = &statement.where_clause {
178 let total_rows = table.row_count();
179 debug!("QueryEngine: Applying WHERE clause to {} rows", total_rows);
180 debug!("QueryEngine: WHERE clause = {:?}", where_clause);
181
182 let filter_start = Instant::now();
183 let mut filtered_rows = Vec::new();
185 for row_idx in visible_rows {
186 if row_idx < 3 {
188 debug!("QueryEngine: Evaluating WHERE clause for row {}", row_idx);
189 }
190 let evaluator = RecursiveWhereEvaluator::with_config(
191 &table,
192 self.case_insensitive,
193 self.date_notation.clone(),
194 );
195 match evaluator.evaluate(where_clause, row_idx) {
196 Ok(result) => {
197 if row_idx < 3 {
198 debug!("QueryEngine: Row {} WHERE result: {}", row_idx, result);
199 }
200 if result {
201 filtered_rows.push(row_idx);
202 }
203 }
204 Err(e) => {
205 if row_idx < 3 {
206 debug!(
207 "QueryEngine: WHERE evaluation error for row {}: {}",
208 row_idx, e
209 );
210 }
211 return Err(e);
213 }
214 }
215 }
216 visible_rows = filtered_rows;
217 let filter_duration = filter_start.elapsed();
218 info!(
219 "WHERE clause filtering: {} rows -> {} rows in {:?}",
220 total_rows,
221 visible_rows.len(),
222 filter_duration
223 );
224
225 }
227
228 let mut view = DataView::new(table.clone());
230 view = view.with_rows(visible_rows);
231
232 if let Some(group_by_columns) = &statement.group_by {
234 if !group_by_columns.is_empty() {
235 debug!("QueryEngine: Processing GROUP BY: {:?}", group_by_columns);
236 view = self.apply_group_by(
237 view,
238 group_by_columns,
239 &statement.select_items,
240 statement.having.as_ref(),
241 )?;
242 }
243 } else {
244 if !statement.select_items.is_empty() {
246 let has_non_star_items = statement
248 .select_items
249 .iter()
250 .any(|item| !matches!(item, SelectItem::Star));
251
252 if has_non_star_items || statement.select_items.len() > 1 {
256 view = self.apply_select_items(view, &statement.select_items)?;
257 }
258 } else if !statement.columns.is_empty() && statement.columns[0] != "*" {
260 let column_indices = self.resolve_column_indices(&table, &statement.columns)?;
262 view = view.with_columns(column_indices);
263 }
264 }
265
266 if let Some(order_by_columns) = &statement.order_by {
268 if !order_by_columns.is_empty() {
269 view = self.apply_multi_order_by(view, order_by_columns)?;
270 }
271 }
272
273 if let Some(limit) = statement.limit {
275 let offset = statement.offset.unwrap_or(0);
276 view = view.with_limit(limit, offset);
277 }
278
279 Ok(view)
280 }
281
282 fn resolve_column_indices(&self, table: &DataTable, columns: &[String]) -> Result<Vec<usize>> {
284 let mut indices = Vec::new();
285 let table_columns = table.column_names();
286
287 for col_name in columns {
288 let index = table_columns
289 .iter()
290 .position(|c| c.eq_ignore_ascii_case(col_name))
291 .ok_or_else(|| {
292 let suggestion = self.find_similar_column(table, col_name);
293 match suggestion {
294 Some(similar) => anyhow::anyhow!(
295 "Column '{}' not found. Did you mean '{}'?",
296 col_name,
297 similar
298 ),
299 None => anyhow::anyhow!("Column '{}' not found", col_name),
300 }
301 })?;
302 indices.push(index);
303 }
304
305 Ok(indices)
306 }
307
308 fn apply_select_items(&self, view: DataView, select_items: &[SelectItem]) -> Result<DataView> {
310 debug!(
311 "QueryEngine::apply_select_items - items: {:?}",
312 select_items
313 );
314 debug!(
315 "QueryEngine::apply_select_items - input view has {} rows",
316 view.row_count()
317 );
318
319 let all_aggregates = select_items.iter().all(|item| match item {
321 SelectItem::Expression { expr, .. } => contains_aggregate(expr),
322 SelectItem::Column(_) => false,
323 SelectItem::Star => false,
324 });
325
326 if all_aggregates && view.row_count() > 0 {
327 return self.apply_aggregate_select(view, select_items);
330 }
331
332 let has_computed_expressions = select_items
334 .iter()
335 .any(|item| matches!(item, SelectItem::Expression { .. }));
336
337 debug!(
338 "QueryEngine::apply_select_items - has_computed_expressions: {}",
339 has_computed_expressions
340 );
341
342 if !has_computed_expressions {
343 let column_indices = self.resolve_select_columns(view.source(), select_items)?;
345 return Ok(view.with_columns(column_indices));
346 }
347
348 let source_table = view.source();
353 let visible_rows = view.visible_row_indices();
354
355 let mut computed_table = DataTable::new("query_result");
358
359 let mut expanded_items = Vec::new();
361 for item in select_items {
362 match item {
363 SelectItem::Star => {
364 for col_name in source_table.column_names() {
366 expanded_items.push(SelectItem::Column(col_name));
367 }
368 }
369 _ => expanded_items.push(item.clone()),
370 }
371 }
372
373 let mut column_name_counts: std::collections::HashMap<String, usize> =
375 std::collections::HashMap::new();
376
377 for item in &expanded_items {
378 let base_name = match item {
379 SelectItem::Column(name) => name.clone(),
380 SelectItem::Expression { alias, .. } => alias.clone(),
381 SelectItem::Star => unreachable!("Star should have been expanded"),
382 };
383
384 let count = column_name_counts.entry(base_name.clone()).or_insert(0);
386 let column_name = if *count == 0 {
387 base_name.clone()
389 } else {
390 format!("{base_name}_{count}")
392 };
393 *count += 1;
394
395 computed_table.add_column(DataColumn::new(&column_name));
396 }
397
398 let mut evaluator =
400 ArithmeticEvaluator::with_date_notation(source_table, self.date_notation.clone());
401
402 for &row_idx in visible_rows {
403 let mut row_values = Vec::new();
404
405 for item in &expanded_items {
406 let value = match item {
407 SelectItem::Column(col_name) => {
408 let col_idx = source_table.get_column_index(col_name).ok_or_else(|| {
410 let suggestion = self.find_similar_column(source_table, col_name);
411 match suggestion {
412 Some(similar) => anyhow::anyhow!(
413 "Column '{}' not found. Did you mean '{}'?",
414 col_name,
415 similar
416 ),
417 None => anyhow::anyhow!("Column '{}' not found", col_name),
418 }
419 })?;
420 let row = source_table
421 .get_row(row_idx)
422 .ok_or_else(|| anyhow::anyhow!("Row {} not found", row_idx))?;
423 row.get(col_idx)
424 .ok_or_else(|| anyhow::anyhow!("Column {} not found in row", col_idx))?
425 .clone()
426 }
427 SelectItem::Expression { expr, .. } => {
428 evaluator.evaluate(expr, row_idx)?
430 }
431 SelectItem::Star => unreachable!("Star should have been expanded"),
432 };
433 row_values.push(value);
434 }
435
436 computed_table
437 .add_row(DataRow::new(row_values))
438 .map_err(|e| anyhow::anyhow!("Failed to add row: {}", e))?;
439 }
440
441 Ok(DataView::new(Arc::new(computed_table)))
444 }
445
446 fn apply_aggregate_select(
448 &self,
449 view: DataView,
450 select_items: &[SelectItem],
451 ) -> Result<DataView> {
452 debug!("QueryEngine::apply_aggregate_select - creating single row aggregate result");
453
454 let source_table = view.source();
455 let mut result_table = DataTable::new("aggregate_result");
456
457 for item in select_items {
459 let column_name = match item {
460 SelectItem::Expression { alias, .. } => alias.clone(),
461 _ => unreachable!("Should only have expressions in aggregate-only query"),
462 };
463 result_table.add_column(DataColumn::new(&column_name));
464 }
465
466 let visible_rows = view.visible_row_indices().to_vec();
468 let mut evaluator =
469 ArithmeticEvaluator::with_date_notation(source_table, self.date_notation.clone())
470 .with_visible_rows(visible_rows);
471
472 let mut row_values = Vec::new();
474 for item in select_items {
475 match item {
476 SelectItem::Expression { expr, .. } => {
477 let value = evaluator.evaluate(expr, 0)?;
480 row_values.push(value);
481 }
482 _ => unreachable!("Should only have expressions in aggregate-only query"),
483 }
484 }
485
486 result_table
488 .add_row(DataRow::new(row_values))
489 .map_err(|e| anyhow::anyhow!("Failed to add aggregate result row: {}", e))?;
490
491 Ok(DataView::new(Arc::new(result_table)))
492 }
493
494 fn resolve_select_columns(
496 &self,
497 table: &DataTable,
498 select_items: &[SelectItem],
499 ) -> Result<Vec<usize>> {
500 let mut indices = Vec::new();
501 let table_columns = table.column_names();
502
503 for item in select_items {
504 match item {
505 SelectItem::Column(col_name) => {
506 let index = table_columns
507 .iter()
508 .position(|c| c.eq_ignore_ascii_case(col_name))
509 .ok_or_else(|| {
510 let suggestion = self.find_similar_column(table, col_name);
511 match suggestion {
512 Some(similar) => anyhow::anyhow!(
513 "Column '{}' not found. Did you mean '{}'?",
514 col_name,
515 similar
516 ),
517 None => anyhow::anyhow!("Column '{}' not found", col_name),
518 }
519 })?;
520 indices.push(index);
521 }
522 SelectItem::Star => {
523 for i in 0..table_columns.len() {
525 indices.push(i);
526 }
527 }
528 SelectItem::Expression { .. } => {
529 return Err(anyhow::anyhow!(
530 "Computed expressions require new table creation"
531 ));
532 }
533 }
534 }
535
536 Ok(indices)
537 }
538
539 fn apply_multi_order_by(
541 &self,
542 mut view: DataView,
543 order_by_columns: &[OrderByColumn],
544 ) -> Result<DataView> {
545 let mut sort_columns = Vec::new();
547
548 for order_col in order_by_columns {
549 let col_index = view
551 .source()
552 .get_column_index(&order_col.column)
553 .ok_or_else(|| {
554 let suggestion = self.find_similar_column(view.source(), &order_col.column);
555 match suggestion {
556 Some(similar) => anyhow::anyhow!(
557 "Column '{}' not found. Did you mean '{}'?",
558 order_col.column,
559 similar
560 ),
561 None => anyhow::anyhow!("Column '{}' not found", order_col.column),
562 }
563 })?;
564
565 let ascending = matches!(order_col.direction, SortDirection::Asc);
566 sort_columns.push((col_index, ascending));
567 }
568
569 view.apply_multi_sort(&sort_columns)?;
571 Ok(view)
572 }
573
574 fn apply_group_by(
576 &self,
577 view: DataView,
578 group_by_columns: &[String],
579 select_items: &[SelectItem],
580 having: Option<&SqlExpression>,
581 ) -> Result<DataView> {
582 debug!(
583 "QueryEngine::apply_group_by - grouping by: {:?}",
584 group_by_columns
585 );
586
587 let groups = view.group_by(group_by_columns)?;
589 debug!(
590 "QueryEngine::apply_group_by - created {} groups",
591 groups.len()
592 );
593
594 let mut result_table = DataTable::new("grouped_result");
596
597 for col_name in group_by_columns {
603 result_table.add_column(DataColumn::new(col_name));
604 }
605
606 let mut aggregate_columns = Vec::new();
608 for item in select_items {
609 match item {
610 SelectItem::Expression { expr, alias } => {
611 if contains_aggregate(expr) {
612 result_table.add_column(DataColumn::new(alias));
614 aggregate_columns.push((expr.clone(), alias.clone()));
615 }
616 }
617 SelectItem::Column(col_name) => {
618 if !group_by_columns.contains(col_name) {
620 return Err(anyhow!(
621 "Column '{}' must appear in GROUP BY clause or be used in an aggregate function",
622 col_name
623 ));
624 }
625 }
626 SelectItem::Star => {
627 }
630 }
631 }
632
633 for (group_key, group_view) in groups {
635 let mut row_values = Vec::new();
636 let mut aggregate_values = std::collections::HashMap::new();
637
638 for (i, value) in group_key.0.iter().enumerate() {
640 row_values.push(value.clone());
641 if i < group_by_columns.len() {
643 aggregate_values.insert(group_by_columns[i].clone(), value.clone());
644 }
645 }
646
647 for (expr, col_name) in &aggregate_columns {
649 let group_rows = group_view.get_visible_rows();
651 let mut evaluator = ArithmeticEvaluator::new(group_view.source())
652 .with_visible_rows(group_rows.clone());
653
654 let value = if group_view.row_count() > 0 && !group_rows.is_empty() {
656 evaluator
659 .evaluate(expr, group_rows[0])
660 .unwrap_or(DataValue::Null)
661 } else {
662 DataValue::Null
663 };
664
665 aggregate_values.insert(col_name.clone(), value.clone());
667 row_values.push(value);
668 }
669
670 if let Some(having_expr) = having {
672 let mut temp_table = DataTable::new("having_eval");
674 for col_name in aggregate_values.keys() {
675 temp_table.add_column(DataColumn::new(col_name));
676 }
677
678 let temp_row_values: Vec<DataValue> = aggregate_values.values().cloned().collect();
679 temp_table
680 .add_row(DataRow::new(temp_row_values))
681 .map_err(|e| anyhow!("Failed to create temp table for HAVING: {}", e))?;
682
683 let mut evaluator = ArithmeticEvaluator::new(&temp_table);
685 let having_result = evaluator
686 .evaluate(having_expr, 0)
687 .unwrap_or(DataValue::Boolean(false));
688
689 match having_result {
691 DataValue::Boolean(false) => continue,
692 DataValue::Null => continue,
693 _ => {} }
695 }
696
697 result_table
699 .add_row(DataRow::new(row_values))
700 .map_err(|e| anyhow!("Failed to add row to result table: {}", e))?;
701 }
702
703 Ok(DataView::new(Arc::new(result_table)))
705 }
706}
707
708#[cfg(test)]
709mod tests {
710 use super::*;
711 use crate::data::datatable::{DataColumn, DataRow, DataValue};
712
713 fn create_test_table() -> Arc<DataTable> {
714 let mut table = DataTable::new("test");
715
716 table.add_column(DataColumn::new("id"));
718 table.add_column(DataColumn::new("name"));
719 table.add_column(DataColumn::new("age"));
720
721 table
723 .add_row(DataRow::new(vec![
724 DataValue::Integer(1),
725 DataValue::String("Alice".to_string()),
726 DataValue::Integer(30),
727 ]))
728 .unwrap();
729
730 table
731 .add_row(DataRow::new(vec![
732 DataValue::Integer(2),
733 DataValue::String("Bob".to_string()),
734 DataValue::Integer(25),
735 ]))
736 .unwrap();
737
738 table
739 .add_row(DataRow::new(vec![
740 DataValue::Integer(3),
741 DataValue::String("Charlie".to_string()),
742 DataValue::Integer(35),
743 ]))
744 .unwrap();
745
746 Arc::new(table)
747 }
748
749 #[test]
750 fn test_select_all() {
751 let table = create_test_table();
752 let engine = QueryEngine::new();
753
754 let view = engine
755 .execute(table.clone(), "SELECT * FROM users")
756 .unwrap();
757 assert_eq!(view.row_count(), 3);
758 assert_eq!(view.column_count(), 3);
759 }
760
761 #[test]
762 fn test_select_columns() {
763 let table = create_test_table();
764 let engine = QueryEngine::new();
765
766 let view = engine
767 .execute(table.clone(), "SELECT name, age FROM users")
768 .unwrap();
769 assert_eq!(view.row_count(), 3);
770 assert_eq!(view.column_count(), 2);
771 }
772
773 #[test]
774 fn test_select_with_limit() {
775 let table = create_test_table();
776 let engine = QueryEngine::new();
777
778 let view = engine
779 .execute(table.clone(), "SELECT * FROM users LIMIT 2")
780 .unwrap();
781 assert_eq!(view.row_count(), 2);
782 }
783
784 #[test]
785 fn test_type_coercion_contains() {
786 let _ = tracing_subscriber::fmt()
788 .with_max_level(tracing::Level::DEBUG)
789 .try_init();
790
791 let mut table = DataTable::new("test");
792 table.add_column(DataColumn::new("id"));
793 table.add_column(DataColumn::new("status"));
794 table.add_column(DataColumn::new("price"));
795
796 table
798 .add_row(DataRow::new(vec![
799 DataValue::Integer(1),
800 DataValue::String("Pending".to_string()),
801 DataValue::Float(99.99),
802 ]))
803 .unwrap();
804
805 table
806 .add_row(DataRow::new(vec![
807 DataValue::Integer(2),
808 DataValue::String("Confirmed".to_string()),
809 DataValue::Float(150.50),
810 ]))
811 .unwrap();
812
813 table
814 .add_row(DataRow::new(vec![
815 DataValue::Integer(3),
816 DataValue::String("Pending".to_string()),
817 DataValue::Float(75.00),
818 ]))
819 .unwrap();
820
821 let table = Arc::new(table);
822 let engine = QueryEngine::new();
823
824 println!("\n=== Testing WHERE clause with Contains ===");
825 println!("Table has {} rows", table.row_count());
826 for i in 0..table.row_count() {
827 let status = table.get_value(i, 1);
828 println!("Row {i}: status = {status:?}");
829 }
830
831 println!("\n--- Test 1: status.Contains('pend') ---");
833 let result = engine.execute(
834 table.clone(),
835 "SELECT * FROM test WHERE status.Contains('pend')",
836 );
837 match result {
838 Ok(view) => {
839 println!("SUCCESS: Found {} matching rows", view.row_count());
840 assert_eq!(view.row_count(), 2); }
842 Err(e) => {
843 panic!("Query failed: {e}");
844 }
845 }
846
847 println!("\n--- Test 2: price.Contains('9') ---");
849 let result = engine.execute(
850 table.clone(),
851 "SELECT * FROM test WHERE price.Contains('9')",
852 );
853 match result {
854 Ok(view) => {
855 println!(
856 "SUCCESS: Found {} matching rows with price containing '9'",
857 view.row_count()
858 );
859 assert!(view.row_count() >= 1);
861 }
862 Err(e) => {
863 panic!("Numeric coercion query failed: {e}");
864 }
865 }
866
867 println!("\n=== All tests passed! ===");
868 }
869
870 #[test]
871 fn test_not_in_clause() {
872 let _ = tracing_subscriber::fmt()
874 .with_max_level(tracing::Level::DEBUG)
875 .try_init();
876
877 let mut table = DataTable::new("test");
878 table.add_column(DataColumn::new("id"));
879 table.add_column(DataColumn::new("country"));
880
881 table
883 .add_row(DataRow::new(vec![
884 DataValue::Integer(1),
885 DataValue::String("CA".to_string()),
886 ]))
887 .unwrap();
888
889 table
890 .add_row(DataRow::new(vec![
891 DataValue::Integer(2),
892 DataValue::String("US".to_string()),
893 ]))
894 .unwrap();
895
896 table
897 .add_row(DataRow::new(vec![
898 DataValue::Integer(3),
899 DataValue::String("UK".to_string()),
900 ]))
901 .unwrap();
902
903 let table = Arc::new(table);
904 let engine = QueryEngine::new();
905
906 println!("\n=== Testing NOT IN clause ===");
907 println!("Table has {} rows", table.row_count());
908 for i in 0..table.row_count() {
909 let country = table.get_value(i, 1);
910 println!("Row {i}: country = {country:?}");
911 }
912
913 println!("\n--- Test: country NOT IN ('CA') ---");
915 let result = engine.execute(
916 table.clone(),
917 "SELECT * FROM test WHERE country NOT IN ('CA')",
918 );
919 match result {
920 Ok(view) => {
921 println!("SUCCESS: Found {} rows not in ('CA')", view.row_count());
922 assert_eq!(view.row_count(), 2); }
924 Err(e) => {
925 panic!("NOT IN query failed: {e}");
926 }
927 }
928
929 println!("\n=== NOT IN test complete! ===");
930 }
931
932 #[test]
933 fn test_case_insensitive_in_and_not_in() {
934 let _ = tracing_subscriber::fmt()
936 .with_max_level(tracing::Level::DEBUG)
937 .try_init();
938
939 let mut table = DataTable::new("test");
940 table.add_column(DataColumn::new("id"));
941 table.add_column(DataColumn::new("country"));
942
943 table
945 .add_row(DataRow::new(vec![
946 DataValue::Integer(1),
947 DataValue::String("CA".to_string()), ]))
949 .unwrap();
950
951 table
952 .add_row(DataRow::new(vec![
953 DataValue::Integer(2),
954 DataValue::String("us".to_string()), ]))
956 .unwrap();
957
958 table
959 .add_row(DataRow::new(vec![
960 DataValue::Integer(3),
961 DataValue::String("UK".to_string()), ]))
963 .unwrap();
964
965 let table = Arc::new(table);
966
967 println!("\n=== Testing Case-Insensitive IN clause ===");
968 println!("Table has {} rows", table.row_count());
969 for i in 0..table.row_count() {
970 let country = table.get_value(i, 1);
971 println!("Row {i}: country = {country:?}");
972 }
973
974 println!("\n--- Test: country IN ('ca') with case_insensitive=true ---");
976 let engine = QueryEngine::with_case_insensitive(true);
977 let result = engine.execute(table.clone(), "SELECT * FROM test WHERE country IN ('ca')");
978 match result {
979 Ok(view) => {
980 println!(
981 "SUCCESS: Found {} rows matching 'ca' (case-insensitive)",
982 view.row_count()
983 );
984 assert_eq!(view.row_count(), 1); }
986 Err(e) => {
987 panic!("Case-insensitive IN query failed: {e}");
988 }
989 }
990
991 println!("\n--- Test: country NOT IN ('ca') with case_insensitive=true ---");
993 let result = engine.execute(
994 table.clone(),
995 "SELECT * FROM test WHERE country NOT IN ('ca')",
996 );
997 match result {
998 Ok(view) => {
999 println!(
1000 "SUCCESS: Found {} rows not matching 'ca' (case-insensitive)",
1001 view.row_count()
1002 );
1003 assert_eq!(view.row_count(), 2); }
1005 Err(e) => {
1006 panic!("Case-insensitive NOT IN query failed: {e}");
1007 }
1008 }
1009
1010 println!("\n--- Test: country IN ('ca') with case_insensitive=false ---");
1012 let engine_case_sensitive = QueryEngine::new(); let result = engine_case_sensitive
1014 .execute(table.clone(), "SELECT * FROM test WHERE country IN ('ca')");
1015 match result {
1016 Ok(view) => {
1017 println!(
1018 "SUCCESS: Found {} rows matching 'ca' (case-sensitive)",
1019 view.row_count()
1020 );
1021 assert_eq!(view.row_count(), 0); }
1023 Err(e) => {
1024 panic!("Case-sensitive IN query failed: {e}");
1025 }
1026 }
1027
1028 println!("\n=== Case-insensitive IN/NOT IN test complete! ===");
1029 }
1030
1031 #[test]
1032 #[ignore = "Parentheses in WHERE clause not yet implemented"]
1033 fn test_parentheses_in_where_clause() {
1034 let _ = tracing_subscriber::fmt()
1036 .with_max_level(tracing::Level::DEBUG)
1037 .try_init();
1038
1039 let mut table = DataTable::new("test");
1040 table.add_column(DataColumn::new("id"));
1041 table.add_column(DataColumn::new("status"));
1042 table.add_column(DataColumn::new("priority"));
1043
1044 table
1046 .add_row(DataRow::new(vec![
1047 DataValue::Integer(1),
1048 DataValue::String("Pending".to_string()),
1049 DataValue::String("High".to_string()),
1050 ]))
1051 .unwrap();
1052
1053 table
1054 .add_row(DataRow::new(vec![
1055 DataValue::Integer(2),
1056 DataValue::String("Complete".to_string()),
1057 DataValue::String("High".to_string()),
1058 ]))
1059 .unwrap();
1060
1061 table
1062 .add_row(DataRow::new(vec![
1063 DataValue::Integer(3),
1064 DataValue::String("Pending".to_string()),
1065 DataValue::String("Low".to_string()),
1066 ]))
1067 .unwrap();
1068
1069 table
1070 .add_row(DataRow::new(vec![
1071 DataValue::Integer(4),
1072 DataValue::String("Complete".to_string()),
1073 DataValue::String("Low".to_string()),
1074 ]))
1075 .unwrap();
1076
1077 let table = Arc::new(table);
1078 let engine = QueryEngine::new();
1079
1080 println!("\n=== Testing Parentheses in WHERE clause ===");
1081 println!("Table has {} rows", table.row_count());
1082 for i in 0..table.row_count() {
1083 let status = table.get_value(i, 1);
1084 let priority = table.get_value(i, 2);
1085 println!("Row {i}: status = {status:?}, priority = {priority:?}");
1086 }
1087
1088 println!("\n--- Test: (status = 'Pending' AND priority = 'High') OR (status = 'Complete' AND priority = 'Low') ---");
1090 let result = engine.execute(
1091 table.clone(),
1092 "SELECT * FROM test WHERE (status = 'Pending' AND priority = 'High') OR (status = 'Complete' AND priority = 'Low')",
1093 );
1094 match result {
1095 Ok(view) => {
1096 println!(
1097 "SUCCESS: Found {} rows with parenthetical logic",
1098 view.row_count()
1099 );
1100 assert_eq!(view.row_count(), 2); }
1102 Err(e) => {
1103 panic!("Parentheses query failed: {e}");
1104 }
1105 }
1106
1107 println!("\n=== Parentheses test complete! ===");
1108 }
1109
1110 #[test]
1111 #[ignore = "Numeric type coercion needs fixing"]
1112 fn test_numeric_type_coercion() {
1113 let _ = tracing_subscriber::fmt()
1115 .with_max_level(tracing::Level::DEBUG)
1116 .try_init();
1117
1118 let mut table = DataTable::new("test");
1119 table.add_column(DataColumn::new("id"));
1120 table.add_column(DataColumn::new("price"));
1121 table.add_column(DataColumn::new("quantity"));
1122
1123 table
1125 .add_row(DataRow::new(vec![
1126 DataValue::Integer(1),
1127 DataValue::Float(99.50), DataValue::Integer(100),
1129 ]))
1130 .unwrap();
1131
1132 table
1133 .add_row(DataRow::new(vec![
1134 DataValue::Integer(2),
1135 DataValue::Float(150.0), DataValue::Integer(200),
1137 ]))
1138 .unwrap();
1139
1140 table
1141 .add_row(DataRow::new(vec![
1142 DataValue::Integer(3),
1143 DataValue::Integer(75), DataValue::Integer(50),
1145 ]))
1146 .unwrap();
1147
1148 let table = Arc::new(table);
1149 let engine = QueryEngine::new();
1150
1151 println!("\n=== Testing Numeric Type Coercion ===");
1152 println!("Table has {} rows", table.row_count());
1153 for i in 0..table.row_count() {
1154 let price = table.get_value(i, 1);
1155 let quantity = table.get_value(i, 2);
1156 println!("Row {i}: price = {price:?}, quantity = {quantity:?}");
1157 }
1158
1159 println!("\n--- Test: price.Contains('.') ---");
1161 let result = engine.execute(
1162 table.clone(),
1163 "SELECT * FROM test WHERE price.Contains('.')",
1164 );
1165 match result {
1166 Ok(view) => {
1167 println!(
1168 "SUCCESS: Found {} rows with decimal points in price",
1169 view.row_count()
1170 );
1171 assert_eq!(view.row_count(), 2); }
1173 Err(e) => {
1174 panic!("Numeric Contains query failed: {e}");
1175 }
1176 }
1177
1178 println!("\n--- Test: quantity.Contains('0') ---");
1180 let result = engine.execute(
1181 table.clone(),
1182 "SELECT * FROM test WHERE quantity.Contains('0')",
1183 );
1184 match result {
1185 Ok(view) => {
1186 println!(
1187 "SUCCESS: Found {} rows with '0' in quantity",
1188 view.row_count()
1189 );
1190 assert_eq!(view.row_count(), 2); }
1192 Err(e) => {
1193 panic!("Integer Contains query failed: {e}");
1194 }
1195 }
1196
1197 println!("\n=== Numeric type coercion test complete! ===");
1198 }
1199
1200 #[test]
1201 fn test_datetime_comparisons() {
1202 let _ = tracing_subscriber::fmt()
1204 .with_max_level(tracing::Level::DEBUG)
1205 .try_init();
1206
1207 let mut table = DataTable::new("test");
1208 table.add_column(DataColumn::new("id"));
1209 table.add_column(DataColumn::new("created_date"));
1210
1211 table
1213 .add_row(DataRow::new(vec![
1214 DataValue::Integer(1),
1215 DataValue::String("2024-12-15".to_string()),
1216 ]))
1217 .unwrap();
1218
1219 table
1220 .add_row(DataRow::new(vec![
1221 DataValue::Integer(2),
1222 DataValue::String("2025-01-15".to_string()),
1223 ]))
1224 .unwrap();
1225
1226 table
1227 .add_row(DataRow::new(vec![
1228 DataValue::Integer(3),
1229 DataValue::String("2025-02-15".to_string()),
1230 ]))
1231 .unwrap();
1232
1233 let table = Arc::new(table);
1234 let engine = QueryEngine::new();
1235
1236 println!("\n=== Testing DateTime Comparisons ===");
1237 println!("Table has {} rows", table.row_count());
1238 for i in 0..table.row_count() {
1239 let date = table.get_value(i, 1);
1240 println!("Row {i}: created_date = {date:?}");
1241 }
1242
1243 println!("\n--- Test: created_date > DateTime(2025,1,1) ---");
1245 let result = engine.execute(
1246 table.clone(),
1247 "SELECT * FROM test WHERE created_date > DateTime(2025,1,1)",
1248 );
1249 match result {
1250 Ok(view) => {
1251 println!("SUCCESS: Found {} rows after 2025-01-01", view.row_count());
1252 assert_eq!(view.row_count(), 2); }
1254 Err(e) => {
1255 panic!("DateTime comparison query failed: {e}");
1256 }
1257 }
1258
1259 println!("\n=== DateTime comparison test complete! ===");
1260 }
1261
1262 #[test]
1263 fn test_not_with_method_calls() {
1264 let _ = tracing_subscriber::fmt()
1266 .with_max_level(tracing::Level::DEBUG)
1267 .try_init();
1268
1269 let mut table = DataTable::new("test");
1270 table.add_column(DataColumn::new("id"));
1271 table.add_column(DataColumn::new("status"));
1272
1273 table
1275 .add_row(DataRow::new(vec![
1276 DataValue::Integer(1),
1277 DataValue::String("Pending Review".to_string()),
1278 ]))
1279 .unwrap();
1280
1281 table
1282 .add_row(DataRow::new(vec![
1283 DataValue::Integer(2),
1284 DataValue::String("Complete".to_string()),
1285 ]))
1286 .unwrap();
1287
1288 table
1289 .add_row(DataRow::new(vec![
1290 DataValue::Integer(3),
1291 DataValue::String("Pending Approval".to_string()),
1292 ]))
1293 .unwrap();
1294
1295 let table = Arc::new(table);
1296 let engine = QueryEngine::with_case_insensitive(true);
1297
1298 println!("\n=== Testing NOT with Method Calls ===");
1299 println!("Table has {} rows", table.row_count());
1300 for i in 0..table.row_count() {
1301 let status = table.get_value(i, 1);
1302 println!("Row {i}: status = {status:?}");
1303 }
1304
1305 println!("\n--- Test: NOT status.Contains('pend') ---");
1307 let result = engine.execute(
1308 table.clone(),
1309 "SELECT * FROM test WHERE NOT status.Contains('pend')",
1310 );
1311 match result {
1312 Ok(view) => {
1313 println!(
1314 "SUCCESS: Found {} rows NOT containing 'pend'",
1315 view.row_count()
1316 );
1317 assert_eq!(view.row_count(), 1); }
1319 Err(e) => {
1320 panic!("NOT Contains query failed: {e}");
1321 }
1322 }
1323
1324 println!("\n--- Test: NOT status.StartsWith('Pending') ---");
1326 let result = engine.execute(
1327 table.clone(),
1328 "SELECT * FROM test WHERE NOT status.StartsWith('Pending')",
1329 );
1330 match result {
1331 Ok(view) => {
1332 println!(
1333 "SUCCESS: Found {} rows NOT starting with 'Pending'",
1334 view.row_count()
1335 );
1336 assert_eq!(view.row_count(), 1); }
1338 Err(e) => {
1339 panic!("NOT StartsWith query failed: {e}");
1340 }
1341 }
1342
1343 println!("\n=== NOT with method calls test complete! ===");
1344 }
1345
1346 #[test]
1347 #[ignore = "Complex logical expressions with parentheses not yet implemented"]
1348 fn test_complex_logical_expressions() {
1349 let _ = tracing_subscriber::fmt()
1351 .with_max_level(tracing::Level::DEBUG)
1352 .try_init();
1353
1354 let mut table = DataTable::new("test");
1355 table.add_column(DataColumn::new("id"));
1356 table.add_column(DataColumn::new("status"));
1357 table.add_column(DataColumn::new("priority"));
1358 table.add_column(DataColumn::new("assigned"));
1359
1360 table
1362 .add_row(DataRow::new(vec![
1363 DataValue::Integer(1),
1364 DataValue::String("Pending".to_string()),
1365 DataValue::String("High".to_string()),
1366 DataValue::String("John".to_string()),
1367 ]))
1368 .unwrap();
1369
1370 table
1371 .add_row(DataRow::new(vec![
1372 DataValue::Integer(2),
1373 DataValue::String("Complete".to_string()),
1374 DataValue::String("High".to_string()),
1375 DataValue::String("Jane".to_string()),
1376 ]))
1377 .unwrap();
1378
1379 table
1380 .add_row(DataRow::new(vec![
1381 DataValue::Integer(3),
1382 DataValue::String("Pending".to_string()),
1383 DataValue::String("Low".to_string()),
1384 DataValue::String("John".to_string()),
1385 ]))
1386 .unwrap();
1387
1388 table
1389 .add_row(DataRow::new(vec![
1390 DataValue::Integer(4),
1391 DataValue::String("In Progress".to_string()),
1392 DataValue::String("Medium".to_string()),
1393 DataValue::String("Jane".to_string()),
1394 ]))
1395 .unwrap();
1396
1397 let table = Arc::new(table);
1398 let engine = QueryEngine::new();
1399
1400 println!("\n=== Testing Complex Logical Expressions ===");
1401 println!("Table has {} rows", table.row_count());
1402 for i in 0..table.row_count() {
1403 let status = table.get_value(i, 1);
1404 let priority = table.get_value(i, 2);
1405 let assigned = table.get_value(i, 3);
1406 println!(
1407 "Row {i}: status = {status:?}, priority = {priority:?}, assigned = {assigned:?}"
1408 );
1409 }
1410
1411 println!("\n--- Test: status = 'Pending' AND (priority = 'High' OR assigned = 'John') ---");
1413 let result = engine.execute(
1414 table.clone(),
1415 "SELECT * FROM test WHERE status = 'Pending' AND (priority = 'High' OR assigned = 'John')",
1416 );
1417 match result {
1418 Ok(view) => {
1419 println!(
1420 "SUCCESS: Found {} rows with complex logic",
1421 view.row_count()
1422 );
1423 assert_eq!(view.row_count(), 2); }
1425 Err(e) => {
1426 panic!("Complex logic query failed: {e}");
1427 }
1428 }
1429
1430 println!("\n--- Test: NOT (status.Contains('Complete') OR priority = 'Low') ---");
1432 let result = engine.execute(
1433 table.clone(),
1434 "SELECT * FROM test WHERE NOT (status.Contains('Complete') OR priority = 'Low')",
1435 );
1436 match result {
1437 Ok(view) => {
1438 println!(
1439 "SUCCESS: Found {} rows with NOT complex logic",
1440 view.row_count()
1441 );
1442 assert_eq!(view.row_count(), 2); }
1444 Err(e) => {
1445 panic!("NOT complex logic query failed: {e}");
1446 }
1447 }
1448
1449 println!("\n=== Complex logical expressions test complete! ===");
1450 }
1451
1452 #[test]
1453 fn test_mixed_data_types_and_edge_cases() {
1454 let _ = tracing_subscriber::fmt()
1456 .with_max_level(tracing::Level::DEBUG)
1457 .try_init();
1458
1459 let mut table = DataTable::new("test");
1460 table.add_column(DataColumn::new("id"));
1461 table.add_column(DataColumn::new("value"));
1462 table.add_column(DataColumn::new("nullable_field"));
1463
1464 table
1466 .add_row(DataRow::new(vec![
1467 DataValue::Integer(1),
1468 DataValue::String("123.45".to_string()),
1469 DataValue::String("present".to_string()),
1470 ]))
1471 .unwrap();
1472
1473 table
1474 .add_row(DataRow::new(vec![
1475 DataValue::Integer(2),
1476 DataValue::Float(678.90),
1477 DataValue::Null,
1478 ]))
1479 .unwrap();
1480
1481 table
1482 .add_row(DataRow::new(vec![
1483 DataValue::Integer(3),
1484 DataValue::Boolean(true),
1485 DataValue::String("also present".to_string()),
1486 ]))
1487 .unwrap();
1488
1489 table
1490 .add_row(DataRow::new(vec![
1491 DataValue::Integer(4),
1492 DataValue::String("false".to_string()),
1493 DataValue::Null,
1494 ]))
1495 .unwrap();
1496
1497 let table = Arc::new(table);
1498 let engine = QueryEngine::new();
1499
1500 println!("\n=== Testing Mixed Data Types and Edge Cases ===");
1501 println!("Table has {} rows", table.row_count());
1502 for i in 0..table.row_count() {
1503 let value = table.get_value(i, 1);
1504 let nullable = table.get_value(i, 2);
1505 println!("Row {i}: value = {value:?}, nullable_field = {nullable:?}");
1506 }
1507
1508 println!("\n--- Test: value.Contains('true') (boolean to string coercion) ---");
1510 let result = engine.execute(
1511 table.clone(),
1512 "SELECT * FROM test WHERE value.Contains('true')",
1513 );
1514 match result {
1515 Ok(view) => {
1516 println!(
1517 "SUCCESS: Found {} rows with boolean coercion",
1518 view.row_count()
1519 );
1520 assert_eq!(view.row_count(), 1); }
1522 Err(e) => {
1523 panic!("Boolean coercion query failed: {e}");
1524 }
1525 }
1526
1527 println!("\n--- Test: id IN (1, 3) ---");
1529 let result = engine.execute(table.clone(), "SELECT * FROM test WHERE id IN (1, 3)");
1530 match result {
1531 Ok(view) => {
1532 println!("SUCCESS: Found {} rows with IN clause", view.row_count());
1533 assert_eq!(view.row_count(), 2); }
1535 Err(e) => {
1536 panic!("Multiple IN values query failed: {e}");
1537 }
1538 }
1539
1540 println!("\n=== Mixed data types test complete! ===");
1541 }
1542
1543 #[test]
1544 fn test_not_in_parsing() {
1545 use crate::sql::recursive_parser::Parser;
1546
1547 let query = "SELECT * FROM test WHERE country NOT IN ('CA')";
1548 println!("\n=== Testing NOT IN parsing ===");
1549 println!("Parsing query: {query}");
1550
1551 let mut parser = Parser::new(query);
1552 match parser.parse() {
1553 Ok(statement) => {
1554 println!("Parsed statement: {statement:#?}");
1555 if let Some(where_clause) = statement.where_clause {
1556 println!("WHERE conditions: {:#?}", where_clause.conditions);
1557 if let Some(first_condition) = where_clause.conditions.first() {
1558 println!("First condition expression: {:#?}", first_condition.expr);
1559 }
1560 }
1561 }
1562 Err(e) => {
1563 panic!("Parse error: {e}");
1564 }
1565 }
1566 }
1567}
1568
1569#[cfg(test)]
1570#[path = "query_engine_tests.rs"]
1571mod query_engine_tests;