Skip to main content

sql_cli/data/
query_engine.rs

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
32/// Look up a CTE by name with case-insensitive fallback.
33/// Exact match is tried first (fast path); if that fails, a case-insensitive
34/// scan finds tables like `Orders` when the query references `orders`.
35/// This matches MySQL/PostgreSQL behaviour for unquoted identifiers.
36fn 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/// Execution context for tracking table aliases and scope during query execution
51#[derive(Debug, Clone)]
52pub struct ExecutionContext {
53    /// Map from alias to actual table/CTE name
54    /// Example: "t" -> "#tmp_trades", "a" -> "data"
55    alias_map: HashMap<String, String>,
56}
57
58impl ExecutionContext {
59    /// Create a new empty execution context
60    pub fn new() -> Self {
61        Self {
62            alias_map: HashMap::new(),
63        }
64    }
65
66    /// Register a table alias
67    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    /// Resolve an alias to its actual table name
73    /// Returns the alias itself if not found in the map
74    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    /// Check if a name is a registered alias
82    pub fn is_alias(&self, name: &str) -> bool {
83        self.alias_map.contains_key(name)
84    }
85
86    /// Get a copy of all registered aliases
87    pub fn get_aliases(&self) -> HashMap<String, String> {
88        self.alias_map.clone()
89    }
90
91    /// Resolve a column reference to its index in the table, handling aliases
92    ///
93    /// This is the unified column resolution function that should be used by all
94    /// SQL clauses (WHERE, SELECT, ORDER BY, GROUP BY) to ensure consistent
95    /// alias resolution behavior.
96    ///
97    /// Resolution strategy:
98    /// 1. If column_ref has a table_prefix (e.g., "t" in "t.amount"):
99    ///    a. Resolve the alias: t -> actual_table_name
100    ///    b. Try qualified lookup: "actual_table_name.amount"
101    ///    c. Fall back to unqualified: "amount"
102    /// 2. If column_ref has no prefix:
103    ///    a. Try simple column name lookup: "amount"
104    ///    b. Try as qualified name if it contains a dot: "table.column"
105    pub fn resolve_column_index(&self, table: &DataTable, column_ref: &ColumnRef) -> Result<usize> {
106        if let Some(table_prefix) = &column_ref.table_prefix {
107            // Qualified column reference: resolve the alias first
108            let actual_table = self.resolve_alias(table_prefix);
109
110            // Try qualified lookup: "actual_table.column"
111            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            // Fall back to unqualified lookup
121            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            // Not found with either qualified or unqualified name
130            Err(anyhow!(
131                "Column '{}' not found. Table '{}' may not support qualified column names",
132                qualified_name,
133                actual_table
134            ))
135        } else {
136            // Unqualified column reference
137            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 the column name contains a dot, try it as a qualified name
146            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            // Column not found - provide helpful error
157            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    /// Find a similar column name using edit distance (for better error messages)
170    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                // Allow up to 2 character differences
178                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
200/// Calculate edit distance between two strings (Levenshtein distance)
201fn 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/// Query engine that executes SQL directly on `DataTable`
242#[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        // Use get_date_notation() to respect environment variable override
269        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(), // Always use the global function
282            _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, // Keep parameter for compatibility but use get_date_notation()
299    ) -> Self {
300        Self {
301            case_insensitive,
302            date_notation: get_date_notation(), // Always use the global function
303            _behavior_config: None,
304        }
305    }
306
307    /// Find a column name similar to the given name using edit distance
308    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            // Only suggest if distance is small (likely a typo)
315            // Allow up to 3 edits for longer names
316            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    /// Calculate Levenshtein edit distance between two strings
332    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, // deletion
349                    std::cmp::min(
350                        matrix[i + 1][j] + 1, // insertion
351                        matrix[i][j] + cost,  // substitution
352                    ),
353                );
354            }
355        }
356
357        matrix[len1][len2]
358    }
359
360    /// Check if an expression contains UNNEST function call
361    fn contains_unnest(expr: &SqlExpression) -> bool {
362        match expr {
363            // Direct UNNEST variant
364            SqlExpression::Unnest { .. } => true,
365            SqlExpression::FunctionCall { name, args, .. } => {
366                if name.to_uppercase() == "UNNEST" {
367                    return true;
368                }
369                // Check recursively in function arguments
370                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, // Subqueries are handled separately
415            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    /// Collect all WindowSpecs from an expression (helper for pre-creating contexts)
425    fn collect_window_specs(expr: &SqlExpression, specs: &mut Vec<WindowSpec>) {
426        match expr {
427            SqlExpression::WindowFunction {
428                window_spec, args, ..
429            } => {
430                // Add this window spec
431                specs.push(window_spec.clone());
432                // Recursively check arguments
433                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            // Leaf nodes - no recursion needed
488            SqlExpression::Column(_)
489            | SqlExpression::NumberLiteral(_)
490            | SqlExpression::StringLiteral(_)
491            | SqlExpression::BooleanLiteral(_)
492            | SqlExpression::Null
493            | SqlExpression::DateTimeToday { .. }
494            | SqlExpression::DateTimeConstructor { .. }
495            | SqlExpression::MethodCall { .. } => {}
496            // Catch-all for any other variants
497            _ => {}
498        }
499    }
500
501    /// Check if an expression contains a window function
502    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    /// Extract all window function specifications from select items
564    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    /// Recursively collect window function specs from an expression
577    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            _ => {} // Other expression types don't contain window functions
672        }
673    }
674
675    /// Execute a SQL query on a `DataTable` and return a `DataView` (for backward compatibility)
676    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    /// Execute a SQL query with optional temp table registry access
682    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    /// Execute a parsed SelectStatement on a `DataTable` and return a `DataView`
693    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    /// Execute a parsed SelectStatement with optional temp table access
702    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        // First process CTEs to build context
709        let mut cte_context = HashMap::new();
710
711        // Add temp tables to CTE context if provided
712        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            // Execute the CTE based on its type
725            let cte_result = match &cte.cte_type {
726                CTEType::Standard(query) => {
727                    // Execute the CTE query (it might reference earlier CTEs)
728                    let view = self.build_view_with_context(
729                        table.clone(),
730                        query.clone(),
731                        &mut cte_context,
732                    )?;
733
734                    // Materialize the view and enrich columns with qualified names
735                    let mut materialized = self.materialize_view(view)?;
736
737                    // Enrich columns with qualified names for proper scoping
738                    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                    // Fetch data from URL
747                    use crate::web::http_fetcher::WebDataFetcher;
748
749                    let fetcher = WebDataFetcher::new()?;
750                    // Pass None for query context (no full SQL available in these contexts)
751                    let mut data_table = fetcher.fetch(web_spec, &cte.name, None)?;
752
753                    // Enrich columns with qualified names for proper scoping
754                    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                    // Convert DataTable to DataView
760                    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            // Store the result in the context for later use
775            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        // Now process subqueries with CTE context available
783        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        // Build the view with the same CTE context
788        self.build_view_with_context(table, processed_statement, &mut cte_context)
789    }
790
791    /// Execute a statement with provided CTE context (for subqueries)
792    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        // Clone the context so we can add any CTEs from this statement
799        let mut local_context = cte_context.clone();
800
801        // Process any CTEs in this statement (they might be nested)
802        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                    // Materialize the view and enrich columns with qualified names
813                    let mut materialized = self.materialize_view(view)?;
814
815                    // Enrich columns with qualified names for proper scoping
816                    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                    // Fetch data from URL
825                    use crate::web::http_fetcher::WebDataFetcher;
826
827                    let fetcher = WebDataFetcher::new()?;
828                    // Pass None for query context (no full SQL available in these contexts)
829                    let mut data_table = fetcher.fetch(web_spec, &cte.name, None)?;
830
831                    // Enrich columns with qualified names for proper scoping
832                    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                    // Convert DataTable to DataView
838                    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        // Process subqueries with the complete context
856        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        // Build the view
861        self.build_view_with_context(table, processed_statement, &mut local_context)
862    }
863
864    /// Execute a query and return both the result and the execution plan
865    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    /// Execute a query with temp tables and return both the result and the execution plan
874    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        // Parse the SQL query
884        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        // First process CTEs to build context
910        let mut cte_context = HashMap::new();
911
912        // Add temp tables to CTE context if provided
913        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                        // Add CTE query details
936                        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                        // Process subqueries in the CTE's query FIRST
964                        // This allows the subqueries to see all previously defined CTEs
965                        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                        // Materialize the view and enrich columns with qualified names
979                        let mut materialized = self.materialize_view(view)?;
980
981                        // Enrich columns with qualified names for proper scoping
982                        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                        // Fetch data from URL
999                        use crate::web::http_fetcher::WebDataFetcher;
1000
1001                        let fetcher = WebDataFetcher::new()?;
1002                        // Pass None for query context - each WEB CTE is independent
1003                        let mut data_table = fetcher.fetch(web_spec, &cte.name, None)?;
1004
1005                        // Enrich columns with qualified names for proper scoping
1006                        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                        // Convert DataTable to DataView
1012                        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                // Record CTE statistics
1039                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        // Process subqueries in the statement with CTE context
1067        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        // Check if there are subqueries to process
1072        let has_subqueries = statement.where_clause.as_ref().map_or(false, |w| {
1073            // This is a simplified check - in reality we'd need to walk the AST
1074            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    /// Build a `DataView` from a parsed SQL statement
1109    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    /// Build a DataView from a SelectStatement with CTE context
1115    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    /// Build a DataView from a SelectStatement with CTE context and execution plan tracking
1133    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    /// Build a DataView with CTE context, execution plan, and alias resolution context
1151    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        // First, process any CTEs that aren't already in the context
1160        for cte in &statement.ctes {
1161            // Skip if already processed (e.g., by execute_select for WEB CTEs)
1162            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            // Execute the CTE query (it might reference earlier CTEs)
1178            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                    // Materialize the view and enrich columns with qualified names
1184                    let mut materialized = self.materialize_view(view)?;
1185
1186                    // Enrich columns with qualified names for proper scoping
1187                    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                    // Web CTEs should have been processed earlier in execute_select
1196                    return Err(anyhow!(
1197                        "Web CTEs should be processed in execute_select method"
1198                    ));
1199                }
1200                CTEType::File(_file_spec) => {
1201                    // FILE CTEs (like WEB) should be processed earlier in execute_select
1202                    return Err(anyhow!(
1203                        "FILE CTEs should be processed in execute_select method"
1204                    ));
1205                }
1206            };
1207
1208            // Store the result in the context for later use
1209            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        // Determine the source table for the main query
1217        let source_table = if let Some(ref from_source) = statement.from_source {
1218            match from_source {
1219                TableSource::Table(table_name) => {
1220                    // Check if this references a CTE (case-insensitive fallback)
1221                    if let Some(cte_view) = resolve_cte(cte_context, table_name) {
1222                        debug!("QueryEngine: Using CTE '{}' as source table", table_name);
1223                        // Materialize the CTE view as a table
1224                        let mut materialized = self.materialize_view((**cte_view).clone())?;
1225
1226                        // Apply alias to qualified column names if present
1227                        #[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                                // Replace the CTE name with the alias in qualified names
1235                                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                                // Update source table to reflect the alias
1244                                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                        // Regular table reference - use the provided table
1253                        table.clone()
1254                    }
1255                }
1256                TableSource::DerivedTable { query, alias } => {
1257                    // Execute the subquery and use its result as the source
1258                    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                    // Convert the DataView to a DataTable for use as source
1266                    // This materializes the subquery result
1267                    let mut materialized = self.materialize_view(subquery_result)?;
1268
1269                    // Apply the alias to all columns in the derived table
1270                    // Note: We set source_table but keep the column names unqualified
1271                    // so they can be referenced without the table prefix
1272                    for column in materialized.columns_mut() {
1273                        column.source_table = Some(alias.clone());
1274                    }
1275
1276                    Arc::new(materialized)
1277                }
1278                TableSource::Pivot { .. } => {
1279                    // PIVOT should have been expanded by PivotExpander transformer
1280                    return Err(anyhow!(
1281                        "PIVOT in FROM clause should have been expanded by preprocessing pipeline"
1282                    ));
1283                }
1284            }
1285        } else {
1286            // Fallback to deprecated fields for backward compatibility
1287            #[allow(deprecated)]
1288            if let Some(ref table_func) = statement.from_function {
1289                // Handle table functions like RANGE()
1290                debug!("QueryEngine: Processing table function (deprecated field)...");
1291                match table_func {
1292                    TableFunction::Generator { name, args } => {
1293                        // Use the generator registry to create the table
1294                        use crate::sql::generators::GeneratorRegistry;
1295
1296                        // Create generator registry (could be cached in QueryEngine)
1297                        let registry = GeneratorRegistry::new();
1298
1299                        if let Some(generator) = registry.get(name) {
1300                            // Evaluate arguments
1301                            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                            // Generate the table
1313                            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                    // Execute the subquery and use its result as the source
1323                    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                    // Convert the DataView to a DataTable for use as source
1331                    // This materializes the subquery result
1332                    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                        // Check if this references a CTE (case-insensitive fallback)
1338                        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                            // Materialize the CTE view as a table
1344                            let mut materialized = self.materialize_view((**cte_view).clone())?;
1345
1346                            // Apply alias to qualified column names if present
1347                            #[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                                    // Replace the CTE name with the alias in qualified names
1355                                    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                                    // Update source table to reflect the alias
1364                                    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                            // Regular table reference - use the provided table
1373                            table.clone()
1374                        }
1375                    } else {
1376                        // No FROM clause (e.g. `SELECT 1 AS k`) must yield exactly one
1377                        // row, independent of any outer/source table. Reusing the caller's
1378                        // `table` here made a FROM-less subquery emit one row per outer row,
1379                        // which exploded `CROSS JOIN (SELECT 1 AS k)` cardinality (P5).
1380                        Arc::new(DataTable::dual())
1381                    }
1382                }
1383            }
1384        };
1385
1386        // Register alias in execution context if present
1387        #[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        // Process JOINs if present
1396        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            // Name of the main FROM table, so a plain base table can be joined to
1406            // itself (P4: `FROM trades a JOIN trades b ...`). Derived tables / CTEs
1407            // in the FROM don't have a re-referenceable base name and are skipped.
1408            #[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                // Resolve the right table for the join
1428                let right_table = match &join_clause.table {
1429                    TableSource::Table(name) => {
1430                        // Check if it's a CTE reference (case-insensitive fallback)
1431                        if let Some(cte_view) = resolve_cte(cte_context, name) {
1432                            let mut materialized = self.materialize_view((**cte_view).clone())?;
1433
1434                            // Apply alias to qualified column names if present
1435                            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                                    // Replace the CTE name with the alias in qualified names
1439                                    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                                    // Update source table to reflect the alias
1448                                    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                            // Self-join of the base table (P4): re-reference the
1463                            // already-loaded source. The right side's columns collide
1464                            // by name with the left, so HashJoinExecutor renames them
1465                            // to `<alias>.<col>` using join_clause.alias; we also rewrite
1466                            // qualified names here so `b.col` resolves in projection.
1467                            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                            // For now, we need the actual table data
1486                            // In a real implementation, this would load from file
1487                            return Err(anyhow!("Cannot resolve table '{}' for JOIN", name));
1488                        }
1489                    }
1490                    TableSource::DerivedTable { query, alias: _ } => {
1491                        // Execute the subquery
1492                        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                        // PIVOT in JOIN is not supported yet (will be handled by transformer)
1502                        return Err(anyhow!("PIVOT in JOIN clause is not yet supported"));
1503                    }
1504                };
1505
1506                // Execute the join
1507                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        // Continue with the existing build_view logic but using final_table
1537        self.build_view_internal_with_plan_and_exec(
1538            final_table,
1539            statement,
1540            plan,
1541            Some(exec_context),
1542        )
1543    }
1544
1545    /// Materialize a DataView into a new DataTable
1546    pub fn materialize_view(&self, view: DataView) -> Result<DataTable> {
1547        let source = view.source();
1548        let mut result_table = DataTable::new("derived");
1549
1550        // Get the visible columns from the view
1551        let visible_cols = view.visible_column_indices().to_vec();
1552
1553        // Copy column definitions
1554        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(), // Preserve qualified name
1564                source_table: col.source_table.clone(),     // Preserve source table
1565            };
1566            result_table.add_column(new_col);
1567        }
1568
1569        // Copy visible rows, honouring the view's LIMIT/OFFSET window as well as
1570        // its filter — `visible_row_indices()` is the *pre-limit* set (parity P28).
1571        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        // Start with all rows visible
1620        let mut visible_rows: Vec<usize> = (0..table.row_count()).collect();
1621
1622        // Apply WHERE clause filtering using recursive evaluator
1623        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            // Add details about WHERE conditions
1633            for condition in &where_clause.conditions {
1634                plan.add_detail(format!("Condition: {:?}", condition.expr));
1635            }
1636
1637            let filter_start = Instant::now();
1638            // Create an evaluation context for caching compiled regexes
1639            let mut eval_context = EvaluationContext::new(self.case_insensitive);
1640
1641            // Create evaluator ONCE before the loop for performance
1642            let mut evaluator = if let Some(exec_ctx) = exec_context {
1643                // Use both contexts: exec_context for alias resolution, eval_context for regex caching
1644                RecursiveWhereEvaluator::with_both_contexts(&table, &mut eval_context, exec_ctx)
1645            } else {
1646                RecursiveWhereEvaluator::with_context(&table, &mut eval_context)
1647            };
1648
1649            // Filter visible rows based on WHERE clause
1650            let mut filtered_rows = Vec::new();
1651            for row_idx in visible_rows {
1652                // Only log for first few rows to avoid performance impact
1653                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                        // Propagate WHERE clause errors instead of silently ignoring them
1674                        return Err(e);
1675                    }
1676                }
1677            }
1678
1679            // Log regex cache statistics
1680            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        // Create initial DataView with filtered rows
1706        let mut view = DataView::new(table.clone());
1707        view = view.with_rows(visible_rows);
1708
1709        // Handle GROUP BY if present
1710        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                // Hide any columns that were promoted from HAVING (synthetic aggregates)
1734                // These have the __hidden_agg_ prefix and should not appear in output
1735                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            // Apply column projection or computed expressions (SELECT clause) - do this AFTER filtering
1763            if !statement.select_items.is_empty() {
1764                // Check if we have ANY non-star items (not just the first one)
1765                let has_non_star_items = statement
1766                    .select_items
1767                    .iter()
1768                    .any(|item| !matches!(item, SelectItem::Star { .. }));
1769
1770                // Apply select items if:
1771                // 1. We have computed expressions or explicit columns
1772                // 2. OR we have a mix of star and other items (e.g., SELECT *, computed_col)
1773                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                // If it's just a single star, no projection needed
1783            } else if !statement.columns.is_empty() && statement.columns[0] != "*" {
1784                debug!("QueryEngine: Using legacy columns path");
1785                // Fallback to legacy column projection for backward compatibility
1786                // Use the current view's source table, not the original table
1787                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        // Apply DISTINCT if specified
1795        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        // Apply ORDER BY sorting
1813        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                    // Format the expression (simplified for now - just show column name or "expr")
1822                    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        // Strip columns promoted by OrderByAliasTransformer for ORDER BY visibility.
1842        // Unlike the HIDDEN_AGG_PREFIX strip (which runs right after GROUP BY)
1843        // this MUST run after ORDER BY — the whole point of the promotion is
1844        // that the column has to survive projection long enough to be sorted on.
1845        {
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        // Apply LIMIT/OFFSET
1866        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        // Process set operations (UNION ALL, UNION, INTERSECT, EXCEPT)
1880        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            // Materialize the first result set
1888            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            // Track if any operation requires deduplication
1893            let mut needs_deduplication = false;
1894
1895            // Process each set operation
1896            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                // Execute the next SELECT statement
1904                // We need to pass the original table and exec_context for proper resolution
1905                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                // Materialize the next result set
1921                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                // Validate schema compatibility
1926                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                // Warn if column names don't match (but allow it - some SQL dialects do)
1936                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                // Perform the set operation
1953                match operation {
1954                    SetOperation::UnionAll => {
1955                        // UNION ALL: Simply concatenate all rows without deduplication
1956                        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                        // UNION: Concatenate all rows first, deduplicate at the end
1966                        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                        // INTERSECT [DISTINCT]: keep only rows present in BOTH
1977                        // sides, deduplicated. The row key is the Debug form of
1978                        // the whole value vector — the same equality basis
1979                        // apply_distinct() uses for UNION.
1980                        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                        // EXCEPT [DISTINCT]: keep rows from the left that do NOT
2003                        // appear in the right, deduplicated.
2004                        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            // Create a new view from the combined table
2044            view = DataView::new(Arc::new(combined_table));
2045
2046            // Apply deduplication if any UNION (not UNION ALL) operation was used
2047            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    /// Resolve column names to indices
2072    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    /// Apply SELECT items (columns and computed expressions) to create new view
2098    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        // Check if any select items contain window functions
2116        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        // Count window functions for detailed reporting
2122        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        // Start timing for window function evaluation if present
2131        let window_start = if has_window_functions {
2132            debug!(
2133                "QueryEngine::apply_select_items - detected {} window functions",
2134                window_func_count
2135            );
2136
2137            // Extract window specs (Step 2: parallel path, not used yet)
2138            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        // Check if any SELECT item contains UNNEST - if so, use row expansion mode
2147        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        // Check if this is an aggregate query:
2158        // 1. At least one aggregate function exists
2159        // 2. All other items are either aggregates or constants (aggregate-compatible)
2160        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, // Columns are not aggregate-compatible
2170            SelectItem::Star { .. } => false,   // Star is not aggregate-compatible
2171            SelectItem::StarExclude { .. } => false, // StarExclude is not aggregate-compatible
2172        });
2173
2174        if has_aggregates && all_aggregate_compatible && view.row_count() > 0 {
2175            // Special handling for aggregate queries with constants (no GROUP BY)
2176            // These should produce exactly one row
2177            debug!("QueryEngine::apply_select_items - detected aggregate query with constants");
2178            return self.apply_aggregate_select(view, select_items);
2179        }
2180
2181        // Check if we need to create computed columns
2182        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            // Simple case: only columns, use existing projection logic
2193            let column_indices = self.resolve_select_columns(view.source(), select_items)?;
2194            return Ok(view.with_columns(column_indices));
2195        }
2196
2197        // Complex case: we have computed expressions
2198        // IMPORTANT: We create a PROJECTED view, not a new table
2199        // This preserves the original DataTable reference
2200
2201        let source_table = view.source();
2202        let visible_rows = view.visible_row_indices();
2203
2204        // Create a temporary table just for the computed result view
2205        // But this table is only used for the current query result
2206        let mut computed_table = DataTable::new("query_result");
2207
2208        // First, expand any Star selectors to actual columns
2209        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                        // Scoped expansion: table.* expands only columns from that table
2215                        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                        // Unscoped expansion: * expands to all columns
2227                        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        // Add columns based on expanded SelectItems, handling duplicates
2242        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            // Check if this column name has been used before
2258            let count = column_name_counts.entry(base_name.clone()).or_insert(0);
2259            let column_name = if *count == 0 {
2260                // First occurrence, use the name as-is
2261                base_name.clone()
2262            } else {
2263                // Duplicate, append a suffix
2264                format!("{base_name}_{count}")
2265            };
2266            *count += 1;
2267
2268            computed_table.add_column(DataColumn::new(&column_name));
2269        }
2270
2271        // Check if batch evaluation can be used
2272        // Batch evaluation is the default but we need to check if all window functions
2273        // are standalone (not embedded in expressions)
2274        let can_use_batch = expanded_items.iter().all(|item| {
2275            match item {
2276                SelectItem::Expression { expr, .. } => {
2277                    // Only use batch evaluation if the expression IS a window function,
2278                    // not if it CONTAINS a window function
2279                    matches!(expr, SqlExpression::WindowFunction { .. })
2280                        || !Self::contains_window_function(expr)
2281                }
2282                _ => true, // Non-expressions are fine
2283            }
2284        });
2285
2286        // Batch evaluation is now the default mode for improved performance
2287        // Users can opt-out by setting SQL_CLI_BATCH_WINDOW=0 or false
2288        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        // Store window specs for batch evaluation if needed
2294        let batch_window_specs = if use_batch_evaluation && has_window_functions {
2295            debug!("BATCH window function evaluation flag is enabled");
2296            // Extract window specs before timing starts
2297            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        // Calculate values for each row.
2308        //
2309        // The evaluator is handed the view's visible rows so that window functions
2310        // partition over the FILTERED set. Without this the evaluator sees the whole
2311        // source table and a WHERE clause has no effect on any window (P21): partition
2312        // counts, rank slots and frames all include rows the query excluded.
2313        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        // Populate table aliases from exec_context if available
2318        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        // OPTIMIZATION: Pre-create WindowContexts before the row loop
2331        // This avoids 50,000+ redundant context lookups
2332        if has_window_functions {
2333            let preload_start = Instant::now();
2334
2335            // Extract all unique WindowSpecs from SELECT items
2336            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            // Pre-create all WindowContexts
2344            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        // Batch evaluation path for window functions
2356        if let Some(window_specs) = batch_window_specs {
2357            debug!("Starting batch window function evaluation");
2358            let batch_start = Instant::now();
2359
2360            // Initialize result table with all rows
2361            let mut batch_results: Vec<Vec<DataValue>> =
2362                vec![vec![DataValue::Null; expanded_items.len()]; visible_rows.len()];
2363
2364            // Use the window specs we extracted earlier
2365            let detailed_window_specs = &window_specs;
2366
2367            // Group window specs by their WindowSpec for batch processing
2368            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            // Process each unique window specification
2381            for (_window_hash, specs) in specs_by_window {
2382                // Get the window context (already pre-created)
2383                let context = evaluator.get_or_create_window_context(&specs[0].spec)?;
2384
2385                // Process each function using this window
2386                for spec in specs {
2387                    match spec.function_name.as_str() {
2388                        "LAG" => {
2389                            // Extract column and offset from arguments
2390                            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 // default offset
2398                                };
2399
2400                                let values = context.evaluate_lag_batch(
2401                                    visible_rows,
2402                                    column_name,
2403                                    offset,
2404                                )?;
2405
2406                                // Write results to the output column
2407                                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                            // Extract column and offset from arguments
2414                            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 // default offset
2422                                };
2423
2424                                let values = context.evaluate_lead_batch(
2425                                    visible_rows,
2426                                    column_name,
2427                                    offset,
2428                                )?;
2429
2430                                // Write results to the output column
2431                                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                            // Write results to the output column
2440                            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                            // Write results to the output column
2448                            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                            // Write results to the output column
2456                            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                            // COUNT can be COUNT(*) or COUNT(column)
2506                            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                            // Fall back to per-row evaluation for unsupported functions
2542                            debug!(
2543                                "Window function {} not supported in batch mode, using per-row",
2544                                spec.function_name
2545                            );
2546                        }
2547                    }
2548                }
2549            }
2550
2551            // Now evaluate non-window columns
2552            for (result_row_idx, &source_row_idx) in visible_rows.iter().enumerate() {
2553                for (col_idx, item) in expanded_items.iter().enumerate() {
2554                    // Skip if this column was already filled by a window function
2555                    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                            // For batch evaluation, we need to handle expressions differently
2578                            // If this is just a window function by itself, we already computed it
2579                            if matches!(expr, SqlExpression::WindowFunction { .. }) {
2580                                // Pure window function - already handled in batch evaluation
2581                                continue;
2582                            }
2583                            // For expressions containing window functions or regular expressions,
2584                            // evaluate them normally
2585                            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            // Add all rows to the table
2597            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            // Original per-row evaluation path
2609            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                            // Use evaluator for column resolution (handles aliases properly)
2618                            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                            // Computed expression
2633                            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        // Log window function timing if applicable
2650        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            // Add to execution plan
2660            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        // Return a view of the computed result
2676        // This is a temporary view for this query only
2677        Ok(DataView::new(Arc::new(computed_table)))
2678    }
2679
2680    /// Apply SELECT with row expansion (for UNNEST, EXPLODE, etc.)
2681    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        // Create result table
2693        let mut result_table = DataTable::new("unnest_result");
2694
2695        // Expand * to columns and set up result columns
2696        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                        // Scoped expansion: table.* expands only columns from that table
2702                        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                        // Unscoped expansion: * expands to all columns
2717                        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        // Add columns to result table
2732        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        // Process each input row
2747        let mut evaluator =
2748            ArithmeticEvaluator::with_date_notation(source_table, self.date_notation.clone());
2749
2750        for &row_idx in visible_rows {
2751            // First pass: identify UNNEST expressions and collect their expansion arrays
2752            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            // Determine how many output rows to generate
2771            let expansion_count = if unnest_expansions.is_empty() {
2772                1 // No UNNEST, just one row
2773            } else {
2774                unnest_expansions
2775                    .iter()
2776                    .map(|exp| exp.row_count())
2777                    .max()
2778                    .unwrap_or(1)
2779            };
2780
2781            // Generate output rows
2782            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                    // Check if this column is an UNNEST column
2787                    let unnest_position = unnest_indices.iter().position(|&idx| idx == col_idx);
2788
2789                    let value = if let Some(unnest_idx) = unnest_position {
2790                        // Get value from expansion array (or NULL if exhausted)
2791                        let expansion = &unnest_expansions[unnest_idx];
2792                        expansion
2793                            .values
2794                            .get(output_idx)
2795                            .cloned()
2796                            .unwrap_or(DataValue::Null)
2797                    } else {
2798                        // Regular column or non-UNNEST expression - replicate from input
2799                        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                                // Non-UNNEST expression - evaluate once and replicate
2818                                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    /// Try to expand an expression if it's an UNNEST call
2846    /// Returns Some(ExpansionResult) if successful, None if not an UNNEST
2847    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        // Check for UNNEST variant (direct syntax)
2856        if let SqlExpression::Unnest { column, delimiter } = expr {
2857            // Evaluate the column expression
2858            let column_value = evaluator.evaluate(column, row_idx)?;
2859
2860            // Delimiter is already a string literal
2861            let delimiter_value = DataValue::String(delimiter.clone());
2862
2863            // Get the UNNEST expander
2864            let expander = expander_registry
2865                .get("UNNEST")
2866                .ok_or_else(|| anyhow::anyhow!("UNNEST expander not found"))?;
2867
2868            // Expand the value
2869            let expansion = expander.expand(&column_value, &[delimiter_value])?;
2870            return Ok(Some(expansion));
2871        }
2872
2873        // Also check for FunctionCall form (for compatibility)
2874        if let SqlExpression::FunctionCall { name, args, .. } = expr {
2875            if name.to_uppercase() == "UNNEST" {
2876                // UNNEST(column, delimiter)
2877                if args.len() != 2 {
2878                    return Err(anyhow::anyhow!(
2879                        "UNNEST requires exactly 2 arguments: UNNEST(column, delimiter)"
2880                    ));
2881                }
2882
2883                // Evaluate the column expression (first arg)
2884                let column_value = evaluator.evaluate(&args[0], row_idx)?;
2885
2886                // Evaluate the delimiter expression (second arg)
2887                let delimiter_value = evaluator.evaluate(&args[1], row_idx)?;
2888
2889                // Get the UNNEST expander
2890                let expander = expander_registry
2891                    .get("UNNEST")
2892                    .ok_or_else(|| anyhow::anyhow!("UNNEST expander not found"))?;
2893
2894                // Expand the value
2895                let expansion = expander.expand(&column_value, &[delimiter_value])?;
2896                return Ok(Some(expansion));
2897            }
2898        }
2899
2900        Ok(None)
2901    }
2902
2903    /// Apply aggregate-only SELECT (no GROUP BY - produces single row)
2904    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        // Add columns for each select item
2915        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        // Create evaluator with visible rows from the view (for filtered aggregates)
2924        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        // Evaluate each aggregate expression once (they handle all rows internally)
2930        let mut row_values = Vec::new();
2931        for item in select_items {
2932            match item {
2933                SelectItem::Expression { expr, .. } => {
2934                    // The evaluator will handle aggregates over all rows
2935                    // We pass row_index=0 but aggregates ignore it and process all rows
2936                    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        // Add the single result row
2944        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    /// Check if a column belongs to a specific table based on source_table or qualified_name
2952    ///
2953    /// This is used for table-scoped star expansion (e.g., `SELECT user.*`)
2954    /// to filter which columns should be included.
2955    ///
2956    /// # Arguments
2957    /// * `col` - The column to check
2958    /// * `table_name` - The table name or alias to match against
2959    ///
2960    /// # Returns
2961    /// `true` if the column belongs to the specified table
2962    fn column_matches_table(col: &DataColumn, table_name: &str) -> bool {
2963        // First, check the source_table field
2964        if let Some(ref source) = col.source_table {
2965            // Direct match or matches with schema qualification
2966            if source == table_name || source.ends_with(&format!(".{}", table_name)) {
2967                return true;
2968            }
2969        }
2970
2971        // Second, check the qualified_name field
2972        if let Some(ref qualified) = col.qualified_name {
2973            // Check if qualified name starts with "table_name."
2974            if qualified.starts_with(&format!("{}.", table_name)) {
2975                return true;
2976            }
2977        }
2978
2979        false
2980    }
2981
2982    /// Resolve `SelectItem` columns to indices (for simple column projections only)
2983    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                    // Check if this has a table prefix
2997                    let index = if let Some(table_prefix) = &col_ref.table_prefix {
2998                        // Qualified reference (e.g. `f.region`). Prefer a qualified
2999                        // match (JOIN/CTE columns carry qualified names), then fall
3000                        // back to an unqualified lookup by column name. The fallback
3001                        // makes aliased single-table queries (`SELECT f.region FROM
3002                        // #tmp f`) behave like WHERE/expression clauses do — base and
3003                        // temp-table columns carry no qualified_name, so a qualified-
3004                        // only lookup would otherwise fail. See
3005                        // `ExecutionContext::resolve_column_index` for the same logic.
3006                        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                                // Check if any columns have qualified names for better error message
3015                                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                        // Simple column name lookup
3028                        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                        // Scoped expansion: table.* expands only columns from that table
3048                        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                        // Unscoped expansion: * expands to all column indices
3055                        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                    // Expand all columns (with optional table prefix), then exclude specified ones
3066                    if let Some(prefix) = table_prefix {
3067                        // Scoped expansion: table.* EXCLUDE expands only columns from that table
3068                        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                        // Unscoped expansion: * EXCLUDE expands to all columns except excluded ones
3077                        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    /// Apply DISTINCT to remove duplicate rows
3099    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        // Build a set to track unique rows
3107        let mut seen_rows = HashSet::new();
3108        let mut unique_row_indices = Vec::new();
3109
3110        for &row_idx in visible_rows {
3111            // Build a key representing this row's visible column values
3112            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                // Convert value to a hashable representation
3118                row_key.push(format!("{:?}", value));
3119            }
3120
3121            // Check if we've seen this row before
3122            if seen_rows.insert(row_key) {
3123                // First time seeing this row combination
3124                unique_row_indices.push(row_idx);
3125            }
3126        }
3127
3128        // Create a new view with only unique rows
3129        Ok(view.with_rows(unique_row_indices))
3130    }
3131
3132    /// Apply multi-column ORDER BY sorting to the view
3133    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    /// Apply multi-column ORDER BY sorting with exec_context for alias resolution
3142    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        // Build list of (source_column_index, ascending) tuples
3149        let mut sort_columns = Vec::new();
3150
3151        for order_col in order_by_columns {
3152            // Extract column name from expression (currently only supports simple columns)
3153            let column_name = match &order_col.expr {
3154                SqlExpression::Column(col_ref) => col_ref.name.clone(),
3155                _ => {
3156                    // TODO: Support expression evaluation in ORDER BY
3157                    return Err(anyhow!(
3158                        "ORDER BY expressions not yet supported - only simple columns allowed"
3159                    ));
3160                }
3161            };
3162
3163            // Try to find the column index, handling qualified column names (table.column)
3164            let col_index = if column_name.contains('.') {
3165                // Qualified column name - extract unqualified part
3166                if let Some(dot_pos) = column_name.rfind('.') {
3167                    let col_name = &column_name[dot_pos + 1..];
3168
3169                    // After SELECT processing, columns are unqualified
3170                    // So just use the column name part
3171                    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                // Simple column name
3181                view.source().get_column_index(&column_name)
3182            }
3183            .ok_or_else(|| {
3184                // If not found, provide helpful error with suggestions
3185                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                        // Also list available columns for debugging
3194                        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        // Apply multi-column sorting
3209        view.apply_multi_sort(&sort_columns)?;
3210        Ok(view)
3211    }
3212
3213    /// Apply GROUP BY to the view with optional HAVING clause
3214    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        // Use the new expression-based GROUP BY implementation
3223        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        // Add detailed phase information to the execution plan
3233        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    /// Estimate the cardinality (number of unique groups) for GROUP BY operations
3265    /// This helps pre-size hash tables for better performance
3266    pub fn estimate_group_cardinality(
3267        &self,
3268        view: &DataView,
3269        group_by_exprs: &[SqlExpression],
3270    ) -> usize {
3271        // If we have few rows, just return the row count as upper bound
3272        let row_count = view.get_visible_rows().len();
3273        if row_count <= 100 {
3274            return row_count;
3275        }
3276
3277        // Sample first 1000 rows or 10% of data, whichever is smaller
3278        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            // Evaluate GROUP BY expressions for this row
3288            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        // Estimate total cardinality based on sample
3299        let sample_cardinality = seen.len();
3300        let estimated = (sample_cardinality * row_count) / sample_size;
3301
3302        // Cap at row count and ensure minimum of sample cardinality
3303        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        // Add columns
3316        table.add_column(DataColumn::new("id"));
3317        table.add_column(DataColumn::new("name"));
3318        table.add_column(DataColumn::new("age"));
3319
3320        // Add rows
3321        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        // Initialize tracing for debug output
3386        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        // Add test data with mixed types
3396        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        // Test 1: Basic string contains (should work)
3431        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); // Should find both Pending rows
3440            }
3441            Err(e) => {
3442                panic!("Query failed: {e}");
3443            }
3444        }
3445
3446        // Test 2: Numeric contains (should work with type coercion)
3447        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                // Should find 99.99 row
3459                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        // Initialize tracing for debug output
3472        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        // Add test data
3481        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        // Test NOT IN clause - should exclude CA, return US and UK (2 rows)
3513        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); // Should find US and UK
3522            }
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        // Initialize tracing for debug output
3534        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        // Add test data with mixed case
3543        table
3544            .add_row(DataRow::new(vec![
3545                DataValue::Integer(1),
3546                DataValue::String("CA".to_string()), // uppercase
3547            ]))
3548            .unwrap();
3549
3550        table
3551            .add_row(DataRow::new(vec![
3552                DataValue::Integer(2),
3553                DataValue::String("us".to_string()), // lowercase
3554            ]))
3555            .unwrap();
3556
3557        table
3558            .add_row(DataRow::new(vec![
3559                DataValue::Integer(3),
3560                DataValue::String("UK".to_string()), // uppercase
3561            ]))
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        // Test case-insensitive IN - should match 'CA' with 'ca'
3574        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); // Should find CA row
3584            }
3585            Err(e) => {
3586                panic!("Case-insensitive IN query failed: {e}");
3587            }
3588        }
3589
3590        // Test case-insensitive NOT IN - should exclude 'CA' when searching for 'ca'
3591        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); // Should find us and UK rows
3603            }
3604            Err(e) => {
3605                panic!("Case-insensitive NOT IN query failed: {e}");
3606            }
3607        }
3608
3609        // Test case-sensitive (default) - should NOT match 'CA' with 'ca'
3610        println!("\n--- Test: country IN ('ca') with case_insensitive=false ---");
3611        let engine_case_sensitive = QueryEngine::new(); // defaults to case_insensitive=false
3612        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); // Should find no rows (CA != ca)
3621            }
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        // Initialize tracing for debug output
3634        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        // Add test data
3644        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        // Test OR with parentheses - should get (Pending AND High) OR (Complete AND Low)
3688        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); // Should find rows 1 and 4
3700            }
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        // Initialize tracing for debug output
3713        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        // Add test data with different numeric types
3723        table
3724            .add_row(DataRow::new(vec![
3725                DataValue::Integer(1),
3726                DataValue::Float(99.50), // Contains '.'
3727                DataValue::Integer(100),
3728            ]))
3729            .unwrap();
3730
3731        table
3732            .add_row(DataRow::new(vec![
3733                DataValue::Integer(2),
3734                DataValue::Float(150.0), // Contains '.' and '0'
3735                DataValue::Integer(200),
3736            ]))
3737            .unwrap();
3738
3739        table
3740            .add_row(DataRow::new(vec![
3741                DataValue::Integer(3),
3742                DataValue::Integer(75), // No decimal point
3743                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        // Test Contains on float values - should find rows with decimal points
3759        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); // Should find 99.50 and 150.0
3771            }
3772            Err(e) => {
3773                panic!("Numeric Contains query failed: {e}");
3774            }
3775        }
3776
3777        // Test Contains on integer values converted to string
3778        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); // Should find 100 and 200
3790            }
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        // Initialize tracing for debug output
3802        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        // Add test data with date strings (as they would come from CSV)
3811        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        // Test DateTime constructor comparison - should find dates after 2025-01-01
3843        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); // Should find 2025-01-15 and 2025-02-15
3852            }
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        // Initialize tracing for debug output
3864        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        // Add test data
3873        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        // Test NOT with Contains - should exclude rows containing "pend"
3905        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); // Should find only "Complete"
3917            }
3918            Err(e) => {
3919                panic!("NOT Contains query failed: {e}");
3920            }
3921        }
3922
3923        // Test NOT with StartsWith
3924        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); // Should find only "Complete"
3936            }
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        // Initialize tracing for debug output
3949        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        // Add comprehensive test data
3960        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        // Test complex AND/OR logic
4011        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); // Should find rows 1 and 3 (both Pending, one High priority, both assigned to John)
4023            }
4024            Err(e) => {
4025                panic!("Complex logic query failed: {e}");
4026            }
4027        }
4028
4029        // Test NOT with complex expressions
4030        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); // Should find rows 1 (Pending+High) and 4 (In Progress+Medium)
4042            }
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        // Initialize tracing for debug output
4054        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        // Add test data with mixed types and edge cases
4064        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        // Test type coercion with boolean Contains
4108        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); // Should find the boolean true row
4120            }
4121            Err(e) => {
4122                panic!("Boolean coercion query failed: {e}");
4123            }
4124        }
4125
4126        // Test multiple IN values with mixed types
4127        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); // Should find rows with id 1 and 3
4133            }
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 that aggregate-only queries return exactly one row (regression test)
4143    #[test]
4144    fn test_aggregate_only_single_row() {
4145        let table = create_test_stock_data();
4146        let engine = QueryEngine::new();
4147
4148        // Test query with multiple aggregates - should return exactly 1 row
4149        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        // Verify the actual values are correct
4164        let source = result.source();
4165        let row = source.get_row(0).expect("Should have first row");
4166
4167        // COUNT(*) should be 5 (total rows)
4168        assert_eq!(row.values[0], DataValue::Integer(5));
4169
4170        // MIN should be 99.5
4171        assert_eq!(row.values[1], DataValue::Float(99.5));
4172
4173        // MAX should be 105.0
4174        assert_eq!(row.values[2], DataValue::Float(105.0));
4175
4176        // AVG should be approximately 102.4
4177        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 single aggregate function returns single row
4189    #[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 aggregate with WHERE clause filtering
4211    #[test]
4212    fn test_aggregate_with_where_single_row() {
4213        let table = create_test_stock_data();
4214        let engine = QueryEngine::new();
4215
4216        // Filter to only high-value stocks (>= 103.0) and aggregate
4217        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        // Should find 2 rows (103.5 and 105.0)
4235        assert_eq!(row.values[0], DataValue::Integer(2));
4236        assert_eq!(row.values[1], DataValue::Float(103.5)); // MIN
4237        assert_eq!(row.values[2], DataValue::Float(105.0)); // MAX
4238    }
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    /// Create test stock data for aggregate testing
4266    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        // Add 5 rows of test data
4274        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;