Skip to main content

ddb_core/engine/
evaluator.rs

1// Expression evaluator for WHERE clauses and SELECT columns
2use crate::error::{DdbError, Result};
3use crate::functions::Value;
4use crate::parser::{BinaryOperator, Expression, Literal, UnaryOperator};
5use super::row::Row;
6use super::system_vars::SystemVariables;
7
8/// Evaluates expressions against row data
9pub struct Evaluator {
10    system_vars: SystemVariables,
11}
12
13impl Evaluator {
14    pub fn new() -> Self {
15        Evaluator {
16            system_vars: SystemVariables::new(),
17        }
18    }
19
20    /// Evaluate an expression against a row of data
21    pub fn evaluate(&self, expr: &Expression, row: &Row) -> Result<Value> {
22        match expr {
23            Expression::Literal(lit) => Ok(self.evaluate_literal(lit)),
24            Expression::Column(name) => self.evaluate_column(name, row),
25            Expression::SystemVariable(name) => self.evaluate_system_variable(name),
26            Expression::Function { name, args } => self.evaluate_function(name, args, row),
27            Expression::BinaryOp { left, op, right } => {
28                self.evaluate_binary_op(left, op, right, row)
29            }
30            Expression::UnaryOp { op, operand } => self.evaluate_unary_op(op, operand, row),
31        }
32    }
33
34    /// Evaluate a literal value
35    fn evaluate_literal(&self, lit: &Literal) -> Value {
36        match lit {
37            Literal::String(s) => Value::String(s.clone()),
38            Literal::Number(n) => Value::Float(*n),
39            Literal::Integer(i) => Value::Integer(*i),
40            Literal::Boolean(b) => Value::Boolean(*b),
41            Literal::Null => Value::Null,
42        }
43    }
44
45    /// Evaluate a column reference
46    fn evaluate_column(&self, name: &str, row: &Row) -> Result<Value> {
47        row.get(name)
48            .cloned()
49            .ok_or_else(|| DdbError::ExecutionError(format!("Column not found: {}", name)))
50    }
51
52    /// Evaluate a system variable (@@VARIABLE)
53    fn evaluate_system_variable(&self, name: &str) -> Result<Value> {
54        self.system_vars
55            .get(name)
56            .cloned()
57            .ok_or_else(|| DdbError::ExecutionError(format!("Unknown system variable: @@{}", name)))
58    }
59
60    /// Evaluate a function call
61    fn evaluate_function(&self, name: &str, args: &[Expression], row: &Row) -> Result<Value> {
62        // Special handling for COUNT(*) - don't evaluate the "*" as a column
63        let is_count_star = name.to_uppercase() == "COUNT"
64            && args.len() == 1
65            && matches!(&args[0], Expression::Column(col) if col == "*");
66
67        // For COUNT(*), we just need to return a placeholder value
68        // The actual counting is done by the executor when aggregating
69        if is_count_star {
70            // For row-level evaluation (non-aggregate context), COUNT(*) = 1
71            return Ok(Value::Integer(1));
72        }
73
74        // Evaluate all arguments first
75        let arg_values: Result<Vec<Value>> = args.iter().map(|arg| self.evaluate(arg, row)).collect();
76        let arg_values = arg_values?;
77
78        // Call the appropriate function
79        match name.to_uppercase().as_str() {
80            // Math functions
81            "ABS" => {
82                if arg_values.len() != 1 {
83                    return Err(DdbError::FunctionError("ABS requires 1 argument".to_string()));
84                }
85                crate::functions::math::abs(&arg_values[0])
86            }
87            "CEIL" | "CEILING" => {
88                if arg_values.len() != 1 {
89                    return Err(DdbError::FunctionError("CEIL requires 1 argument".to_string()));
90                }
91                crate::functions::math::ceil(&arg_values[0])
92            }
93            "FLOOR" => {
94                if arg_values.len() != 1 {
95                    return Err(DdbError::FunctionError("FLOOR requires 1 argument".to_string()));
96                }
97                crate::functions::math::floor(&arg_values[0])
98            }
99            "ROUND" => {
100                if arg_values.is_empty() || arg_values.len() > 2 {
101                    return Err(DdbError::FunctionError("ROUND requires 1-2 arguments".to_string()));
102                }
103                let decimals = if arg_values.len() == 2 {
104                    Some(&arg_values[1])
105                } else {
106                    None
107                };
108                crate::functions::math::round(&arg_values[0], decimals)
109            }
110            "SQRT" => {
111                if arg_values.len() != 1 {
112                    return Err(DdbError::FunctionError("SQRT requires 1 argument".to_string()));
113                }
114                crate::functions::math::sqrt(&arg_values[0])
115            }
116            "POW" | "POWER" => {
117                if arg_values.len() != 2 {
118                    return Err(DdbError::FunctionError("POW requires 2 arguments".to_string()));
119                }
120                crate::functions::math::pow(&arg_values[0], &arg_values[1])
121            }
122
123            // String functions
124            "UPPER" | "UCASE" => {
125                if arg_values.len() != 1 {
126                    return Err(DdbError::FunctionError("UPPER requires 1 argument".to_string()));
127                }
128                crate::functions::string::upper(&arg_values[0])
129            }
130            "LOWER" | "LCASE" => {
131                if arg_values.len() != 1 {
132                    return Err(DdbError::FunctionError("LOWER requires 1 argument".to_string()));
133                }
134                crate::functions::string::lower(&arg_values[0])
135            }
136            "LENGTH" | "CHAR_LENGTH" => {
137                if arg_values.len() != 1 {
138                    return Err(DdbError::FunctionError("LENGTH requires 1 argument".to_string()));
139                }
140                crate::functions::string::length(&arg_values[0])
141            }
142            "TRIM" => {
143                if arg_values.len() != 1 {
144                    return Err(DdbError::FunctionError("TRIM requires 1 argument".to_string()));
145                }
146                crate::functions::string::trim(&arg_values[0])
147            }
148            "CONCAT" => crate::functions::string::concat(&arg_values),
149            "SUBSTR" | "SUBSTRING" => {
150                if arg_values.len() < 2 || arg_values.len() > 3 {
151                    return Err(DdbError::FunctionError("SUBSTR requires 2-3 arguments".to_string()));
152                }
153                let len = if arg_values.len() == 3 {
154                    Some(&arg_values[2])
155                } else {
156                    None
157                };
158                crate::functions::string::substr(&arg_values[0], &arg_values[1], len)
159            }
160
161            // Type conversion
162            "CAST" => {
163                if arg_values.len() != 2 {
164                    return Err(DdbError::FunctionError("CAST requires 2 arguments (value, type)".to_string()));
165                }
166                let type_name = arg_values[1].to_string();
167                crate::functions::conversion::cast(&arg_values[0], &type_name)
168            }
169            "ATOF" => {
170                if arg_values.len() != 1 {
171                    return Err(DdbError::FunctionError("ATOF requires 1 argument".to_string()));
172                }
173                crate::functions::conversion::atof(&arg_values[0])
174            }
175            "ATOI" => {
176                if arg_values.len() != 1 {
177                    return Err(DdbError::FunctionError("ATOI requires 1 argument".to_string()));
178                }
179                crate::functions::conversion::atoi(&arg_values[0])
180            }
181
182            // Date/Time functions
183            "NOW" | "CURRENT_TIMESTAMP" => Ok(crate::functions::datetime::now()),
184            "CURDATE" | "CURRENT_DATE" => Ok(crate::functions::datetime::curdate()),
185            "CURTIME" | "CURRENT_TIME" => Ok(crate::functions::datetime::curtime()),
186            "YEAR" => {
187                if arg_values.len() != 1 {
188                    return Err(DdbError::FunctionError("YEAR requires 1 argument".to_string()));
189                }
190                crate::functions::datetime::year(&arg_values[0])
191            }
192            "MONTH" => {
193                if arg_values.len() != 1 {
194                    return Err(DdbError::FunctionError("MONTH requires 1 argument".to_string()));
195                }
196                crate::functions::datetime::month(&arg_values[0])
197            }
198            "DAY" => {
199                if arg_values.len() != 1 {
200                    return Err(DdbError::FunctionError("DAY requires 1 argument".to_string()));
201                }
202                crate::functions::datetime::day(&arg_values[0])
203            }
204            "DATEDIFF" => {
205                if arg_values.len() != 2 {
206                    return Err(DdbError::FunctionError("DATEDIFF requires 2 arguments".to_string()));
207                }
208                crate::functions::datetime::datediff(&arg_values[0], &arg_values[1])
209            }
210            "DATEADD" => {
211                if arg_values.len() != 3 {
212                    return Err(DdbError::FunctionError("DATEADD requires 3 arguments (date, interval, unit)".to_string()));
213                }
214                let unit = arg_values[2].to_string();
215                crate::functions::datetime::dateadd(&arg_values[0], &arg_values[1], &unit)
216            }
217
218            // Conditional functions
219            "IF" => {
220                if arg_values.len() != 3 {
221                    return Err(DdbError::FunctionError("IF requires 3 arguments".to_string()));
222                }
223                crate::functions::conditional::if_fn(&arg_values[0], &arg_values[1], &arg_values[2])
224            }
225            "IFNULL" => {
226                if arg_values.len() != 2 {
227                    return Err(DdbError::FunctionError("IFNULL requires 2 arguments".to_string()));
228                }
229                crate::functions::conditional::ifnull(&arg_values[0], &arg_values[1])
230            }
231            "COALESCE" => crate::functions::conditional::coalesce(&arg_values),
232
233            // System functions
234            "VERSION" => Ok(crate::functions::system::version()),
235            "DATABASE" => Ok(crate::functions::system::database(None)),
236            "UUID" => Ok(crate::functions::system::uuid()),
237
238            // Utility functions
239            "HASH" => {
240                if arg_values.len() != 1 {
241                    return Err(DdbError::FunctionError("HASH requires 1 argument".to_string()));
242                }
243                crate::functions::utility::hash(&arg_values[0])
244            }
245            "BASE64_ENCODE" => {
246                if arg_values.len() != 1 {
247                    return Err(DdbError::FunctionError("BASE64_ENCODE requires 1 argument".to_string()));
248                }
249                crate::functions::utility::base64_encode(&arg_values[0])
250            }
251            "BASE64_DECODE" => {
252                if arg_values.len() != 1 {
253                    return Err(DdbError::FunctionError("BASE64_DECODE requires 1 argument".to_string()));
254                }
255                crate::functions::utility::base64_decode(&arg_values[0])
256            }
257
258            _ => Err(DdbError::FunctionError(format!("Unknown function: {}", name))),
259        }
260    }
261
262    /// Evaluate a binary operation
263    fn evaluate_binary_op(
264        &self,
265        left: &Expression,
266        op: &BinaryOperator,
267        right: &Expression,
268        row: &Row,
269    ) -> Result<Value> {
270        let left_val = self.evaluate(left, row)?;
271        let right_val = self.evaluate(right, row)?;
272
273        match op {
274            BinaryOperator::Equal => Ok(Value::Boolean(self.compare_values(&left_val, &right_val) == std::cmp::Ordering::Equal)),
275            BinaryOperator::NotEqual => Ok(Value::Boolean(self.compare_values(&left_val, &right_val) != std::cmp::Ordering::Equal)),
276            BinaryOperator::GreaterThan => Ok(Value::Boolean(self.compare_values(&left_val, &right_val) == std::cmp::Ordering::Greater)),
277            BinaryOperator::GreaterEqual => {
278                let cmp = self.compare_values(&left_val, &right_val);
279                Ok(Value::Boolean(cmp == std::cmp::Ordering::Greater || cmp == std::cmp::Ordering::Equal))
280            }
281            BinaryOperator::LessThan => Ok(Value::Boolean(self.compare_values(&left_val, &right_val) == std::cmp::Ordering::Less)),
282            BinaryOperator::LessEqual => {
283                let cmp = self.compare_values(&left_val, &right_val);
284                Ok(Value::Boolean(cmp == std::cmp::Ordering::Less || cmp == std::cmp::Ordering::Equal))
285            }
286            BinaryOperator::Like => self.evaluate_like(&left_val, &right_val),
287            BinaryOperator::And => {
288                let left_bool = self.to_boolean(&left_val)?;
289                let right_bool = self.to_boolean(&right_val)?;
290                Ok(Value::Boolean(left_bool && right_bool))
291            }
292            BinaryOperator::Or => {
293                let left_bool = self.to_boolean(&left_val)?;
294                let right_bool = self.to_boolean(&right_val)?;
295                Ok(Value::Boolean(left_bool || right_bool))
296            }
297        }
298    }
299
300    /// Evaluate a unary operation
301    fn evaluate_unary_op(
302        &self,
303        op: &UnaryOperator,
304        operand: &Expression,
305        row: &Row,
306    ) -> Result<Value> {
307        let val = self.evaluate(operand, row)?;
308
309        match op {
310            UnaryOperator::Not => {
311                let bool_val = self.to_boolean(&val)?;
312                Ok(Value::Boolean(!bool_val))
313            }
314            UnaryOperator::IsNull => Ok(Value::Boolean(val.is_null())),
315        }
316    }
317
318    /// Compare two values
319    fn compare_values(&self, left: &Value, right: &Value) -> std::cmp::Ordering {
320        use std::cmp::Ordering;
321
322        match (left, right) {
323            (Value::Null, Value::Null) => Ordering::Equal,
324            (Value::Null, _) => Ordering::Less,
325            (_, Value::Null) => Ordering::Greater,
326            (Value::Integer(a), Value::Integer(b)) => a.cmp(b),
327            (Value::Float(a), Value::Float(b)) => {
328                a.partial_cmp(b).unwrap_or(Ordering::Equal)
329            }
330            (Value::Integer(a), Value::Float(b)) => {
331                (*a as f64).partial_cmp(b).unwrap_or(Ordering::Equal)
332            }
333            (Value::Float(a), Value::Integer(b)) => {
334                a.partial_cmp(&(*b as f64)).unwrap_or(Ordering::Equal)
335            }
336            (Value::String(a), Value::String(b)) => a.cmp(b),
337            (Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
338            _ => Ordering::Equal,
339        }
340    }
341
342    /// Evaluate LIKE pattern matching
343    /// Optimized to avoid regex compilation for simple patterns
344    fn evaluate_like(&self, value: &Value, pattern: &Value) -> Result<Value> {
345        let val_str = value.to_string();
346        let pattern_str = pattern.to_string();
347
348        // Fast path for common pattern types (avoids expensive regex compilation)
349        let matched = if !pattern_str.contains('_') {
350            // No single-char wildcards, only % wildcards
351            if pattern_str.starts_with('%') && pattern_str.ends_with('%') {
352                // %text% -> contains
353                let search = &pattern_str[1..pattern_str.len()-1];
354                if !search.contains('%') {
355                    return Ok(Value::Boolean(val_str.contains(search)));
356                }
357            } else if pattern_str.starts_with('%') {
358                // %text -> ends_with
359                let suffix = &pattern_str[1..];
360                if !suffix.contains('%') {
361                    return Ok(Value::Boolean(val_str.ends_with(suffix)));
362                }
363            } else if pattern_str.ends_with('%') {
364                // text% -> starts_with
365                let prefix = &pattern_str[..pattern_str.len()-1];
366                if !prefix.contains('%') {
367                    return Ok(Value::Boolean(val_str.starts_with(prefix)));
368                }
369            } else if !pattern_str.contains('%') {
370                // No wildcards at all -> exact match
371                return Ok(Value::Boolean(val_str == pattern_str));
372            }
373
374            // Complex pattern with multiple % - fall through to regex
375            self.evaluate_like_regex(&val_str, &pattern_str)?
376        } else {
377            // Pattern contains _ wildcard - use regex
378            self.evaluate_like_regex(&val_str, &pattern_str)?
379        };
380
381        Ok(Value::Boolean(matched))
382    }
383
384    /// Evaluate LIKE using regex (for complex patterns)
385    #[inline]
386    fn evaluate_like_regex(&self, val_str: &str, pattern_str: &str) -> Result<bool> {
387        // Escape regex special chars, but keep SQL wildcards
388        let mut regex_pattern = String::with_capacity(pattern_str.len() * 2);
389
390        for ch in pattern_str.chars() {
391            match ch {
392                '%' => regex_pattern.push_str(".*"),
393                '_' => regex_pattern.push('.'),
394                // Escape regex special characters
395                '.' | '^' | '$' | '*' | '+' | '?' | '(' | ')' | '[' | ']' |
396                '{' | '}' | '\\' | '|' => {
397                    regex_pattern.push('\\');
398                    regex_pattern.push(ch);
399                }
400                _ => regex_pattern.push(ch),
401            }
402        }
403
404        let regex = regex::Regex::new(&format!("^{}$", regex_pattern))
405            .map_err(|e| DdbError::ExecutionError(format!("Invalid LIKE pattern: {}", e)))?;
406
407        Ok(regex.is_match(val_str))
408    }
409
410    /// Convert value to boolean
411    fn to_boolean(&self, value: &Value) -> Result<bool> {
412        match value {
413            Value::Boolean(b) => Ok(*b),
414            Value::Integer(i) => Ok(*i != 0),
415            Value::Float(f) => Ok(*f != 0.0),
416            Value::String(s) => Ok(!s.is_empty()),
417            Value::Null => Ok(false),
418            _ => Ok(true),
419        }
420    }
421}
422
423impl Default for Evaluator {
424    fn default() -> Self {
425        Self::new()
426    }
427}
428
429#[cfg(test)]
430mod tests {
431    use super::*;
432    use crate::parser::Literal;
433
434    #[test]
435    fn test_evaluate_literal() {
436        let evaluator = Evaluator::new();
437        let row = Row::new();
438
439        let expr = Expression::Literal(Literal::Integer(42));
440        let result = evaluator.evaluate(&expr, &row).unwrap();
441        assert_eq!(result, Value::Integer(42));
442
443        let expr = Expression::Literal(Literal::String("hello".to_string()));
444        let result = evaluator.evaluate(&expr, &row).unwrap();
445        assert_eq!(result, Value::String("hello".to_string()));
446    }
447
448    #[test]
449    fn test_evaluate_column() {
450        let evaluator = Evaluator::new();
451        let mut row = Row::new();
452        row.set("age", Value::Integer(25));
453
454        let expr = Expression::Column("age".to_string());
455        let result = evaluator.evaluate(&expr, &row).unwrap();
456        assert_eq!(result, Value::Integer(25));
457    }
458
459    #[test]
460    fn test_evaluate_binary_op() {
461        let evaluator = Evaluator::new();
462        let mut row = Row::new();
463        row.set("age", Value::Integer(25));
464
465        // age > 18
466        let expr = Expression::BinaryOp {
467            left: Box::new(Expression::Column("age".to_string())),
468            op: BinaryOperator::GreaterThan,
469            right: Box::new(Expression::Literal(Literal::Integer(18))),
470        };
471
472        let result = evaluator.evaluate(&expr, &row).unwrap();
473        assert_eq!(result, Value::Boolean(true));
474    }
475
476    #[test]
477    fn test_evaluate_function() {
478        let evaluator = Evaluator::new();
479        let mut row = Row::new();
480        row.set("name", Value::String("john".to_string()));
481
482        // UPPER(name)
483        let expr = Expression::Function {
484            name: "UPPER".to_string(),
485            args: vec![Expression::Column("name".to_string())],
486        };
487
488        let result = evaluator.evaluate(&expr, &row).unwrap();
489        assert_eq!(result, Value::String("JOHN".to_string()));
490    }
491
492    #[test]
493    fn test_evaluate_like() {
494        let evaluator = Evaluator::new();
495        let _row = Row::new();
496
497        let value = Value::String("hello world".to_string());
498        let pattern = Value::String("hello%".to_string());
499
500        let result = evaluator.evaluate_like(&value, &pattern).unwrap();
501        assert_eq!(result, Value::Boolean(true));
502
503        let pattern = Value::String("%world".to_string());
504        let result = evaluator.evaluate_like(&value, &pattern).unwrap();
505        assert_eq!(result, Value::Boolean(true));
506
507        // _ is a single-character wildcard in SQL LIKE
508        let pattern = Value::String("hello_world".to_string());
509        let result = evaluator.evaluate_like(&value, &pattern).unwrap();
510        assert_eq!(result, Value::Boolean(true)); // _ matches the space character
511    }
512}