sql_cli/data/
recursive_where_evaluator.rs

1use crate::data::arithmetic_evaluator::ArithmeticEvaluator;
2use crate::data::datatable::{DataTable, DataValue};
3use crate::sql::recursive_parser::{Condition, LogicalOp, SqlExpression, WhereClause};
4use anyhow::{anyhow, Result};
5use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
6use tracing::debug;
7
8/// Evaluates WHERE clauses from recursive_parser directly against DataTable
9pub struct RecursiveWhereEvaluator<'a> {
10    table: &'a DataTable,
11    case_insensitive: bool,
12    date_notation: String,
13}
14
15impl<'a> RecursiveWhereEvaluator<'a> {
16    pub fn new(table: &'a DataTable) -> Self {
17        Self {
18            table,
19            case_insensitive: false,
20            date_notation: "us".to_string(),
21        }
22    }
23
24    pub fn with_date_notation(table: &'a DataTable, date_notation: String) -> Self {
25        Self {
26            table,
27            case_insensitive: false,
28            date_notation,
29        }
30    }
31
32    /// Find a column name similar to the given name using edit distance
33    fn find_similar_column(&self, name: &str) -> Option<String> {
34        let columns = self.table.column_names();
35        let mut best_match: Option<(String, usize)> = None;
36
37        for col in columns {
38            let distance = self.edit_distance(&col.to_lowercase(), &name.to_lowercase());
39            // Only suggest if distance is small (likely a typo)
40            // Allow up to 3 edits for longer names
41            let max_distance = if name.len() > 10 { 3 } else { 2 };
42            if distance <= max_distance {
43                match &best_match {
44                    None => best_match = Some((col, distance)),
45                    Some((_, best_dist)) if distance < *best_dist => {
46                        best_match = Some((col, distance));
47                    }
48                    _ => {}
49                }
50            }
51        }
52
53        best_match.map(|(name, _)| name)
54    }
55
56    /// Calculate Levenshtein edit distance between two strings
57    fn edit_distance(&self, s1: &str, s2: &str) -> usize {
58        let len1 = s1.len();
59        let len2 = s2.len();
60        let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
61
62        for i in 0..=len1 {
63            matrix[i][0] = i;
64        }
65        for j in 0..=len2 {
66            matrix[0][j] = j;
67        }
68
69        for (i, c1) in s1.chars().enumerate() {
70            for (j, c2) in s2.chars().enumerate() {
71                let cost = if c1 == c2 { 0 } else { 1 };
72                matrix[i + 1][j + 1] = std::cmp::min(
73                    matrix[i][j + 1] + 1, // deletion
74                    std::cmp::min(
75                        matrix[i + 1][j] + 1, // insertion
76                        matrix[i][j] + cost,  // substitution
77                    ),
78                );
79            }
80        }
81
82        matrix[len1][len2]
83    }
84
85    pub fn with_case_insensitive(table: &'a DataTable, case_insensitive: bool) -> Self {
86        Self {
87            table,
88            case_insensitive,
89            date_notation: "us".to_string(),
90        }
91    }
92
93    pub fn with_config(
94        table: &'a DataTable,
95        case_insensitive: bool,
96        date_notation: String,
97    ) -> Self {
98        Self {
99            table,
100            case_insensitive,
101            date_notation,
102        }
103    }
104
105    /// Helper function to compare two datetime values based on the operator
106    fn compare_datetime(op: &str, left: &DateTime<Utc>, right: &DateTime<Utc>) -> bool {
107        match op {
108            "=" => left == right,
109            "!=" | "<>" => left != right,
110            ">" => left > right,
111            ">=" => left >= right,
112            "<" => left < right,
113            "<=" => left <= right,
114            _ => false,
115        }
116    }
117
118    /// Evaluate the Length() method on a column value
119    fn evaluate_length(
120        &self,
121        object: &str,
122        row_index: usize,
123    ) -> Result<(Option<DataValue>, String)> {
124        let col_index = self.table.get_column_index(object).ok_or_else(|| {
125            let suggestion = self.find_similar_column(object);
126            match suggestion {
127                Some(similar) => {
128                    anyhow!("Column '{}' not found. Did you mean '{}'?", object, similar)
129                }
130                None => anyhow!("Column '{}' not found", object),
131            }
132        })?;
133
134        let value = self.table.get_value(row_index, col_index);
135        let length_value = match value {
136            Some(DataValue::String(s)) => Some(DataValue::Integer(s.len() as i64)),
137            Some(DataValue::InternedString(s)) => Some(DataValue::Integer(s.len() as i64)),
138            Some(DataValue::Integer(n)) => Some(DataValue::Integer(n.to_string().len() as i64)),
139            Some(DataValue::Float(f)) => Some(DataValue::Integer(f.to_string().len() as i64)),
140            _ => Some(DataValue::Integer(0)),
141        };
142        Ok((length_value, format!("{}.Length()", object)))
143    }
144
145    /// Evaluate the IndexOf() method on a column value
146    fn evaluate_indexof(
147        &self,
148        object: &str,
149        search_str: &str,
150        row_index: usize,
151    ) -> Result<(Option<DataValue>, String)> {
152        let col_index = self.table.get_column_index(object).ok_or_else(|| {
153            let suggestion = self.find_similar_column(object);
154            match suggestion {
155                Some(similar) => {
156                    anyhow!("Column '{}' not found. Did you mean '{}'?", object, similar)
157                }
158                None => anyhow!("Column '{}' not found", object),
159            }
160        })?;
161
162        let value = self.table.get_value(row_index, col_index);
163        let index_value = match value {
164            Some(DataValue::String(s)) => {
165                // Case-insensitive search by default, following Contains behavior
166                let pos = s
167                    .to_lowercase()
168                    .find(&search_str.to_lowercase())
169                    .map(|idx| idx as i64)
170                    .unwrap_or(-1);
171                Some(DataValue::Integer(pos))
172            }
173            Some(DataValue::InternedString(s)) => {
174                let pos = s
175                    .to_lowercase()
176                    .find(&search_str.to_lowercase())
177                    .map(|idx| idx as i64)
178                    .unwrap_or(-1);
179                Some(DataValue::Integer(pos))
180            }
181            Some(DataValue::Integer(n)) => {
182                let str_val = n.to_string();
183                let pos = str_val.find(search_str).map(|idx| idx as i64).unwrap_or(-1);
184                Some(DataValue::Integer(pos))
185            }
186            Some(DataValue::Float(f)) => {
187                let str_val = f.to_string();
188                let pos = str_val.find(search_str).map(|idx| idx as i64).unwrap_or(-1);
189                Some(DataValue::Integer(pos))
190            }
191            _ => Some(DataValue::Integer(-1)), // Return -1 for not found
192        };
193
194        if row_index < 3 {
195            debug!(
196                "RecursiveWhereEvaluator: Row {} IndexOf('{}') = {:?}",
197                row_index, search_str, index_value
198            );
199        }
200        Ok((index_value, format!("{}.IndexOf('{}')", object, search_str)))
201    }
202
203    /// Apply the appropriate trim operation based on trim_type
204    fn apply_trim<'b>(s: &'b str, trim_type: &str) -> &'b str {
205        match trim_type {
206            "trim" => s.trim(),
207            "trimstart" => s.trim_start(),
208            "trimend" => s.trim_end(),
209            _ => s,
210        }
211    }
212
213    /// Evaluate trim methods (Trim, TrimStart, TrimEnd) on a column value
214    fn evaluate_trim(
215        &self,
216        object: &str,
217        row_index: usize,
218        trim_type: &str,
219    ) -> Result<(Option<DataValue>, String)> {
220        let col_index = self.table.get_column_index(object).ok_or_else(|| {
221            let suggestion = self.find_similar_column(object);
222            match suggestion {
223                Some(similar) => {
224                    anyhow!("Column '{}' not found. Did you mean '{}'?", object, similar)
225                }
226                None => anyhow!("Column '{}' not found", object),
227            }
228        })?;
229
230        let value = self.table.get_value(row_index, col_index);
231        let trimmed_value = match value {
232            Some(DataValue::String(s)) => Some(DataValue::String(
233                Self::apply_trim(&s, trim_type).to_string(),
234            )),
235            Some(DataValue::InternedString(s)) => Some(DataValue::String(
236                Self::apply_trim(&s, trim_type).to_string(),
237            )),
238            Some(DataValue::Integer(n)) => {
239                let str_val = n.to_string();
240                Some(DataValue::String(
241                    Self::apply_trim(&str_val, trim_type).to_string(),
242                ))
243            }
244            Some(DataValue::Float(f)) => {
245                let str_val = f.to_string();
246                Some(DataValue::String(
247                    Self::apply_trim(&str_val, trim_type).to_string(),
248                ))
249            }
250            _ => Some(DataValue::String(String::new())),
251        };
252
253        let method_name = match trim_type {
254            "trim" => "Trim",
255            "trimstart" => "TrimStart",
256            "trimend" => "TrimEnd",
257            _ => "Trim",
258        };
259        Ok((trimmed_value, format!("{}.{}()", object, method_name)))
260    }
261
262    /// Evaluate a WHERE clause for a specific row
263    pub fn evaluate(&self, where_clause: &WhereClause, row_index: usize) -> Result<bool> {
264        // Only log for first few rows to avoid performance impact
265        if row_index < 3 {
266            debug!(
267                "RecursiveWhereEvaluator: evaluate() ENTRY - row {}, {} conditions, case_insensitive={}",
268                row_index,
269                where_clause.conditions.len(),
270                self.case_insensitive
271            );
272        }
273
274        if where_clause.conditions.is_empty() {
275            if row_index < 3 {
276                debug!("RecursiveWhereEvaluator: evaluate() EXIT - no conditions, returning true");
277            }
278            return Ok(true);
279        }
280
281        // Evaluate first condition
282        if row_index < 3 {
283            debug!(
284                "RecursiveWhereEvaluator: evaluate() - evaluating first condition for row {}",
285                row_index
286            );
287        }
288        let mut result = self.evaluate_condition(&where_clause.conditions[0], row_index)?;
289
290        // Apply connectors (AND/OR) with subsequent conditions
291        for i in 1..where_clause.conditions.len() {
292            let next_result = self.evaluate_condition(&where_clause.conditions[i], row_index)?;
293
294            // Use the connector from the previous condition
295            if let Some(connector) = &where_clause.conditions[i - 1].connector {
296                result = match connector {
297                    LogicalOp::And => result && next_result,
298                    LogicalOp::Or => result || next_result,
299                };
300            }
301        }
302
303        Ok(result)
304    }
305
306    fn evaluate_condition(&self, condition: &Condition, row_index: usize) -> Result<bool> {
307        // Only log first few rows to avoid performance impact
308        if row_index < 3 {
309            debug!(
310                "RecursiveWhereEvaluator: evaluate_condition() ENTRY - row {}",
311                row_index
312            );
313        }
314        let result = self.evaluate_expression(&condition.expr, row_index);
315        if row_index < 3 {
316            debug!(
317                "RecursiveWhereEvaluator: evaluate_condition() EXIT - row {}, result = {:?}",
318                row_index, result
319            );
320        }
321        result
322    }
323
324    fn evaluate_expression(&self, expr: &SqlExpression, row_index: usize) -> Result<bool> {
325        // Only log first few rows to avoid performance impact
326        if row_index < 3 {
327            debug!(
328                "RecursiveWhereEvaluator: evaluate_expression() ENTRY - row {}, expr = {:?}",
329                row_index, expr
330            );
331        }
332
333        let result = match expr {
334            SqlExpression::BinaryOp { left, op, right } => {
335                self.evaluate_binary_op(left, op, right, row_index)
336            }
337            SqlExpression::InList { expr, values } => {
338                self.evaluate_in_list(expr, values, row_index, false)
339            }
340            SqlExpression::NotInList { expr, values } => {
341                let in_result = self.evaluate_in_list(expr, values, row_index, false)?;
342                Ok(!in_result)
343            }
344            SqlExpression::Between { expr, lower, upper } => {
345                self.evaluate_between(expr, lower, upper, row_index)
346            }
347            SqlExpression::Not { expr } => {
348                let inner_result = self.evaluate_expression(expr, row_index)?;
349                Ok(!inner_result)
350            }
351            SqlExpression::MethodCall {
352                object,
353                method,
354                args,
355            } => {
356                if row_index < 3 {
357                    debug!("RecursiveWhereEvaluator: evaluate_expression() - found MethodCall, delegating to evaluate_method_call");
358                }
359                self.evaluate_method_call(object, method, args, row_index)
360            }
361            SqlExpression::CaseExpression {
362                when_branches,
363                else_branch,
364            } => {
365                if row_index < 3 {
366                    debug!("RecursiveWhereEvaluator: evaluate_expression() - found CaseExpression, evaluating");
367                }
368                self.evaluate_case_expression_as_bool(when_branches, else_branch, row_index)
369            }
370            _ => {
371                if row_index < 3 {
372                    debug!("RecursiveWhereEvaluator: evaluate_expression() - unsupported expression type, returning false");
373                }
374                Ok(false) // Default to false for unsupported expressions
375            }
376        };
377
378        if row_index < 3 {
379            debug!(
380                "RecursiveWhereEvaluator: evaluate_expression() EXIT - row {}, result = {:?}",
381                row_index, result
382            );
383        }
384        result
385    }
386
387    fn evaluate_binary_op(
388        &self,
389        left: &SqlExpression,
390        op: &str,
391        right: &SqlExpression,
392        row_index: usize,
393    ) -> Result<bool> {
394        // Only log first few rows to avoid performance impact
395        if row_index < 3 {
396            debug!(
397                "RecursiveWhereEvaluator: evaluate_binary_op() ENTRY - row {}, op = '{}'",
398                row_index, op
399            );
400        }
401
402        // Handle left side - could be a column or a method call
403        let (cell_value, column_name) = match left {
404            SqlExpression::MethodCall {
405                object,
406                method,
407                args,
408            } => {
409                // Handle method calls that return values (like Length(), IndexOf())
410                match method.to_lowercase().as_str() {
411                    "length" => {
412                        if !args.is_empty() {
413                            return Err(anyhow::anyhow!("Length() takes no arguments"));
414                        }
415                        self.evaluate_length(object, row_index)?
416                    }
417                    "indexof" => {
418                        if args.len() != 1 {
419                            return Err(anyhow::anyhow!("IndexOf() requires exactly 1 argument"));
420                        }
421                        let search_str = self.extract_string_value(&args[0])?;
422                        self.evaluate_indexof(object, &search_str, row_index)?
423                    }
424                    "trim" => {
425                        if !args.is_empty() {
426                            return Err(anyhow::anyhow!("Trim() takes no arguments"));
427                        }
428                        self.evaluate_trim(object, row_index, "trim")?
429                    }
430                    "trimstart" => {
431                        if !args.is_empty() {
432                            return Err(anyhow::anyhow!("TrimStart() takes no arguments"));
433                        }
434                        self.evaluate_trim(object, row_index, "trimstart")?
435                    }
436                    "trimend" => {
437                        if !args.is_empty() {
438                            return Err(anyhow::anyhow!("TrimEnd() takes no arguments"));
439                        }
440                        self.evaluate_trim(object, row_index, "trimend")?
441                    }
442                    _ => {
443                        return Err(anyhow::anyhow!(
444                            "Method '{}' cannot be used in comparisons",
445                            method
446                        ));
447                    }
448                }
449            }
450            SqlExpression::BinaryOp {
451                left: _expr_left,
452                op: arith_op,
453                right: _expr_right,
454            } if matches!(arith_op.as_str(), "+" | "-" | "*" | "/") => {
455                // Handle arithmetic expressions using ArithmeticEvaluator
456                let evaluator =
457                    ArithmeticEvaluator::with_date_notation(self.table, self.date_notation.clone());
458                let computed_value = evaluator.evaluate(left, row_index)?;
459                if row_index < 3 {
460                    debug!(
461                        "RecursiveWhereEvaluator: evaluate_binary_op() - computed arithmetic expression = {:?}",
462                        computed_value
463                    );
464                }
465                (Some(computed_value), "computed_expression".to_string())
466            }
467            SqlExpression::FunctionCall { name, .. } => {
468                // Handle function calls using ArithmeticEvaluator
469                let evaluator =
470                    ArithmeticEvaluator::with_date_notation(self.table, self.date_notation.clone());
471                let computed_value = evaluator.evaluate(left, row_index)?;
472                if row_index < 3 {
473                    debug!(
474                        "RecursiveWhereEvaluator: evaluate_binary_op() - computed function {} = {:?}",
475                        name, computed_value
476                    );
477                }
478                (Some(computed_value), format!("{}()", name))
479            }
480            _ => {
481                // Regular column reference
482                let column_name = self.extract_column_name(left)?;
483                if row_index < 3 {
484                    debug!(
485                        "RecursiveWhereEvaluator: evaluate_binary_op() - column_name = '{}'",
486                        column_name
487                    );
488                }
489
490                let col_index = self.table.get_column_index(&column_name).ok_or_else(|| {
491                    let suggestion = self.find_similar_column(&column_name);
492                    match suggestion {
493                        Some(similar) => anyhow!(
494                            "Column '{}' not found. Did you mean '{}'?",
495                            column_name,
496                            similar
497                        ),
498                        None => anyhow!("Column '{}' not found", column_name),
499                    }
500                })?;
501
502                let cell_value = self.table.get_value(row_index, col_index).cloned();
503                (cell_value, column_name)
504            }
505        };
506
507        if row_index < 3 {
508            debug!(
509                "RecursiveWhereEvaluator: evaluate_binary_op() - row {} column '{}' value = {:?}",
510                row_index, column_name, cell_value
511            );
512        }
513
514        // Get comparison value from right side
515        let compare_value = self.extract_value(right)?;
516
517        // Perform comparison
518        match (cell_value, op.to_uppercase().as_str(), &compare_value) {
519            (Some(DataValue::String(ref a)), "=", ExprValue::String(b)) => {
520                if row_index < 3 {
521                    debug!(
522                        "RecursiveWhereEvaluator: String comparison '{}' = '{}' (case_insensitive={})",
523                        a, b, self.case_insensitive
524                    );
525                }
526                if self.case_insensitive {
527                    Ok(a.to_lowercase() == b.to_lowercase())
528                } else {
529                    Ok(a == b)
530                }
531            }
532            (Some(DataValue::InternedString(ref a)), "=", ExprValue::String(b)) => {
533                if row_index < 3 {
534                    debug!(
535                        "RecursiveWhereEvaluator: InternedString comparison '{}' = '{}' (case_insensitive={})",
536                        a, b, self.case_insensitive
537                    );
538                }
539                if self.case_insensitive {
540                    Ok(a.to_lowercase() == b.to_lowercase())
541                } else {
542                    Ok(a.as_ref() == b)
543                }
544            }
545            (Some(DataValue::String(ref a)), "!=", ExprValue::String(b))
546            | (Some(DataValue::String(ref a)), "<>", ExprValue::String(b)) => {
547                if row_index < 3 {
548                    debug!(
549                        "RecursiveWhereEvaluator: String comparison '{}' != '{}' (case_insensitive={})",
550                        a, b, self.case_insensitive
551                    );
552                }
553                if self.case_insensitive {
554                    Ok(a.to_lowercase() != b.to_lowercase())
555                } else {
556                    Ok(a != b)
557                }
558            }
559            (Some(DataValue::InternedString(ref a)), "!=", ExprValue::String(b))
560            | (Some(DataValue::InternedString(ref a)), "<>", ExprValue::String(b)) => {
561                if row_index < 3 {
562                    debug!(
563                        "RecursiveWhereEvaluator: InternedString comparison '{}' != '{}' (case_insensitive={})",
564                        a, b, self.case_insensitive
565                    );
566                }
567                if self.case_insensitive {
568                    Ok(a.to_lowercase() != b.to_lowercase())
569                } else {
570                    Ok(a.as_ref() != b)
571                }
572            }
573            (Some(DataValue::String(ref a)), ">", ExprValue::String(b)) => {
574                if self.case_insensitive {
575                    Ok(a.to_lowercase() > b.to_lowercase())
576                } else {
577                    Ok(a > b)
578                }
579            }
580            (Some(DataValue::InternedString(ref a)), ">", ExprValue::String(b)) => {
581                if self.case_insensitive {
582                    Ok(a.to_lowercase() > b.to_lowercase())
583                } else {
584                    Ok(a.as_ref() > b)
585                }
586            }
587            (Some(DataValue::String(ref a)), ">=", ExprValue::String(b)) => {
588                if self.case_insensitive {
589                    Ok(a.to_lowercase() >= b.to_lowercase())
590                } else {
591                    Ok(a >= b)
592                }
593            }
594            (Some(DataValue::InternedString(ref a)), ">=", ExprValue::String(b)) => {
595                if self.case_insensitive {
596                    Ok(a.to_lowercase() >= b.to_lowercase())
597                } else {
598                    Ok(a.as_ref() >= b)
599                }
600            }
601            (Some(DataValue::String(ref a)), "<", ExprValue::String(b)) => {
602                if self.case_insensitive {
603                    Ok(a.to_lowercase() < b.to_lowercase())
604                } else {
605                    Ok(a < b)
606                }
607            }
608            (Some(DataValue::InternedString(ref a)), "<", ExprValue::String(b)) => {
609                if self.case_insensitive {
610                    Ok(a.to_lowercase() < b.to_lowercase())
611                } else {
612                    Ok(a.as_ref() < b)
613                }
614            }
615            (Some(DataValue::String(ref a)), "<=", ExprValue::String(b)) => {
616                if self.case_insensitive {
617                    Ok(a.to_lowercase() <= b.to_lowercase())
618                } else {
619                    Ok(a <= b)
620                }
621            }
622            (Some(DataValue::InternedString(ref a)), "<=", ExprValue::String(b)) => {
623                if self.case_insensitive {
624                    Ok(a.to_lowercase() <= b.to_lowercase())
625                } else {
626                    Ok(a.as_ref() <= b)
627                }
628            }
629
630            (Some(DataValue::Integer(a)), "=", ExprValue::Number(b)) => Ok(a as f64 == *b),
631            (Some(DataValue::Integer(a)), "!=", ExprValue::Number(b))
632            | (Some(DataValue::Integer(a)), "<>", ExprValue::Number(b)) => Ok(a as f64 != *b),
633            (Some(DataValue::Integer(a)), ">", ExprValue::Number(b)) => Ok(a as f64 > *b),
634            (Some(DataValue::Integer(a)), ">=", ExprValue::Number(b)) => Ok(a as f64 >= *b),
635            (Some(DataValue::Integer(a)), "<", ExprValue::Number(b)) => Ok((a as f64) < *b),
636            (Some(DataValue::Integer(a)), "<=", ExprValue::Number(b)) => Ok(a as f64 <= *b),
637
638            (Some(DataValue::Float(a)), "=", ExprValue::Number(b)) => {
639                Ok((a - b).abs() < f64::EPSILON)
640            }
641            (Some(DataValue::Float(a)), "!=", ExprValue::Number(b))
642            | (Some(DataValue::Float(a)), "<>", ExprValue::Number(b)) => {
643                Ok((a - b).abs() >= f64::EPSILON)
644            }
645            (Some(DataValue::Float(a)), ">", ExprValue::Number(b)) => Ok(a > *b),
646            (Some(DataValue::Float(a)), ">=", ExprValue::Number(b)) => Ok(a >= *b),
647            (Some(DataValue::Float(a)), "<", ExprValue::Number(b)) => Ok(a < *b),
648            (Some(DataValue::Float(a)), "<=", ExprValue::Number(b)) => Ok(a <= *b),
649
650            // LIKE operator
651            (Some(DataValue::String(ref text)), "LIKE", ExprValue::String(pattern)) => {
652                let regex_pattern = pattern.replace('%', ".*").replace('_', ".");
653                let regex = regex::RegexBuilder::new(&format!("^{}$", regex_pattern))
654                    .case_insensitive(true)
655                    .build()
656                    .map_err(|e| anyhow::anyhow!("Invalid LIKE pattern: {}", e))?;
657                Ok(regex.is_match(text))
658            }
659            (Some(DataValue::InternedString(ref text)), "LIKE", ExprValue::String(pattern)) => {
660                let regex_pattern = pattern.replace('%', ".*").replace('_', ".");
661                let regex = regex::RegexBuilder::new(&format!("^{}$", regex_pattern))
662                    .case_insensitive(true)
663                    .build()
664                    .map_err(|e| anyhow::anyhow!("Invalid LIKE pattern: {}", e))?;
665                Ok(regex.is_match(text.as_ref()))
666            }
667
668            // IS NULL / IS NOT NULL
669            (None, "IS", ExprValue::Null) | (Some(DataValue::Null), "IS", ExprValue::Null) => {
670                Ok(true)
671            }
672            (Some(_), "IS", ExprValue::Null) => Ok(false),
673            (None, "IS NOT", ExprValue::Null)
674            | (Some(DataValue::Null), "IS NOT", ExprValue::Null) => Ok(false),
675            (Some(_), "IS NOT", ExprValue::Null) => Ok(true),
676
677            // DateTime comparisons
678            (Some(DataValue::String(ref date_str)), op_str, ExprValue::DateTime(dt)) => {
679                if row_index < 3 {
680                    debug!(
681                        "RecursiveWhereEvaluator: DateTime comparison '{}' {} '{}' - attempting parse",
682                        date_str,
683                        op_str,
684                        dt.format("%Y-%m-%d %H:%M:%S")
685                    );
686                }
687
688                // Try to parse the string as a datetime - first try ISO 8601 with UTC
689                if let Ok(parsed_dt) = date_str.parse::<DateTime<Utc>>() {
690                    let result = Self::compare_datetime(op_str, &parsed_dt, dt);
691                    if row_index < 3 {
692                        debug!(
693                            "RecursiveWhereEvaluator: DateTime parsed as UTC: '{}' {} '{}' = {}",
694                            parsed_dt.format("%Y-%m-%d %H:%M:%S"),
695                            op_str,
696                            dt.format("%Y-%m-%d %H:%M:%S"),
697                            result
698                        );
699                    }
700                    Ok(result)
701                }
702                // Try ISO 8601 format without timezone (assume UTC)
703                else if let Ok(parsed_dt) =
704                    NaiveDateTime::parse_from_str(&date_str, "%Y-%m-%dT%H:%M:%S")
705                {
706                    let parsed_utc = Utc.from_utc_datetime(&parsed_dt);
707                    let result = Self::compare_datetime(op_str, &parsed_utc, dt);
708                    if row_index < 3 {
709                        debug!(
710                            "RecursiveWhereEvaluator: DateTime parsed as ISO 8601: '{}' {} '{}' = {}",
711                            parsed_utc.format("%Y-%m-%d %H:%M:%S"),
712                            op_str,
713                            dt.format("%Y-%m-%d %H:%M:%S"),
714                            result
715                        );
716                    }
717                    Ok(result)
718                }
719                // Try standard datetime format
720                else if let Ok(parsed_dt) =
721                    NaiveDateTime::parse_from_str(&date_str, "%Y-%m-%d %H:%M:%S")
722                {
723                    let parsed_utc = Utc.from_utc_datetime(&parsed_dt);
724                    let result = Self::compare_datetime(op_str, &parsed_utc, dt);
725                    if row_index < 3 {
726                        debug!(
727                            "RecursiveWhereEvaluator: DateTime parsed as standard format: '{}' {} '{}' = {}",
728                            parsed_utc.format("%Y-%m-%d %H:%M:%S"), op_str, dt.format("%Y-%m-%d %H:%M:%S"), result
729                        );
730                    }
731                    Ok(result)
732                }
733                // Try date-only format
734                else if let Ok(parsed_date) = NaiveDate::parse_from_str(&date_str, "%Y-%m-%d") {
735                    let parsed_dt =
736                        NaiveDateTime::new(parsed_date, NaiveTime::from_hms_opt(0, 0, 0).unwrap());
737                    let parsed_utc = Utc.from_utc_datetime(&parsed_dt);
738                    let result = Self::compare_datetime(op_str, &parsed_utc, dt);
739                    if row_index < 3 {
740                        debug!(
741                            "RecursiveWhereEvaluator: DateTime parsed as date-only: '{}' {} '{}' = {}",
742                            parsed_utc.format("%Y-%m-%d %H:%M:%S"),
743                            op_str,
744                            dt.format("%Y-%m-%d %H:%M:%S"),
745                            result
746                        );
747                    }
748                    Ok(result)
749                } else {
750                    if row_index < 3 {
751                        debug!(
752                            "RecursiveWhereEvaluator: DateTime parse FAILED for '{}' - no matching format",
753                            date_str
754                        );
755                    }
756                    Ok(false)
757                }
758            }
759            (Some(DataValue::InternedString(ref date_str)), op_str, ExprValue::DateTime(dt)) => {
760                if row_index < 3 {
761                    debug!(
762                        "RecursiveWhereEvaluator: DateTime comparison (interned) '{}' {} '{}' - attempting parse",
763                        date_str,
764                        op_str,
765                        dt.format("%Y-%m-%d %H:%M:%S")
766                    );
767                }
768
769                // Try to parse the string as a datetime - first try ISO 8601 with UTC
770                if let Ok(parsed_dt) = date_str.parse::<DateTime<Utc>>() {
771                    let result = Self::compare_datetime(op_str, &parsed_dt, dt);
772                    if row_index < 3 {
773                        debug!(
774                            "RecursiveWhereEvaluator: DateTime parsed as UTC: '{}' {} '{}' = {}",
775                            parsed_dt.format("%Y-%m-%d %H:%M:%S"),
776                            op_str,
777                            dt.format("%Y-%m-%d %H:%M:%S"),
778                            result
779                        );
780                    }
781                    Ok(result)
782                }
783                // Try ISO 8601 format without timezone (assume UTC)
784                else if let Ok(parsed_dt) =
785                    NaiveDateTime::parse_from_str(date_str.as_ref(), "%Y-%m-%dT%H:%M:%S")
786                {
787                    let parsed_utc = Utc.from_utc_datetime(&parsed_dt);
788                    let result = Self::compare_datetime(op_str, &parsed_utc, dt);
789                    if row_index < 3 {
790                        debug!(
791                            "RecursiveWhereEvaluator: DateTime parsed as ISO 8601: '{}' {} '{}' = {}",
792                            parsed_utc.format("%Y-%m-%d %H:%M:%S"),
793                            op_str,
794                            dt.format("%Y-%m-%d %H:%M:%S"),
795                            result
796                        );
797                    }
798                    Ok(result)
799                }
800                // Try standard datetime format
801                else if let Ok(parsed_dt) =
802                    NaiveDateTime::parse_from_str(date_str.as_ref(), "%Y-%m-%d %H:%M:%S")
803                {
804                    let parsed_utc = Utc.from_utc_datetime(&parsed_dt);
805                    let result = Self::compare_datetime(op_str, &parsed_utc, dt);
806                    if row_index < 3 {
807                        debug!(
808                            "RecursiveWhereEvaluator: DateTime parsed as standard format: '{}' {} '{}' = {}",
809                            parsed_utc.format("%Y-%m-%d %H:%M:%S"), op_str, dt.format("%Y-%m-%d %H:%M:%S"), result
810                        );
811                    }
812                    Ok(result)
813                }
814                // Try date-only format
815                else if let Ok(parsed_date) =
816                    NaiveDate::parse_from_str(date_str.as_ref(), "%Y-%m-%d")
817                {
818                    let parsed_dt =
819                        NaiveDateTime::new(parsed_date, NaiveTime::from_hms_opt(0, 0, 0).unwrap());
820                    let parsed_utc = Utc.from_utc_datetime(&parsed_dt);
821                    let result = Self::compare_datetime(op_str, &parsed_utc, dt);
822                    if row_index < 3 {
823                        debug!(
824                            "RecursiveWhereEvaluator: DateTime parsed as date-only: '{}' {} '{}' = {}",
825                            parsed_utc.format("%Y-%m-%d %H:%M:%S"),
826                            op_str,
827                            dt.format("%Y-%m-%d %H:%M:%S"),
828                            result
829                        );
830                    }
831                    Ok(result)
832                } else {
833                    if row_index < 3 {
834                        debug!(
835                            "RecursiveWhereEvaluator: DateTime parse FAILED for '{}' - no matching format",
836                            date_str
837                        );
838                    }
839                    Ok(false)
840                }
841            }
842
843            // DateTime vs DateTime comparisons (when column is already parsed as DateTime)
844            (Some(DataValue::DateTime(ref date_str)), op_str, ExprValue::DateTime(dt)) => {
845                if row_index < 3 {
846                    debug!(
847                        "RecursiveWhereEvaluator: DateTime vs DateTime comparison '{}' {} '{}' - direct comparison",
848                        date_str, op_str, dt.format("%Y-%m-%d %H:%M:%S")
849                    );
850                }
851
852                // Parse the DataValue::DateTime string to DateTime<Utc>
853                if let Ok(parsed_dt) = date_str.parse::<DateTime<Utc>>() {
854                    let result = Self::compare_datetime(op_str, &parsed_dt, dt);
855                    if row_index < 3 {
856                        debug!(
857                            "RecursiveWhereEvaluator: DateTime vs DateTime parsed successfully: '{}' {} '{}' = {}",
858                            parsed_dt.format("%Y-%m-%d %H:%M:%S"), op_str, dt.format("%Y-%m-%d %H:%M:%S"), result
859                        );
860                    }
861                    Ok(result)
862                }
863                // Try ISO 8601 format without timezone (assume UTC)
864                else if let Ok(parsed_dt) =
865                    NaiveDateTime::parse_from_str(&date_str, "%Y-%m-%dT%H:%M:%S")
866                {
867                    let parsed_utc = Utc.from_utc_datetime(&parsed_dt);
868                    let result = Self::compare_datetime(op_str, &parsed_utc, dt);
869                    if row_index < 3 {
870                        debug!(
871                            "RecursiveWhereEvaluator: DateTime vs DateTime ISO 8601: '{}' {} '{}' = {}",
872                            parsed_utc.format("%Y-%m-%d %H:%M:%S"),
873                            op_str,
874                            dt.format("%Y-%m-%d %H:%M:%S"),
875                            result
876                        );
877                    }
878                    Ok(result)
879                } else {
880                    if row_index < 3 {
881                        debug!(
882                            "RecursiveWhereEvaluator: DateTime vs DateTime parse FAILED for '{}' - no matching format",
883                            date_str
884                        );
885                    }
886                    Ok(false)
887                }
888            }
889
890            _ => Ok(false),
891        }
892    }
893
894    fn evaluate_in_list(
895        &self,
896        expr: &SqlExpression,
897        values: &[SqlExpression],
898        row_index: usize,
899        _ignore_case: bool,
900    ) -> Result<bool> {
901        let column_name = self.extract_column_name(expr)?;
902        let col_index = self
903            .table
904            .get_column_index(&column_name)
905            .ok_or_else(|| anyhow::anyhow!("Column '{}' not found", column_name))?;
906
907        let cell_value = self.table.get_value(row_index, col_index).cloned();
908
909        for value_expr in values {
910            let compare_value = self.extract_value(value_expr)?;
911            let matches = match (cell_value.as_ref(), &compare_value) {
912                (Some(DataValue::String(a)), ExprValue::String(b)) => {
913                    if self.case_insensitive {
914                        if row_index < 3 {
915                            debug!("RecursiveWhereEvaluator: IN list string comparison '{}' in '{}' (case_insensitive={})", a, b, self.case_insensitive);
916                        }
917                        a.to_lowercase() == b.to_lowercase()
918                    } else {
919                        a == b
920                    }
921                }
922                (Some(DataValue::InternedString(a)), ExprValue::String(b)) => {
923                    if self.case_insensitive {
924                        if row_index < 3 {
925                            debug!("RecursiveWhereEvaluator: IN list interned string comparison '{}' in '{}' (case_insensitive={})", a, b, self.case_insensitive);
926                        }
927                        a.to_lowercase() == b.to_lowercase()
928                    } else {
929                        a.as_ref() == b
930                    }
931                }
932                (Some(DataValue::Integer(a)), ExprValue::Number(b)) => *a as f64 == *b,
933                (Some(DataValue::Float(a)), ExprValue::Number(b)) => (*a - b).abs() < f64::EPSILON,
934                _ => false,
935            };
936
937            if matches {
938                return Ok(true);
939            }
940        }
941
942        Ok(false)
943    }
944
945    fn evaluate_between(
946        &self,
947        expr: &SqlExpression,
948        lower: &SqlExpression,
949        upper: &SqlExpression,
950        row_index: usize,
951    ) -> Result<bool> {
952        let column_name = self.extract_column_name(expr)?;
953        let col_index = self
954            .table
955            .get_column_index(&column_name)
956            .ok_or_else(|| anyhow::anyhow!("Column '{}' not found", column_name))?;
957
958        let cell_value = self.table.get_value(row_index, col_index).cloned();
959        let lower_value = self.extract_value(lower)?;
960        let upper_value = self.extract_value(upper)?;
961
962        match (cell_value, &lower_value, &upper_value) {
963            (Some(DataValue::Integer(n)), ExprValue::Number(l), ExprValue::Number(u)) => {
964                Ok(n as f64 >= *l && n as f64 <= *u)
965            }
966            (Some(DataValue::Float(n)), ExprValue::Number(l), ExprValue::Number(u)) => {
967                Ok(n >= *l && n <= *u)
968            }
969            (Some(DataValue::String(ref s)), ExprValue::String(l), ExprValue::String(u)) => {
970                Ok(s >= l && s <= u)
971            }
972            (
973                Some(DataValue::InternedString(ref s)),
974                ExprValue::String(l),
975                ExprValue::String(u),
976            ) => Ok(s.as_ref() >= l && s.as_ref() <= u),
977            _ => Ok(false),
978        }
979    }
980
981    fn evaluate_method_call(
982        &self,
983        object: &str,
984        method: &str,
985        args: &[SqlExpression],
986        row_index: usize,
987    ) -> Result<bool> {
988        if row_index < 3 {
989            debug!(
990                "RecursiveWhereEvaluator: evaluate_method_call - object='{}', method='{}', row={}",
991                object, method, row_index
992            );
993        }
994
995        // Get column value
996        let col_index = self.table.get_column_index(object).ok_or_else(|| {
997            let suggestion = self.find_similar_column(object);
998            match suggestion {
999                Some(similar) => {
1000                    anyhow!("Column '{}' not found. Did you mean '{}'?", object, similar)
1001                }
1002                None => anyhow!("Column '{}' not found", object),
1003            }
1004        })?;
1005
1006        let cell_value = self.table.get_value(row_index, col_index).cloned();
1007        if row_index < 3 {
1008            debug!(
1009                "RecursiveWhereEvaluator: Row {} column '{}' value = {:?}",
1010                row_index, object, cell_value
1011            );
1012        }
1013
1014        match method.to_lowercase().as_str() {
1015            "contains" => {
1016                if args.len() != 1 {
1017                    return Err(anyhow::anyhow!("Contains requires exactly 1 argument"));
1018                }
1019                let search_str = self.extract_string_value(&args[0])?;
1020                // Pre-compute lowercase once instead of for every row
1021                let search_lower = search_str.to_lowercase();
1022
1023                // Type coercion: convert numeric values to strings for string methods
1024                match cell_value {
1025                    Some(DataValue::String(ref s)) => {
1026                        let result = s.to_lowercase().contains(&search_lower);
1027                        // Only log first few rows to avoid performance impact
1028                        if row_index < 3 {
1029                            debug!("RecursiveWhereEvaluator: Row {} contains('{}') on '{}' = {} (case-insensitive)", row_index, search_str, s, result);
1030                        }
1031                        Ok(result)
1032                    }
1033                    Some(DataValue::InternedString(ref s)) => {
1034                        let result = s.to_lowercase().contains(&search_lower);
1035                        // Only log first few rows to avoid performance impact
1036                        if row_index < 3 {
1037                            debug!("RecursiveWhereEvaluator: Row {} contains('{}') on interned '{}' = {} (case-insensitive)", row_index, search_str, s, result);
1038                        }
1039                        Ok(result)
1040                    }
1041                    Some(DataValue::Integer(n)) => {
1042                        let str_val = n.to_string();
1043                        let result = str_val.contains(&search_str);
1044                        if row_index < 3 {
1045                            debug!("RecursiveWhereEvaluator: Row {} contains('{}') on integer '{}' = {}", row_index, search_str, str_val, result);
1046                        }
1047                        Ok(result)
1048                    }
1049                    Some(DataValue::Float(f)) => {
1050                        let str_val = f.to_string();
1051                        let result = str_val.contains(&search_str);
1052                        if row_index < 3 {
1053                            debug!(
1054                                "RecursiveWhereEvaluator: Row {} contains('{}') on float '{}' = {}",
1055                                row_index, search_str, str_val, result
1056                            );
1057                        }
1058                        Ok(result)
1059                    }
1060                    Some(DataValue::Boolean(b)) => {
1061                        let str_val = b.to_string();
1062                        let result = str_val.contains(&search_str);
1063                        if row_index < 3 {
1064                            debug!("RecursiveWhereEvaluator: Row {} contains('{}') on boolean '{}' = {}", row_index, search_str, str_val, result);
1065                        }
1066                        Ok(result)
1067                    }
1068                    Some(DataValue::DateTime(dt)) => {
1069                        // DateTime columns can use string methods via coercion
1070                        let result = dt.contains(&search_str);
1071                        if row_index < 3 {
1072                            debug!("RecursiveWhereEvaluator: Row {} contains('{}') on datetime '{}' = {}", row_index, search_str, dt, result);
1073                        }
1074                        Ok(result)
1075                    }
1076                    _ => {
1077                        if row_index < 3 {
1078                            debug!("RecursiveWhereEvaluator: Row {} contains('{}') on null/empty value = false", row_index, search_str);
1079                        }
1080                        Ok(false)
1081                    }
1082                }
1083            }
1084            "startswith" => {
1085                if args.len() != 1 {
1086                    return Err(anyhow::anyhow!("StartsWith requires exactly 1 argument"));
1087                }
1088                let prefix = self.extract_string_value(&args[0])?;
1089
1090                // Type coercion: convert numeric values to strings for string methods
1091                match cell_value {
1092                    Some(DataValue::String(ref s)) => {
1093                        Ok(s.to_lowercase().starts_with(&prefix.to_lowercase()))
1094                    }
1095                    Some(DataValue::InternedString(ref s)) => {
1096                        Ok(s.to_lowercase().starts_with(&prefix.to_lowercase()))
1097                    }
1098                    Some(DataValue::Integer(n)) => Ok(n.to_string().starts_with(&prefix)),
1099                    Some(DataValue::Float(f)) => Ok(f.to_string().starts_with(&prefix)),
1100                    Some(DataValue::Boolean(b)) => Ok(b.to_string().starts_with(&prefix)),
1101                    Some(DataValue::DateTime(dt)) => Ok(dt.starts_with(&prefix)),
1102                    _ => Ok(false),
1103                }
1104            }
1105            "endswith" => {
1106                if args.len() != 1 {
1107                    return Err(anyhow::anyhow!("EndsWith requires exactly 1 argument"));
1108                }
1109                let suffix = self.extract_string_value(&args[0])?;
1110
1111                // Type coercion: convert numeric values to strings for string methods
1112                match cell_value {
1113                    Some(DataValue::String(ref s)) => {
1114                        Ok(s.to_lowercase().ends_with(&suffix.to_lowercase()))
1115                    }
1116                    Some(DataValue::InternedString(ref s)) => {
1117                        Ok(s.to_lowercase().ends_with(&suffix.to_lowercase()))
1118                    }
1119                    Some(DataValue::Integer(n)) => Ok(n.to_string().ends_with(&suffix)),
1120                    Some(DataValue::Float(f)) => Ok(f.to_string().ends_with(&suffix)),
1121                    Some(DataValue::Boolean(b)) => Ok(b.to_string().ends_with(&suffix)),
1122                    Some(DataValue::DateTime(dt)) => Ok(dt.ends_with(&suffix)),
1123                    _ => Ok(false),
1124                }
1125            }
1126            _ => Err(anyhow::anyhow!("Unsupported method: {}", method)),
1127        }
1128    }
1129
1130    fn extract_column_name(&self, expr: &SqlExpression) -> Result<String> {
1131        match expr {
1132            SqlExpression::Column(name) => Ok(name.clone()),
1133            _ => Err(anyhow::anyhow!("Expected column name, got: {:?}", expr)),
1134        }
1135    }
1136
1137    fn extract_string_value(&self, expr: &SqlExpression) -> Result<String> {
1138        match expr {
1139            SqlExpression::StringLiteral(s) => Ok(s.clone()),
1140            _ => Err(anyhow::anyhow!("Expected string literal, got: {:?}", expr)),
1141        }
1142    }
1143
1144    fn extract_value(&self, expr: &SqlExpression) -> Result<ExprValue> {
1145        match expr {
1146            SqlExpression::StringLiteral(s) => Ok(ExprValue::String(s.clone())),
1147            SqlExpression::NumberLiteral(n) => {
1148                if let Ok(num) = n.parse::<f64>() {
1149                    Ok(ExprValue::Number(num))
1150                } else {
1151                    Ok(ExprValue::String(n.clone()))
1152                }
1153            }
1154            SqlExpression::DateTimeConstructor {
1155                year,
1156                month,
1157                day,
1158                hour,
1159                minute,
1160                second,
1161            } => {
1162                // Create a DateTime from the constructor
1163                let naive_date = NaiveDate::from_ymd_opt(*year, *month, *day)
1164                    .ok_or_else(|| anyhow::anyhow!("Invalid date: {}-{}-{}", year, month, day))?;
1165                let naive_time = NaiveTime::from_hms_opt(
1166                    hour.unwrap_or(0),
1167                    minute.unwrap_or(0),
1168                    second.unwrap_or(0),
1169                )
1170                .ok_or_else(|| anyhow::anyhow!("Invalid time"))?;
1171                let naive_datetime = NaiveDateTime::new(naive_date, naive_time);
1172                let datetime = Utc.from_utc_datetime(&naive_datetime);
1173                Ok(ExprValue::DateTime(datetime))
1174            }
1175            SqlExpression::DateTimeToday {
1176                hour,
1177                minute,
1178                second,
1179            } => {
1180                // Get today's date with optional time
1181                let today = Local::now().date_naive();
1182                let time = NaiveTime::from_hms_opt(
1183                    hour.unwrap_or(0),
1184                    minute.unwrap_or(0),
1185                    second.unwrap_or(0),
1186                )
1187                .ok_or_else(|| anyhow::anyhow!("Invalid time"))?;
1188                let naive_datetime = NaiveDateTime::new(today, time);
1189                let datetime = Utc.from_utc_datetime(&naive_datetime);
1190                Ok(ExprValue::DateTime(datetime))
1191            }
1192            _ => Ok(ExprValue::Null),
1193        }
1194    }
1195
1196    /// Evaluate a CASE expression as a boolean (for WHERE clauses)
1197    fn evaluate_case_expression_as_bool(
1198        &self,
1199        when_branches: &[crate::sql::recursive_parser::WhenBranch],
1200        else_branch: &Option<Box<SqlExpression>>,
1201        row_index: usize,
1202    ) -> Result<bool> {
1203        debug!(
1204            "RecursiveWhereEvaluator: evaluating CASE expression as bool for row {}",
1205            row_index
1206        );
1207
1208        // Evaluate each WHEN condition in order
1209        for branch in when_branches {
1210            // Evaluate the condition as a boolean
1211            let condition_result = self.evaluate_expression(&branch.condition, row_index)?;
1212
1213            if condition_result {
1214                debug!("CASE: WHEN condition matched, evaluating result expression as bool");
1215                // Evaluate the result and convert to boolean
1216                return self.evaluate_expression_as_bool(&branch.result, row_index);
1217            }
1218        }
1219
1220        // If no WHEN condition matched, evaluate ELSE clause (or return false)
1221        match else_branch {
1222            Some(else_expr) => {
1223                debug!("CASE: No WHEN matched, evaluating ELSE expression as bool");
1224                self.evaluate_expression_as_bool(else_expr, row_index)
1225            }
1226            None => {
1227                debug!("CASE: No WHEN matched and no ELSE, returning false");
1228                Ok(false)
1229            }
1230        }
1231    }
1232
1233    /// Helper method to evaluate any expression as a boolean
1234    fn evaluate_expression_as_bool(&self, expr: &SqlExpression, row_index: usize) -> Result<bool> {
1235        match expr {
1236            // For expressions that naturally return booleans, use the existing evaluator
1237            SqlExpression::BinaryOp { .. }
1238            | SqlExpression::InList { .. }
1239            | SqlExpression::NotInList { .. }
1240            | SqlExpression::Between { .. }
1241            | SqlExpression::Not { .. }
1242            | SqlExpression::MethodCall { .. } => self.evaluate_expression(expr, row_index),
1243            // For CASE expressions, recurse
1244            SqlExpression::CaseExpression {
1245                when_branches,
1246                else_branch,
1247            } => self.evaluate_case_expression_as_bool(when_branches, else_branch, row_index),
1248            // For other expressions (columns, literals), use ArithmeticEvaluator and convert
1249            _ => {
1250                // Use ArithmeticEvaluator to get the value, then convert to boolean
1251                let evaluator =
1252                    crate::data::arithmetic_evaluator::ArithmeticEvaluator::with_date_notation(
1253                        self.table,
1254                        self.date_notation.clone(),
1255                    );
1256                let value = evaluator.evaluate(expr, row_index)?;
1257
1258                match value {
1259                    crate::data::datatable::DataValue::Boolean(b) => Ok(b),
1260                    crate::data::datatable::DataValue::Integer(i) => Ok(i != 0),
1261                    crate::data::datatable::DataValue::Float(f) => Ok(f != 0.0),
1262                    crate::data::datatable::DataValue::Null => Ok(false),
1263                    crate::data::datatable::DataValue::String(s) => Ok(!s.is_empty()),
1264                    crate::data::datatable::DataValue::InternedString(s) => Ok(!s.is_empty()),
1265                    _ => Ok(true), // Other types are considered truthy
1266                }
1267            }
1268        }
1269    }
1270}
1271
1272enum ExprValue {
1273    String(String),
1274    Number(f64),
1275    DateTime(DateTime<Utc>),
1276    Null,
1277}