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 debug!(
115 "QueryEngine::build_view - select_items: {:?}",
116 statement.select_items
117 );
118 debug!(
119 "QueryEngine::build_view - where_clause: {:?}",
120 statement.where_clause
121 );
122
123 let mut visible_rows: Vec<usize> = (0..table.row_count()).collect();
125
126 if let Some(where_clause) = &statement.where_clause {
128 let total_rows = table.row_count();
129 debug!("QueryEngine: Applying WHERE clause to {} rows", total_rows);
130 debug!("QueryEngine: WHERE clause = {:?}", where_clause);
131
132 let filter_start = Instant::now();
133 let mut filtered_rows = Vec::new();
135 for row_idx in visible_rows {
136 if row_idx < 3 {
138 debug!("QueryEngine: Evaluating WHERE clause for row {}", row_idx);
139 }
140 let evaluator =
141 RecursiveWhereEvaluator::with_case_insensitive(&*table, self.case_insensitive);
142 match evaluator.evaluate(where_clause, row_idx) {
143 Ok(result) => {
144 if row_idx < 3 {
145 debug!("QueryEngine: Row {} WHERE result: {}", row_idx, result);
146 }
147 if result {
148 filtered_rows.push(row_idx);
149 }
150 }
151 Err(e) => {
152 if row_idx < 3 {
153 debug!(
154 "QueryEngine: WHERE evaluation error for row {}: {}",
155 row_idx, e
156 );
157 }
158 return Err(e);
160 }
161 }
162 }
163 visible_rows = filtered_rows;
164 let filter_duration = filter_start.elapsed();
165 info!(
166 "WHERE clause filtering: {} rows -> {} rows in {:?}",
167 total_rows,
168 visible_rows.len(),
169 filter_duration
170 );
171
172 }
174
175 let mut view = DataView::new(table.clone());
177 view = view.with_rows(visible_rows);
178
179 if !statement.select_items.is_empty() {
181 let has_non_star_items = statement
183 .select_items
184 .iter()
185 .any(|item| !matches!(item, SelectItem::Star));
186
187 if has_non_star_items || statement.select_items.len() > 1 {
191 view = self.apply_select_items(view, &statement.select_items)?;
192 }
193 } else if !statement.columns.is_empty() && statement.columns[0] != "*" {
195 let column_indices = self.resolve_column_indices(&*table, &statement.columns)?;
197 view = view.with_columns(column_indices);
198 }
199
200 if let Some(order_by_columns) = &statement.order_by {
202 if !order_by_columns.is_empty() {
203 view = self.apply_multi_order_by(view, order_by_columns)?;
204 }
205 }
206
207 if let Some(limit) = statement.limit {
209 let offset = statement.offset.unwrap_or(0);
210 view = view.with_limit(limit, offset);
211 }
212
213 Ok(view)
214 }
215
216 fn resolve_column_indices(&self, table: &DataTable, columns: &[String]) -> Result<Vec<usize>> {
218 let mut indices = Vec::new();
219 let table_columns = table.column_names();
220
221 for col_name in columns {
222 let index = table_columns
223 .iter()
224 .position(|c| c.eq_ignore_ascii_case(col_name))
225 .ok_or_else(|| {
226 let suggestion = self.find_similar_column(table, col_name);
227 match suggestion {
228 Some(similar) => anyhow::anyhow!(
229 "Column '{}' not found. Did you mean '{}'?",
230 col_name,
231 similar
232 ),
233 None => anyhow::anyhow!("Column '{}' not found", col_name),
234 }
235 })?;
236 indices.push(index);
237 }
238
239 Ok(indices)
240 }
241
242 fn apply_select_items(&self, view: DataView, select_items: &[SelectItem]) -> Result<DataView> {
244 debug!(
245 "QueryEngine::apply_select_items - items: {:?}",
246 select_items
247 );
248 debug!(
249 "QueryEngine::apply_select_items - input view has {} rows",
250 view.row_count()
251 );
252
253 let has_computed_expressions = select_items
255 .iter()
256 .any(|item| matches!(item, SelectItem::Expression { .. }));
257
258 debug!(
259 "QueryEngine::apply_select_items - has_computed_expressions: {}",
260 has_computed_expressions
261 );
262
263 if !has_computed_expressions {
264 let column_indices = self.resolve_select_columns(view.source(), select_items)?;
266 return Ok(view.with_columns(column_indices));
267 }
268
269 let source_table = view.source();
274 let visible_rows = view.visible_row_indices();
275
276 let mut computed_table = DataTable::new("query_result");
279
280 let mut expanded_items = Vec::new();
282 for item in select_items {
283 match item {
284 SelectItem::Star => {
285 for col_name in source_table.column_names() {
287 expanded_items.push(SelectItem::Column(col_name));
288 }
289 }
290 _ => expanded_items.push(item.clone()),
291 }
292 }
293
294 let mut column_name_counts: std::collections::HashMap<String, usize> =
296 std::collections::HashMap::new();
297
298 for item in &expanded_items {
299 let base_name = match item {
300 SelectItem::Column(name) => name.clone(),
301 SelectItem::Expression { alias, .. } => alias.clone(),
302 SelectItem::Star => unreachable!("Star should have been expanded"),
303 };
304
305 let count = column_name_counts.entry(base_name.clone()).or_insert(0);
307 let column_name = if *count == 0 {
308 base_name.clone()
310 } else {
311 format!("{}_{}", base_name, count)
313 };
314 *count += 1;
315
316 computed_table.add_column(DataColumn::new(&column_name));
317 }
318
319 let evaluator = ArithmeticEvaluator::new(source_table);
321
322 for &row_idx in visible_rows {
323 let mut row_values = Vec::new();
324
325 for item in &expanded_items {
326 let value = match item {
327 SelectItem::Column(col_name) => {
328 let col_idx = source_table.get_column_index(col_name).ok_or_else(|| {
330 let suggestion = self.find_similar_column(source_table, col_name);
331 match suggestion {
332 Some(similar) => anyhow::anyhow!(
333 "Column '{}' not found. Did you mean '{}'?",
334 col_name,
335 similar
336 ),
337 None => anyhow::anyhow!("Column '{}' not found", col_name),
338 }
339 })?;
340 let row = source_table
341 .get_row(row_idx)
342 .ok_or_else(|| anyhow::anyhow!("Row {} not found", row_idx))?;
343 row.get(col_idx)
344 .ok_or_else(|| anyhow::anyhow!("Column {} not found in row", col_idx))?
345 .clone()
346 }
347 SelectItem::Expression { expr, .. } => {
348 evaluator.evaluate(expr, row_idx)?
350 }
351 SelectItem::Star => unreachable!("Star should have been expanded"),
352 };
353 row_values.push(value);
354 }
355
356 computed_table
357 .add_row(DataRow::new(row_values))
358 .map_err(|e| anyhow::anyhow!("Failed to add row: {}", e))?;
359 }
360
361 Ok(DataView::new(Arc::new(computed_table)))
364 }
365
366 fn resolve_select_columns(
368 &self,
369 table: &DataTable,
370 select_items: &[SelectItem],
371 ) -> Result<Vec<usize>> {
372 let mut indices = Vec::new();
373 let table_columns = table.column_names();
374
375 for item in select_items {
376 match item {
377 SelectItem::Column(col_name) => {
378 let index = table_columns
379 .iter()
380 .position(|c| c.eq_ignore_ascii_case(col_name))
381 .ok_or_else(|| {
382 let suggestion = self.find_similar_column(table, col_name);
383 match suggestion {
384 Some(similar) => anyhow::anyhow!(
385 "Column '{}' not found. Did you mean '{}'?",
386 col_name,
387 similar
388 ),
389 None => anyhow::anyhow!("Column '{}' not found", col_name),
390 }
391 })?;
392 indices.push(index);
393 }
394 SelectItem::Star => {
395 for i in 0..table_columns.len() {
397 indices.push(i);
398 }
399 }
400 SelectItem::Expression { .. } => {
401 return Err(anyhow::anyhow!(
402 "Computed expressions require new table creation"
403 ));
404 }
405 }
406 }
407
408 Ok(indices)
409 }
410
411 fn apply_multi_order_by(
413 &self,
414 mut view: DataView,
415 order_by_columns: &[OrderByColumn],
416 ) -> Result<DataView> {
417 let mut sort_columns = Vec::new();
419
420 for order_col in order_by_columns {
421 let col_index = view
423 .source()
424 .get_column_index(&order_col.column)
425 .ok_or_else(|| {
426 let suggestion = self.find_similar_column(view.source(), &order_col.column);
427 match suggestion {
428 Some(similar) => anyhow::anyhow!(
429 "Column '{}' not found. Did you mean '{}'?",
430 order_col.column,
431 similar
432 ),
433 None => anyhow::anyhow!("Column '{}' not found", order_col.column),
434 }
435 })?;
436
437 let ascending = matches!(order_col.direction, SortDirection::Asc);
438 sort_columns.push((col_index, ascending));
439 }
440
441 view.apply_multi_sort(&sort_columns)?;
443 Ok(view)
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450 use crate::data::datatable::{DataColumn, DataRow, DataValue};
451
452 fn create_test_table() -> Arc<DataTable> {
453 let mut table = DataTable::new("test");
454
455 table.add_column(DataColumn::new("id"));
457 table.add_column(DataColumn::new("name"));
458 table.add_column(DataColumn::new("age"));
459
460 table
462 .add_row(DataRow::new(vec![
463 DataValue::Integer(1),
464 DataValue::String("Alice".to_string()),
465 DataValue::Integer(30),
466 ]))
467 .unwrap();
468
469 table
470 .add_row(DataRow::new(vec![
471 DataValue::Integer(2),
472 DataValue::String("Bob".to_string()),
473 DataValue::Integer(25),
474 ]))
475 .unwrap();
476
477 table
478 .add_row(DataRow::new(vec![
479 DataValue::Integer(3),
480 DataValue::String("Charlie".to_string()),
481 DataValue::Integer(35),
482 ]))
483 .unwrap();
484
485 Arc::new(table)
486 }
487
488 #[test]
489 fn test_select_all() {
490 let table = create_test_table();
491 let engine = QueryEngine::new();
492
493 let view = engine
494 .execute(table.clone(), "SELECT * FROM users")
495 .unwrap();
496 assert_eq!(view.row_count(), 3);
497 assert_eq!(view.column_count(), 3);
498 }
499
500 #[test]
501 fn test_select_columns() {
502 let table = create_test_table();
503 let engine = QueryEngine::new();
504
505 let view = engine
506 .execute(table.clone(), "SELECT name, age FROM users")
507 .unwrap();
508 assert_eq!(view.row_count(), 3);
509 assert_eq!(view.column_count(), 2);
510 }
511
512 #[test]
513 fn test_select_with_limit() {
514 let table = create_test_table();
515 let engine = QueryEngine::new();
516
517 let view = engine
518 .execute(table.clone(), "SELECT * FROM users LIMIT 2")
519 .unwrap();
520 assert_eq!(view.row_count(), 2);
521 }
522
523 #[test]
524 fn test_type_coercion_contains() {
525 let _ = tracing_subscriber::fmt()
527 .with_max_level(tracing::Level::DEBUG)
528 .try_init();
529
530 let mut table = DataTable::new("test");
531 table.add_column(DataColumn::new("id"));
532 table.add_column(DataColumn::new("status"));
533 table.add_column(DataColumn::new("price"));
534
535 table
537 .add_row(DataRow::new(vec![
538 DataValue::Integer(1),
539 DataValue::String("Pending".to_string()),
540 DataValue::Float(99.99),
541 ]))
542 .unwrap();
543
544 table
545 .add_row(DataRow::new(vec![
546 DataValue::Integer(2),
547 DataValue::String("Confirmed".to_string()),
548 DataValue::Float(150.50),
549 ]))
550 .unwrap();
551
552 table
553 .add_row(DataRow::new(vec![
554 DataValue::Integer(3),
555 DataValue::String("Pending".to_string()),
556 DataValue::Float(75.00),
557 ]))
558 .unwrap();
559
560 let table = Arc::new(table);
561 let engine = QueryEngine::new();
562
563 println!("\n=== Testing WHERE clause with Contains ===");
564 println!("Table has {} rows", table.row_count());
565 for i in 0..table.row_count() {
566 let status = table.get_value(i, 1);
567 println!("Row {}: status = {:?}", i, status);
568 }
569
570 println!("\n--- Test 1: status.Contains('pend') ---");
572 let result = engine.execute(
573 table.clone(),
574 "SELECT * FROM test WHERE status.Contains('pend')",
575 );
576 match result {
577 Ok(view) => {
578 println!("SUCCESS: Found {} matching rows", view.row_count());
579 assert_eq!(view.row_count(), 2); }
581 Err(e) => {
582 panic!("Query failed: {}", e);
583 }
584 }
585
586 println!("\n--- Test 2: price.Contains('9') ---");
588 let result = engine.execute(
589 table.clone(),
590 "SELECT * FROM test WHERE price.Contains('9')",
591 );
592 match result {
593 Ok(view) => {
594 println!(
595 "SUCCESS: Found {} matching rows with price containing '9'",
596 view.row_count()
597 );
598 assert!(view.row_count() >= 1);
600 }
601 Err(e) => {
602 panic!("Numeric coercion query failed: {}", e);
603 }
604 }
605
606 println!("\n=== All tests passed! ===");
607 }
608
609 #[test]
610 fn test_not_in_clause() {
611 let _ = tracing_subscriber::fmt()
613 .with_max_level(tracing::Level::DEBUG)
614 .try_init();
615
616 let mut table = DataTable::new("test");
617 table.add_column(DataColumn::new("id"));
618 table.add_column(DataColumn::new("country"));
619
620 table
622 .add_row(DataRow::new(vec![
623 DataValue::Integer(1),
624 DataValue::String("CA".to_string()),
625 ]))
626 .unwrap();
627
628 table
629 .add_row(DataRow::new(vec![
630 DataValue::Integer(2),
631 DataValue::String("US".to_string()),
632 ]))
633 .unwrap();
634
635 table
636 .add_row(DataRow::new(vec![
637 DataValue::Integer(3),
638 DataValue::String("UK".to_string()),
639 ]))
640 .unwrap();
641
642 let table = Arc::new(table);
643 let engine = QueryEngine::new();
644
645 println!("\n=== Testing NOT IN clause ===");
646 println!("Table has {} rows", table.row_count());
647 for i in 0..table.row_count() {
648 let country = table.get_value(i, 1);
649 println!("Row {}: country = {:?}", i, country);
650 }
651
652 println!("\n--- Test: country NOT IN ('CA') ---");
654 let result = engine.execute(
655 table.clone(),
656 "SELECT * FROM test WHERE country NOT IN ('CA')",
657 );
658 match result {
659 Ok(view) => {
660 println!("SUCCESS: Found {} rows not in ('CA')", view.row_count());
661 assert_eq!(view.row_count(), 2); }
663 Err(e) => {
664 panic!("NOT IN query failed: {}", e);
665 }
666 }
667
668 println!("\n=== NOT IN test complete! ===");
669 }
670
671 #[test]
672 fn test_case_insensitive_in_and_not_in() {
673 let _ = tracing_subscriber::fmt()
675 .with_max_level(tracing::Level::DEBUG)
676 .try_init();
677
678 let mut table = DataTable::new("test");
679 table.add_column(DataColumn::new("id"));
680 table.add_column(DataColumn::new("country"));
681
682 table
684 .add_row(DataRow::new(vec![
685 DataValue::Integer(1),
686 DataValue::String("CA".to_string()), ]))
688 .unwrap();
689
690 table
691 .add_row(DataRow::new(vec![
692 DataValue::Integer(2),
693 DataValue::String("us".to_string()), ]))
695 .unwrap();
696
697 table
698 .add_row(DataRow::new(vec![
699 DataValue::Integer(3),
700 DataValue::String("UK".to_string()), ]))
702 .unwrap();
703
704 let table = Arc::new(table);
705
706 println!("\n=== Testing Case-Insensitive IN clause ===");
707 println!("Table has {} rows", table.row_count());
708 for i in 0..table.row_count() {
709 let country = table.get_value(i, 1);
710 println!("Row {}: country = {:?}", i, country);
711 }
712
713 println!("\n--- Test: country IN ('ca') with case_insensitive=true ---");
715 let engine = QueryEngine::with_case_insensitive(true);
716 let result = engine.execute(table.clone(), "SELECT * FROM test WHERE country IN ('ca')");
717 match result {
718 Ok(view) => {
719 println!(
720 "SUCCESS: Found {} rows matching 'ca' (case-insensitive)",
721 view.row_count()
722 );
723 assert_eq!(view.row_count(), 1); }
725 Err(e) => {
726 panic!("Case-insensitive IN query failed: {}", e);
727 }
728 }
729
730 println!("\n--- Test: country NOT IN ('ca') with case_insensitive=true ---");
732 let result = engine.execute(
733 table.clone(),
734 "SELECT * FROM test WHERE country NOT IN ('ca')",
735 );
736 match result {
737 Ok(view) => {
738 println!(
739 "SUCCESS: Found {} rows not matching 'ca' (case-insensitive)",
740 view.row_count()
741 );
742 assert_eq!(view.row_count(), 2); }
744 Err(e) => {
745 panic!("Case-insensitive NOT IN query failed: {}", e);
746 }
747 }
748
749 println!("\n--- Test: country IN ('ca') with case_insensitive=false ---");
751 let engine_case_sensitive = QueryEngine::new(); let result = engine_case_sensitive
753 .execute(table.clone(), "SELECT * FROM test WHERE country IN ('ca')");
754 match result {
755 Ok(view) => {
756 println!(
757 "SUCCESS: Found {} rows matching 'ca' (case-sensitive)",
758 view.row_count()
759 );
760 assert_eq!(view.row_count(), 0); }
762 Err(e) => {
763 panic!("Case-sensitive IN query failed: {}", e);
764 }
765 }
766
767 println!("\n=== Case-insensitive IN/NOT IN test complete! ===");
768 }
769
770 #[test]
771 #[ignore = "Parentheses in WHERE clause not yet implemented"]
772 fn test_parentheses_in_where_clause() {
773 let _ = tracing_subscriber::fmt()
775 .with_max_level(tracing::Level::DEBUG)
776 .try_init();
777
778 let mut table = DataTable::new("test");
779 table.add_column(DataColumn::new("id"));
780 table.add_column(DataColumn::new("status"));
781 table.add_column(DataColumn::new("priority"));
782
783 table
785 .add_row(DataRow::new(vec![
786 DataValue::Integer(1),
787 DataValue::String("Pending".to_string()),
788 DataValue::String("High".to_string()),
789 ]))
790 .unwrap();
791
792 table
793 .add_row(DataRow::new(vec![
794 DataValue::Integer(2),
795 DataValue::String("Complete".to_string()),
796 DataValue::String("High".to_string()),
797 ]))
798 .unwrap();
799
800 table
801 .add_row(DataRow::new(vec![
802 DataValue::Integer(3),
803 DataValue::String("Pending".to_string()),
804 DataValue::String("Low".to_string()),
805 ]))
806 .unwrap();
807
808 table
809 .add_row(DataRow::new(vec![
810 DataValue::Integer(4),
811 DataValue::String("Complete".to_string()),
812 DataValue::String("Low".to_string()),
813 ]))
814 .unwrap();
815
816 let table = Arc::new(table);
817 let engine = QueryEngine::new();
818
819 println!("\n=== Testing Parentheses in WHERE clause ===");
820 println!("Table has {} rows", table.row_count());
821 for i in 0..table.row_count() {
822 let status = table.get_value(i, 1);
823 let priority = table.get_value(i, 2);
824 println!(
825 "Row {}: status = {:?}, priority = {:?}",
826 i, status, priority
827 );
828 }
829
830 println!("\n--- Test: (status = 'Pending' AND priority = 'High') OR (status = 'Complete' AND priority = 'Low') ---");
832 let result = engine.execute(
833 table.clone(),
834 "SELECT * FROM test WHERE (status = 'Pending' AND priority = 'High') OR (status = 'Complete' AND priority = 'Low')",
835 );
836 match result {
837 Ok(view) => {
838 println!(
839 "SUCCESS: Found {} rows with parenthetical logic",
840 view.row_count()
841 );
842 assert_eq!(view.row_count(), 2); }
844 Err(e) => {
845 panic!("Parentheses query failed: {}", e);
846 }
847 }
848
849 println!("\n=== Parentheses test complete! ===");
850 }
851
852 #[test]
853 #[ignore = "Numeric type coercion needs fixing"]
854 fn test_numeric_type_coercion() {
855 let _ = tracing_subscriber::fmt()
857 .with_max_level(tracing::Level::DEBUG)
858 .try_init();
859
860 let mut table = DataTable::new("test");
861 table.add_column(DataColumn::new("id"));
862 table.add_column(DataColumn::new("price"));
863 table.add_column(DataColumn::new("quantity"));
864
865 table
867 .add_row(DataRow::new(vec![
868 DataValue::Integer(1),
869 DataValue::Float(99.50), DataValue::Integer(100),
871 ]))
872 .unwrap();
873
874 table
875 .add_row(DataRow::new(vec![
876 DataValue::Integer(2),
877 DataValue::Float(150.0), DataValue::Integer(200),
879 ]))
880 .unwrap();
881
882 table
883 .add_row(DataRow::new(vec![
884 DataValue::Integer(3),
885 DataValue::Integer(75), DataValue::Integer(50),
887 ]))
888 .unwrap();
889
890 let table = Arc::new(table);
891 let engine = QueryEngine::new();
892
893 println!("\n=== Testing Numeric Type Coercion ===");
894 println!("Table has {} rows", table.row_count());
895 for i in 0..table.row_count() {
896 let price = table.get_value(i, 1);
897 let quantity = table.get_value(i, 2);
898 println!("Row {}: price = {:?}, quantity = {:?}", i, price, quantity);
899 }
900
901 println!("\n--- Test: price.Contains('.') ---");
903 let result = engine.execute(
904 table.clone(),
905 "SELECT * FROM test WHERE price.Contains('.')",
906 );
907 match result {
908 Ok(view) => {
909 println!(
910 "SUCCESS: Found {} rows with decimal points in price",
911 view.row_count()
912 );
913 assert_eq!(view.row_count(), 2); }
915 Err(e) => {
916 panic!("Numeric Contains query failed: {}", e);
917 }
918 }
919
920 println!("\n--- Test: quantity.Contains('0') ---");
922 let result = engine.execute(
923 table.clone(),
924 "SELECT * FROM test WHERE quantity.Contains('0')",
925 );
926 match result {
927 Ok(view) => {
928 println!(
929 "SUCCESS: Found {} rows with '0' in quantity",
930 view.row_count()
931 );
932 assert_eq!(view.row_count(), 2); }
934 Err(e) => {
935 panic!("Integer Contains query failed: {}", e);
936 }
937 }
938
939 println!("\n=== Numeric type coercion test complete! ===");
940 }
941
942 #[test]
943 fn test_datetime_comparisons() {
944 let _ = tracing_subscriber::fmt()
946 .with_max_level(tracing::Level::DEBUG)
947 .try_init();
948
949 let mut table = DataTable::new("test");
950 table.add_column(DataColumn::new("id"));
951 table.add_column(DataColumn::new("created_date"));
952
953 table
955 .add_row(DataRow::new(vec![
956 DataValue::Integer(1),
957 DataValue::String("2024-12-15".to_string()),
958 ]))
959 .unwrap();
960
961 table
962 .add_row(DataRow::new(vec![
963 DataValue::Integer(2),
964 DataValue::String("2025-01-15".to_string()),
965 ]))
966 .unwrap();
967
968 table
969 .add_row(DataRow::new(vec![
970 DataValue::Integer(3),
971 DataValue::String("2025-02-15".to_string()),
972 ]))
973 .unwrap();
974
975 let table = Arc::new(table);
976 let engine = QueryEngine::new();
977
978 println!("\n=== Testing DateTime Comparisons ===");
979 println!("Table has {} rows", table.row_count());
980 for i in 0..table.row_count() {
981 let date = table.get_value(i, 1);
982 println!("Row {}: created_date = {:?}", i, date);
983 }
984
985 println!("\n--- Test: created_date > DateTime(2025,1,1) ---");
987 let result = engine.execute(
988 table.clone(),
989 "SELECT * FROM test WHERE created_date > DateTime(2025,1,1)",
990 );
991 match result {
992 Ok(view) => {
993 println!("SUCCESS: Found {} rows after 2025-01-01", view.row_count());
994 assert_eq!(view.row_count(), 2); }
996 Err(e) => {
997 panic!("DateTime comparison query failed: {}", e);
998 }
999 }
1000
1001 println!("\n=== DateTime comparison test complete! ===");
1002 }
1003
1004 #[test]
1005 fn test_not_with_method_calls() {
1006 let _ = tracing_subscriber::fmt()
1008 .with_max_level(tracing::Level::DEBUG)
1009 .try_init();
1010
1011 let mut table = DataTable::new("test");
1012 table.add_column(DataColumn::new("id"));
1013 table.add_column(DataColumn::new("status"));
1014
1015 table
1017 .add_row(DataRow::new(vec![
1018 DataValue::Integer(1),
1019 DataValue::String("Pending Review".to_string()),
1020 ]))
1021 .unwrap();
1022
1023 table
1024 .add_row(DataRow::new(vec![
1025 DataValue::Integer(2),
1026 DataValue::String("Complete".to_string()),
1027 ]))
1028 .unwrap();
1029
1030 table
1031 .add_row(DataRow::new(vec![
1032 DataValue::Integer(3),
1033 DataValue::String("Pending Approval".to_string()),
1034 ]))
1035 .unwrap();
1036
1037 let table = Arc::new(table);
1038 let engine = QueryEngine::with_case_insensitive(true);
1039
1040 println!("\n=== Testing NOT with Method Calls ===");
1041 println!("Table has {} rows", table.row_count());
1042 for i in 0..table.row_count() {
1043 let status = table.get_value(i, 1);
1044 println!("Row {}: status = {:?}", i, status);
1045 }
1046
1047 println!("\n--- Test: NOT status.Contains('pend') ---");
1049 let result = engine.execute(
1050 table.clone(),
1051 "SELECT * FROM test WHERE NOT status.Contains('pend')",
1052 );
1053 match result {
1054 Ok(view) => {
1055 println!(
1056 "SUCCESS: Found {} rows NOT containing 'pend'",
1057 view.row_count()
1058 );
1059 assert_eq!(view.row_count(), 1); }
1061 Err(e) => {
1062 panic!("NOT Contains query failed: {}", e);
1063 }
1064 }
1065
1066 println!("\n--- Test: NOT status.StartsWith('Pending') ---");
1068 let result = engine.execute(
1069 table.clone(),
1070 "SELECT * FROM test WHERE NOT status.StartsWith('Pending')",
1071 );
1072 match result {
1073 Ok(view) => {
1074 println!(
1075 "SUCCESS: Found {} rows NOT starting with 'Pending'",
1076 view.row_count()
1077 );
1078 assert_eq!(view.row_count(), 1); }
1080 Err(e) => {
1081 panic!("NOT StartsWith query failed: {}", e);
1082 }
1083 }
1084
1085 println!("\n=== NOT with method calls test complete! ===");
1086 }
1087
1088 #[test]
1089 #[ignore = "Complex logical expressions with parentheses not yet implemented"]
1090 fn test_complex_logical_expressions() {
1091 let _ = tracing_subscriber::fmt()
1093 .with_max_level(tracing::Level::DEBUG)
1094 .try_init();
1095
1096 let mut table = DataTable::new("test");
1097 table.add_column(DataColumn::new("id"));
1098 table.add_column(DataColumn::new("status"));
1099 table.add_column(DataColumn::new("priority"));
1100 table.add_column(DataColumn::new("assigned"));
1101
1102 table
1104 .add_row(DataRow::new(vec![
1105 DataValue::Integer(1),
1106 DataValue::String("Pending".to_string()),
1107 DataValue::String("High".to_string()),
1108 DataValue::String("John".to_string()),
1109 ]))
1110 .unwrap();
1111
1112 table
1113 .add_row(DataRow::new(vec![
1114 DataValue::Integer(2),
1115 DataValue::String("Complete".to_string()),
1116 DataValue::String("High".to_string()),
1117 DataValue::String("Jane".to_string()),
1118 ]))
1119 .unwrap();
1120
1121 table
1122 .add_row(DataRow::new(vec![
1123 DataValue::Integer(3),
1124 DataValue::String("Pending".to_string()),
1125 DataValue::String("Low".to_string()),
1126 DataValue::String("John".to_string()),
1127 ]))
1128 .unwrap();
1129
1130 table
1131 .add_row(DataRow::new(vec![
1132 DataValue::Integer(4),
1133 DataValue::String("In Progress".to_string()),
1134 DataValue::String("Medium".to_string()),
1135 DataValue::String("Jane".to_string()),
1136 ]))
1137 .unwrap();
1138
1139 let table = Arc::new(table);
1140 let engine = QueryEngine::new();
1141
1142 println!("\n=== Testing Complex Logical Expressions ===");
1143 println!("Table has {} rows", table.row_count());
1144 for i in 0..table.row_count() {
1145 let status = table.get_value(i, 1);
1146 let priority = table.get_value(i, 2);
1147 let assigned = table.get_value(i, 3);
1148 println!(
1149 "Row {}: status = {:?}, priority = {:?}, assigned = {:?}",
1150 i, status, priority, assigned
1151 );
1152 }
1153
1154 println!("\n--- Test: status = 'Pending' AND (priority = 'High' OR assigned = 'John') ---");
1156 let result = engine.execute(
1157 table.clone(),
1158 "SELECT * FROM test WHERE status = 'Pending' AND (priority = 'High' OR assigned = 'John')",
1159 );
1160 match result {
1161 Ok(view) => {
1162 println!(
1163 "SUCCESS: Found {} rows with complex logic",
1164 view.row_count()
1165 );
1166 assert_eq!(view.row_count(), 2); }
1168 Err(e) => {
1169 panic!("Complex logic query failed: {}", e);
1170 }
1171 }
1172
1173 println!("\n--- Test: NOT (status.Contains('Complete') OR priority = 'Low') ---");
1175 let result = engine.execute(
1176 table.clone(),
1177 "SELECT * FROM test WHERE NOT (status.Contains('Complete') OR priority = 'Low')",
1178 );
1179 match result {
1180 Ok(view) => {
1181 println!(
1182 "SUCCESS: Found {} rows with NOT complex logic",
1183 view.row_count()
1184 );
1185 assert_eq!(view.row_count(), 2); }
1187 Err(e) => {
1188 panic!("NOT complex logic query failed: {}", e);
1189 }
1190 }
1191
1192 println!("\n=== Complex logical expressions test complete! ===");
1193 }
1194
1195 #[test]
1196 fn test_mixed_data_types_and_edge_cases() {
1197 let _ = tracing_subscriber::fmt()
1199 .with_max_level(tracing::Level::DEBUG)
1200 .try_init();
1201
1202 let mut table = DataTable::new("test");
1203 table.add_column(DataColumn::new("id"));
1204 table.add_column(DataColumn::new("value"));
1205 table.add_column(DataColumn::new("nullable_field"));
1206
1207 table
1209 .add_row(DataRow::new(vec![
1210 DataValue::Integer(1),
1211 DataValue::String("123.45".to_string()),
1212 DataValue::String("present".to_string()),
1213 ]))
1214 .unwrap();
1215
1216 table
1217 .add_row(DataRow::new(vec![
1218 DataValue::Integer(2),
1219 DataValue::Float(678.90),
1220 DataValue::Null,
1221 ]))
1222 .unwrap();
1223
1224 table
1225 .add_row(DataRow::new(vec![
1226 DataValue::Integer(3),
1227 DataValue::Boolean(true),
1228 DataValue::String("also present".to_string()),
1229 ]))
1230 .unwrap();
1231
1232 table
1233 .add_row(DataRow::new(vec![
1234 DataValue::Integer(4),
1235 DataValue::String("false".to_string()),
1236 DataValue::Null,
1237 ]))
1238 .unwrap();
1239
1240 let table = Arc::new(table);
1241 let engine = QueryEngine::new();
1242
1243 println!("\n=== Testing Mixed Data Types and Edge Cases ===");
1244 println!("Table has {} rows", table.row_count());
1245 for i in 0..table.row_count() {
1246 let value = table.get_value(i, 1);
1247 let nullable = table.get_value(i, 2);
1248 println!(
1249 "Row {}: value = {:?}, nullable_field = {:?}",
1250 i, value, nullable
1251 );
1252 }
1253
1254 println!("\n--- Test: value.Contains('true') (boolean to string coercion) ---");
1256 let result = engine.execute(
1257 table.clone(),
1258 "SELECT * FROM test WHERE value.Contains('true')",
1259 );
1260 match result {
1261 Ok(view) => {
1262 println!(
1263 "SUCCESS: Found {} rows with boolean coercion",
1264 view.row_count()
1265 );
1266 assert_eq!(view.row_count(), 1); }
1268 Err(e) => {
1269 panic!("Boolean coercion query failed: {}", e);
1270 }
1271 }
1272
1273 println!("\n--- Test: id IN (1, 3) ---");
1275 let result = engine.execute(table.clone(), "SELECT * FROM test WHERE id IN (1, 3)");
1276 match result {
1277 Ok(view) => {
1278 println!("SUCCESS: Found {} rows with IN clause", view.row_count());
1279 assert_eq!(view.row_count(), 2); }
1281 Err(e) => {
1282 panic!("Multiple IN values query failed: {}", e);
1283 }
1284 }
1285
1286 println!("\n=== Mixed data types test complete! ===");
1287 }
1288
1289 #[test]
1290 fn test_not_in_parsing() {
1291 use crate::sql::recursive_parser::Parser;
1292
1293 let query = "SELECT * FROM test WHERE country NOT IN ('CA')";
1294 println!("\n=== Testing NOT IN parsing ===");
1295 println!("Parsing query: {}", query);
1296
1297 let mut parser = Parser::new(query);
1298 match parser.parse() {
1299 Ok(statement) => {
1300 println!("Parsed statement: {:#?}", statement);
1301 if let Some(where_clause) = statement.where_clause {
1302 println!("WHERE conditions: {:#?}", where_clause.conditions);
1303 if let Some(first_condition) = where_clause.conditions.first() {
1304 println!("First condition expression: {:#?}", first_condition.expr);
1305 }
1306 }
1307 }
1308 Err(e) => {
1309 panic!("Parse error: {}", e);
1310 }
1311 }
1312 }
1313}
1314
1315#[cfg(test)]
1316#[path = "query_engine_tests.rs"]
1317mod query_engine_tests;