1use anyhow::{anyhow, Result};
2use fxhash::FxHashSet;
3use std::cmp::min;
4use std::collections::HashMap;
5use std::sync::Arc;
6use std::time::{Duration, Instant};
7use tracing::{debug, info};
8
9use crate::config::config::BehaviorConfig;
10use crate::config::global::get_date_notation;
11use crate::data::arithmetic_evaluator::ArithmeticEvaluator;
12use crate::data::data_view::DataView;
13use crate::data::datatable::{DataColumn, DataRow, DataTable, DataValue};
14use crate::data::evaluation_context::EvaluationContext;
15use crate::data::group_by_expressions::GroupByExpressions;
16use crate::data::hash_join::HashJoinExecutor;
17use crate::data::recursive_where_evaluator::RecursiveWhereEvaluator;
18use crate::data::row_expanders::RowExpanderRegistry;
19use crate::data::subquery_executor::SubqueryExecutor;
20use crate::data::temp_table_registry::TempTableRegistry;
21use crate::execution_plan::{ExecutionPlan, ExecutionPlanBuilder, StepType};
22use crate::sql::aggregates::{contains_aggregate, is_aggregate_compatible};
23use crate::sql::parser::ast::ColumnRef;
24use crate::sql::parser::ast::SetOperation;
25use crate::sql::parser::ast::TableSource;
26use crate::sql::parser::ast::WindowSpec;
27use crate::sql::recursive_parser::{
28 CTEType, OrderByItem, Parser, SelectItem, SelectStatement, SortDirection, SqlExpression,
29 TableFunction,
30};
31
32fn resolve_cte<'a>(
37 context: &'a HashMap<String, Arc<DataView>>,
38 name: &str,
39) -> Option<&'a Arc<DataView>> {
40 if let Some(v) = context.get(name) {
41 return Some(v);
42 }
43 let lower = name.to_lowercase();
44 context
45 .iter()
46 .find(|(k, _)| k.to_lowercase() == lower)
47 .map(|(_, v)| v)
48}
49
50#[derive(Debug, Clone)]
52pub struct ExecutionContext {
53 alias_map: HashMap<String, String>,
56}
57
58impl ExecutionContext {
59 pub fn new() -> Self {
61 Self {
62 alias_map: HashMap::new(),
63 }
64 }
65
66 pub fn register_alias(&mut self, alias: String, table_name: String) {
68 debug!("Registering alias: {} -> {}", alias, table_name);
69 self.alias_map.insert(alias, table_name);
70 }
71
72 pub fn resolve_alias(&self, name: &str) -> String {
75 self.alias_map
76 .get(name)
77 .cloned()
78 .unwrap_or_else(|| name.to_string())
79 }
80
81 pub fn is_alias(&self, name: &str) -> bool {
83 self.alias_map.contains_key(name)
84 }
85
86 pub fn get_aliases(&self) -> HashMap<String, String> {
88 self.alias_map.clone()
89 }
90
91 pub fn resolve_column_index(&self, table: &DataTable, column_ref: &ColumnRef) -> Result<usize> {
106 if let Some(table_prefix) = &column_ref.table_prefix {
107 let actual_table = self.resolve_alias(table_prefix);
109
110 let qualified_name = format!("{}.{}", actual_table, column_ref.name);
112 if let Some(idx) = table.find_column_by_qualified_name(&qualified_name) {
113 debug!(
114 "Resolved {}.{} -> qualified column '{}' at index {}",
115 table_prefix, column_ref.name, qualified_name, idx
116 );
117 return Ok(idx);
118 }
119
120 if let Some(idx) = table.get_column_index(&column_ref.name) {
122 debug!(
123 "Resolved {}.{} -> unqualified column '{}' at index {}",
124 table_prefix, column_ref.name, column_ref.name, idx
125 );
126 return Ok(idx);
127 }
128
129 Err(anyhow!(
131 "Column '{}' not found. Table '{}' may not support qualified column names",
132 qualified_name,
133 actual_table
134 ))
135 } else {
136 if let Some(idx) = table.get_column_index(&column_ref.name) {
138 debug!(
139 "Resolved unqualified column '{}' at index {}",
140 column_ref.name, idx
141 );
142 return Ok(idx);
143 }
144
145 if column_ref.name.contains('.') {
147 if let Some(idx) = table.find_column_by_qualified_name(&column_ref.name) {
148 debug!(
149 "Resolved '{}' as qualified column at index {}",
150 column_ref.name, idx
151 );
152 return Ok(idx);
153 }
154 }
155
156 let suggestion = self.find_similar_column(table, &column_ref.name);
158 match suggestion {
159 Some(similar) => Err(anyhow!(
160 "Column '{}' not found. Did you mean '{}'?",
161 column_ref.name,
162 similar
163 )),
164 None => Err(anyhow!("Column '{}' not found", column_ref.name)),
165 }
166 }
167 }
168
169 fn find_similar_column(&self, table: &DataTable, name: &str) -> Option<String> {
171 let columns = table.column_names();
172 let mut best_match: Option<(String, usize)> = None;
173
174 for col in columns {
175 let distance = edit_distance(name, &col);
176 if distance <= 2 {
177 match best_match {
179 Some((_, best_dist)) if distance < best_dist => {
180 best_match = Some((col.clone(), distance));
181 }
182 None => {
183 best_match = Some((col.clone(), distance));
184 }
185 _ => {}
186 }
187 }
188 }
189
190 best_match.map(|(name, _)| name)
191 }
192}
193
194impl Default for ExecutionContext {
195 fn default() -> Self {
196 Self::new()
197 }
198}
199
200fn edit_distance(a: &str, b: &str) -> usize {
202 let len_a = a.chars().count();
203 let len_b = b.chars().count();
204
205 if len_a == 0 {
206 return len_b;
207 }
208 if len_b == 0 {
209 return len_a;
210 }
211
212 let mut matrix = vec![vec![0; len_b + 1]; len_a + 1];
213
214 for i in 0..=len_a {
215 matrix[i][0] = i;
216 }
217 for j in 0..=len_b {
218 matrix[0][j] = j;
219 }
220
221 let a_chars: Vec<char> = a.chars().collect();
222 let b_chars: Vec<char> = b.chars().collect();
223
224 for i in 1..=len_a {
225 for j in 1..=len_b {
226 let cost = if a_chars[i - 1] == b_chars[j - 1] {
227 0
228 } else {
229 1
230 };
231 matrix[i][j] = min(
232 min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1),
233 matrix[i - 1][j - 1] + cost,
234 );
235 }
236 }
237
238 matrix[len_a][len_b]
239}
240
241#[derive(Clone)]
243pub struct QueryEngine {
244 case_insensitive: bool,
245 date_notation: String,
246 _behavior_config: Option<BehaviorConfig>,
247}
248
249impl Default for QueryEngine {
250 fn default() -> Self {
251 Self::new()
252 }
253}
254
255impl QueryEngine {
256 #[must_use]
257 pub fn new() -> Self {
258 Self {
259 case_insensitive: false,
260 date_notation: get_date_notation(),
261 _behavior_config: None,
262 }
263 }
264
265 #[must_use]
266 pub fn with_behavior_config(config: BehaviorConfig) -> Self {
267 let case_insensitive = config.case_insensitive_default;
268 let date_notation = get_date_notation();
270 Self {
271 case_insensitive,
272 date_notation,
273 _behavior_config: Some(config),
274 }
275 }
276
277 #[must_use]
278 pub fn with_date_notation(_date_notation: String) -> Self {
279 Self {
280 case_insensitive: false,
281 date_notation: get_date_notation(), _behavior_config: None,
283 }
284 }
285
286 #[must_use]
287 pub fn with_case_insensitive(case_insensitive: bool) -> Self {
288 Self {
289 case_insensitive,
290 date_notation: get_date_notation(),
291 _behavior_config: None,
292 }
293 }
294
295 #[must_use]
296 pub fn with_case_insensitive_and_date_notation(
297 case_insensitive: bool,
298 _date_notation: String, ) -> Self {
300 Self {
301 case_insensitive,
302 date_notation: get_date_notation(), _behavior_config: None,
304 }
305 }
306
307 fn find_similar_column(&self, table: &DataTable, name: &str) -> Option<String> {
309 let columns = table.column_names();
310 let mut best_match: Option<(String, usize)> = None;
311
312 for col in columns {
313 let distance = self.edit_distance(&col.to_lowercase(), &name.to_lowercase());
314 let max_distance = if name.len() > 10 { 3 } else { 2 };
317 if distance <= max_distance {
318 match &best_match {
319 None => best_match = Some((col, distance)),
320 Some((_, best_dist)) if distance < *best_dist => {
321 best_match = Some((col, distance));
322 }
323 _ => {}
324 }
325 }
326 }
327
328 best_match.map(|(name, _)| name)
329 }
330
331 fn edit_distance(&self, s1: &str, s2: &str) -> usize {
333 let len1 = s1.len();
334 let len2 = s2.len();
335 let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
336
337 for i in 0..=len1 {
338 matrix[i][0] = i;
339 }
340 for j in 0..=len2 {
341 matrix[0][j] = j;
342 }
343
344 for (i, c1) in s1.chars().enumerate() {
345 for (j, c2) in s2.chars().enumerate() {
346 let cost = usize::from(c1 != c2);
347 matrix[i + 1][j + 1] = std::cmp::min(
348 matrix[i][j + 1] + 1, std::cmp::min(
350 matrix[i + 1][j] + 1, matrix[i][j] + cost, ),
353 );
354 }
355 }
356
357 matrix[len1][len2]
358 }
359
360 fn contains_unnest(expr: &SqlExpression) -> bool {
362 match expr {
363 SqlExpression::Unnest { .. } => true,
365 SqlExpression::FunctionCall { name, args, .. } => {
366 if name.to_uppercase() == "UNNEST" {
367 return true;
368 }
369 args.iter().any(Self::contains_unnest)
371 }
372 SqlExpression::BinaryOp { left, right, .. } => {
373 Self::contains_unnest(left) || Self::contains_unnest(right)
374 }
375 SqlExpression::Not { expr } => Self::contains_unnest(expr),
376 SqlExpression::CaseExpression {
377 when_branches,
378 else_branch,
379 } => {
380 when_branches.iter().any(|branch| {
381 Self::contains_unnest(&branch.condition)
382 || Self::contains_unnest(&branch.result)
383 }) || else_branch
384 .as_ref()
385 .map_or(false, |e| Self::contains_unnest(e))
386 }
387 SqlExpression::SimpleCaseExpression {
388 expr,
389 when_branches,
390 else_branch,
391 } => {
392 Self::contains_unnest(expr)
393 || when_branches.iter().any(|branch| {
394 Self::contains_unnest(&branch.value)
395 || Self::contains_unnest(&branch.result)
396 })
397 || else_branch
398 .as_ref()
399 .map_or(false, |e| Self::contains_unnest(e))
400 }
401 SqlExpression::InList { expr, values } => {
402 Self::contains_unnest(expr) || values.iter().any(Self::contains_unnest)
403 }
404 SqlExpression::NotInList { expr, values } => {
405 Self::contains_unnest(expr) || values.iter().any(Self::contains_unnest)
406 }
407 SqlExpression::Between { expr, lower, upper } => {
408 Self::contains_unnest(expr)
409 || Self::contains_unnest(lower)
410 || Self::contains_unnest(upper)
411 }
412 SqlExpression::InSubquery { expr, .. } => Self::contains_unnest(expr),
413 SqlExpression::NotInSubquery { expr, .. } => Self::contains_unnest(expr),
414 SqlExpression::ScalarSubquery { .. } => false, SqlExpression::WindowFunction { args, .. } => args.iter().any(Self::contains_unnest),
416 SqlExpression::MethodCall { args, .. } => args.iter().any(Self::contains_unnest),
417 SqlExpression::ChainedMethodCall { base, args, .. } => {
418 Self::contains_unnest(base) || args.iter().any(Self::contains_unnest)
419 }
420 _ => false,
421 }
422 }
423
424 fn collect_window_specs(expr: &SqlExpression, specs: &mut Vec<WindowSpec>) {
426 match expr {
427 SqlExpression::WindowFunction {
428 window_spec, args, ..
429 } => {
430 specs.push(window_spec.clone());
432 for arg in args {
434 Self::collect_window_specs(arg, specs);
435 }
436 }
437 SqlExpression::BinaryOp { left, right, .. } => {
438 Self::collect_window_specs(left, specs);
439 Self::collect_window_specs(right, specs);
440 }
441 SqlExpression::Not { expr } => {
442 Self::collect_window_specs(expr, specs);
443 }
444 SqlExpression::FunctionCall { args, .. } => {
445 for arg in args {
446 Self::collect_window_specs(arg, specs);
447 }
448 }
449 SqlExpression::CaseExpression {
450 when_branches,
451 else_branch,
452 } => {
453 for branch in when_branches {
454 Self::collect_window_specs(&branch.condition, specs);
455 Self::collect_window_specs(&branch.result, specs);
456 }
457 if let Some(else_expr) = else_branch {
458 Self::collect_window_specs(else_expr, specs);
459 }
460 }
461 SqlExpression::SimpleCaseExpression {
462 expr,
463 when_branches,
464 else_branch,
465 } => {
466 Self::collect_window_specs(expr, specs);
467 for branch in when_branches {
468 Self::collect_window_specs(&branch.value, specs);
469 Self::collect_window_specs(&branch.result, specs);
470 }
471 if let Some(else_expr) = else_branch {
472 Self::collect_window_specs(else_expr, specs);
473 }
474 }
475 SqlExpression::InList { expr, values, .. } => {
476 Self::collect_window_specs(expr, specs);
477 for item in values {
478 Self::collect_window_specs(item, specs);
479 }
480 }
481 SqlExpression::ChainedMethodCall { base, args, .. } => {
482 Self::collect_window_specs(base, specs);
483 for arg in args {
484 Self::collect_window_specs(arg, specs);
485 }
486 }
487 SqlExpression::Column(_)
489 | SqlExpression::NumberLiteral(_)
490 | SqlExpression::StringLiteral(_)
491 | SqlExpression::BooleanLiteral(_)
492 | SqlExpression::Null
493 | SqlExpression::DateTimeToday { .. }
494 | SqlExpression::DateTimeConstructor { .. }
495 | SqlExpression::MethodCall { .. } => {}
496 _ => {}
498 }
499 }
500
501 fn contains_window_function(expr: &SqlExpression) -> bool {
503 match expr {
504 SqlExpression::WindowFunction { .. } => true,
505 SqlExpression::BinaryOp { left, right, .. } => {
506 Self::contains_window_function(left) || Self::contains_window_function(right)
507 }
508 SqlExpression::Not { expr } => Self::contains_window_function(expr),
509 SqlExpression::FunctionCall { args, .. } => {
510 args.iter().any(Self::contains_window_function)
511 }
512 SqlExpression::CaseExpression {
513 when_branches,
514 else_branch,
515 } => {
516 when_branches.iter().any(|branch| {
517 Self::contains_window_function(&branch.condition)
518 || Self::contains_window_function(&branch.result)
519 }) || else_branch
520 .as_ref()
521 .map_or(false, |e| Self::contains_window_function(e))
522 }
523 SqlExpression::SimpleCaseExpression {
524 expr,
525 when_branches,
526 else_branch,
527 } => {
528 Self::contains_window_function(expr)
529 || when_branches.iter().any(|branch| {
530 Self::contains_window_function(&branch.value)
531 || Self::contains_window_function(&branch.result)
532 })
533 || else_branch
534 .as_ref()
535 .map_or(false, |e| Self::contains_window_function(e))
536 }
537 SqlExpression::InList { expr, values } => {
538 Self::contains_window_function(expr)
539 || values.iter().any(Self::contains_window_function)
540 }
541 SqlExpression::NotInList { expr, values } => {
542 Self::contains_window_function(expr)
543 || values.iter().any(Self::contains_window_function)
544 }
545 SqlExpression::Between { expr, lower, upper } => {
546 Self::contains_window_function(expr)
547 || Self::contains_window_function(lower)
548 || Self::contains_window_function(upper)
549 }
550 SqlExpression::InSubquery { expr, .. } => Self::contains_window_function(expr),
551 SqlExpression::NotInSubquery { expr, .. } => Self::contains_window_function(expr),
552 SqlExpression::MethodCall { args, .. } => {
553 args.iter().any(Self::contains_window_function)
554 }
555 SqlExpression::ChainedMethodCall { base, args, .. } => {
556 Self::contains_window_function(base)
557 || args.iter().any(Self::contains_window_function)
558 }
559 _ => false,
560 }
561 }
562
563 fn extract_window_specs(
565 items: &[SelectItem],
566 ) -> Vec<crate::data::batch_window_evaluator::WindowFunctionSpec> {
567 let mut specs = Vec::new();
568 for (idx, item) in items.iter().enumerate() {
569 if let SelectItem::Expression { expr, .. } = item {
570 Self::collect_window_function_specs(expr, idx, &mut specs);
571 }
572 }
573 specs
574 }
575
576 fn collect_window_function_specs(
578 expr: &SqlExpression,
579 output_column_index: usize,
580 specs: &mut Vec<crate::data::batch_window_evaluator::WindowFunctionSpec>,
581 ) {
582 match expr {
583 SqlExpression::WindowFunction {
584 name,
585 args,
586 window_spec,
587 } => {
588 specs.push(crate::data::batch_window_evaluator::WindowFunctionSpec {
589 spec: window_spec.clone(),
590 function_name: name.clone(),
591 args: args.clone(),
592 output_column_index,
593 });
594 }
595 SqlExpression::BinaryOp { left, right, .. } => {
596 Self::collect_window_function_specs(left, output_column_index, specs);
597 Self::collect_window_function_specs(right, output_column_index, specs);
598 }
599 SqlExpression::Not { expr } => {
600 Self::collect_window_function_specs(expr, output_column_index, specs);
601 }
602 SqlExpression::FunctionCall { args, .. } => {
603 for arg in args {
604 Self::collect_window_function_specs(arg, output_column_index, specs);
605 }
606 }
607 SqlExpression::CaseExpression {
608 when_branches,
609 else_branch,
610 } => {
611 for branch in when_branches {
612 Self::collect_window_function_specs(
613 &branch.condition,
614 output_column_index,
615 specs,
616 );
617 Self::collect_window_function_specs(&branch.result, output_column_index, specs);
618 }
619 if let Some(e) = else_branch {
620 Self::collect_window_function_specs(e, output_column_index, specs);
621 }
622 }
623 SqlExpression::SimpleCaseExpression {
624 expr,
625 when_branches,
626 else_branch,
627 } => {
628 Self::collect_window_function_specs(expr, output_column_index, specs);
629 for branch in when_branches {
630 Self::collect_window_function_specs(&branch.value, output_column_index, specs);
631 Self::collect_window_function_specs(&branch.result, output_column_index, specs);
632 }
633 if let Some(e) = else_branch {
634 Self::collect_window_function_specs(e, output_column_index, specs);
635 }
636 }
637 SqlExpression::InList { expr, values } => {
638 Self::collect_window_function_specs(expr, output_column_index, specs);
639 for val in values {
640 Self::collect_window_function_specs(val, output_column_index, specs);
641 }
642 }
643 SqlExpression::NotInList { expr, values } => {
644 Self::collect_window_function_specs(expr, output_column_index, specs);
645 for val in values {
646 Self::collect_window_function_specs(val, output_column_index, specs);
647 }
648 }
649 SqlExpression::Between { expr, lower, upper } => {
650 Self::collect_window_function_specs(expr, output_column_index, specs);
651 Self::collect_window_function_specs(lower, output_column_index, specs);
652 Self::collect_window_function_specs(upper, output_column_index, specs);
653 }
654 SqlExpression::InSubquery { expr, .. } => {
655 Self::collect_window_function_specs(expr, output_column_index, specs);
656 }
657 SqlExpression::NotInSubquery { expr, .. } => {
658 Self::collect_window_function_specs(expr, output_column_index, specs);
659 }
660 SqlExpression::MethodCall { args, .. } => {
661 for arg in args {
662 Self::collect_window_function_specs(arg, output_column_index, specs);
663 }
664 }
665 SqlExpression::ChainedMethodCall { base, args, .. } => {
666 Self::collect_window_function_specs(base, output_column_index, specs);
667 for arg in args {
668 Self::collect_window_function_specs(arg, output_column_index, specs);
669 }
670 }
671 _ => {} }
673 }
674
675 pub fn execute(&self, table: Arc<DataTable>, sql: &str) -> Result<DataView> {
677 let (view, _plan) = self.execute_with_plan(table, sql)?;
678 Ok(view)
679 }
680
681 pub fn execute_with_temp_tables(
683 &self,
684 table: Arc<DataTable>,
685 sql: &str,
686 temp_tables: Option<&TempTableRegistry>,
687 ) -> Result<DataView> {
688 let (view, _plan) = self.execute_with_plan_and_temp_tables(table, sql, temp_tables)?;
689 Ok(view)
690 }
691
692 pub fn execute_statement(
694 &self,
695 table: Arc<DataTable>,
696 statement: SelectStatement,
697 ) -> Result<DataView> {
698 self.execute_statement_with_temp_tables(table, statement, None)
699 }
700
701 pub fn execute_statement_with_temp_tables(
703 &self,
704 table: Arc<DataTable>,
705 statement: SelectStatement,
706 temp_tables: Option<&TempTableRegistry>,
707 ) -> Result<DataView> {
708 let mut cte_context = HashMap::new();
710
711 if let Some(temp_registry) = temp_tables {
713 for table_name in temp_registry.list_tables() {
714 if let Some(temp_table) = temp_registry.get(&table_name) {
715 debug!("Adding temp table {} to CTE context", table_name);
716 let view = DataView::new(temp_table);
717 cte_context.insert(table_name, Arc::new(view));
718 }
719 }
720 }
721
722 for cte in &statement.ctes {
723 debug!("QueryEngine: Pre-processing CTE '{}'...", cte.name);
724 let cte_result = match &cte.cte_type {
726 CTEType::Standard(query) => {
727 let view = self.build_view_with_context(
729 table.clone(),
730 query.clone(),
731 &mut cte_context,
732 )?;
733
734 let mut materialized = self.materialize_view(view)?;
736
737 for column in materialized.columns_mut() {
739 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
740 column.source_table = Some(cte.name.clone());
741 }
742
743 DataView::new(Arc::new(materialized))
744 }
745 CTEType::Web(web_spec) => {
746 use crate::web::http_fetcher::WebDataFetcher;
748
749 let fetcher = WebDataFetcher::new()?;
750 let mut data_table = fetcher.fetch(web_spec, &cte.name, None)?;
752
753 for column in data_table.columns_mut() {
755 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
756 column.source_table = Some(cte.name.clone());
757 }
758
759 DataView::new(Arc::new(data_table))
761 }
762 CTEType::File(file_spec) => {
763 let mut data_table =
764 crate::data::file_walker::walk_filesystem(file_spec, &cte.name)?;
765
766 for column in data_table.columns_mut() {
767 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
768 column.source_table = Some(cte.name.clone());
769 }
770
771 DataView::new(Arc::new(data_table))
772 }
773 };
774 cte_context.insert(cte.name.clone(), Arc::new(cte_result));
776 debug!(
777 "QueryEngine: CTE '{}' pre-processed, stored in context",
778 cte.name
779 );
780 }
781
782 let mut subquery_executor =
784 SubqueryExecutor::with_cte_context(self.clone(), table.clone(), cte_context.clone());
785 let processed_statement = subquery_executor.execute_subqueries(&statement)?;
786
787 self.build_view_with_context(table, processed_statement, &mut cte_context)
789 }
790
791 pub fn execute_statement_with_cte_context(
793 &self,
794 table: Arc<DataTable>,
795 statement: SelectStatement,
796 cte_context: &HashMap<String, Arc<DataView>>,
797 ) -> Result<DataView> {
798 let mut local_context = cte_context.clone();
800
801 for cte in &statement.ctes {
803 debug!("QueryEngine: Processing nested CTE '{}'...", cte.name);
804 let cte_result = match &cte.cte_type {
805 CTEType::Standard(query) => {
806 let view = self.build_view_with_context(
807 table.clone(),
808 query.clone(),
809 &mut local_context,
810 )?;
811
812 let mut materialized = self.materialize_view(view)?;
814
815 for column in materialized.columns_mut() {
817 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
818 column.source_table = Some(cte.name.clone());
819 }
820
821 DataView::new(Arc::new(materialized))
822 }
823 CTEType::Web(web_spec) => {
824 use crate::web::http_fetcher::WebDataFetcher;
826
827 let fetcher = WebDataFetcher::new()?;
828 let mut data_table = fetcher.fetch(web_spec, &cte.name, None)?;
830
831 for column in data_table.columns_mut() {
833 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
834 column.source_table = Some(cte.name.clone());
835 }
836
837 DataView::new(Arc::new(data_table))
839 }
840 CTEType::File(file_spec) => {
841 let mut data_table =
842 crate::data::file_walker::walk_filesystem(file_spec, &cte.name)?;
843
844 for column in data_table.columns_mut() {
845 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
846 column.source_table = Some(cte.name.clone());
847 }
848
849 DataView::new(Arc::new(data_table))
850 }
851 };
852 local_context.insert(cte.name.clone(), Arc::new(cte_result));
853 }
854
855 let mut subquery_executor =
857 SubqueryExecutor::with_cte_context(self.clone(), table.clone(), local_context.clone());
858 let processed_statement = subquery_executor.execute_subqueries(&statement)?;
859
860 self.build_view_with_context(table, processed_statement, &mut local_context)
862 }
863
864 pub fn execute_with_plan(
866 &self,
867 table: Arc<DataTable>,
868 sql: &str,
869 ) -> Result<(DataView, ExecutionPlan)> {
870 self.execute_with_plan_and_temp_tables(table, sql, None)
871 }
872
873 pub fn execute_with_plan_and_temp_tables(
875 &self,
876 table: Arc<DataTable>,
877 sql: &str,
878 temp_tables: Option<&TempTableRegistry>,
879 ) -> Result<(DataView, ExecutionPlan)> {
880 let mut plan_builder = ExecutionPlanBuilder::new();
881 let start_time = Instant::now();
882
883 plan_builder.begin_step(StepType::Parse, "Parse SQL query".to_string());
885 plan_builder.add_detail(format!("Query: {}", sql));
886 let mut parser = Parser::new(sql);
887 let statement = parser
888 .parse()
889 .map_err(|e| anyhow::anyhow!("Parse error: {}", e))?;
890 plan_builder.add_detail(format!("Parsed successfully"));
891 if let Some(ref from_source) = statement.from_source {
892 match from_source {
893 TableSource::Table(name) => {
894 plan_builder.add_detail(format!("FROM: {}", name));
895 }
896 TableSource::DerivedTable { alias, .. } => {
897 plan_builder.add_detail(format!("FROM: derived table (alias: {})", alias));
898 }
899 TableSource::Pivot { .. } => {
900 plan_builder.add_detail("FROM: PIVOT".to_string());
901 }
902 }
903 }
904 if statement.where_clause.is_some() {
905 plan_builder.add_detail("WHERE clause present".to_string());
906 }
907 plan_builder.end_step();
908
909 let mut cte_context = HashMap::new();
911
912 if let Some(temp_registry) = temp_tables {
914 for table_name in temp_registry.list_tables() {
915 if let Some(temp_table) = temp_registry.get(&table_name) {
916 debug!("Adding temp table {} to CTE context", table_name);
917 let view = DataView::new(temp_table);
918 cte_context.insert(table_name, Arc::new(view));
919 }
920 }
921 }
922
923 if !statement.ctes.is_empty() {
924 plan_builder.begin_step(
925 StepType::CTE,
926 format!("Process {} CTEs", statement.ctes.len()),
927 );
928
929 for cte in &statement.ctes {
930 let cte_start = Instant::now();
931 plan_builder.begin_step(StepType::CTE, format!("CTE '{}'", cte.name));
932
933 let cte_result = match &cte.cte_type {
934 CTEType::Standard(query) => {
935 if let Some(ref from_source) = query.from_source {
937 match from_source {
938 TableSource::Table(name) => {
939 plan_builder.add_detail(format!("Source: {}", name));
940 }
941 TableSource::DerivedTable { alias, .. } => {
942 plan_builder
943 .add_detail(format!("Source: derived table ({})", alias));
944 }
945 TableSource::Pivot { .. } => {
946 plan_builder.add_detail("Source: PIVOT".to_string());
947 }
948 }
949 }
950 if query.where_clause.is_some() {
951 plan_builder.add_detail("Has WHERE clause".to_string());
952 }
953 if query.group_by.is_some() {
954 plan_builder.add_detail("Has GROUP BY".to_string());
955 }
956
957 debug!(
958 "QueryEngine: Processing CTE '{}' with existing context: {:?}",
959 cte.name,
960 cte_context.keys().collect::<Vec<_>>()
961 );
962
963 let mut subquery_executor = SubqueryExecutor::with_cte_context(
966 self.clone(),
967 table.clone(),
968 cte_context.clone(),
969 );
970 let processed_query = subquery_executor.execute_subqueries(query)?;
971
972 let view = self.build_view_with_context(
973 table.clone(),
974 processed_query,
975 &mut cte_context,
976 )?;
977
978 let mut materialized = self.materialize_view(view)?;
980
981 for column in materialized.columns_mut() {
983 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
984 column.source_table = Some(cte.name.clone());
985 }
986
987 DataView::new(Arc::new(materialized))
988 }
989 CTEType::Web(web_spec) => {
990 plan_builder.add_detail(format!("URL: {}", web_spec.url));
991 if let Some(format) = &web_spec.format {
992 plan_builder.add_detail(format!("Format: {:?}", format));
993 }
994 if let Some(cache) = web_spec.cache_seconds {
995 plan_builder.add_detail(format!("Cache: {} seconds", cache));
996 }
997
998 use crate::web::http_fetcher::WebDataFetcher;
1000
1001 let fetcher = WebDataFetcher::new()?;
1002 let mut data_table = fetcher.fetch(web_spec, &cte.name, None)?;
1004
1005 for column in data_table.columns_mut() {
1007 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
1008 column.source_table = Some(cte.name.clone());
1009 }
1010
1011 DataView::new(Arc::new(data_table))
1013 }
1014 CTEType::File(file_spec) => {
1015 plan_builder.add_detail(format!("PATH: {}", file_spec.path));
1016 if file_spec.recursive {
1017 plan_builder.add_detail("RECURSIVE".to_string());
1018 }
1019 if let Some(ref g) = file_spec.glob {
1020 plan_builder.add_detail(format!("GLOB: {}", g));
1021 }
1022 if let Some(d) = file_spec.max_depth {
1023 plan_builder.add_detail(format!("MAX_DEPTH: {}", d));
1024 }
1025
1026 let mut data_table =
1027 crate::data::file_walker::walk_filesystem(file_spec, &cte.name)?;
1028
1029 for column in data_table.columns_mut() {
1030 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
1031 column.source_table = Some(cte.name.clone());
1032 }
1033
1034 DataView::new(Arc::new(data_table))
1035 }
1036 };
1037
1038 plan_builder.set_rows_out(cte_result.row_count());
1040 plan_builder.add_detail(format!(
1041 "Result: {} rows, {} columns",
1042 cte_result.row_count(),
1043 cte_result.column_count()
1044 ));
1045 plan_builder.add_detail(format!(
1046 "Execution time: {:.3}ms",
1047 cte_start.elapsed().as_secs_f64() * 1000.0
1048 ));
1049
1050 debug!(
1051 "QueryEngine: Storing CTE '{}' in context with {} rows",
1052 cte.name,
1053 cte_result.row_count()
1054 );
1055 cte_context.insert(cte.name.clone(), Arc::new(cte_result));
1056 plan_builder.end_step();
1057 }
1058
1059 plan_builder.add_detail(format!(
1060 "All {} CTEs cached in context",
1061 statement.ctes.len()
1062 ));
1063 plan_builder.end_step();
1064 }
1065
1066 plan_builder.begin_step(StepType::Subquery, "Process subqueries".to_string());
1068 let mut subquery_executor =
1069 SubqueryExecutor::with_cte_context(self.clone(), table.clone(), cte_context.clone());
1070
1071 let has_subqueries = statement.where_clause.as_ref().map_or(false, |w| {
1073 format!("{:?}", w).contains("Subquery")
1075 });
1076
1077 if has_subqueries {
1078 plan_builder.add_detail("Evaluating subqueries in WHERE clause".to_string());
1079 }
1080
1081 let processed_statement = subquery_executor.execute_subqueries(&statement)?;
1082
1083 if has_subqueries {
1084 plan_builder.add_detail("Subqueries replaced with materialized values".to_string());
1085 } else {
1086 plan_builder.add_detail("No subqueries to process".to_string());
1087 }
1088
1089 plan_builder.end_step();
1090 let result = self.build_view_with_context_and_plan(
1091 table,
1092 processed_statement,
1093 &mut cte_context,
1094 &mut plan_builder,
1095 )?;
1096
1097 let total_duration = start_time.elapsed();
1098 info!(
1099 "Query execution complete: total={:?}, rows={}",
1100 total_duration,
1101 result.row_count()
1102 );
1103
1104 let plan = plan_builder.build();
1105 Ok((result, plan))
1106 }
1107
1108 fn build_view(&self, table: Arc<DataTable>, statement: SelectStatement) -> Result<DataView> {
1110 let mut cte_context = HashMap::new();
1111 self.build_view_with_context(table, statement, &mut cte_context)
1112 }
1113
1114 fn build_view_with_context(
1116 &self,
1117 table: Arc<DataTable>,
1118 statement: SelectStatement,
1119 cte_context: &mut HashMap<String, Arc<DataView>>,
1120 ) -> Result<DataView> {
1121 let mut dummy_plan = ExecutionPlanBuilder::new();
1122 let mut exec_context = ExecutionContext::new();
1123 self.build_view_with_context_and_plan_and_exec(
1124 table,
1125 statement,
1126 cte_context,
1127 &mut dummy_plan,
1128 &mut exec_context,
1129 )
1130 }
1131
1132 fn build_view_with_context_and_plan(
1134 &self,
1135 table: Arc<DataTable>,
1136 statement: SelectStatement,
1137 cte_context: &mut HashMap<String, Arc<DataView>>,
1138 plan: &mut ExecutionPlanBuilder,
1139 ) -> Result<DataView> {
1140 let mut exec_context = ExecutionContext::new();
1141 self.build_view_with_context_and_plan_and_exec(
1142 table,
1143 statement,
1144 cte_context,
1145 plan,
1146 &mut exec_context,
1147 )
1148 }
1149
1150 fn build_view_with_context_and_plan_and_exec(
1152 &self,
1153 table: Arc<DataTable>,
1154 statement: SelectStatement,
1155 cte_context: &mut HashMap<String, Arc<DataView>>,
1156 plan: &mut ExecutionPlanBuilder,
1157 exec_context: &mut ExecutionContext,
1158 ) -> Result<DataView> {
1159 for cte in &statement.ctes {
1161 if cte_context.contains_key(&cte.name) {
1163 debug!(
1164 "QueryEngine: CTE '{}' already in context, skipping",
1165 cte.name
1166 );
1167 continue;
1168 }
1169
1170 debug!("QueryEngine: Processing CTE '{}'...", cte.name);
1171 debug!(
1172 "QueryEngine: Available CTEs for '{}': {:?}",
1173 cte.name,
1174 cte_context.keys().collect::<Vec<_>>()
1175 );
1176
1177 let cte_result = match &cte.cte_type {
1179 CTEType::Standard(query) => {
1180 let view =
1181 self.build_view_with_context(table.clone(), query.clone(), cte_context)?;
1182
1183 let mut materialized = self.materialize_view(view)?;
1185
1186 for column in materialized.columns_mut() {
1188 column.qualified_name = Some(format!("{}.{}", cte.name, column.name));
1189 column.source_table = Some(cte.name.clone());
1190 }
1191
1192 DataView::new(Arc::new(materialized))
1193 }
1194 CTEType::Web(_web_spec) => {
1195 return Err(anyhow!(
1197 "Web CTEs should be processed in execute_select method"
1198 ));
1199 }
1200 CTEType::File(_file_spec) => {
1201 return Err(anyhow!(
1203 "FILE CTEs should be processed in execute_select method"
1204 ));
1205 }
1206 };
1207
1208 cte_context.insert(cte.name.clone(), Arc::new(cte_result));
1210 debug!(
1211 "QueryEngine: CTE '{}' processed, stored in context",
1212 cte.name
1213 );
1214 }
1215
1216 let source_table = if let Some(ref from_source) = statement.from_source {
1218 match from_source {
1219 TableSource::Table(table_name) => {
1220 if let Some(cte_view) = resolve_cte(cte_context, table_name) {
1222 debug!("QueryEngine: Using CTE '{}' as source table", table_name);
1223 let mut materialized = self.materialize_view((**cte_view).clone())?;
1225
1226 #[allow(deprecated)]
1228 if let Some(ref alias) = statement.from_alias {
1229 debug!(
1230 "QueryEngine: Applying alias '{}' to CTE '{}' qualified column names",
1231 alias, table_name
1232 );
1233 for column in materialized.columns_mut() {
1234 if let Some(ref qualified_name) = column.qualified_name {
1236 if qualified_name.starts_with(&format!("{}.", table_name)) {
1237 column.qualified_name = Some(qualified_name.replace(
1238 &format!("{}.", table_name),
1239 &format!("{}.", alias),
1240 ));
1241 }
1242 }
1243 if column.source_table.as_ref() == Some(table_name) {
1245 column.source_table = Some(alias.clone());
1246 }
1247 }
1248 }
1249
1250 Arc::new(materialized)
1251 } else {
1252 table.clone()
1254 }
1255 }
1256 TableSource::DerivedTable { query, alias } => {
1257 debug!(
1259 "QueryEngine: Processing FROM derived table (alias: {})",
1260 alias
1261 );
1262 let subquery_result =
1263 self.build_view_with_context(table.clone(), *query.clone(), cte_context)?;
1264
1265 let mut materialized = self.materialize_view(subquery_result)?;
1268
1269 for column in materialized.columns_mut() {
1273 column.source_table = Some(alias.clone());
1274 }
1275
1276 Arc::new(materialized)
1277 }
1278 TableSource::Pivot { .. } => {
1279 return Err(anyhow!(
1281 "PIVOT in FROM clause should have been expanded by preprocessing pipeline"
1282 ));
1283 }
1284 }
1285 } else {
1286 #[allow(deprecated)]
1288 if let Some(ref table_func) = statement.from_function {
1289 debug!("QueryEngine: Processing table function (deprecated field)...");
1291 match table_func {
1292 TableFunction::Generator { name, args } => {
1293 use crate::sql::generators::GeneratorRegistry;
1295
1296 let registry = GeneratorRegistry::new();
1298
1299 if let Some(generator) = registry.get(name) {
1300 let mut evaluator = ArithmeticEvaluator::with_date_notation(
1302 &table,
1303 self.date_notation.clone(),
1304 );
1305 let dummy_row = 0;
1306
1307 let mut evaluated_args = Vec::new();
1308 for arg in args {
1309 evaluated_args.push(evaluator.evaluate(arg, dummy_row)?);
1310 }
1311
1312 generator.generate(evaluated_args)?
1314 } else {
1315 return Err(anyhow!("Unknown generator function: {}", name));
1316 }
1317 }
1318 }
1319 } else {
1320 #[allow(deprecated)]
1321 if let Some(ref subquery) = statement.from_subquery {
1322 debug!("QueryEngine: Processing FROM subquery (deprecated field)...");
1324 let subquery_result = self.build_view_with_context(
1325 table.clone(),
1326 *subquery.clone(),
1327 cte_context,
1328 )?;
1329
1330 let materialized = self.materialize_view(subquery_result)?;
1333 Arc::new(materialized)
1334 } else {
1335 #[allow(deprecated)]
1336 if let Some(ref table_name) = statement.from_table {
1337 if let Some(cte_view) = resolve_cte(cte_context, table_name) {
1339 debug!(
1340 "QueryEngine: Using CTE '{}' as source table (deprecated field)",
1341 table_name
1342 );
1343 let mut materialized = self.materialize_view((**cte_view).clone())?;
1345
1346 #[allow(deprecated)]
1348 if let Some(ref alias) = statement.from_alias {
1349 debug!(
1350 "QueryEngine: Applying alias '{}' to CTE '{}' qualified column names",
1351 alias, table_name
1352 );
1353 for column in materialized.columns_mut() {
1354 if let Some(ref qualified_name) = column.qualified_name {
1356 if qualified_name.starts_with(&format!("{}.", table_name)) {
1357 column.qualified_name = Some(qualified_name.replace(
1358 &format!("{}.", table_name),
1359 &format!("{}.", alias),
1360 ));
1361 }
1362 }
1363 if column.source_table.as_ref() == Some(table_name) {
1365 column.source_table = Some(alias.clone());
1366 }
1367 }
1368 }
1369
1370 Arc::new(materialized)
1371 } else {
1372 table.clone()
1374 }
1375 } else {
1376 Arc::new(DataTable::dual())
1381 }
1382 }
1383 }
1384 };
1385
1386 #[allow(deprecated)]
1388 if let Some(ref alias) = statement.from_alias {
1389 #[allow(deprecated)]
1390 if let Some(ref table_name) = statement.from_table {
1391 exec_context.register_alias(alias.clone(), table_name.clone());
1392 }
1393 }
1394
1395 let final_table = if !statement.joins.is_empty() {
1397 plan.begin_step(
1398 StepType::Join,
1399 format!("Process {} JOINs", statement.joins.len()),
1400 );
1401 plan.set_rows_in(source_table.row_count());
1402
1403 let join_executor = HashJoinExecutor::new(self.case_insensitive);
1404
1405 #[allow(deprecated)]
1409 let base_table_name = match statement.from_source {
1410 Some(TableSource::Table(ref n)) => Some(n.clone()),
1411 _ => statement.from_table.clone(),
1412 };
1413
1414 let mut current_table = source_table;
1415
1416 for (idx, join_clause) in statement.joins.iter().enumerate() {
1417 let join_start = Instant::now();
1418 plan.begin_step(StepType::Join, format!("JOIN #{}", idx + 1));
1419 plan.add_detail(format!("Type: {:?}", join_clause.join_type));
1420 plan.add_detail(format!("Left table: {} rows", current_table.row_count()));
1421 plan.add_detail(format!(
1422 "Executing {:?} JOIN on {} condition(s)",
1423 join_clause.join_type,
1424 join_clause.condition.conditions.len()
1425 ));
1426
1427 let right_table = match &join_clause.table {
1429 TableSource::Table(name) => {
1430 if let Some(cte_view) = resolve_cte(cte_context, name) {
1432 let mut materialized = self.materialize_view((**cte_view).clone())?;
1433
1434 if let Some(ref alias) = join_clause.alias {
1436 debug!("QueryEngine: Applying JOIN alias '{}' to CTE '{}' qualified column names", alias, name);
1437 for column in materialized.columns_mut() {
1438 if let Some(ref qualified_name) = column.qualified_name {
1440 if qualified_name.starts_with(&format!("{}.", name)) {
1441 column.qualified_name = Some(qualified_name.replace(
1442 &format!("{}.", name),
1443 &format!("{}.", alias),
1444 ));
1445 }
1446 }
1447 if column.source_table.as_ref() == Some(name) {
1449 column.source_table = Some(alias.clone());
1450 }
1451 }
1452 }
1453
1454 Arc::new(materialized)
1455 } else if base_table_name.as_deref().is_some_and(|base| {
1456 if self.case_insensitive {
1457 base.eq_ignore_ascii_case(name)
1458 } else {
1459 base == name
1460 }
1461 }) {
1462 let mut materialized = (*table).clone();
1468 if let Some(ref alias) = join_clause.alias {
1469 for column in materialized.columns_mut() {
1470 if let Some(ref qualified_name) = column.qualified_name {
1471 if qualified_name.starts_with(&format!("{}.", name)) {
1472 column.qualified_name = Some(qualified_name.replace(
1473 &format!("{}.", name),
1474 &format!("{}.", alias),
1475 ));
1476 }
1477 }
1478 if column.source_table.as_ref() == Some(name) {
1479 column.source_table = Some(alias.clone());
1480 }
1481 }
1482 }
1483 Arc::new(materialized)
1484 } else {
1485 return Err(anyhow!("Cannot resolve table '{}' for JOIN", name));
1488 }
1489 }
1490 TableSource::DerivedTable { query, alias: _ } => {
1491 let subquery_result = self.build_view_with_context(
1493 table.clone(),
1494 *query.clone(),
1495 cte_context,
1496 )?;
1497 let materialized = self.materialize_view(subquery_result)?;
1498 Arc::new(materialized)
1499 }
1500 TableSource::Pivot { .. } => {
1501 return Err(anyhow!("PIVOT in JOIN clause is not yet supported"));
1503 }
1504 };
1505
1506 let joined = join_executor.execute_join(
1508 current_table.clone(),
1509 join_clause,
1510 right_table.clone(),
1511 )?;
1512
1513 plan.add_detail(format!("Right table: {} rows", right_table.row_count()));
1514 plan.set_rows_out(joined.row_count());
1515 plan.add_detail(format!("Result: {} rows", joined.row_count()));
1516 plan.add_detail(format!(
1517 "Join time: {:.3}ms",
1518 join_start.elapsed().as_secs_f64() * 1000.0
1519 ));
1520 plan.end_step();
1521
1522 current_table = Arc::new(joined);
1523 }
1524
1525 plan.set_rows_out(current_table.row_count());
1526 plan.add_detail(format!(
1527 "Final result after all joins: {} rows",
1528 current_table.row_count()
1529 ));
1530 plan.end_step();
1531 current_table
1532 } else {
1533 source_table
1534 };
1535
1536 self.build_view_internal_with_plan_and_exec(
1538 final_table,
1539 statement,
1540 plan,
1541 Some(exec_context),
1542 )
1543 }
1544
1545 pub fn materialize_view(&self, view: DataView) -> Result<DataTable> {
1547 let source = view.source();
1548 let mut result_table = DataTable::new("derived");
1549
1550 let visible_cols = view.visible_column_indices().to_vec();
1552
1553 for col_idx in &visible_cols {
1555 let col = &source.columns[*col_idx];
1556 let new_col = DataColumn {
1557 name: col.name.clone(),
1558 data_type: col.data_type.clone(),
1559 nullable: col.nullable,
1560 unique_values: col.unique_values,
1561 null_count: col.null_count,
1562 metadata: col.metadata.clone(),
1563 qualified_name: col.qualified_name.clone(), source_table: col.source_table.clone(), };
1566 result_table.add_column(new_col);
1567 }
1568
1569 for row_idx in view.windowed_row_indices() {
1572 let source_row = &source.rows[*row_idx];
1573 let mut new_row = DataRow { values: Vec::new() };
1574
1575 for col_idx in &visible_cols {
1576 new_row.values.push(source_row.values[*col_idx].clone());
1577 }
1578
1579 result_table.add_row(new_row);
1580 }
1581
1582 Ok(result_table)
1583 }
1584
1585 fn build_view_internal(
1586 &self,
1587 table: Arc<DataTable>,
1588 statement: SelectStatement,
1589 ) -> Result<DataView> {
1590 let mut dummy_plan = ExecutionPlanBuilder::new();
1591 self.build_view_internal_with_plan(table, statement, &mut dummy_plan)
1592 }
1593
1594 fn build_view_internal_with_plan(
1595 &self,
1596 table: Arc<DataTable>,
1597 statement: SelectStatement,
1598 plan: &mut ExecutionPlanBuilder,
1599 ) -> Result<DataView> {
1600 self.build_view_internal_with_plan_and_exec(table, statement, plan, None)
1601 }
1602
1603 fn build_view_internal_with_plan_and_exec(
1604 &self,
1605 table: Arc<DataTable>,
1606 statement: SelectStatement,
1607 plan: &mut ExecutionPlanBuilder,
1608 exec_context: Option<&ExecutionContext>,
1609 ) -> Result<DataView> {
1610 debug!(
1611 "QueryEngine::build_view - select_items: {:?}",
1612 statement.select_items
1613 );
1614 debug!(
1615 "QueryEngine::build_view - where_clause: {:?}",
1616 statement.where_clause
1617 );
1618
1619 let mut visible_rows: Vec<usize> = (0..table.row_count()).collect();
1621
1622 if let Some(where_clause) = &statement.where_clause {
1624 let total_rows = table.row_count();
1625 debug!("QueryEngine: Applying WHERE clause to {} rows", total_rows);
1626 debug!("QueryEngine: WHERE clause = {:?}", where_clause);
1627
1628 plan.begin_step(StepType::Filter, "WHERE clause filtering".to_string());
1629 plan.set_rows_in(total_rows);
1630 plan.add_detail(format!("Input: {} rows", total_rows));
1631
1632 for condition in &where_clause.conditions {
1634 plan.add_detail(format!("Condition: {:?}", condition.expr));
1635 }
1636
1637 let filter_start = Instant::now();
1638 let mut eval_context = EvaluationContext::new(self.case_insensitive);
1640
1641 let mut evaluator = if let Some(exec_ctx) = exec_context {
1643 RecursiveWhereEvaluator::with_both_contexts(&table, &mut eval_context, exec_ctx)
1645 } else {
1646 RecursiveWhereEvaluator::with_context(&table, &mut eval_context)
1647 };
1648
1649 let mut filtered_rows = Vec::new();
1651 for row_idx in visible_rows {
1652 if row_idx < 3 {
1654 debug!("QueryEngine: Evaluating WHERE clause for row {}", row_idx);
1655 }
1656
1657 match evaluator.evaluate(where_clause, row_idx) {
1658 Ok(result) => {
1659 if row_idx < 3 {
1660 debug!("QueryEngine: Row {} WHERE result: {}", row_idx, result);
1661 }
1662 if result {
1663 filtered_rows.push(row_idx);
1664 }
1665 }
1666 Err(e) => {
1667 if row_idx < 3 {
1668 debug!(
1669 "QueryEngine: WHERE evaluation error for row {}: {}",
1670 row_idx, e
1671 );
1672 }
1673 return Err(e);
1675 }
1676 }
1677 }
1678
1679 let (compilations, cache_hits) = eval_context.get_stats();
1681 if compilations > 0 || cache_hits > 0 {
1682 debug!(
1683 "LIKE pattern cache: {} compilations, {} cache hits",
1684 compilations, cache_hits
1685 );
1686 }
1687 visible_rows = filtered_rows;
1688 let filter_duration = filter_start.elapsed();
1689 info!(
1690 "WHERE clause filtering: {} rows -> {} rows in {:?}",
1691 total_rows,
1692 visible_rows.len(),
1693 filter_duration
1694 );
1695
1696 plan.set_rows_out(visible_rows.len());
1697 plan.add_detail(format!("Output: {} rows", visible_rows.len()));
1698 plan.add_detail(format!(
1699 "Filter time: {:.3}ms",
1700 filter_duration.as_secs_f64() * 1000.0
1701 ));
1702 plan.end_step();
1703 }
1704
1705 let mut view = DataView::new(table.clone());
1707 view = view.with_rows(visible_rows);
1708
1709 if let Some(group_by_exprs) = &statement.group_by {
1711 if !group_by_exprs.is_empty() {
1712 debug!("QueryEngine: Processing GROUP BY: {:?}", group_by_exprs);
1713
1714 plan.begin_step(
1715 StepType::GroupBy,
1716 format!("GROUP BY {} expressions", group_by_exprs.len()),
1717 );
1718 plan.set_rows_in(view.row_count());
1719 plan.add_detail(format!("Input: {} rows", view.row_count()));
1720 for expr in group_by_exprs {
1721 plan.add_detail(format!("Group by: {:?}", expr));
1722 }
1723
1724 let group_start = Instant::now();
1725 view = self.apply_group_by(
1726 view,
1727 group_by_exprs,
1728 &statement.select_items,
1729 statement.having.as_ref(),
1730 plan,
1731 )?;
1732
1733 use crate::query_plan::having_alias_transformer::HIDDEN_AGG_PREFIX;
1736 let hidden_indices: Vec<usize> = view
1737 .source()
1738 .columns
1739 .iter()
1740 .enumerate()
1741 .filter_map(|(i, c)| {
1742 if c.name.starts_with(HIDDEN_AGG_PREFIX) {
1743 Some(i)
1744 } else {
1745 None
1746 }
1747 })
1748 .collect();
1749 for &idx in hidden_indices.iter().rev() {
1750 view.hide_column(idx);
1751 }
1752
1753 plan.set_rows_out(view.row_count());
1754 plan.add_detail(format!("Output: {} groups", view.row_count()));
1755 plan.add_detail(format!(
1756 "Overall time: {:.3}ms",
1757 group_start.elapsed().as_secs_f64() * 1000.0
1758 ));
1759 plan.end_step();
1760 }
1761 } else {
1762 if !statement.select_items.is_empty() {
1764 let has_non_star_items = statement
1766 .select_items
1767 .iter()
1768 .any(|item| !matches!(item, SelectItem::Star { .. }));
1769
1770 if has_non_star_items || statement.select_items.len() > 1 {
1774 view = self.apply_select_items(
1775 view,
1776 &statement.select_items,
1777 &statement,
1778 exec_context,
1779 plan,
1780 )?;
1781 }
1782 } else if !statement.columns.is_empty() && statement.columns[0] != "*" {
1784 debug!("QueryEngine: Using legacy columns path");
1785 let source_table = view.source();
1788 let column_indices =
1789 self.resolve_column_indices(source_table, &statement.columns)?;
1790 view = view.with_columns(column_indices);
1791 }
1792 }
1793
1794 if statement.distinct {
1796 plan.begin_step(StepType::Distinct, "Remove duplicate rows".to_string());
1797 plan.set_rows_in(view.row_count());
1798 plan.add_detail(format!("Input: {} rows", view.row_count()));
1799
1800 let distinct_start = Instant::now();
1801 view = self.apply_distinct(view)?;
1802
1803 plan.set_rows_out(view.row_count());
1804 plan.add_detail(format!("Output: {} unique rows", view.row_count()));
1805 plan.add_detail(format!(
1806 "Distinct time: {:.3}ms",
1807 distinct_start.elapsed().as_secs_f64() * 1000.0
1808 ));
1809 plan.end_step();
1810 }
1811
1812 if let Some(order_by_columns) = &statement.order_by {
1814 if !order_by_columns.is_empty() {
1815 plan.begin_step(
1816 StepType::Sort,
1817 format!("ORDER BY {} columns", order_by_columns.len()),
1818 );
1819 plan.set_rows_in(view.row_count());
1820 for col in order_by_columns {
1821 let expr_str = match &col.expr {
1823 SqlExpression::Column(col_ref) => col_ref.name.clone(),
1824 _ => "expr".to_string(),
1825 };
1826 plan.add_detail(format!("{} {:?}", expr_str, col.direction));
1827 }
1828
1829 let sort_start = Instant::now();
1830 view =
1831 self.apply_multi_order_by_with_context(view, order_by_columns, exec_context)?;
1832
1833 plan.add_detail(format!(
1834 "Sort time: {:.3}ms",
1835 sort_start.elapsed().as_secs_f64() * 1000.0
1836 ));
1837 plan.end_step();
1838 }
1839 }
1840
1841 {
1846 use crate::query_plan::order_by_alias_transformer::HIDDEN_ORDERBY_PREFIX;
1847 let hidden_indices: Vec<usize> = view
1848 .source()
1849 .columns
1850 .iter()
1851 .enumerate()
1852 .filter_map(|(i, c)| {
1853 if c.name.starts_with(HIDDEN_ORDERBY_PREFIX) {
1854 Some(i)
1855 } else {
1856 None
1857 }
1858 })
1859 .collect();
1860 for &idx in hidden_indices.iter().rev() {
1861 view.hide_column(idx);
1862 }
1863 }
1864
1865 if let Some(limit) = statement.limit {
1867 let offset = statement.offset.unwrap_or(0);
1868 plan.begin_step(StepType::Limit, format!("LIMIT {}", limit));
1869 plan.set_rows_in(view.row_count());
1870 if offset > 0 {
1871 plan.add_detail(format!("OFFSET: {}", offset));
1872 }
1873 view = view.with_limit(limit, offset);
1874 plan.set_rows_out(view.row_count());
1875 plan.add_detail(format!("Output: {} rows", view.row_count()));
1876 plan.end_step();
1877 }
1878
1879 if !statement.set_operations.is_empty() {
1881 plan.begin_step(
1882 StepType::SetOperation,
1883 format!("Process {} set operations", statement.set_operations.len()),
1884 );
1885 plan.set_rows_in(view.row_count());
1886
1887 let mut combined_table = self.materialize_view(view)?;
1889 let first_columns = combined_table.column_names();
1890 let first_column_count = first_columns.len();
1891
1892 let mut needs_deduplication = false;
1894
1895 for (idx, (operation, next_statement)) in statement.set_operations.iter().enumerate() {
1897 let op_start = Instant::now();
1898 plan.begin_step(
1899 StepType::SetOperation,
1900 format!("{:?} operation #{}", operation, idx + 1),
1901 );
1902
1903 let next_view = if let Some(exec_ctx) = exec_context {
1906 self.build_view_internal_with_plan_and_exec(
1907 table.clone(),
1908 *next_statement.clone(),
1909 plan,
1910 Some(exec_ctx),
1911 )?
1912 } else {
1913 self.build_view_internal_with_plan(
1914 table.clone(),
1915 *next_statement.clone(),
1916 plan,
1917 )?
1918 };
1919
1920 let next_table = self.materialize_view(next_view)?;
1922 let next_columns = next_table.column_names();
1923 let next_column_count = next_columns.len();
1924
1925 if first_column_count != next_column_count {
1927 return Err(anyhow!(
1928 "UNION queries must have the same number of columns: first query has {} columns, but query #{} has {} columns",
1929 first_column_count,
1930 idx + 2,
1931 next_column_count
1932 ));
1933 }
1934
1935 for (col_idx, (first_col, next_col)) in
1937 first_columns.iter().zip(next_columns.iter()).enumerate()
1938 {
1939 if !first_col.eq_ignore_ascii_case(next_col) {
1940 debug!(
1941 "UNION column name mismatch at position {}: '{}' vs '{}' (using first query's name)",
1942 col_idx + 1,
1943 first_col,
1944 next_col
1945 );
1946 }
1947 }
1948
1949 plan.add_detail(format!("Left: {} rows", combined_table.row_count()));
1950 plan.add_detail(format!("Right: {} rows", next_table.row_count()));
1951
1952 match operation {
1954 SetOperation::UnionAll => {
1955 for row in next_table.rows.iter() {
1957 combined_table.add_row(row.clone());
1958 }
1959 plan.add_detail(format!(
1960 "Result: {} rows (no deduplication)",
1961 combined_table.row_count()
1962 ));
1963 }
1964 SetOperation::Union => {
1965 for row in next_table.rows.iter() {
1967 combined_table.add_row(row.clone());
1968 }
1969 needs_deduplication = true;
1970 plan.add_detail(format!(
1971 "Combined: {} rows (deduplication pending)",
1972 combined_table.row_count()
1973 ));
1974 }
1975 SetOperation::Intersect => {
1976 let right_keys: std::collections::HashSet<String> = next_table
1981 .rows
1982 .iter()
1983 .map(|r| format!("{:?}", r.values))
1984 .collect();
1985 let mut seen = std::collections::HashSet::new();
1986 let retained: Vec<_> = combined_table
1987 .rows
1988 .iter()
1989 .filter(|r| {
1990 let key = format!("{:?}", r.values);
1991 right_keys.contains(&key) && seen.insert(key)
1992 })
1993 .cloned()
1994 .collect();
1995 combined_table.rows = retained;
1996 plan.add_detail(format!(
1997 "Result: {} rows (intersection, deduplicated)",
1998 combined_table.row_count()
1999 ));
2000 }
2001 SetOperation::Except => {
2002 let right_keys: std::collections::HashSet<String> = next_table
2005 .rows
2006 .iter()
2007 .map(|r| format!("{:?}", r.values))
2008 .collect();
2009 let mut seen = std::collections::HashSet::new();
2010 let retained: Vec<_> = combined_table
2011 .rows
2012 .iter()
2013 .filter(|r| {
2014 let key = format!("{:?}", r.values);
2015 !right_keys.contains(&key) && seen.insert(key)
2016 })
2017 .cloned()
2018 .collect();
2019 combined_table.rows = retained;
2020 plan.add_detail(format!(
2021 "Result: {} rows (difference, deduplicated)",
2022 combined_table.row_count()
2023 ));
2024 }
2025 }
2026
2027 plan.add_detail(format!(
2028 "Operation time: {:.3}ms",
2029 op_start.elapsed().as_secs_f64() * 1000.0
2030 ));
2031 plan.set_rows_out(combined_table.row_count());
2032 plan.end_step();
2033 }
2034
2035 plan.set_rows_out(combined_table.row_count());
2036 plan.add_detail(format!(
2037 "Combined result: {} rows after {} operations",
2038 combined_table.row_count(),
2039 statement.set_operations.len()
2040 ));
2041 plan.end_step();
2042
2043 view = DataView::new(Arc::new(combined_table));
2045
2046 if needs_deduplication {
2048 plan.begin_step(
2049 StepType::Distinct,
2050 "UNION deduplication - remove duplicate rows".to_string(),
2051 );
2052 plan.set_rows_in(view.row_count());
2053 plan.add_detail(format!("Input: {} rows", view.row_count()));
2054
2055 let distinct_start = Instant::now();
2056 view = self.apply_distinct(view)?;
2057
2058 plan.set_rows_out(view.row_count());
2059 plan.add_detail(format!("Output: {} unique rows", view.row_count()));
2060 plan.add_detail(format!(
2061 "Deduplication time: {:.3}ms",
2062 distinct_start.elapsed().as_secs_f64() * 1000.0
2063 ));
2064 plan.end_step();
2065 }
2066 }
2067
2068 Ok(view)
2069 }
2070
2071 fn resolve_column_indices(&self, table: &DataTable, columns: &[String]) -> Result<Vec<usize>> {
2073 let mut indices = Vec::new();
2074 let table_columns = table.column_names();
2075
2076 for col_name in columns {
2077 let index = table_columns
2078 .iter()
2079 .position(|c| c.eq_ignore_ascii_case(col_name))
2080 .ok_or_else(|| {
2081 let suggestion = self.find_similar_column(table, col_name);
2082 match suggestion {
2083 Some(similar) => anyhow::anyhow!(
2084 "Column '{}' not found. Did you mean '{}'?",
2085 col_name,
2086 similar
2087 ),
2088 None => anyhow::anyhow!("Column '{}' not found", col_name),
2089 }
2090 })?;
2091 indices.push(index);
2092 }
2093
2094 Ok(indices)
2095 }
2096
2097 fn apply_select_items(
2099 &self,
2100 view: DataView,
2101 select_items: &[SelectItem],
2102 _statement: &SelectStatement,
2103 exec_context: Option<&ExecutionContext>,
2104 plan: &mut ExecutionPlanBuilder,
2105 ) -> Result<DataView> {
2106 debug!(
2107 "QueryEngine::apply_select_items - items: {:?}",
2108 select_items
2109 );
2110 debug!(
2111 "QueryEngine::apply_select_items - input view has {} rows",
2112 view.row_count()
2113 );
2114
2115 let has_window_functions = select_items.iter().any(|item| match item {
2117 SelectItem::Expression { expr, .. } => Self::contains_window_function(expr),
2118 _ => false,
2119 });
2120
2121 let window_func_count: usize = select_items
2123 .iter()
2124 .filter(|item| match item {
2125 SelectItem::Expression { expr, .. } => Self::contains_window_function(expr),
2126 _ => false,
2127 })
2128 .count();
2129
2130 let window_start = if has_window_functions {
2132 debug!(
2133 "QueryEngine::apply_select_items - detected {} window functions",
2134 window_func_count
2135 );
2136
2137 let window_specs = Self::extract_window_specs(select_items);
2139 debug!("Extracted {} window function specs", window_specs.len());
2140
2141 Some(Instant::now())
2142 } else {
2143 None
2144 };
2145
2146 let has_unnest = select_items.iter().any(|item| match item {
2148 SelectItem::Expression { expr, .. } => Self::contains_unnest(expr),
2149 _ => false,
2150 });
2151
2152 if has_unnest {
2153 debug!("QueryEngine::apply_select_items - UNNEST detected, using row expansion");
2154 return self.apply_select_with_row_expansion(view, select_items);
2155 }
2156
2157 let has_aggregates = select_items.iter().any(|item| match item {
2161 SelectItem::Expression { expr, .. } => contains_aggregate(expr),
2162 SelectItem::Column { .. } => false,
2163 SelectItem::Star { .. } => false,
2164 SelectItem::StarExclude { .. } => false,
2165 });
2166
2167 let all_aggregate_compatible = select_items.iter().all(|item| match item {
2168 SelectItem::Expression { expr, .. } => is_aggregate_compatible(expr),
2169 SelectItem::Column { .. } => false, SelectItem::Star { .. } => false, SelectItem::StarExclude { .. } => false, });
2173
2174 if has_aggregates && all_aggregate_compatible && view.row_count() > 0 {
2175 debug!("QueryEngine::apply_select_items - detected aggregate query with constants");
2178 return self.apply_aggregate_select(view, select_items);
2179 }
2180
2181 let has_computed_expressions = select_items
2183 .iter()
2184 .any(|item| matches!(item, SelectItem::Expression { .. }));
2185
2186 debug!(
2187 "QueryEngine::apply_select_items - has_computed_expressions: {}",
2188 has_computed_expressions
2189 );
2190
2191 if !has_computed_expressions {
2192 let column_indices = self.resolve_select_columns(view.source(), select_items)?;
2194 return Ok(view.with_columns(column_indices));
2195 }
2196
2197 let source_table = view.source();
2202 let visible_rows = view.visible_row_indices();
2203
2204 let mut computed_table = DataTable::new("query_result");
2207
2208 let mut expanded_items = Vec::new();
2210 for item in select_items {
2211 match item {
2212 SelectItem::Star { table_prefix, .. } => {
2213 if let Some(prefix) = table_prefix {
2214 debug!("QueryEngine::apply_select_items - expanding {}.*", prefix);
2216 for col in &source_table.columns {
2217 if Self::column_matches_table(col, prefix) {
2218 expanded_items.push(SelectItem::Column {
2219 column: ColumnRef::unquoted(col.name.clone()),
2220 leading_comments: vec![],
2221 trailing_comment: None,
2222 });
2223 }
2224 }
2225 } else {
2226 debug!("QueryEngine::apply_select_items - expanding *");
2228 for col_name in source_table.column_names() {
2229 expanded_items.push(SelectItem::Column {
2230 column: ColumnRef::unquoted(col_name.to_string()),
2231 leading_comments: vec![],
2232 trailing_comment: None,
2233 });
2234 }
2235 }
2236 }
2237 _ => expanded_items.push(item.clone()),
2238 }
2239 }
2240
2241 let mut column_name_counts: std::collections::HashMap<String, usize> =
2243 std::collections::HashMap::new();
2244
2245 for item in &expanded_items {
2246 let base_name = match item {
2247 SelectItem::Column {
2248 column: col_ref, ..
2249 } => col_ref.name.clone(),
2250 SelectItem::Expression { alias, .. } => alias.clone(),
2251 SelectItem::Star { .. } => unreachable!("Star should have been expanded"),
2252 SelectItem::StarExclude { .. } => {
2253 unreachable!("StarExclude should have been expanded")
2254 }
2255 };
2256
2257 let count = column_name_counts.entry(base_name.clone()).or_insert(0);
2259 let column_name = if *count == 0 {
2260 base_name.clone()
2262 } else {
2263 format!("{base_name}_{count}")
2265 };
2266 *count += 1;
2267
2268 computed_table.add_column(DataColumn::new(&column_name));
2269 }
2270
2271 let can_use_batch = expanded_items.iter().all(|item| {
2275 match item {
2276 SelectItem::Expression { expr, .. } => {
2277 matches!(expr, SqlExpression::WindowFunction { .. })
2280 || !Self::contains_window_function(expr)
2281 }
2282 _ => true, }
2284 });
2285
2286 let use_batch_evaluation = can_use_batch
2289 && std::env::var("SQL_CLI_BATCH_WINDOW")
2290 .map(|v| v != "0" && v.to_lowercase() != "false")
2291 .unwrap_or(true);
2292
2293 let batch_window_specs = if use_batch_evaluation && has_window_functions {
2295 debug!("BATCH window function evaluation flag is enabled");
2296 let specs = Self::extract_window_specs(&expanded_items);
2298 debug!(
2299 "Extracted {} window function specs for batch evaluation",
2300 specs.len()
2301 );
2302 Some(specs)
2303 } else {
2304 None
2305 };
2306
2307 let mut evaluator =
2314 ArithmeticEvaluator::with_date_notation(source_table, self.date_notation.clone())
2315 .with_visible_rows(view.visible_row_indices().to_vec());
2316
2317 if let Some(exec_ctx) = exec_context {
2319 let aliases = exec_ctx.get_aliases();
2320 if !aliases.is_empty() {
2321 debug!(
2322 "Applying {} aliases to evaluator: {:?}",
2323 aliases.len(),
2324 aliases
2325 );
2326 evaluator = evaluator.with_table_aliases(aliases);
2327 }
2328 }
2329
2330 if has_window_functions {
2333 let preload_start = Instant::now();
2334
2335 let mut window_specs = Vec::new();
2337 for item in &expanded_items {
2338 if let SelectItem::Expression { expr, .. } = item {
2339 Self::collect_window_specs(expr, &mut window_specs);
2340 }
2341 }
2342
2343 for spec in &window_specs {
2345 let _ = evaluator.get_or_create_window_context(spec);
2346 }
2347
2348 debug!(
2349 "Pre-created {} WindowContext(s) in {:.2}ms",
2350 window_specs.len(),
2351 preload_start.elapsed().as_secs_f64() * 1000.0
2352 );
2353 }
2354
2355 if let Some(window_specs) = batch_window_specs {
2357 debug!("Starting batch window function evaluation");
2358 let batch_start = Instant::now();
2359
2360 let mut batch_results: Vec<Vec<DataValue>> =
2362 vec![vec![DataValue::Null; expanded_items.len()]; visible_rows.len()];
2363
2364 let detailed_window_specs = &window_specs;
2366
2367 let mut specs_by_window: HashMap<
2369 u64,
2370 Vec<&crate::data::batch_window_evaluator::WindowFunctionSpec>,
2371 > = HashMap::new();
2372 for spec in detailed_window_specs {
2373 let hash = spec.spec.compute_hash();
2374 specs_by_window
2375 .entry(hash)
2376 .or_insert_with(Vec::new)
2377 .push(spec);
2378 }
2379
2380 for (_window_hash, specs) in specs_by_window {
2382 let context = evaluator.get_or_create_window_context(&specs[0].spec)?;
2384
2385 for spec in specs {
2387 match spec.function_name.as_str() {
2388 "LAG" => {
2389 if let Some(SqlExpression::Column(col_ref)) = spec.args.get(0) {
2391 let column_name = col_ref.name.as_str();
2392 let offset = if let Some(SqlExpression::NumberLiteral(n)) =
2393 spec.args.get(1)
2394 {
2395 n.parse::<i64>().unwrap_or(1)
2396 } else {
2397 1 };
2399
2400 let values = context.evaluate_lag_batch(
2401 visible_rows,
2402 column_name,
2403 offset,
2404 )?;
2405
2406 for (row_idx, value) in values.into_iter().enumerate() {
2408 batch_results[row_idx][spec.output_column_index] = value;
2409 }
2410 }
2411 }
2412 "LEAD" => {
2413 if let Some(SqlExpression::Column(col_ref)) = spec.args.get(0) {
2415 let column_name = col_ref.name.as_str();
2416 let offset = if let Some(SqlExpression::NumberLiteral(n)) =
2417 spec.args.get(1)
2418 {
2419 n.parse::<i64>().unwrap_or(1)
2420 } else {
2421 1 };
2423
2424 let values = context.evaluate_lead_batch(
2425 visible_rows,
2426 column_name,
2427 offset,
2428 )?;
2429
2430 for (row_idx, value) in values.into_iter().enumerate() {
2432 batch_results[row_idx][spec.output_column_index] = value;
2433 }
2434 }
2435 }
2436 "ROW_NUMBER" => {
2437 let values = context.evaluate_row_number_batch(visible_rows)?;
2438
2439 for (row_idx, value) in values.into_iter().enumerate() {
2441 batch_results[row_idx][spec.output_column_index] = value;
2442 }
2443 }
2444 "RANK" => {
2445 let values = context.evaluate_rank_batch(visible_rows)?;
2446
2447 for (row_idx, value) in values.into_iter().enumerate() {
2449 batch_results[row_idx][spec.output_column_index] = value;
2450 }
2451 }
2452 "DENSE_RANK" => {
2453 let values = context.evaluate_dense_rank_batch(visible_rows)?;
2454
2455 for (row_idx, value) in values.into_iter().enumerate() {
2457 batch_results[row_idx][spec.output_column_index] = value;
2458 }
2459 }
2460 "SUM" => {
2461 if let Some(SqlExpression::Column(col_ref)) = spec.args.get(0) {
2462 let column_name = col_ref.name.as_str();
2463 let values =
2464 context.evaluate_sum_batch(visible_rows, column_name)?;
2465
2466 for (row_idx, value) in values.into_iter().enumerate() {
2467 batch_results[row_idx][spec.output_column_index] = value;
2468 }
2469 }
2470 }
2471 "AVG" => {
2472 if let Some(SqlExpression::Column(col_ref)) = spec.args.get(0) {
2473 let column_name = col_ref.name.as_str();
2474 let values =
2475 context.evaluate_avg_batch(visible_rows, column_name)?;
2476
2477 for (row_idx, value) in values.into_iter().enumerate() {
2478 batch_results[row_idx][spec.output_column_index] = value;
2479 }
2480 }
2481 }
2482 "MIN" => {
2483 if let Some(SqlExpression::Column(col_ref)) = spec.args.get(0) {
2484 let column_name = col_ref.name.as_str();
2485 let values =
2486 context.evaluate_min_batch(visible_rows, column_name)?;
2487
2488 for (row_idx, value) in values.into_iter().enumerate() {
2489 batch_results[row_idx][spec.output_column_index] = value;
2490 }
2491 }
2492 }
2493 "MAX" => {
2494 if let Some(SqlExpression::Column(col_ref)) = spec.args.get(0) {
2495 let column_name = col_ref.name.as_str();
2496 let values =
2497 context.evaluate_max_batch(visible_rows, column_name)?;
2498
2499 for (row_idx, value) in values.into_iter().enumerate() {
2500 batch_results[row_idx][spec.output_column_index] = value;
2501 }
2502 }
2503 }
2504 "COUNT" => {
2505 let column_name = match spec.args.get(0) {
2507 Some(SqlExpression::Column(col_ref)) => Some(col_ref.name.as_str()),
2508 Some(SqlExpression::StringLiteral(s)) if s == "*" => None,
2509 _ => None,
2510 };
2511
2512 let values = context.evaluate_count_batch(visible_rows, column_name)?;
2513
2514 for (row_idx, value) in values.into_iter().enumerate() {
2515 batch_results[row_idx][spec.output_column_index] = value;
2516 }
2517 }
2518 "FIRST_VALUE" => {
2519 if let Some(SqlExpression::Column(col_ref)) = spec.args.get(0) {
2520 let column_name = col_ref.name.as_str();
2521 let values = context
2522 .evaluate_first_value_batch(visible_rows, column_name)?;
2523
2524 for (row_idx, value) in values.into_iter().enumerate() {
2525 batch_results[row_idx][spec.output_column_index] = value;
2526 }
2527 }
2528 }
2529 "LAST_VALUE" => {
2530 if let Some(SqlExpression::Column(col_ref)) = spec.args.get(0) {
2531 let column_name = col_ref.name.as_str();
2532 let values =
2533 context.evaluate_last_value_batch(visible_rows, column_name)?;
2534
2535 for (row_idx, value) in values.into_iter().enumerate() {
2536 batch_results[row_idx][spec.output_column_index] = value;
2537 }
2538 }
2539 }
2540 _ => {
2541 debug!(
2543 "Window function {} not supported in batch mode, using per-row",
2544 spec.function_name
2545 );
2546 }
2547 }
2548 }
2549 }
2550
2551 for (result_row_idx, &source_row_idx) in visible_rows.iter().enumerate() {
2553 for (col_idx, item) in expanded_items.iter().enumerate() {
2554 if !matches!(batch_results[result_row_idx][col_idx], DataValue::Null) {
2556 continue;
2557 }
2558
2559 let value = match item {
2560 SelectItem::Column {
2561 column: col_ref, ..
2562 } => {
2563 match evaluator
2564 .evaluate(&SqlExpression::Column(col_ref.clone()), source_row_idx)
2565 {
2566 Ok(val) => val,
2567 Err(e) => {
2568 return Err(anyhow!(
2569 "Failed to evaluate column {}: {}",
2570 col_ref.to_sql(),
2571 e
2572 ));
2573 }
2574 }
2575 }
2576 SelectItem::Expression { expr, .. } => {
2577 if matches!(expr, SqlExpression::WindowFunction { .. }) {
2580 continue;
2582 }
2583 evaluator.evaluate(&expr, source_row_idx)?
2586 }
2587 SelectItem::Star { .. } => unreachable!("Star should have been expanded"),
2588 SelectItem::StarExclude { .. } => {
2589 unreachable!("StarExclude should have been expanded")
2590 }
2591 };
2592 batch_results[result_row_idx][col_idx] = value;
2593 }
2594 }
2595
2596 for row_values in batch_results {
2598 computed_table
2599 .add_row(DataRow::new(row_values))
2600 .map_err(|e| anyhow::anyhow!("Failed to add row: {}", e))?;
2601 }
2602
2603 debug!(
2604 "Batch window evaluation completed in {:.3}ms",
2605 batch_start.elapsed().as_secs_f64() * 1000.0
2606 );
2607 } else {
2608 for &row_idx in visible_rows {
2610 let mut row_values = Vec::new();
2611
2612 for item in &expanded_items {
2613 let value = match item {
2614 SelectItem::Column {
2615 column: col_ref, ..
2616 } => {
2617 match evaluator
2619 .evaluate(&SqlExpression::Column(col_ref.clone()), row_idx)
2620 {
2621 Ok(val) => val,
2622 Err(e) => {
2623 return Err(anyhow!(
2624 "Failed to evaluate column {}: {}",
2625 col_ref.to_sql(),
2626 e
2627 ));
2628 }
2629 }
2630 }
2631 SelectItem::Expression { expr, .. } => {
2632 evaluator.evaluate(&expr, row_idx)?
2634 }
2635 SelectItem::Star { .. } => unreachable!("Star should have been expanded"),
2636 SelectItem::StarExclude { .. } => {
2637 unreachable!("StarExclude should have been expanded")
2638 }
2639 };
2640 row_values.push(value);
2641 }
2642
2643 computed_table
2644 .add_row(DataRow::new(row_values))
2645 .map_err(|e| anyhow::anyhow!("Failed to add row: {}", e))?;
2646 }
2647 }
2648
2649 if let Some(start) = window_start {
2651 let window_duration = start.elapsed();
2652 info!(
2653 "Window function evaluation took {:.2}ms for {} rows ({} window functions)",
2654 window_duration.as_secs_f64() * 1000.0,
2655 visible_rows.len(),
2656 window_func_count
2657 );
2658
2659 plan.begin_step(
2661 StepType::WindowFunction,
2662 format!("Evaluate {} window function(s)", window_func_count),
2663 );
2664 plan.set_rows_in(visible_rows.len());
2665 plan.set_rows_out(visible_rows.len());
2666 plan.add_detail(format!("Input: {} rows", visible_rows.len()));
2667 plan.add_detail(format!("{} window functions evaluated", window_func_count));
2668 plan.add_detail(format!(
2669 "Evaluation time: {:.3}ms",
2670 window_duration.as_secs_f64() * 1000.0
2671 ));
2672 plan.end_step();
2673 }
2674
2675 Ok(DataView::new(Arc::new(computed_table)))
2678 }
2679
2680 fn apply_select_with_row_expansion(
2682 &self,
2683 view: DataView,
2684 select_items: &[SelectItem],
2685 ) -> Result<DataView> {
2686 debug!("QueryEngine::apply_select_with_row_expansion - expanding rows");
2687
2688 let source_table = view.source();
2689 let visible_rows = view.visible_row_indices();
2690 let expander_registry = RowExpanderRegistry::new();
2691
2692 let mut result_table = DataTable::new("unnest_result");
2694
2695 let mut expanded_items = Vec::new();
2697 for item in select_items {
2698 match item {
2699 SelectItem::Star { table_prefix, .. } => {
2700 if let Some(prefix) = table_prefix {
2701 debug!(
2703 "QueryEngine::apply_select_with_row_expansion - expanding {}.*",
2704 prefix
2705 );
2706 for col in &source_table.columns {
2707 if Self::column_matches_table(col, prefix) {
2708 expanded_items.push(SelectItem::Column {
2709 column: ColumnRef::unquoted(col.name.clone()),
2710 leading_comments: vec![],
2711 trailing_comment: None,
2712 });
2713 }
2714 }
2715 } else {
2716 debug!("QueryEngine::apply_select_with_row_expansion - expanding *");
2718 for col_name in source_table.column_names() {
2719 expanded_items.push(SelectItem::Column {
2720 column: ColumnRef::unquoted(col_name.to_string()),
2721 leading_comments: vec![],
2722 trailing_comment: None,
2723 });
2724 }
2725 }
2726 }
2727 _ => expanded_items.push(item.clone()),
2728 }
2729 }
2730
2731 for item in &expanded_items {
2733 let column_name = match item {
2734 SelectItem::Column {
2735 column: col_ref, ..
2736 } => col_ref.name.clone(),
2737 SelectItem::Expression { alias, .. } => alias.clone(),
2738 SelectItem::Star { .. } => unreachable!("Star should have been expanded"),
2739 SelectItem::StarExclude { .. } => {
2740 unreachable!("StarExclude should have been expanded")
2741 }
2742 };
2743 result_table.add_column(DataColumn::new(&column_name));
2744 }
2745
2746 let mut evaluator =
2748 ArithmeticEvaluator::with_date_notation(source_table, self.date_notation.clone());
2749
2750 for &row_idx in visible_rows {
2751 let mut unnest_expansions = Vec::new();
2753 let mut unnest_indices = Vec::new();
2754
2755 for (col_idx, item) in expanded_items.iter().enumerate() {
2756 if let SelectItem::Expression { expr, .. } = item {
2757 if let Some(expansion_result) = self.try_expand_unnest(
2758 &expr,
2759 source_table,
2760 row_idx,
2761 &mut evaluator,
2762 &expander_registry,
2763 )? {
2764 unnest_expansions.push(expansion_result);
2765 unnest_indices.push(col_idx);
2766 }
2767 }
2768 }
2769
2770 let expansion_count = if unnest_expansions.is_empty() {
2772 1 } else {
2774 unnest_expansions
2775 .iter()
2776 .map(|exp| exp.row_count())
2777 .max()
2778 .unwrap_or(1)
2779 };
2780
2781 for output_idx in 0..expansion_count {
2783 let mut row_values = Vec::new();
2784
2785 for (col_idx, item) in expanded_items.iter().enumerate() {
2786 let unnest_position = unnest_indices.iter().position(|&idx| idx == col_idx);
2788
2789 let value = if let Some(unnest_idx) = unnest_position {
2790 let expansion = &unnest_expansions[unnest_idx];
2792 expansion
2793 .values
2794 .get(output_idx)
2795 .cloned()
2796 .unwrap_or(DataValue::Null)
2797 } else {
2798 match item {
2800 SelectItem::Column {
2801 column: col_ref, ..
2802 } => {
2803 let col_idx =
2804 source_table.get_column_index(&col_ref.name).ok_or_else(
2805 || anyhow::anyhow!("Column '{}' not found", col_ref.name),
2806 )?;
2807 let row = source_table
2808 .get_row(row_idx)
2809 .ok_or_else(|| anyhow::anyhow!("Row {} not found", row_idx))?;
2810 row.get(col_idx)
2811 .ok_or_else(|| {
2812 anyhow::anyhow!("Column {} not found in row", col_idx)
2813 })?
2814 .clone()
2815 }
2816 SelectItem::Expression { expr, .. } => {
2817 evaluator.evaluate(&expr, row_idx)?
2819 }
2820 SelectItem::Star { .. } => unreachable!(),
2821 SelectItem::StarExclude { .. } => {
2822 unreachable!("StarExclude should have been expanded")
2823 }
2824 }
2825 };
2826
2827 row_values.push(value);
2828 }
2829
2830 result_table
2831 .add_row(DataRow::new(row_values))
2832 .map_err(|e| anyhow::anyhow!("Failed to add expanded row: {}", e))?;
2833 }
2834 }
2835
2836 debug!(
2837 "QueryEngine::apply_select_with_row_expansion - input rows: {}, output rows: {}",
2838 visible_rows.len(),
2839 result_table.row_count()
2840 );
2841
2842 Ok(DataView::new(Arc::new(result_table)))
2843 }
2844
2845 fn try_expand_unnest(
2848 &self,
2849 expr: &SqlExpression,
2850 _source_table: &DataTable,
2851 row_idx: usize,
2852 evaluator: &mut ArithmeticEvaluator,
2853 expander_registry: &RowExpanderRegistry,
2854 ) -> Result<Option<crate::data::row_expanders::ExpansionResult>> {
2855 if let SqlExpression::Unnest { column, delimiter } = expr {
2857 let column_value = evaluator.evaluate(column, row_idx)?;
2859
2860 let delimiter_value = DataValue::String(delimiter.clone());
2862
2863 let expander = expander_registry
2865 .get("UNNEST")
2866 .ok_or_else(|| anyhow::anyhow!("UNNEST expander not found"))?;
2867
2868 let expansion = expander.expand(&column_value, &[delimiter_value])?;
2870 return Ok(Some(expansion));
2871 }
2872
2873 if let SqlExpression::FunctionCall { name, args, .. } = expr {
2875 if name.to_uppercase() == "UNNEST" {
2876 if args.len() != 2 {
2878 return Err(anyhow::anyhow!(
2879 "UNNEST requires exactly 2 arguments: UNNEST(column, delimiter)"
2880 ));
2881 }
2882
2883 let column_value = evaluator.evaluate(&args[0], row_idx)?;
2885
2886 let delimiter_value = evaluator.evaluate(&args[1], row_idx)?;
2888
2889 let expander = expander_registry
2891 .get("UNNEST")
2892 .ok_or_else(|| anyhow::anyhow!("UNNEST expander not found"))?;
2893
2894 let expansion = expander.expand(&column_value, &[delimiter_value])?;
2896 return Ok(Some(expansion));
2897 }
2898 }
2899
2900 Ok(None)
2901 }
2902
2903 fn apply_aggregate_select(
2905 &self,
2906 view: DataView,
2907 select_items: &[SelectItem],
2908 ) -> Result<DataView> {
2909 debug!("QueryEngine::apply_aggregate_select - creating single row aggregate result");
2910
2911 let source_table = view.source();
2912 let mut result_table = DataTable::new("aggregate_result");
2913
2914 for item in select_items {
2916 let column_name = match item {
2917 SelectItem::Expression { alias, .. } => alias.clone(),
2918 _ => unreachable!("Should only have expressions in aggregate-only query"),
2919 };
2920 result_table.add_column(DataColumn::new(&column_name));
2921 }
2922
2923 let visible_rows = view.visible_row_indices().to_vec();
2925 let mut evaluator =
2926 ArithmeticEvaluator::with_date_notation(source_table, self.date_notation.clone())
2927 .with_visible_rows(visible_rows);
2928
2929 let mut row_values = Vec::new();
2931 for item in select_items {
2932 match item {
2933 SelectItem::Expression { expr, .. } => {
2934 let value = evaluator.evaluate(expr, 0)?;
2937 row_values.push(value);
2938 }
2939 _ => unreachable!("Should only have expressions in aggregate-only query"),
2940 }
2941 }
2942
2943 result_table
2945 .add_row(DataRow::new(row_values))
2946 .map_err(|e| anyhow::anyhow!("Failed to add aggregate result row: {}", e))?;
2947
2948 Ok(DataView::new(Arc::new(result_table)))
2949 }
2950
2951 fn column_matches_table(col: &DataColumn, table_name: &str) -> bool {
2963 if let Some(ref source) = col.source_table {
2965 if source == table_name || source.ends_with(&format!(".{}", table_name)) {
2967 return true;
2968 }
2969 }
2970
2971 if let Some(ref qualified) = col.qualified_name {
2973 if qualified.starts_with(&format!("{}.", table_name)) {
2975 return true;
2976 }
2977 }
2978
2979 false
2980 }
2981
2982 fn resolve_select_columns(
2984 &self,
2985 table: &DataTable,
2986 select_items: &[SelectItem],
2987 ) -> Result<Vec<usize>> {
2988 let mut indices = Vec::new();
2989 let table_columns = table.column_names();
2990
2991 for item in select_items {
2992 match item {
2993 SelectItem::Column {
2994 column: col_ref, ..
2995 } => {
2996 let index = if let Some(table_prefix) = &col_ref.table_prefix {
2998 let qualified_name = format!("{}.{}", table_prefix, col_ref.name);
3007 table.find_column_by_qualified_name(&qualified_name)
3008 .or_else(|| {
3009 table_columns
3010 .iter()
3011 .position(|c| c.eq_ignore_ascii_case(&col_ref.name))
3012 })
3013 .ok_or_else(|| {
3014 let has_qualified = table.columns.iter()
3016 .any(|c| c.qualified_name.is_some());
3017 if !has_qualified {
3018 anyhow::anyhow!(
3019 "Column '{}' not found. Note: Table '{}' may not support qualified column names",
3020 qualified_name, table_prefix
3021 )
3022 } else {
3023 anyhow::anyhow!("Column '{}' not found", qualified_name)
3024 }
3025 })?
3026 } else {
3027 table_columns
3029 .iter()
3030 .position(|c| c.eq_ignore_ascii_case(&col_ref.name))
3031 .ok_or_else(|| {
3032 let suggestion = self.find_similar_column(table, &col_ref.name);
3033 match suggestion {
3034 Some(similar) => anyhow::anyhow!(
3035 "Column '{}' not found. Did you mean '{}'?",
3036 col_ref.name,
3037 similar
3038 ),
3039 None => anyhow::anyhow!("Column '{}' not found", col_ref.name),
3040 }
3041 })?
3042 };
3043 indices.push(index);
3044 }
3045 SelectItem::Star { table_prefix, .. } => {
3046 if let Some(prefix) = table_prefix {
3047 for (i, col) in table.columns.iter().enumerate() {
3049 if Self::column_matches_table(col, prefix) {
3050 indices.push(i);
3051 }
3052 }
3053 } else {
3054 for i in 0..table_columns.len() {
3056 indices.push(i);
3057 }
3058 }
3059 }
3060 SelectItem::StarExclude {
3061 table_prefix,
3062 excluded_columns,
3063 ..
3064 } => {
3065 if let Some(prefix) = table_prefix {
3067 for (i, col) in table.columns.iter().enumerate() {
3069 if Self::column_matches_table(col, prefix)
3070 && !excluded_columns.contains(&col.name)
3071 {
3072 indices.push(i);
3073 }
3074 }
3075 } else {
3076 for (i, col_name) in table_columns.iter().enumerate() {
3078 if !excluded_columns
3079 .iter()
3080 .any(|exc| exc.eq_ignore_ascii_case(col_name))
3081 {
3082 indices.push(i);
3083 }
3084 }
3085 }
3086 }
3087 SelectItem::Expression { .. } => {
3088 return Err(anyhow::anyhow!(
3089 "Computed expressions require new table creation"
3090 ));
3091 }
3092 }
3093 }
3094
3095 Ok(indices)
3096 }
3097
3098 fn apply_distinct(&self, view: DataView) -> Result<DataView> {
3100 use std::collections::HashSet;
3101
3102 let source = view.source();
3103 let visible_cols = view.visible_column_indices();
3104 let visible_rows = view.visible_row_indices();
3105
3106 let mut seen_rows = HashSet::new();
3108 let mut unique_row_indices = Vec::new();
3109
3110 for &row_idx in visible_rows {
3111 let mut row_key = Vec::new();
3113 for &col_idx in visible_cols {
3114 let value = source
3115 .get_value(row_idx, col_idx)
3116 .ok_or_else(|| anyhow!("Invalid cell reference"))?;
3117 row_key.push(format!("{:?}", value));
3119 }
3120
3121 if seen_rows.insert(row_key) {
3123 unique_row_indices.push(row_idx);
3125 }
3126 }
3127
3128 Ok(view.with_rows(unique_row_indices))
3130 }
3131
3132 fn apply_multi_order_by(
3134 &self,
3135 view: DataView,
3136 order_by_columns: &[OrderByItem],
3137 ) -> Result<DataView> {
3138 self.apply_multi_order_by_with_context(view, order_by_columns, None)
3139 }
3140
3141 fn apply_multi_order_by_with_context(
3143 &self,
3144 mut view: DataView,
3145 order_by_columns: &[OrderByItem],
3146 _exec_context: Option<&ExecutionContext>,
3147 ) -> Result<DataView> {
3148 let mut sort_columns = Vec::new();
3150
3151 for order_col in order_by_columns {
3152 let column_name = match &order_col.expr {
3154 SqlExpression::Column(col_ref) => col_ref.name.clone(),
3155 _ => {
3156 return Err(anyhow!(
3158 "ORDER BY expressions not yet supported - only simple columns allowed"
3159 ));
3160 }
3161 };
3162
3163 let col_index = if column_name.contains('.') {
3165 if let Some(dot_pos) = column_name.rfind('.') {
3167 let col_name = &column_name[dot_pos + 1..];
3168
3169 debug!(
3172 "ORDER BY: Extracting unqualified column '{}' from '{}'",
3173 col_name, column_name
3174 );
3175 view.source().get_column_index(col_name)
3176 } else {
3177 view.source().get_column_index(&column_name)
3178 }
3179 } else {
3180 view.source().get_column_index(&column_name)
3182 }
3183 .ok_or_else(|| {
3184 let suggestion = self.find_similar_column(view.source(), &column_name);
3186 match suggestion {
3187 Some(similar) => anyhow::anyhow!(
3188 "Column '{}' not found. Did you mean '{}'?",
3189 column_name,
3190 similar
3191 ),
3192 None => {
3193 let available_cols = view.source().column_names().join(", ");
3195 anyhow::anyhow!(
3196 "Column '{}' not found. Available columns: {}",
3197 column_name,
3198 available_cols
3199 )
3200 }
3201 }
3202 })?;
3203
3204 let ascending = matches!(order_col.direction, SortDirection::Asc);
3205 sort_columns.push((col_index, ascending));
3206 }
3207
3208 view.apply_multi_sort(&sort_columns)?;
3210 Ok(view)
3211 }
3212
3213 fn apply_group_by(
3215 &self,
3216 view: DataView,
3217 group_by_exprs: &[SqlExpression],
3218 select_items: &[SelectItem],
3219 having: Option<&SqlExpression>,
3220 plan: &mut ExecutionPlanBuilder,
3221 ) -> Result<DataView> {
3222 let (result_view, phase_info) = self.apply_group_by_expressions(
3224 view,
3225 group_by_exprs,
3226 select_items,
3227 having,
3228 self.case_insensitive,
3229 self.date_notation.clone(),
3230 )?;
3231
3232 plan.add_detail(format!("=== GROUP BY Phase Breakdown ==="));
3234 plan.add_detail(format!(
3235 "Phase 1 - Group Building: {:.3}ms",
3236 phase_info.phase2_key_building.as_secs_f64() * 1000.0
3237 ));
3238 plan.add_detail(format!(
3239 " • Processing {} rows into {} groups",
3240 phase_info.total_rows, phase_info.num_groups
3241 ));
3242 plan.add_detail(format!(
3243 "Phase 2 - Aggregation: {:.3}ms",
3244 phase_info.phase4_aggregation.as_secs_f64() * 1000.0
3245 ));
3246 if phase_info.phase4_having_evaluation > Duration::ZERO {
3247 plan.add_detail(format!(
3248 "Phase 3 - HAVING Filter: {:.3}ms",
3249 phase_info.phase4_having_evaluation.as_secs_f64() * 1000.0
3250 ));
3251 plan.add_detail(format!(
3252 " • Filtered {} groups",
3253 phase_info.groups_filtered_by_having
3254 ));
3255 }
3256 plan.add_detail(format!(
3257 "Total GROUP BY time: {:.3}ms",
3258 phase_info.total_time.as_secs_f64() * 1000.0
3259 ));
3260
3261 Ok(result_view)
3262 }
3263
3264 pub fn estimate_group_cardinality(
3267 &self,
3268 view: &DataView,
3269 group_by_exprs: &[SqlExpression],
3270 ) -> usize {
3271 let row_count = view.get_visible_rows().len();
3273 if row_count <= 100 {
3274 return row_count;
3275 }
3276
3277 let sample_size = min(1000, row_count / 10).max(100);
3279 let mut seen = FxHashSet::default();
3280
3281 let visible_rows = view.get_visible_rows();
3282 for (i, &row_idx) in visible_rows.iter().enumerate() {
3283 if i >= sample_size {
3284 break;
3285 }
3286
3287 let mut key_values = Vec::new();
3289 for expr in group_by_exprs {
3290 let mut evaluator = ArithmeticEvaluator::new(view.source());
3291 let value = evaluator.evaluate(expr, row_idx).unwrap_or(DataValue::Null);
3292 key_values.push(value);
3293 }
3294
3295 seen.insert(key_values);
3296 }
3297
3298 let sample_cardinality = seen.len();
3300 let estimated = (sample_cardinality * row_count) / sample_size;
3301
3302 estimated.min(row_count).max(sample_cardinality)
3304 }
3305}
3306
3307#[cfg(test)]
3308mod tests {
3309 use super::*;
3310 use crate::data::datatable::{DataColumn, DataRow, DataValue};
3311
3312 fn create_test_table() -> Arc<DataTable> {
3313 let mut table = DataTable::new("test");
3314
3315 table.add_column(DataColumn::new("id"));
3317 table.add_column(DataColumn::new("name"));
3318 table.add_column(DataColumn::new("age"));
3319
3320 table
3322 .add_row(DataRow::new(vec![
3323 DataValue::Integer(1),
3324 DataValue::String("Alice".to_string()),
3325 DataValue::Integer(30),
3326 ]))
3327 .unwrap();
3328
3329 table
3330 .add_row(DataRow::new(vec![
3331 DataValue::Integer(2),
3332 DataValue::String("Bob".to_string()),
3333 DataValue::Integer(25),
3334 ]))
3335 .unwrap();
3336
3337 table
3338 .add_row(DataRow::new(vec![
3339 DataValue::Integer(3),
3340 DataValue::String("Charlie".to_string()),
3341 DataValue::Integer(35),
3342 ]))
3343 .unwrap();
3344
3345 Arc::new(table)
3346 }
3347
3348 #[test]
3349 fn test_select_all() {
3350 let table = create_test_table();
3351 let engine = QueryEngine::new();
3352
3353 let view = engine
3354 .execute(table.clone(), "SELECT * FROM users")
3355 .unwrap();
3356 assert_eq!(view.row_count(), 3);
3357 assert_eq!(view.column_count(), 3);
3358 }
3359
3360 #[test]
3361 fn test_select_columns() {
3362 let table = create_test_table();
3363 let engine = QueryEngine::new();
3364
3365 let view = engine
3366 .execute(table.clone(), "SELECT name, age FROM users")
3367 .unwrap();
3368 assert_eq!(view.row_count(), 3);
3369 assert_eq!(view.column_count(), 2);
3370 }
3371
3372 #[test]
3373 fn test_select_with_limit() {
3374 let table = create_test_table();
3375 let engine = QueryEngine::new();
3376
3377 let view = engine
3378 .execute(table.clone(), "SELECT * FROM users LIMIT 2")
3379 .unwrap();
3380 assert_eq!(view.row_count(), 2);
3381 }
3382
3383 #[test]
3384 fn test_type_coercion_contains() {
3385 let _ = tracing_subscriber::fmt()
3387 .with_max_level(tracing::Level::DEBUG)
3388 .try_init();
3389
3390 let mut table = DataTable::new("test");
3391 table.add_column(DataColumn::new("id"));
3392 table.add_column(DataColumn::new("status"));
3393 table.add_column(DataColumn::new("price"));
3394
3395 table
3397 .add_row(DataRow::new(vec![
3398 DataValue::Integer(1),
3399 DataValue::String("Pending".to_string()),
3400 DataValue::Float(99.99),
3401 ]))
3402 .unwrap();
3403
3404 table
3405 .add_row(DataRow::new(vec![
3406 DataValue::Integer(2),
3407 DataValue::String("Confirmed".to_string()),
3408 DataValue::Float(150.50),
3409 ]))
3410 .unwrap();
3411
3412 table
3413 .add_row(DataRow::new(vec![
3414 DataValue::Integer(3),
3415 DataValue::String("Pending".to_string()),
3416 DataValue::Float(75.00),
3417 ]))
3418 .unwrap();
3419
3420 let table = Arc::new(table);
3421 let engine = QueryEngine::new();
3422
3423 println!("\n=== Testing WHERE clause with Contains ===");
3424 println!("Table has {} rows", table.row_count());
3425 for i in 0..table.row_count() {
3426 let status = table.get_value(i, 1);
3427 println!("Row {i}: status = {status:?}");
3428 }
3429
3430 println!("\n--- Test 1: status.Contains('pend') ---");
3432 let result = engine.execute(
3433 table.clone(),
3434 "SELECT * FROM test WHERE status.Contains('pend')",
3435 );
3436 match result {
3437 Ok(view) => {
3438 println!("SUCCESS: Found {} matching rows", view.row_count());
3439 assert_eq!(view.row_count(), 2); }
3441 Err(e) => {
3442 panic!("Query failed: {e}");
3443 }
3444 }
3445
3446 println!("\n--- Test 2: price.Contains('9') ---");
3448 let result = engine.execute(
3449 table.clone(),
3450 "SELECT * FROM test WHERE price.Contains('9')",
3451 );
3452 match result {
3453 Ok(view) => {
3454 println!(
3455 "SUCCESS: Found {} matching rows with price containing '9'",
3456 view.row_count()
3457 );
3458 assert!(view.row_count() >= 1);
3460 }
3461 Err(e) => {
3462 panic!("Numeric coercion query failed: {e}");
3463 }
3464 }
3465
3466 println!("\n=== All tests passed! ===");
3467 }
3468
3469 #[test]
3470 fn test_not_in_clause() {
3471 let _ = tracing_subscriber::fmt()
3473 .with_max_level(tracing::Level::DEBUG)
3474 .try_init();
3475
3476 let mut table = DataTable::new("test");
3477 table.add_column(DataColumn::new("id"));
3478 table.add_column(DataColumn::new("country"));
3479
3480 table
3482 .add_row(DataRow::new(vec![
3483 DataValue::Integer(1),
3484 DataValue::String("CA".to_string()),
3485 ]))
3486 .unwrap();
3487
3488 table
3489 .add_row(DataRow::new(vec![
3490 DataValue::Integer(2),
3491 DataValue::String("US".to_string()),
3492 ]))
3493 .unwrap();
3494
3495 table
3496 .add_row(DataRow::new(vec![
3497 DataValue::Integer(3),
3498 DataValue::String("UK".to_string()),
3499 ]))
3500 .unwrap();
3501
3502 let table = Arc::new(table);
3503 let engine = QueryEngine::new();
3504
3505 println!("\n=== Testing NOT IN clause ===");
3506 println!("Table has {} rows", table.row_count());
3507 for i in 0..table.row_count() {
3508 let country = table.get_value(i, 1);
3509 println!("Row {i}: country = {country:?}");
3510 }
3511
3512 println!("\n--- Test: country NOT IN ('CA') ---");
3514 let result = engine.execute(
3515 table.clone(),
3516 "SELECT * FROM test WHERE country NOT IN ('CA')",
3517 );
3518 match result {
3519 Ok(view) => {
3520 println!("SUCCESS: Found {} rows not in ('CA')", view.row_count());
3521 assert_eq!(view.row_count(), 2); }
3523 Err(e) => {
3524 panic!("NOT IN query failed: {e}");
3525 }
3526 }
3527
3528 println!("\n=== NOT IN test complete! ===");
3529 }
3530
3531 #[test]
3532 fn test_case_insensitive_in_and_not_in() {
3533 let _ = tracing_subscriber::fmt()
3535 .with_max_level(tracing::Level::DEBUG)
3536 .try_init();
3537
3538 let mut table = DataTable::new("test");
3539 table.add_column(DataColumn::new("id"));
3540 table.add_column(DataColumn::new("country"));
3541
3542 table
3544 .add_row(DataRow::new(vec![
3545 DataValue::Integer(1),
3546 DataValue::String("CA".to_string()), ]))
3548 .unwrap();
3549
3550 table
3551 .add_row(DataRow::new(vec![
3552 DataValue::Integer(2),
3553 DataValue::String("us".to_string()), ]))
3555 .unwrap();
3556
3557 table
3558 .add_row(DataRow::new(vec![
3559 DataValue::Integer(3),
3560 DataValue::String("UK".to_string()), ]))
3562 .unwrap();
3563
3564 let table = Arc::new(table);
3565
3566 println!("\n=== Testing Case-Insensitive IN clause ===");
3567 println!("Table has {} rows", table.row_count());
3568 for i in 0..table.row_count() {
3569 let country = table.get_value(i, 1);
3570 println!("Row {i}: country = {country:?}");
3571 }
3572
3573 println!("\n--- Test: country IN ('ca') with case_insensitive=true ---");
3575 let engine = QueryEngine::with_case_insensitive(true);
3576 let result = engine.execute(table.clone(), "SELECT * FROM test WHERE country IN ('ca')");
3577 match result {
3578 Ok(view) => {
3579 println!(
3580 "SUCCESS: Found {} rows matching 'ca' (case-insensitive)",
3581 view.row_count()
3582 );
3583 assert_eq!(view.row_count(), 1); }
3585 Err(e) => {
3586 panic!("Case-insensitive IN query failed: {e}");
3587 }
3588 }
3589
3590 println!("\n--- Test: country NOT IN ('ca') with case_insensitive=true ---");
3592 let result = engine.execute(
3593 table.clone(),
3594 "SELECT * FROM test WHERE country NOT IN ('ca')",
3595 );
3596 match result {
3597 Ok(view) => {
3598 println!(
3599 "SUCCESS: Found {} rows not matching 'ca' (case-insensitive)",
3600 view.row_count()
3601 );
3602 assert_eq!(view.row_count(), 2); }
3604 Err(e) => {
3605 panic!("Case-insensitive NOT IN query failed: {e}");
3606 }
3607 }
3608
3609 println!("\n--- Test: country IN ('ca') with case_insensitive=false ---");
3611 let engine_case_sensitive = QueryEngine::new(); let result = engine_case_sensitive
3613 .execute(table.clone(), "SELECT * FROM test WHERE country IN ('ca')");
3614 match result {
3615 Ok(view) => {
3616 println!(
3617 "SUCCESS: Found {} rows matching 'ca' (case-sensitive)",
3618 view.row_count()
3619 );
3620 assert_eq!(view.row_count(), 0); }
3622 Err(e) => {
3623 panic!("Case-sensitive IN query failed: {e}");
3624 }
3625 }
3626
3627 println!("\n=== Case-insensitive IN/NOT IN test complete! ===");
3628 }
3629
3630 #[test]
3631 #[ignore = "Parentheses in WHERE clause not yet implemented"]
3632 fn test_parentheses_in_where_clause() {
3633 let _ = tracing_subscriber::fmt()
3635 .with_max_level(tracing::Level::DEBUG)
3636 .try_init();
3637
3638 let mut table = DataTable::new("test");
3639 table.add_column(DataColumn::new("id"));
3640 table.add_column(DataColumn::new("status"));
3641 table.add_column(DataColumn::new("priority"));
3642
3643 table
3645 .add_row(DataRow::new(vec![
3646 DataValue::Integer(1),
3647 DataValue::String("Pending".to_string()),
3648 DataValue::String("High".to_string()),
3649 ]))
3650 .unwrap();
3651
3652 table
3653 .add_row(DataRow::new(vec![
3654 DataValue::Integer(2),
3655 DataValue::String("Complete".to_string()),
3656 DataValue::String("High".to_string()),
3657 ]))
3658 .unwrap();
3659
3660 table
3661 .add_row(DataRow::new(vec![
3662 DataValue::Integer(3),
3663 DataValue::String("Pending".to_string()),
3664 DataValue::String("Low".to_string()),
3665 ]))
3666 .unwrap();
3667
3668 table
3669 .add_row(DataRow::new(vec![
3670 DataValue::Integer(4),
3671 DataValue::String("Complete".to_string()),
3672 DataValue::String("Low".to_string()),
3673 ]))
3674 .unwrap();
3675
3676 let table = Arc::new(table);
3677 let engine = QueryEngine::new();
3678
3679 println!("\n=== Testing Parentheses in WHERE clause ===");
3680 println!("Table has {} rows", table.row_count());
3681 for i in 0..table.row_count() {
3682 let status = table.get_value(i, 1);
3683 let priority = table.get_value(i, 2);
3684 println!("Row {i}: status = {status:?}, priority = {priority:?}");
3685 }
3686
3687 println!("\n--- Test: (status = 'Pending' AND priority = 'High') OR (status = 'Complete' AND priority = 'Low') ---");
3689 let result = engine.execute(
3690 table.clone(),
3691 "SELECT * FROM test WHERE (status = 'Pending' AND priority = 'High') OR (status = 'Complete' AND priority = 'Low')",
3692 );
3693 match result {
3694 Ok(view) => {
3695 println!(
3696 "SUCCESS: Found {} rows with parenthetical logic",
3697 view.row_count()
3698 );
3699 assert_eq!(view.row_count(), 2); }
3701 Err(e) => {
3702 panic!("Parentheses query failed: {e}");
3703 }
3704 }
3705
3706 println!("\n=== Parentheses test complete! ===");
3707 }
3708
3709 #[test]
3710 #[ignore = "Numeric type coercion needs fixing"]
3711 fn test_numeric_type_coercion() {
3712 let _ = tracing_subscriber::fmt()
3714 .with_max_level(tracing::Level::DEBUG)
3715 .try_init();
3716
3717 let mut table = DataTable::new("test");
3718 table.add_column(DataColumn::new("id"));
3719 table.add_column(DataColumn::new("price"));
3720 table.add_column(DataColumn::new("quantity"));
3721
3722 table
3724 .add_row(DataRow::new(vec![
3725 DataValue::Integer(1),
3726 DataValue::Float(99.50), DataValue::Integer(100),
3728 ]))
3729 .unwrap();
3730
3731 table
3732 .add_row(DataRow::new(vec![
3733 DataValue::Integer(2),
3734 DataValue::Float(150.0), DataValue::Integer(200),
3736 ]))
3737 .unwrap();
3738
3739 table
3740 .add_row(DataRow::new(vec![
3741 DataValue::Integer(3),
3742 DataValue::Integer(75), DataValue::Integer(50),
3744 ]))
3745 .unwrap();
3746
3747 let table = Arc::new(table);
3748 let engine = QueryEngine::new();
3749
3750 println!("\n=== Testing Numeric Type Coercion ===");
3751 println!("Table has {} rows", table.row_count());
3752 for i in 0..table.row_count() {
3753 let price = table.get_value(i, 1);
3754 let quantity = table.get_value(i, 2);
3755 println!("Row {i}: price = {price:?}, quantity = {quantity:?}");
3756 }
3757
3758 println!("\n--- Test: price.Contains('.') ---");
3760 let result = engine.execute(
3761 table.clone(),
3762 "SELECT * FROM test WHERE price.Contains('.')",
3763 );
3764 match result {
3765 Ok(view) => {
3766 println!(
3767 "SUCCESS: Found {} rows with decimal points in price",
3768 view.row_count()
3769 );
3770 assert_eq!(view.row_count(), 2); }
3772 Err(e) => {
3773 panic!("Numeric Contains query failed: {e}");
3774 }
3775 }
3776
3777 println!("\n--- Test: quantity.Contains('0') ---");
3779 let result = engine.execute(
3780 table.clone(),
3781 "SELECT * FROM test WHERE quantity.Contains('0')",
3782 );
3783 match result {
3784 Ok(view) => {
3785 println!(
3786 "SUCCESS: Found {} rows with '0' in quantity",
3787 view.row_count()
3788 );
3789 assert_eq!(view.row_count(), 2); }
3791 Err(e) => {
3792 panic!("Integer Contains query failed: {e}");
3793 }
3794 }
3795
3796 println!("\n=== Numeric type coercion test complete! ===");
3797 }
3798
3799 #[test]
3800 fn test_datetime_comparisons() {
3801 let _ = tracing_subscriber::fmt()
3803 .with_max_level(tracing::Level::DEBUG)
3804 .try_init();
3805
3806 let mut table = DataTable::new("test");
3807 table.add_column(DataColumn::new("id"));
3808 table.add_column(DataColumn::new("created_date"));
3809
3810 table
3812 .add_row(DataRow::new(vec![
3813 DataValue::Integer(1),
3814 DataValue::String("2024-12-15".to_string()),
3815 ]))
3816 .unwrap();
3817
3818 table
3819 .add_row(DataRow::new(vec![
3820 DataValue::Integer(2),
3821 DataValue::String("2025-01-15".to_string()),
3822 ]))
3823 .unwrap();
3824
3825 table
3826 .add_row(DataRow::new(vec![
3827 DataValue::Integer(3),
3828 DataValue::String("2025-02-15".to_string()),
3829 ]))
3830 .unwrap();
3831
3832 let table = Arc::new(table);
3833 let engine = QueryEngine::new();
3834
3835 println!("\n=== Testing DateTime Comparisons ===");
3836 println!("Table has {} rows", table.row_count());
3837 for i in 0..table.row_count() {
3838 let date = table.get_value(i, 1);
3839 println!("Row {i}: created_date = {date:?}");
3840 }
3841
3842 println!("\n--- Test: created_date > DateTime(2025,1,1) ---");
3844 let result = engine.execute(
3845 table.clone(),
3846 "SELECT * FROM test WHERE created_date > DateTime(2025,1,1)",
3847 );
3848 match result {
3849 Ok(view) => {
3850 println!("SUCCESS: Found {} rows after 2025-01-01", view.row_count());
3851 assert_eq!(view.row_count(), 2); }
3853 Err(e) => {
3854 panic!("DateTime comparison query failed: {e}");
3855 }
3856 }
3857
3858 println!("\n=== DateTime comparison test complete! ===");
3859 }
3860
3861 #[test]
3862 fn test_not_with_method_calls() {
3863 let _ = tracing_subscriber::fmt()
3865 .with_max_level(tracing::Level::DEBUG)
3866 .try_init();
3867
3868 let mut table = DataTable::new("test");
3869 table.add_column(DataColumn::new("id"));
3870 table.add_column(DataColumn::new("status"));
3871
3872 table
3874 .add_row(DataRow::new(vec![
3875 DataValue::Integer(1),
3876 DataValue::String("Pending Review".to_string()),
3877 ]))
3878 .unwrap();
3879
3880 table
3881 .add_row(DataRow::new(vec![
3882 DataValue::Integer(2),
3883 DataValue::String("Complete".to_string()),
3884 ]))
3885 .unwrap();
3886
3887 table
3888 .add_row(DataRow::new(vec![
3889 DataValue::Integer(3),
3890 DataValue::String("Pending Approval".to_string()),
3891 ]))
3892 .unwrap();
3893
3894 let table = Arc::new(table);
3895 let engine = QueryEngine::with_case_insensitive(true);
3896
3897 println!("\n=== Testing NOT with Method Calls ===");
3898 println!("Table has {} rows", table.row_count());
3899 for i in 0..table.row_count() {
3900 let status = table.get_value(i, 1);
3901 println!("Row {i}: status = {status:?}");
3902 }
3903
3904 println!("\n--- Test: NOT status.Contains('pend') ---");
3906 let result = engine.execute(
3907 table.clone(),
3908 "SELECT * FROM test WHERE NOT status.Contains('pend')",
3909 );
3910 match result {
3911 Ok(view) => {
3912 println!(
3913 "SUCCESS: Found {} rows NOT containing 'pend'",
3914 view.row_count()
3915 );
3916 assert_eq!(view.row_count(), 1); }
3918 Err(e) => {
3919 panic!("NOT Contains query failed: {e}");
3920 }
3921 }
3922
3923 println!("\n--- Test: NOT status.StartsWith('Pending') ---");
3925 let result = engine.execute(
3926 table.clone(),
3927 "SELECT * FROM test WHERE NOT status.StartsWith('Pending')",
3928 );
3929 match result {
3930 Ok(view) => {
3931 println!(
3932 "SUCCESS: Found {} rows NOT starting with 'Pending'",
3933 view.row_count()
3934 );
3935 assert_eq!(view.row_count(), 1); }
3937 Err(e) => {
3938 panic!("NOT StartsWith query failed: {e}");
3939 }
3940 }
3941
3942 println!("\n=== NOT with method calls test complete! ===");
3943 }
3944
3945 #[test]
3946 #[ignore = "Complex logical expressions with parentheses not yet implemented"]
3947 fn test_complex_logical_expressions() {
3948 let _ = tracing_subscriber::fmt()
3950 .with_max_level(tracing::Level::DEBUG)
3951 .try_init();
3952
3953 let mut table = DataTable::new("test");
3954 table.add_column(DataColumn::new("id"));
3955 table.add_column(DataColumn::new("status"));
3956 table.add_column(DataColumn::new("priority"));
3957 table.add_column(DataColumn::new("assigned"));
3958
3959 table
3961 .add_row(DataRow::new(vec![
3962 DataValue::Integer(1),
3963 DataValue::String("Pending".to_string()),
3964 DataValue::String("High".to_string()),
3965 DataValue::String("John".to_string()),
3966 ]))
3967 .unwrap();
3968
3969 table
3970 .add_row(DataRow::new(vec![
3971 DataValue::Integer(2),
3972 DataValue::String("Complete".to_string()),
3973 DataValue::String("High".to_string()),
3974 DataValue::String("Jane".to_string()),
3975 ]))
3976 .unwrap();
3977
3978 table
3979 .add_row(DataRow::new(vec![
3980 DataValue::Integer(3),
3981 DataValue::String("Pending".to_string()),
3982 DataValue::String("Low".to_string()),
3983 DataValue::String("John".to_string()),
3984 ]))
3985 .unwrap();
3986
3987 table
3988 .add_row(DataRow::new(vec![
3989 DataValue::Integer(4),
3990 DataValue::String("In Progress".to_string()),
3991 DataValue::String("Medium".to_string()),
3992 DataValue::String("Jane".to_string()),
3993 ]))
3994 .unwrap();
3995
3996 let table = Arc::new(table);
3997 let engine = QueryEngine::new();
3998
3999 println!("\n=== Testing Complex Logical Expressions ===");
4000 println!("Table has {} rows", table.row_count());
4001 for i in 0..table.row_count() {
4002 let status = table.get_value(i, 1);
4003 let priority = table.get_value(i, 2);
4004 let assigned = table.get_value(i, 3);
4005 println!(
4006 "Row {i}: status = {status:?}, priority = {priority:?}, assigned = {assigned:?}"
4007 );
4008 }
4009
4010 println!("\n--- Test: status = 'Pending' AND (priority = 'High' OR assigned = 'John') ---");
4012 let result = engine.execute(
4013 table.clone(),
4014 "SELECT * FROM test WHERE status = 'Pending' AND (priority = 'High' OR assigned = 'John')",
4015 );
4016 match result {
4017 Ok(view) => {
4018 println!(
4019 "SUCCESS: Found {} rows with complex logic",
4020 view.row_count()
4021 );
4022 assert_eq!(view.row_count(), 2); }
4024 Err(e) => {
4025 panic!("Complex logic query failed: {e}");
4026 }
4027 }
4028
4029 println!("\n--- Test: NOT (status.Contains('Complete') OR priority = 'Low') ---");
4031 let result = engine.execute(
4032 table.clone(),
4033 "SELECT * FROM test WHERE NOT (status.Contains('Complete') OR priority = 'Low')",
4034 );
4035 match result {
4036 Ok(view) => {
4037 println!(
4038 "SUCCESS: Found {} rows with NOT complex logic",
4039 view.row_count()
4040 );
4041 assert_eq!(view.row_count(), 2); }
4043 Err(e) => {
4044 panic!("NOT complex logic query failed: {e}");
4045 }
4046 }
4047
4048 println!("\n=== Complex logical expressions test complete! ===");
4049 }
4050
4051 #[test]
4052 fn test_mixed_data_types_and_edge_cases() {
4053 let _ = tracing_subscriber::fmt()
4055 .with_max_level(tracing::Level::DEBUG)
4056 .try_init();
4057
4058 let mut table = DataTable::new("test");
4059 table.add_column(DataColumn::new("id"));
4060 table.add_column(DataColumn::new("value"));
4061 table.add_column(DataColumn::new("nullable_field"));
4062
4063 table
4065 .add_row(DataRow::new(vec![
4066 DataValue::Integer(1),
4067 DataValue::String("123.45".to_string()),
4068 DataValue::String("present".to_string()),
4069 ]))
4070 .unwrap();
4071
4072 table
4073 .add_row(DataRow::new(vec![
4074 DataValue::Integer(2),
4075 DataValue::Float(678.90),
4076 DataValue::Null,
4077 ]))
4078 .unwrap();
4079
4080 table
4081 .add_row(DataRow::new(vec![
4082 DataValue::Integer(3),
4083 DataValue::Boolean(true),
4084 DataValue::String("also present".to_string()),
4085 ]))
4086 .unwrap();
4087
4088 table
4089 .add_row(DataRow::new(vec![
4090 DataValue::Integer(4),
4091 DataValue::String("false".to_string()),
4092 DataValue::Null,
4093 ]))
4094 .unwrap();
4095
4096 let table = Arc::new(table);
4097 let engine = QueryEngine::new();
4098
4099 println!("\n=== Testing Mixed Data Types and Edge Cases ===");
4100 println!("Table has {} rows", table.row_count());
4101 for i in 0..table.row_count() {
4102 let value = table.get_value(i, 1);
4103 let nullable = table.get_value(i, 2);
4104 println!("Row {i}: value = {value:?}, nullable_field = {nullable:?}");
4105 }
4106
4107 println!("\n--- Test: value.Contains('true') (boolean to string coercion) ---");
4109 let result = engine.execute(
4110 table.clone(),
4111 "SELECT * FROM test WHERE value.Contains('true')",
4112 );
4113 match result {
4114 Ok(view) => {
4115 println!(
4116 "SUCCESS: Found {} rows with boolean coercion",
4117 view.row_count()
4118 );
4119 assert_eq!(view.row_count(), 1); }
4121 Err(e) => {
4122 panic!("Boolean coercion query failed: {e}");
4123 }
4124 }
4125
4126 println!("\n--- Test: id IN (1, 3) ---");
4128 let result = engine.execute(table.clone(), "SELECT * FROM test WHERE id IN (1, 3)");
4129 match result {
4130 Ok(view) => {
4131 println!("SUCCESS: Found {} rows with IN clause", view.row_count());
4132 assert_eq!(view.row_count(), 2); }
4134 Err(e) => {
4135 panic!("Multiple IN values query failed: {e}");
4136 }
4137 }
4138
4139 println!("\n=== Mixed data types test complete! ===");
4140 }
4141
4142 #[test]
4144 fn test_aggregate_only_single_row() {
4145 let table = create_test_stock_data();
4146 let engine = QueryEngine::new();
4147
4148 let result = engine
4150 .execute(
4151 table.clone(),
4152 "SELECT COUNT(*), MIN(close), MAX(close), AVG(close) FROM stock",
4153 )
4154 .expect("Query should succeed");
4155
4156 assert_eq!(
4157 result.row_count(),
4158 1,
4159 "Aggregate-only query should return exactly 1 row"
4160 );
4161 assert_eq!(result.column_count(), 4, "Should have 4 aggregate columns");
4162
4163 let source = result.source();
4165 let row = source.get_row(0).expect("Should have first row");
4166
4167 assert_eq!(row.values[0], DataValue::Integer(5));
4169
4170 assert_eq!(row.values[1], DataValue::Float(99.5));
4172
4173 assert_eq!(row.values[2], DataValue::Float(105.0));
4175
4176 if let DataValue::Float(avg) = &row.values[3] {
4178 assert!(
4179 (avg - 102.4).abs() < 0.01,
4180 "Average should be approximately 102.4, got {}",
4181 avg
4182 );
4183 } else {
4184 panic!("AVG should return a Float value");
4185 }
4186 }
4187
4188 #[test]
4190 fn test_single_aggregate_single_row() {
4191 let table = create_test_stock_data();
4192 let engine = QueryEngine::new();
4193
4194 let result = engine
4195 .execute(table.clone(), "SELECT COUNT(*) FROM stock")
4196 .expect("Query should succeed");
4197
4198 assert_eq!(
4199 result.row_count(),
4200 1,
4201 "Single aggregate query should return exactly 1 row"
4202 );
4203 assert_eq!(result.column_count(), 1, "Should have 1 column");
4204
4205 let source = result.source();
4206 let row = source.get_row(0).expect("Should have first row");
4207 assert_eq!(row.values[0], DataValue::Integer(5));
4208 }
4209
4210 #[test]
4212 fn test_aggregate_with_where_single_row() {
4213 let table = create_test_stock_data();
4214 let engine = QueryEngine::new();
4215
4216 let result = engine
4218 .execute(
4219 table.clone(),
4220 "SELECT COUNT(*), MIN(close), MAX(close) FROM stock WHERE close >= 103.0",
4221 )
4222 .expect("Query should succeed");
4223
4224 assert_eq!(
4225 result.row_count(),
4226 1,
4227 "Filtered aggregate query should return exactly 1 row"
4228 );
4229 assert_eq!(result.column_count(), 3, "Should have 3 aggregate columns");
4230
4231 let source = result.source();
4232 let row = source.get_row(0).expect("Should have first row");
4233
4234 assert_eq!(row.values[0], DataValue::Integer(2));
4236 assert_eq!(row.values[1], DataValue::Float(103.5)); assert_eq!(row.values[2], DataValue::Float(105.0)); }
4239
4240 #[test]
4241 fn test_not_in_parsing() {
4242 use crate::sql::recursive_parser::Parser;
4243
4244 let query = "SELECT * FROM test WHERE country NOT IN ('CA')";
4245 println!("\n=== Testing NOT IN parsing ===");
4246 println!("Parsing query: {query}");
4247
4248 let mut parser = Parser::new(query);
4249 match parser.parse() {
4250 Ok(statement) => {
4251 println!("Parsed statement: {statement:#?}");
4252 if let Some(where_clause) = statement.where_clause {
4253 println!("WHERE conditions: {:#?}", where_clause.conditions);
4254 if let Some(first_condition) = where_clause.conditions.first() {
4255 println!("First condition expression: {:#?}", first_condition.expr);
4256 }
4257 }
4258 }
4259 Err(e) => {
4260 panic!("Parse error: {e}");
4261 }
4262 }
4263 }
4264
4265 fn create_test_stock_data() -> Arc<DataTable> {
4267 let mut table = DataTable::new("stock");
4268
4269 table.add_column(DataColumn::new("symbol"));
4270 table.add_column(DataColumn::new("close"));
4271 table.add_column(DataColumn::new("volume"));
4272
4273 let test_data = vec![
4275 ("AAPL", 99.5, 1000),
4276 ("AAPL", 101.2, 1500),
4277 ("AAPL", 103.5, 2000),
4278 ("AAPL", 105.0, 1200),
4279 ("AAPL", 102.8, 1800),
4280 ];
4281
4282 for (symbol, close, volume) in test_data {
4283 table
4284 .add_row(DataRow::new(vec![
4285 DataValue::String(symbol.to_string()),
4286 DataValue::Float(close),
4287 DataValue::Integer(volume),
4288 ]))
4289 .expect("Should add row successfully");
4290 }
4291
4292 Arc::new(table)
4293 }
4294}
4295
4296#[cfg(test)]
4297#[path = "query_engine_tests.rs"]
4298mod query_engine_tests;