1use anyhow::Result;
2use std::sync::Arc;
3use std::time::Instant;
4use tracing::{debug, info};
5
6use crate::data::arithmetic_evaluator::ArithmeticEvaluator;
7use crate::data::data_view::DataView;
8use crate::data::datatable::{DataColumn, DataRow, DataTable};
9use crate::data::recursive_where_evaluator::RecursiveWhereEvaluator;
10use crate::sql::recursive_parser::{
11 OrderByColumn, Parser, SelectItem, SelectStatement, SortDirection,
12};
13
14pub struct QueryEngine {
16 case_insensitive: bool,
17}
18
19impl QueryEngine {
20 pub fn new() -> Self {
21 Self {
22 case_insensitive: false,
23 }
24 }
25
26 fn find_similar_column(&self, table: &DataTable, name: &str) -> Option<String> {
28 let columns = table.column_names();
29 let mut best_match: Option<(String, usize)> = None;
30
31 for col in columns {
32 let distance = self.edit_distance(&col.to_lowercase(), &name.to_lowercase());
33 let max_distance = if name.len() > 10 { 3 } else { 2 };
36 if distance <= max_distance {
37 match &best_match {
38 None => best_match = Some((col, distance)),
39 Some((_, best_dist)) if distance < *best_dist => {
40 best_match = Some((col, distance));
41 }
42 _ => {}
43 }
44 }
45 }
46
47 best_match.map(|(name, _)| name)
48 }
49
50 fn edit_distance(&self, s1: &str, s2: &str) -> usize {
52 let len1 = s1.len();
53 let len2 = s2.len();
54 let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
55
56 for i in 0..=len1 {
57 matrix[i][0] = i;
58 }
59 for j in 0..=len2 {
60 matrix[0][j] = j;
61 }
62
63 for (i, c1) in s1.chars().enumerate() {
64 for (j, c2) in s2.chars().enumerate() {
65 let cost = if c1 == c2 { 0 } else { 1 };
66 matrix[i + 1][j + 1] = std::cmp::min(
67 matrix[i][j + 1] + 1, std::cmp::min(
69 matrix[i + 1][j] + 1, matrix[i][j] + cost, ),
72 );
73 }
74 }
75
76 matrix[len1][len2]
77 }
78
79 pub fn with_case_insensitive(case_insensitive: bool) -> Self {
80 Self { case_insensitive }
81 }
82
83 pub fn execute(&self, table: Arc<DataTable>, sql: &str) -> Result<DataView> {
85 let start_time = Instant::now();
86
87 let parse_start = Instant::now();
89 let mut parser = Parser::new(sql);
90 let statement = parser
91 .parse()
92 .map_err(|e| anyhow::anyhow!("Parse error: {}", e))?;
93 let parse_duration = parse_start.elapsed();
94
95 let build_start = Instant::now();
97 let result = self.build_view(table, statement)?;
98 let build_duration = build_start.elapsed();
99
100 let total_duration = start_time.elapsed();
101 info!(
102 "Query execution complete: parse={:?}, build={:?}, total={:?}, rows={}",
103 parse_duration,
104 build_duration,
105 total_duration,
106 result.row_count()
107 );
108
109 Ok(result)
110 }
111
112 fn build_view(&self, table: Arc<DataTable>, statement: SelectStatement) -> Result<DataView> {
114 let mut visible_rows: Vec<usize> = (0..table.row_count()).collect();
116
117 if let Some(where_clause) = &statement.where_clause {
119 let total_rows = table.row_count();
120 debug!("QueryEngine: Applying WHERE clause to {} rows", total_rows);
121 debug!("QueryEngine: WHERE clause = {:?}", where_clause);
122
123 let filter_start = Instant::now();
124 let mut filtered_rows = Vec::new();
126 for row_idx in visible_rows {
127 if row_idx < 3 {
129 debug!("QueryEngine: Evaluating WHERE clause for row {}", row_idx);
130 }
131 let evaluator =
132 RecursiveWhereEvaluator::with_case_insensitive(&*table, self.case_insensitive);
133 match evaluator.evaluate(where_clause, row_idx) {
134 Ok(result) => {
135 if row_idx < 3 {
136 debug!("QueryEngine: Row {} WHERE result: {}", row_idx, result);
137 }
138 if result {
139 filtered_rows.push(row_idx);
140 }
141 }
142 Err(e) => {
143 if row_idx < 3 {
144 debug!(
145 "QueryEngine: WHERE evaluation error for row {}: {}",
146 row_idx, e
147 );
148 }
149 return Err(e);
151 }
152 }
153 }
154 visible_rows = filtered_rows;
155 let filter_duration = filter_start.elapsed();
156 info!(
157 "WHERE clause filtering: {} rows -> {} rows in {:?}",
158 total_rows,
159 visible_rows.len(),
160 filter_duration
161 );
162
163 }
165
166 let mut view = DataView::new(table.clone());
168 view = view.with_rows(visible_rows);
169
170 if !statement.select_items.is_empty() {
172 let has_non_star_items = statement
174 .select_items
175 .iter()
176 .any(|item| !matches!(item, SelectItem::Star));
177
178 if has_non_star_items || statement.select_items.len() > 1 {
182 view = self.apply_select_items(view, &statement.select_items)?;
183 }
184 } else if !statement.columns.is_empty() && statement.columns[0] != "*" {
186 let column_indices = self.resolve_column_indices(&*table, &statement.columns)?;
188 view = view.with_columns(column_indices);
189 }
190
191 if let Some(order_by_columns) = &statement.order_by {
193 if !order_by_columns.is_empty() {
194 view = self.apply_multi_order_by(view, order_by_columns)?;
195 }
196 }
197
198 if let Some(limit) = statement.limit {
200 let offset = statement.offset.unwrap_or(0);
201 view = view.with_limit(limit, offset);
202 }
203
204 Ok(view)
205 }
206
207 fn resolve_column_indices(&self, table: &DataTable, columns: &[String]) -> Result<Vec<usize>> {
209 let mut indices = Vec::new();
210 let table_columns = table.column_names();
211
212 for col_name in columns {
213 let index = table_columns
214 .iter()
215 .position(|c| c.eq_ignore_ascii_case(col_name))
216 .ok_or_else(|| {
217 let suggestion = self.find_similar_column(table, col_name);
218 match suggestion {
219 Some(similar) => anyhow::anyhow!(
220 "Column '{}' not found. Did you mean '{}'?",
221 col_name,
222 similar
223 ),
224 None => anyhow::anyhow!("Column '{}' not found", col_name),
225 }
226 })?;
227 indices.push(index);
228 }
229
230 Ok(indices)
231 }
232
233 fn apply_select_items(&self, view: DataView, select_items: &[SelectItem]) -> Result<DataView> {
235 let has_computed_expressions = select_items
237 .iter()
238 .any(|item| matches!(item, SelectItem::Expression { .. }));
239
240 if !has_computed_expressions {
241 let column_indices = self.resolve_select_columns(view.source(), select_items)?;
243 return Ok(view.with_columns(column_indices));
244 }
245
246 let source_table = view.source();
251 let visible_rows = view.visible_row_indices();
252
253 let mut computed_table = DataTable::new("query_result");
256
257 let mut expanded_items = Vec::new();
259 for item in select_items {
260 match item {
261 SelectItem::Star => {
262 for col_name in source_table.column_names() {
264 expanded_items.push(SelectItem::Column(col_name));
265 }
266 }
267 _ => expanded_items.push(item.clone()),
268 }
269 }
270
271 let mut column_name_counts: std::collections::HashMap<String, usize> =
273 std::collections::HashMap::new();
274
275 for item in &expanded_items {
276 let base_name = match item {
277 SelectItem::Column(name) => name.clone(),
278 SelectItem::Expression { alias, .. } => alias.clone(),
279 SelectItem::Star => unreachable!("Star should have been expanded"),
280 };
281
282 let count = column_name_counts.entry(base_name.clone()).or_insert(0);
284 let column_name = if *count == 0 {
285 base_name.clone()
287 } else {
288 format!("{}_{}", base_name, count)
290 };
291 *count += 1;
292
293 computed_table.add_column(DataColumn::new(&column_name));
294 }
295
296 let evaluator = ArithmeticEvaluator::new(source_table);
298
299 for &row_idx in visible_rows {
300 let mut row_values = Vec::new();
301
302 for item in &expanded_items {
303 let value = match item {
304 SelectItem::Column(col_name) => {
305 let col_idx = source_table.get_column_index(col_name).ok_or_else(|| {
307 let suggestion = self.find_similar_column(source_table, col_name);
308 match suggestion {
309 Some(similar) => anyhow::anyhow!(
310 "Column '{}' not found. Did you mean '{}'?",
311 col_name,
312 similar
313 ),
314 None => anyhow::anyhow!("Column '{}' not found", col_name),
315 }
316 })?;
317 let row = source_table
318 .get_row(row_idx)
319 .ok_or_else(|| anyhow::anyhow!("Row {} not found", row_idx))?;
320 row.get(col_idx)
321 .ok_or_else(|| anyhow::anyhow!("Column {} not found in row", col_idx))?
322 .clone()
323 }
324 SelectItem::Expression { expr, .. } => {
325 evaluator.evaluate(expr, row_idx)?
327 }
328 SelectItem::Star => unreachable!("Star should have been expanded"),
329 };
330 row_values.push(value);
331 }
332
333 computed_table
334 .add_row(DataRow::new(row_values))
335 .map_err(|e| anyhow::anyhow!("Failed to add row: {}", e))?;
336 }
337
338 Ok(DataView::new(Arc::new(computed_table)))
341 }
342
343 fn resolve_select_columns(
345 &self,
346 table: &DataTable,
347 select_items: &[SelectItem],
348 ) -> Result<Vec<usize>> {
349 let mut indices = Vec::new();
350 let table_columns = table.column_names();
351
352 for item in select_items {
353 match item {
354 SelectItem::Column(col_name) => {
355 let index = table_columns
356 .iter()
357 .position(|c| c.eq_ignore_ascii_case(col_name))
358 .ok_or_else(|| {
359 let suggestion = self.find_similar_column(table, col_name);
360 match suggestion {
361 Some(similar) => anyhow::anyhow!(
362 "Column '{}' not found. Did you mean '{}'?",
363 col_name,
364 similar
365 ),
366 None => anyhow::anyhow!("Column '{}' not found", col_name),
367 }
368 })?;
369 indices.push(index);
370 }
371 SelectItem::Star => {
372 for i in 0..table_columns.len() {
374 indices.push(i);
375 }
376 }
377 SelectItem::Expression { .. } => {
378 return Err(anyhow::anyhow!(
379 "Computed expressions require new table creation"
380 ));
381 }
382 }
383 }
384
385 Ok(indices)
386 }
387
388 fn apply_multi_order_by(
390 &self,
391 mut view: DataView,
392 order_by_columns: &[OrderByColumn],
393 ) -> Result<DataView> {
394 let mut sort_columns = Vec::new();
396
397 for order_col in order_by_columns {
398 let col_index = view
400 .source()
401 .get_column_index(&order_col.column)
402 .ok_or_else(|| {
403 let suggestion = self.find_similar_column(view.source(), &order_col.column);
404 match suggestion {
405 Some(similar) => anyhow::anyhow!(
406 "Column '{}' not found. Did you mean '{}'?",
407 order_col.column,
408 similar
409 ),
410 None => anyhow::anyhow!("Column '{}' not found", order_col.column),
411 }
412 })?;
413
414 let ascending = matches!(order_col.direction, SortDirection::Asc);
415 sort_columns.push((col_index, ascending));
416 }
417
418 view.apply_multi_sort(&sort_columns)?;
420 Ok(view)
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427 use crate::data::datatable::{DataColumn, DataRow, DataValue};
428
429 fn create_test_table() -> Arc<DataTable> {
430 let mut table = DataTable::new("test");
431
432 table.add_column(DataColumn::new("id"));
434 table.add_column(DataColumn::new("name"));
435 table.add_column(DataColumn::new("age"));
436
437 table
439 .add_row(DataRow::new(vec![
440 DataValue::Integer(1),
441 DataValue::String("Alice".to_string()),
442 DataValue::Integer(30),
443 ]))
444 .unwrap();
445
446 table
447 .add_row(DataRow::new(vec![
448 DataValue::Integer(2),
449 DataValue::String("Bob".to_string()),
450 DataValue::Integer(25),
451 ]))
452 .unwrap();
453
454 table
455 .add_row(DataRow::new(vec![
456 DataValue::Integer(3),
457 DataValue::String("Charlie".to_string()),
458 DataValue::Integer(35),
459 ]))
460 .unwrap();
461
462 Arc::new(table)
463 }
464
465 #[test]
466 fn test_select_all() {
467 let table = create_test_table();
468 let engine = QueryEngine::new();
469
470 let view = engine
471 .execute(table.clone(), "SELECT * FROM users")
472 .unwrap();
473 assert_eq!(view.row_count(), 3);
474 assert_eq!(view.column_count(), 3);
475 }
476
477 #[test]
478 fn test_select_columns() {
479 let table = create_test_table();
480 let engine = QueryEngine::new();
481
482 let view = engine
483 .execute(table.clone(), "SELECT name, age FROM users")
484 .unwrap();
485 assert_eq!(view.row_count(), 3);
486 assert_eq!(view.column_count(), 2);
487 }
488
489 #[test]
490 fn test_select_with_limit() {
491 let table = create_test_table();
492 let engine = QueryEngine::new();
493
494 let view = engine
495 .execute(table.clone(), "SELECT * FROM users LIMIT 2")
496 .unwrap();
497 assert_eq!(view.row_count(), 2);
498 }
499
500 #[test]
501 fn test_type_coercion_contains() {
502 let _ = tracing_subscriber::fmt()
504 .with_max_level(tracing::Level::DEBUG)
505 .try_init();
506
507 let mut table = DataTable::new("test");
508 table.add_column(DataColumn::new("id"));
509 table.add_column(DataColumn::new("status"));
510 table.add_column(DataColumn::new("price"));
511
512 table
514 .add_row(DataRow::new(vec![
515 DataValue::Integer(1),
516 DataValue::String("Pending".to_string()),
517 DataValue::Float(99.99),
518 ]))
519 .unwrap();
520
521 table
522 .add_row(DataRow::new(vec![
523 DataValue::Integer(2),
524 DataValue::String("Confirmed".to_string()),
525 DataValue::Float(150.50),
526 ]))
527 .unwrap();
528
529 table
530 .add_row(DataRow::new(vec![
531 DataValue::Integer(3),
532 DataValue::String("Pending".to_string()),
533 DataValue::Float(75.00),
534 ]))
535 .unwrap();
536
537 let table = Arc::new(table);
538 let engine = QueryEngine::new();
539
540 println!("\n=== Testing WHERE clause with Contains ===");
541 println!("Table has {} rows", table.row_count());
542 for i in 0..table.row_count() {
543 let status = table.get_value(i, 1);
544 println!("Row {}: status = {:?}", i, status);
545 }
546
547 println!("\n--- Test 1: status.Contains('pend') ---");
549 let result = engine.execute(
550 table.clone(),
551 "SELECT * FROM test WHERE status.Contains('pend')",
552 );
553 match result {
554 Ok(view) => {
555 println!("SUCCESS: Found {} matching rows", view.row_count());
556 assert_eq!(view.row_count(), 2); }
558 Err(e) => {
559 panic!("Query failed: {}", e);
560 }
561 }
562
563 println!("\n--- Test 2: price.Contains('9') ---");
565 let result = engine.execute(
566 table.clone(),
567 "SELECT * FROM test WHERE price.Contains('9')",
568 );
569 match result {
570 Ok(view) => {
571 println!(
572 "SUCCESS: Found {} matching rows with price containing '9'",
573 view.row_count()
574 );
575 assert!(view.row_count() >= 1);
577 }
578 Err(e) => {
579 panic!("Numeric coercion query failed: {}", e);
580 }
581 }
582
583 println!("\n=== All tests passed! ===");
584 }
585
586 #[test]
587 fn test_not_in_clause() {
588 let _ = tracing_subscriber::fmt()
590 .with_max_level(tracing::Level::DEBUG)
591 .try_init();
592
593 let mut table = DataTable::new("test");
594 table.add_column(DataColumn::new("id"));
595 table.add_column(DataColumn::new("country"));
596
597 table
599 .add_row(DataRow::new(vec![
600 DataValue::Integer(1),
601 DataValue::String("CA".to_string()),
602 ]))
603 .unwrap();
604
605 table
606 .add_row(DataRow::new(vec![
607 DataValue::Integer(2),
608 DataValue::String("US".to_string()),
609 ]))
610 .unwrap();
611
612 table
613 .add_row(DataRow::new(vec![
614 DataValue::Integer(3),
615 DataValue::String("UK".to_string()),
616 ]))
617 .unwrap();
618
619 let table = Arc::new(table);
620 let engine = QueryEngine::new();
621
622 println!("\n=== Testing NOT IN clause ===");
623 println!("Table has {} rows", table.row_count());
624 for i in 0..table.row_count() {
625 let country = table.get_value(i, 1);
626 println!("Row {}: country = {:?}", i, country);
627 }
628
629 println!("\n--- Test: country NOT IN ('CA') ---");
631 let result = engine.execute(
632 table.clone(),
633 "SELECT * FROM test WHERE country NOT IN ('CA')",
634 );
635 match result {
636 Ok(view) => {
637 println!("SUCCESS: Found {} rows not in ('CA')", view.row_count());
638 assert_eq!(view.row_count(), 2); }
640 Err(e) => {
641 panic!("NOT IN query failed: {}", e);
642 }
643 }
644
645 println!("\n=== NOT IN test complete! ===");
646 }
647
648 #[test]
649 fn test_case_insensitive_in_and_not_in() {
650 let _ = tracing_subscriber::fmt()
652 .with_max_level(tracing::Level::DEBUG)
653 .try_init();
654
655 let mut table = DataTable::new("test");
656 table.add_column(DataColumn::new("id"));
657 table.add_column(DataColumn::new("country"));
658
659 table
661 .add_row(DataRow::new(vec![
662 DataValue::Integer(1),
663 DataValue::String("CA".to_string()), ]))
665 .unwrap();
666
667 table
668 .add_row(DataRow::new(vec![
669 DataValue::Integer(2),
670 DataValue::String("us".to_string()), ]))
672 .unwrap();
673
674 table
675 .add_row(DataRow::new(vec![
676 DataValue::Integer(3),
677 DataValue::String("UK".to_string()), ]))
679 .unwrap();
680
681 let table = Arc::new(table);
682
683 println!("\n=== Testing Case-Insensitive IN clause ===");
684 println!("Table has {} rows", table.row_count());
685 for i in 0..table.row_count() {
686 let country = table.get_value(i, 1);
687 println!("Row {}: country = {:?}", i, country);
688 }
689
690 println!("\n--- Test: country IN ('ca') with case_insensitive=true ---");
692 let engine = QueryEngine::with_case_insensitive(true);
693 let result = engine.execute(table.clone(), "SELECT * FROM test WHERE country IN ('ca')");
694 match result {
695 Ok(view) => {
696 println!(
697 "SUCCESS: Found {} rows matching 'ca' (case-insensitive)",
698 view.row_count()
699 );
700 assert_eq!(view.row_count(), 1); }
702 Err(e) => {
703 panic!("Case-insensitive IN query failed: {}", e);
704 }
705 }
706
707 println!("\n--- Test: country NOT IN ('ca') with case_insensitive=true ---");
709 let result = engine.execute(
710 table.clone(),
711 "SELECT * FROM test WHERE country NOT IN ('ca')",
712 );
713 match result {
714 Ok(view) => {
715 println!(
716 "SUCCESS: Found {} rows not matching 'ca' (case-insensitive)",
717 view.row_count()
718 );
719 assert_eq!(view.row_count(), 2); }
721 Err(e) => {
722 panic!("Case-insensitive NOT IN query failed: {}", e);
723 }
724 }
725
726 println!("\n--- Test: country IN ('ca') with case_insensitive=false ---");
728 let engine_case_sensitive = QueryEngine::new(); let result = engine_case_sensitive
730 .execute(table.clone(), "SELECT * FROM test WHERE country IN ('ca')");
731 match result {
732 Ok(view) => {
733 println!(
734 "SUCCESS: Found {} rows matching 'ca' (case-sensitive)",
735 view.row_count()
736 );
737 assert_eq!(view.row_count(), 0); }
739 Err(e) => {
740 panic!("Case-sensitive IN query failed: {}", e);
741 }
742 }
743
744 println!("\n=== Case-insensitive IN/NOT IN test complete! ===");
745 }
746
747 #[test]
748 #[ignore = "Parentheses in WHERE clause not yet implemented"]
749 fn test_parentheses_in_where_clause() {
750 let _ = tracing_subscriber::fmt()
752 .with_max_level(tracing::Level::DEBUG)
753 .try_init();
754
755 let mut table = DataTable::new("test");
756 table.add_column(DataColumn::new("id"));
757 table.add_column(DataColumn::new("status"));
758 table.add_column(DataColumn::new("priority"));
759
760 table
762 .add_row(DataRow::new(vec![
763 DataValue::Integer(1),
764 DataValue::String("Pending".to_string()),
765 DataValue::String("High".to_string()),
766 ]))
767 .unwrap();
768
769 table
770 .add_row(DataRow::new(vec![
771 DataValue::Integer(2),
772 DataValue::String("Complete".to_string()),
773 DataValue::String("High".to_string()),
774 ]))
775 .unwrap();
776
777 table
778 .add_row(DataRow::new(vec![
779 DataValue::Integer(3),
780 DataValue::String("Pending".to_string()),
781 DataValue::String("Low".to_string()),
782 ]))
783 .unwrap();
784
785 table
786 .add_row(DataRow::new(vec![
787 DataValue::Integer(4),
788 DataValue::String("Complete".to_string()),
789 DataValue::String("Low".to_string()),
790 ]))
791 .unwrap();
792
793 let table = Arc::new(table);
794 let engine = QueryEngine::new();
795
796 println!("\n=== Testing Parentheses in WHERE clause ===");
797 println!("Table has {} rows", table.row_count());
798 for i in 0..table.row_count() {
799 let status = table.get_value(i, 1);
800 let priority = table.get_value(i, 2);
801 println!(
802 "Row {}: status = {:?}, priority = {:?}",
803 i, status, priority
804 );
805 }
806
807 println!("\n--- Test: (status = 'Pending' AND priority = 'High') OR (status = 'Complete' AND priority = 'Low') ---");
809 let result = engine.execute(
810 table.clone(),
811 "SELECT * FROM test WHERE (status = 'Pending' AND priority = 'High') OR (status = 'Complete' AND priority = 'Low')",
812 );
813 match result {
814 Ok(view) => {
815 println!(
816 "SUCCESS: Found {} rows with parenthetical logic",
817 view.row_count()
818 );
819 assert_eq!(view.row_count(), 2); }
821 Err(e) => {
822 panic!("Parentheses query failed: {}", e);
823 }
824 }
825
826 println!("\n=== Parentheses test complete! ===");
827 }
828
829 #[test]
830 #[ignore = "Numeric type coercion needs fixing"]
831 fn test_numeric_type_coercion() {
832 let _ = tracing_subscriber::fmt()
834 .with_max_level(tracing::Level::DEBUG)
835 .try_init();
836
837 let mut table = DataTable::new("test");
838 table.add_column(DataColumn::new("id"));
839 table.add_column(DataColumn::new("price"));
840 table.add_column(DataColumn::new("quantity"));
841
842 table
844 .add_row(DataRow::new(vec![
845 DataValue::Integer(1),
846 DataValue::Float(99.50), DataValue::Integer(100),
848 ]))
849 .unwrap();
850
851 table
852 .add_row(DataRow::new(vec![
853 DataValue::Integer(2),
854 DataValue::Float(150.0), DataValue::Integer(200),
856 ]))
857 .unwrap();
858
859 table
860 .add_row(DataRow::new(vec![
861 DataValue::Integer(3),
862 DataValue::Integer(75), DataValue::Integer(50),
864 ]))
865 .unwrap();
866
867 let table = Arc::new(table);
868 let engine = QueryEngine::new();
869
870 println!("\n=== Testing Numeric Type Coercion ===");
871 println!("Table has {} rows", table.row_count());
872 for i in 0..table.row_count() {
873 let price = table.get_value(i, 1);
874 let quantity = table.get_value(i, 2);
875 println!("Row {}: price = {:?}, quantity = {:?}", i, price, quantity);
876 }
877
878 println!("\n--- Test: price.Contains('.') ---");
880 let result = engine.execute(
881 table.clone(),
882 "SELECT * FROM test WHERE price.Contains('.')",
883 );
884 match result {
885 Ok(view) => {
886 println!(
887 "SUCCESS: Found {} rows with decimal points in price",
888 view.row_count()
889 );
890 assert_eq!(view.row_count(), 2); }
892 Err(e) => {
893 panic!("Numeric Contains query failed: {}", e);
894 }
895 }
896
897 println!("\n--- Test: quantity.Contains('0') ---");
899 let result = engine.execute(
900 table.clone(),
901 "SELECT * FROM test WHERE quantity.Contains('0')",
902 );
903 match result {
904 Ok(view) => {
905 println!(
906 "SUCCESS: Found {} rows with '0' in quantity",
907 view.row_count()
908 );
909 assert_eq!(view.row_count(), 2); }
911 Err(e) => {
912 panic!("Integer Contains query failed: {}", e);
913 }
914 }
915
916 println!("\n=== Numeric type coercion test complete! ===");
917 }
918
919 #[test]
920 fn test_datetime_comparisons() {
921 let _ = tracing_subscriber::fmt()
923 .with_max_level(tracing::Level::DEBUG)
924 .try_init();
925
926 let mut table = DataTable::new("test");
927 table.add_column(DataColumn::new("id"));
928 table.add_column(DataColumn::new("created_date"));
929
930 table
932 .add_row(DataRow::new(vec![
933 DataValue::Integer(1),
934 DataValue::String("2024-12-15".to_string()),
935 ]))
936 .unwrap();
937
938 table
939 .add_row(DataRow::new(vec![
940 DataValue::Integer(2),
941 DataValue::String("2025-01-15".to_string()),
942 ]))
943 .unwrap();
944
945 table
946 .add_row(DataRow::new(vec![
947 DataValue::Integer(3),
948 DataValue::String("2025-02-15".to_string()),
949 ]))
950 .unwrap();
951
952 let table = Arc::new(table);
953 let engine = QueryEngine::new();
954
955 println!("\n=== Testing DateTime Comparisons ===");
956 println!("Table has {} rows", table.row_count());
957 for i in 0..table.row_count() {
958 let date = table.get_value(i, 1);
959 println!("Row {}: created_date = {:?}", i, date);
960 }
961
962 println!("\n--- Test: created_date > DateTime(2025,1,1) ---");
964 let result = engine.execute(
965 table.clone(),
966 "SELECT * FROM test WHERE created_date > DateTime(2025,1,1)",
967 );
968 match result {
969 Ok(view) => {
970 println!("SUCCESS: Found {} rows after 2025-01-01", view.row_count());
971 assert_eq!(view.row_count(), 2); }
973 Err(e) => {
974 panic!("DateTime comparison query failed: {}", e);
975 }
976 }
977
978 println!("\n=== DateTime comparison test complete! ===");
979 }
980
981 #[test]
982 fn test_not_with_method_calls() {
983 let _ = tracing_subscriber::fmt()
985 .with_max_level(tracing::Level::DEBUG)
986 .try_init();
987
988 let mut table = DataTable::new("test");
989 table.add_column(DataColumn::new("id"));
990 table.add_column(DataColumn::new("status"));
991
992 table
994 .add_row(DataRow::new(vec![
995 DataValue::Integer(1),
996 DataValue::String("Pending Review".to_string()),
997 ]))
998 .unwrap();
999
1000 table
1001 .add_row(DataRow::new(vec![
1002 DataValue::Integer(2),
1003 DataValue::String("Complete".to_string()),
1004 ]))
1005 .unwrap();
1006
1007 table
1008 .add_row(DataRow::new(vec![
1009 DataValue::Integer(3),
1010 DataValue::String("Pending Approval".to_string()),
1011 ]))
1012 .unwrap();
1013
1014 let table = Arc::new(table);
1015 let engine = QueryEngine::with_case_insensitive(true);
1016
1017 println!("\n=== Testing NOT with Method Calls ===");
1018 println!("Table has {} rows", table.row_count());
1019 for i in 0..table.row_count() {
1020 let status = table.get_value(i, 1);
1021 println!("Row {}: status = {:?}", i, status);
1022 }
1023
1024 println!("\n--- Test: NOT status.Contains('pend') ---");
1026 let result = engine.execute(
1027 table.clone(),
1028 "SELECT * FROM test WHERE NOT status.Contains('pend')",
1029 );
1030 match result {
1031 Ok(view) => {
1032 println!(
1033 "SUCCESS: Found {} rows NOT containing 'pend'",
1034 view.row_count()
1035 );
1036 assert_eq!(view.row_count(), 1); }
1038 Err(e) => {
1039 panic!("NOT Contains query failed: {}", e);
1040 }
1041 }
1042
1043 println!("\n--- Test: NOT status.StartsWith('Pending') ---");
1045 let result = engine.execute(
1046 table.clone(),
1047 "SELECT * FROM test WHERE NOT status.StartsWith('Pending')",
1048 );
1049 match result {
1050 Ok(view) => {
1051 println!(
1052 "SUCCESS: Found {} rows NOT starting with 'Pending'",
1053 view.row_count()
1054 );
1055 assert_eq!(view.row_count(), 1); }
1057 Err(e) => {
1058 panic!("NOT StartsWith query failed: {}", e);
1059 }
1060 }
1061
1062 println!("\n=== NOT with method calls test complete! ===");
1063 }
1064
1065 #[test]
1066 #[ignore = "Complex logical expressions with parentheses not yet implemented"]
1067 fn test_complex_logical_expressions() {
1068 let _ = tracing_subscriber::fmt()
1070 .with_max_level(tracing::Level::DEBUG)
1071 .try_init();
1072
1073 let mut table = DataTable::new("test");
1074 table.add_column(DataColumn::new("id"));
1075 table.add_column(DataColumn::new("status"));
1076 table.add_column(DataColumn::new("priority"));
1077 table.add_column(DataColumn::new("assigned"));
1078
1079 table
1081 .add_row(DataRow::new(vec![
1082 DataValue::Integer(1),
1083 DataValue::String("Pending".to_string()),
1084 DataValue::String("High".to_string()),
1085 DataValue::String("John".to_string()),
1086 ]))
1087 .unwrap();
1088
1089 table
1090 .add_row(DataRow::new(vec![
1091 DataValue::Integer(2),
1092 DataValue::String("Complete".to_string()),
1093 DataValue::String("High".to_string()),
1094 DataValue::String("Jane".to_string()),
1095 ]))
1096 .unwrap();
1097
1098 table
1099 .add_row(DataRow::new(vec![
1100 DataValue::Integer(3),
1101 DataValue::String("Pending".to_string()),
1102 DataValue::String("Low".to_string()),
1103 DataValue::String("John".to_string()),
1104 ]))
1105 .unwrap();
1106
1107 table
1108 .add_row(DataRow::new(vec![
1109 DataValue::Integer(4),
1110 DataValue::String("In Progress".to_string()),
1111 DataValue::String("Medium".to_string()),
1112 DataValue::String("Jane".to_string()),
1113 ]))
1114 .unwrap();
1115
1116 let table = Arc::new(table);
1117 let engine = QueryEngine::new();
1118
1119 println!("\n=== Testing Complex Logical Expressions ===");
1120 println!("Table has {} rows", table.row_count());
1121 for i in 0..table.row_count() {
1122 let status = table.get_value(i, 1);
1123 let priority = table.get_value(i, 2);
1124 let assigned = table.get_value(i, 3);
1125 println!(
1126 "Row {}: status = {:?}, priority = {:?}, assigned = {:?}",
1127 i, status, priority, assigned
1128 );
1129 }
1130
1131 println!("\n--- Test: status = 'Pending' AND (priority = 'High' OR assigned = 'John') ---");
1133 let result = engine.execute(
1134 table.clone(),
1135 "SELECT * FROM test WHERE status = 'Pending' AND (priority = 'High' OR assigned = 'John')",
1136 );
1137 match result {
1138 Ok(view) => {
1139 println!(
1140 "SUCCESS: Found {} rows with complex logic",
1141 view.row_count()
1142 );
1143 assert_eq!(view.row_count(), 2); }
1145 Err(e) => {
1146 panic!("Complex logic query failed: {}", e);
1147 }
1148 }
1149
1150 println!("\n--- Test: NOT (status.Contains('Complete') OR priority = 'Low') ---");
1152 let result = engine.execute(
1153 table.clone(),
1154 "SELECT * FROM test WHERE NOT (status.Contains('Complete') OR priority = 'Low')",
1155 );
1156 match result {
1157 Ok(view) => {
1158 println!(
1159 "SUCCESS: Found {} rows with NOT complex logic",
1160 view.row_count()
1161 );
1162 assert_eq!(view.row_count(), 2); }
1164 Err(e) => {
1165 panic!("NOT complex logic query failed: {}", e);
1166 }
1167 }
1168
1169 println!("\n=== Complex logical expressions test complete! ===");
1170 }
1171
1172 #[test]
1173 fn test_mixed_data_types_and_edge_cases() {
1174 let _ = tracing_subscriber::fmt()
1176 .with_max_level(tracing::Level::DEBUG)
1177 .try_init();
1178
1179 let mut table = DataTable::new("test");
1180 table.add_column(DataColumn::new("id"));
1181 table.add_column(DataColumn::new("value"));
1182 table.add_column(DataColumn::new("nullable_field"));
1183
1184 table
1186 .add_row(DataRow::new(vec![
1187 DataValue::Integer(1),
1188 DataValue::String("123.45".to_string()),
1189 DataValue::String("present".to_string()),
1190 ]))
1191 .unwrap();
1192
1193 table
1194 .add_row(DataRow::new(vec![
1195 DataValue::Integer(2),
1196 DataValue::Float(678.90),
1197 DataValue::Null,
1198 ]))
1199 .unwrap();
1200
1201 table
1202 .add_row(DataRow::new(vec![
1203 DataValue::Integer(3),
1204 DataValue::Boolean(true),
1205 DataValue::String("also present".to_string()),
1206 ]))
1207 .unwrap();
1208
1209 table
1210 .add_row(DataRow::new(vec![
1211 DataValue::Integer(4),
1212 DataValue::String("false".to_string()),
1213 DataValue::Null,
1214 ]))
1215 .unwrap();
1216
1217 let table = Arc::new(table);
1218 let engine = QueryEngine::new();
1219
1220 println!("\n=== Testing Mixed Data Types and Edge Cases ===");
1221 println!("Table has {} rows", table.row_count());
1222 for i in 0..table.row_count() {
1223 let value = table.get_value(i, 1);
1224 let nullable = table.get_value(i, 2);
1225 println!(
1226 "Row {}: value = {:?}, nullable_field = {:?}",
1227 i, value, nullable
1228 );
1229 }
1230
1231 println!("\n--- Test: value.Contains('true') (boolean to string coercion) ---");
1233 let result = engine.execute(
1234 table.clone(),
1235 "SELECT * FROM test WHERE value.Contains('true')",
1236 );
1237 match result {
1238 Ok(view) => {
1239 println!(
1240 "SUCCESS: Found {} rows with boolean coercion",
1241 view.row_count()
1242 );
1243 assert_eq!(view.row_count(), 1); }
1245 Err(e) => {
1246 panic!("Boolean coercion query failed: {}", e);
1247 }
1248 }
1249
1250 println!("\n--- Test: id IN (1, 3) ---");
1252 let result = engine.execute(table.clone(), "SELECT * FROM test WHERE id IN (1, 3)");
1253 match result {
1254 Ok(view) => {
1255 println!("SUCCESS: Found {} rows with IN clause", view.row_count());
1256 assert_eq!(view.row_count(), 2); }
1258 Err(e) => {
1259 panic!("Multiple IN values query failed: {}", e);
1260 }
1261 }
1262
1263 println!("\n=== Mixed data types test complete! ===");
1264 }
1265
1266 #[test]
1267 fn test_not_in_parsing() {
1268 use crate::sql::recursive_parser::Parser;
1269
1270 let query = "SELECT * FROM test WHERE country NOT IN ('CA')";
1271 println!("\n=== Testing NOT IN parsing ===");
1272 println!("Parsing query: {}", query);
1273
1274 let mut parser = Parser::new(query);
1275 match parser.parse() {
1276 Ok(statement) => {
1277 println!("Parsed statement: {:#?}", statement);
1278 if let Some(where_clause) = statement.where_clause {
1279 println!("WHERE conditions: {:#?}", where_clause.conditions);
1280 if let Some(first_condition) = where_clause.conditions.first() {
1281 println!("First condition expression: {:#?}", first_condition.expr);
1282 }
1283 }
1284 }
1285 Err(e) => {
1286 panic!("Parse error: {}", e);
1287 }
1288 }
1289 }
1290}
1291
1292#[cfg(test)]
1293#[path = "query_engine_tests.rs"]
1294mod query_engine_tests;