Skip to main content

akar_processor/
expression_evaluator.rs

1//! Expression evaluator — recursively evaluates an expression tree against a DataChunk.
2//!
3//! This replaces the ad-hoc `PhysicalFilter::evaluate_expression` with a proper
4//! expression evaluator that dispatches function calls through the scalar function
5//! registry (Akar-function::evaluate_scalar), supporting:
6//! - Variable:        reads values from a DataChunk field by name
7//! - Constant:        returns a literal value
8//! - BinaryOp:        dispatches to arithmetic/comparison/boolean scalar functions
9//! - UnaryOp:         dispatches to NOT/negate scalar functions
10//! - FunctionCall:    resolves the function name in the registry and evaluates
11//! - PropertyAccess:  reads a property from a struct/object expression
12//! - List/Map:        evaluated via list_creation/map_creation scalar functions
13
14use akar_common::arrow_vector::{ArrowVector, VectorAccess};
15use akar_common::error::ProcessorError;
16use akar_common::types::{PhysicalTypeID, Value};
17use akar_common::vector::{DataChunk, ValueVector};
18use akar_function::registry::{FunctionRegistry, ScalarFunction};
19use akar_function::scalar::evaluate_scalar;
20use akar_parser::ast::{BinaryOp, Constant, Expression, Query, UnaryOp};
21use arrow::array::ArrayRef;
22use std::sync::{Arc, Mutex};
23
24pub type SubqueryFn = Arc<dyn Fn(&Query) -> Result<Vec<DataChunk>, ProcessorError> + Send + Sync>;
25pub type SequenceFn = Arc<dyn Fn(&str, bool) -> Result<Value, ProcessorError> + Send + Sync>;
26
27/// Evaluates expressions against DataChunks using the function registry.
28impl std::fmt::Debug for ExpressionEvaluator {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.debug_struct("ExpressionEvaluator").finish()
31    }
32}
33
34pub struct ExpressionEvaluator {
35    registry: Arc<Mutex<FunctionRegistry>>,
36    /// Optional callback to execute subqueries at evaluation time.
37    /// Takes a parsed Query and returns DataChunks.
38    pub subquery_fn: Option<SubqueryFn>,
39    /// Optional callback for sequence operations (nextval/currval).
40    /// Takes (sequence_name, is_nextval) and returns the resulting value.
41    pub sequence_fn: Option<SequenceFn>,
42}
43
44impl ExpressionEvaluator {
45    pub fn new(registry: Arc<Mutex<FunctionRegistry>>) -> Self {
46        Self {
47            registry,
48            subquery_fn: None,
49            sequence_fn: None,
50        }
51    }
52
53    /// Set the subquery execution callback.
54    pub fn with_subquery_fn(mut self, f: SubqueryFn) -> Self {
55        self.subquery_fn = Some(f);
56        self
57    }
58
59    /// Set the sequence operation callback (for nextval/currval).
60    pub fn with_sequence_fn(mut self, f: SequenceFn) -> Self {
61        self.sequence_fn = Some(f);
62        self
63    }
64
65    /// Evaluate a subquery by calling the stored callback.
66    fn evaluate_subquery(&self, query: &Query) -> Result<Vec<DataChunk>, ProcessorError> {
67        if let Some(ref f) = self.subquery_fn {
68            f(query)
69        } else {
70            Err("No subquery executor configured".into())
71        }
72    }
73
74    /// Evaluate an expression for every row in the chunk, returning an ArrowVector
75    /// directly. Delegates to evaluate_arrow which uses Arrow compute kernels for
76    /// the hot path (comparisons, arithmetic, boolean ops).
77    pub fn evaluate_to_arrow(&self, expr: &Expression, chunk: &DataChunk) -> Result<ArrowVector, ProcessorError> {
78        self.evaluate_arrow(expr, chunk)
79    }
80
81    /// Evaluate an expression for every row in the chunk, returning a ValueVector.
82    pub fn evaluate(&self, expr: &Expression, chunk: &DataChunk) -> Result<ValueVector, ProcessorError> {
83        match expr {
84            Expression::Constant(c) => self.evaluate_constant(c, chunk.size),
85            Expression::Variable(name) => self.evaluate_variable(name, chunk),
86            Expression::PropertyAccess(obj, prop) => self.evaluate_property_access(obj, prop, chunk),
87            Expression::FunctionCall(name, args) => {
88                let refs: Vec<&Expression> = args.iter().collect();
89                self.evaluate_function_call(name, &refs, chunk)
90            }
91            Expression::BinaryOp(op, left, right) => self.evaluate_binary_op(op, left, right, chunk),
92            Expression::UnaryOp(op, inner) => self.evaluate_unary_op(op, inner, chunk),
93            Expression::List(items) => self.evaluate_list_literal(items, chunk),
94            Expression::Map(items) => self.evaluate_map_literal(items, chunk),
95            Expression::Parameter(_) => {
96                // Parameters should be substituted by the binder/prepared statement layer.
97                // If they reach the evaluator, return a null Int64 vector matching chunk size.
98                let mut v = ValueVector::new(akar_common::types::PhysicalTypeID::Int64, chunk.size);
99                v.resize(chunk.size);
100                for i in 0..chunk.size {
101                    v.set_null(i, true);
102                }
103                Ok(v)
104            }
105            Expression::ExistsSubquery(query) => {
106                // Evaluate EXISTS subquery: execute the inner query against
107                // the database. If it returns at least one row → true, else false.
108                // For uncorrelated subqueries, execute once and fill all rows.
109                let result = self.evaluate_subquery(query)?;
110                let exists = !result.is_empty() && result.iter().any(|c| c.size > 0);
111                let mut v = ValueVector::new(akar_common::types::PhysicalTypeID::Bool, chunk.size);
112                v.resize(chunk.size);
113                for i in 0..chunk.size {
114                    store_value_in_vector_simple(&mut v, i, &Value::Bool(exists))?;
115                }
116                Ok(v)
117            }
118            Expression::Case(case_expr) => self.evaluate_case(case_expr, chunk),
119            Expression::Star => {
120                Err("STAR expression should be expanded by the binder before reaching the evaluator".into())
121            }
122            Expression::ListPredicate {
123                quantifier,
124                list,
125                var_name,
126                predicate,
127            } => self.evaluate_list_predicate(quantifier, list, var_name, predicate, chunk),
128            Expression::Lambda { .. } => {
129                Err("Lambda expression should only appear as argument to list_transform/filter/reduce".into())
130            }
131        }
132    }
133
134    // ==================== Arrow-native evaluation ====================
135
136    /// Evaluate an expression returning an ArrowVector directly.
137    /// For operations supported by Arrow compute kernels (comparisons,
138    /// arithmetic, boolean), this is vectorized and avoids Value enum boxing.
139    pub fn evaluate_arrow(&self, expr: &Expression, chunk: &DataChunk) -> Result<ArrowVector, ProcessorError> {
140        match expr {
141            Expression::Constant(c) => self.evaluate_arrow_constant(c, chunk.size),
142            Expression::Variable(name) => self.evaluate_arrow_variable(name, chunk),
143            Expression::PropertyAccess(obj, prop) => self.evaluate_arrow_property_access(obj, prop, chunk),
144            Expression::FunctionCall(name, args) => {
145                let refs: Vec<&Expression> = args.iter().collect();
146                self.evaluate_arrow_function_call(name, &refs, chunk)
147            }
148            Expression::BinaryOp(op, left, right) => self.evaluate_arrow_binary_op(op, left, right, chunk),
149            Expression::UnaryOp(op, inner) => self.evaluate_arrow_unary_op(op, inner, chunk),
150            // Fallback for complex types: use evaluate + from_legacy
151            _ => {
152                let legacy = self.evaluate(expr, chunk)?;
153                Ok(ArrowVector::from_legacy(&legacy))
154            }
155        }
156    }
157
158    /// Evaluate a constant directly as ArrowVector using typed Arrow builders.
159    fn evaluate_arrow_constant(&self, c: &Constant, size: usize) -> Result<ArrowVector, ProcessorError> {
160        match c {
161            Constant::Null => {
162                let mut builder = arrow::array::Int64Builder::with_capacity(size);
163                builder.append_nulls(size);
164                Ok(ArrowVector::new(Arc::new(builder.finish()), PhysicalTypeID::Int64))
165            }
166            Constant::Bool(b) => {
167                let mut builder = arrow::array::BooleanBuilder::with_capacity(size);
168                for _ in 0..size {
169                    builder.append_value(*b);
170                }
171                Ok(ArrowVector::new(Arc::new(builder.finish()), PhysicalTypeID::Bool))
172            }
173            Constant::Integer(i) => {
174                let mut builder = arrow::array::Int64Builder::with_capacity(size);
175                let v = *i;
176                for _ in 0..size {
177                    builder.append_value(v);
178                }
179                Ok(ArrowVector::new(Arc::new(builder.finish()), PhysicalTypeID::Int64))
180            }
181            Constant::Float(f) => {
182                let mut builder = arrow::array::Float64Builder::with_capacity(size);
183                let v = *f;
184                for _ in 0..size {
185                    builder.append_value(v);
186                }
187                Ok(ArrowVector::new(Arc::new(builder.finish()), PhysicalTypeID::Double))
188            }
189            Constant::String(s) => {
190                let mut builder = arrow::array::StringBuilder::with_capacity(size, size * s.len().max(1));
191                for _ in 0..size {
192                    builder.append_value(s);
193                }
194                Ok(ArrowVector::new(Arc::new(builder.finish()), PhysicalTypeID::String))
195            }
196        }
197    }
198
199    /// Evaluate a variable expression, converting the ValueVector to ArrowVector.
200    fn evaluate_arrow_variable(&self, name: &str, chunk: &DataChunk) -> Result<ArrowVector, ProcessorError> {
201        let idx = if let Ok(idx) = name.parse::<usize>() {
202            idx
203        } else if !chunk.field_names.is_empty() {
204            if let Some(idx) = chunk.field_names.iter().position(|n| n == name) {
205                idx
206            } else {
207                return Err(format!("Variable '{}' not found in field_names", name).into());
208            }
209        } else {
210            return Err(format!("Variable '{}' not found (chunk has no field_names)", name).into());
211        };
212
213        let field = chunk
214            .fields
215            .get(idx)
216            .ok_or_else(|| format!("Variable '{}' (index {}) not found in chunk fields", name, idx))?;
217        Ok(ArrowVector::new(field.clone(), chunk.field_types[idx]))
218    }
219
220    /// Evaluate a property access expression, returning ArrowVector.
221    fn evaluate_arrow_property_access(
222        &self,
223        obj: &Expression,
224        prop: &str,
225        chunk: &DataChunk,
226    ) -> Result<ArrowVector, ProcessorError> {
227        let qualified_prop = if let Expression::Variable(var_name) = obj {
228            format!("{}.{}", var_name, prop)
229        } else {
230            prop.to_string()
231        };
232
233        if !chunk.field_names.is_empty()
234            && let Some(idx) = chunk.field_names.iter().position(|n| n == &qualified_prop || n == prop)
235        {
236            let field = chunk
237                .fields
238                .get(idx)
239                .ok_or_else(|| format!("Property '{}' not found in chunk", prop))?;
240            return Ok(ArrowVector::new(field.clone(), chunk.field_types[idx]));
241        }
242        let legacy = self.evaluate(obj, chunk)?;
243        Ok(ArrowVector::from_legacy(&legacy))
244    }
245
246    /// Evaluate a binary operation using Arrow compute kernels when possible.
247    fn evaluate_arrow_binary_op(
248        &self,
249        op: &BinaryOp,
250        left: &Expression,
251        right: &Expression,
252        chunk: &DataChunk,
253    ) -> Result<ArrowVector, ProcessorError> {
254        match op {
255            BinaryOp::In | BinaryOp::NotIn => {
256                let legacy = self.evaluate_in_op(op, left, right, chunk)?;
257                return Ok(ArrowVector::from_legacy(&legacy));
258            }
259            BinaryOp::Concat | BinaryOp::StartsWith | BinaryOp::EndsWith | BinaryOp::Contains | BinaryOp::Like => {
260                let func_name = match op {
261                    BinaryOp::Concat => "concat",
262                    BinaryOp::StartsWith => "starts_with",
263                    BinaryOp::EndsWith => "ends_with",
264                    BinaryOp::Contains => "contains",
265                    BinaryOp::Like => "like",
266                    _ => unreachable!(),
267                };
268                return self.evaluate_arrow_function_call(func_name, &[left, right], chunk);
269            }
270            _ => {}
271        }
272
273        let kernel_name = match op {
274            BinaryOp::Add => "add",
275            BinaryOp::Subtract => "sub",
276            BinaryOp::Multiply => "mul",
277            BinaryOp::Divide => "div",
278            BinaryOp::Modulo => "mod",
279            BinaryOp::Equal => "eq",
280            BinaryOp::NotEqual => "neq",
281            BinaryOp::LessThan => "lt",
282            BinaryOp::LessThanOrEqual => "lt_eq",
283            BinaryOp::GreaterThan => "gt",
284            BinaryOp::GreaterThanOrEqual => "gt_eq",
285            BinaryOp::And => "and",
286            BinaryOp::Or => "or",
287            BinaryOp::Xor => "xor",
288            _ => return Err(format!("Unsupported binary op: {:?}", op).into()),
289        };
290
291        let left_arrow = self.evaluate_arrow(left, chunk)?;
292        let right_arrow = self.evaluate_arrow(right, chunk)?;
293
294        match self.apply_arrow_kernel(kernel_name, &left_arrow, &right_arrow) {
295            Ok(result) => Ok(result),
296            Err(_) => {
297                let legacy = self.evaluate_binary_op(op, left, right, chunk)?;
298                Ok(ArrowVector::from_legacy(&legacy))
299            }
300        }
301    }
302
303    /// Evaluate a unary operation using Arrow compute kernels when possible.
304    fn evaluate_arrow_unary_op(
305        &self,
306        op: &UnaryOp,
307        inner: &Expression,
308        chunk: &DataChunk,
309    ) -> Result<ArrowVector, ProcessorError> {
310        match op {
311            UnaryOp::Not => {
312                let inner_arrow = self.evaluate_arrow(inner, chunk)?;
313                self.apply_arrow_unary_kernel("not", &inner_arrow).or_else(|_| {
314                    let legacy = self.evaluate_unary_op(&UnaryOp::Not, inner, chunk)?;
315                    Ok(ArrowVector::from_legacy(&legacy))
316                })
317            }
318            UnaryOp::Negate => {
319                let inner_arrow = self.evaluate_arrow(inner, chunk)?;
320                self.apply_arrow_unary_kernel("negate", &inner_arrow).or_else(|_| {
321                    let legacy = self.evaluate_unary_op(&UnaryOp::Negate, inner, chunk)?;
322                    Ok(ArrowVector::from_legacy(&legacy))
323                })
324            }
325            UnaryOp::IsNull => {
326                let inner_arrow = self.evaluate_arrow(inner, chunk)?;
327                self.apply_arrow_unary_kernel("is_null", &inner_arrow)
328            }
329            UnaryOp::IsNotNull => {
330                let inner_arrow = self.evaluate_arrow(inner, chunk)?;
331                self.apply_arrow_unary_kernel("is_not_null", &inner_arrow)
332            }
333        }
334    }
335
336    /// Apply an Arrow binary compute kernel.
337    fn apply_arrow_kernel(
338        &self,
339        name: &str,
340        left: &ArrowVector,
341        right: &ArrowVector,
342    ) -> Result<ArrowVector, ProcessorError> {
343        use arrow::compute::kernels::boolean::{and_kleene, or_kleene};
344        use arrow::compute::kernels::cmp::{eq, gt, gt_eq, lt, lt_eq, neq};
345        use arrow::compute::kernels::numeric::{add, div, mul, rem, sub};
346
347        let result: ArrayRef = match name {
348            "add" => Arc::new(add(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
349            "sub" => Arc::new(sub(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
350            "mul" => Arc::new(mul(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
351            "div" => Arc::new(div(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
352            "mod" => Arc::new(rem(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
353            "eq" => Arc::new(eq(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
354            "neq" => Arc::new(neq(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
355            "lt" => Arc::new(lt(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
356            "lt_eq" => Arc::new(lt_eq(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
357            "gt" => Arc::new(gt(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
358            "gt_eq" => Arc::new(gt_eq(&left.array, &right.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
359            "and" => {
360                let l = left
361                    .array
362                    .as_any()
363                    .downcast_ref::<arrow::array::BooleanArray>()
364                    .ok_or_else(|| format!("Arrow {name}: expected BooleanArray, got {:?}", left.array.data_type()))?;
365                let r = right
366                    .array
367                    .as_any()
368                    .downcast_ref::<arrow::array::BooleanArray>()
369                    .ok_or_else(|| format!("Arrow {name}: expected BooleanArray, got {:?}", right.array.data_type()))?;
370                Arc::new(and_kleene(l, r).map_err(|e| format!("Arrow {name} failed: {e}"))?)
371            }
372            "or" => {
373                let l = left
374                    .array
375                    .as_any()
376                    .downcast_ref::<arrow::array::BooleanArray>()
377                    .ok_or_else(|| format!("Arrow {name}: expected BooleanArray, got {:?}", left.array.data_type()))?;
378                let r = right
379                    .array
380                    .as_any()
381                    .downcast_ref::<arrow::array::BooleanArray>()
382                    .ok_or_else(|| format!("Arrow {name}: expected BooleanArray, got {:?}", right.array.data_type()))?;
383                Arc::new(or_kleene(l, r).map_err(|e| format!("Arrow {name} failed: {e}"))?)
384            }
385            "xor" => {
386                // XOR = (l AND NOT r) OR (NOT l AND r)
387                let l = left
388                    .array
389                    .as_any()
390                    .downcast_ref::<arrow::array::BooleanArray>()
391                    .ok_or_else(|| format!("Arrow {name}: expected BooleanArray, got {:?}", left.array.data_type()))?;
392                let r = right
393                    .array
394                    .as_any()
395                    .downcast_ref::<arrow::array::BooleanArray>()
396                    .ok_or_else(|| format!("Arrow {name}: expected BooleanArray, got {:?}", right.array.data_type()))?;
397                let not_r =
398                    arrow::compute::kernels::boolean::not(r).map_err(|e| format!("Arrow xor/not failed: {e}"))?;
399                let not_l =
400                    arrow::compute::kernels::boolean::not(l).map_err(|e| format!("Arrow xor/not failed: {e}"))?;
401                let l_and_not_r = and_kleene(l, &not_r).map_err(|e| format!("Arrow xor/and failed: {e}"))?;
402                let not_l_and_r = and_kleene(&not_l, r).map_err(|e| format!("Arrow xor/and failed: {e}"))?;
403                Arc::new(or_kleene(&l_and_not_r, &not_l_and_r).map_err(|e| format!("Arrow xor/or failed: {e}"))?)
404            }
405            _ => return Err(format!("Unknown binary kernel: {name}").into()),
406        };
407
408        let phys_type = match name {
409            "eq" | "neq" | "lt" | "lt_eq" | "gt" | "gt_eq" | "and" | "or" | "xor" => PhysicalTypeID::Bool,
410            _ => left.physical_type,
411        };
412
413        Ok(ArrowVector::new(result, phys_type))
414    }
415
416    /// Apply an Arrow unary compute kernel.
417    fn apply_arrow_unary_kernel(&self, name: &str, arr: &ArrowVector) -> Result<ArrowVector, ProcessorError> {
418        use arrow::compute::kernels::boolean::{is_not_null, is_null, not};
419        use arrow::compute::kernels::numeric::neg;
420
421        let result: ArrayRef = match name {
422            "not" => {
423                let arr_ref = arr
424                    .array
425                    .as_any()
426                    .downcast_ref::<arrow::array::BooleanArray>()
427                    .ok_or_else(|| format!("Arrow {name}: expected BooleanArray, got {:?}", arr.array.data_type()))?;
428                Arc::new(not(arr_ref).map_err(|e| format!("Arrow {name} failed: {e}"))?)
429            }
430            "negate" => Arc::new(neg(&*arr.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
431            "is_null" => Arc::new(is_null(&*arr.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
432            "is_not_null" => Arc::new(is_not_null(&*arr.array).map_err(|e| format!("Arrow {name} failed: {e}"))?),
433            _ => return Err(format!("Unknown unary kernel: {name}").into()),
434        };
435
436        let phys_type = if matches!(name, "is_null" | "is_not_null" | "not") {
437            PhysicalTypeID::Bool
438        } else {
439            arr.physical_type
440        };
441
442        Ok(ArrowVector::new(result, phys_type))
443    }
444
445    /// Evaluate a function call, producing ArrowVector directly.
446    /// For fixed-width return types, uses typed Vec<T> collection to
447    /// avoid the intermediate ValueVector allocation.
448    fn evaluate_arrow_function_call(
449        &self,
450        name: &str,
451        args: &[&Expression],
452        chunk: &DataChunk,
453    ) -> Result<ArrowVector, ProcessorError> {
454        // Lambda-based functions use complex control flow — stick with evaluate
455        if let Some(_lambda) = self.extract_lambda_arg(args) {
456            match name {
457                "list_transform" | "list_filter" | "list_reduce" => {
458                    let legacy = self.evaluate_function_call(name, args, chunk)?;
459                    return Ok(ArrowVector::from_legacy(&legacy));
460                }
461                _ => {}
462            }
463        }
464
465        // Evaluate arguments as ArrowVectors
466        let arg_arrows: Vec<ArrowVector> = args
467            .iter()
468            .map(|arg| self.evaluate_arrow(arg, chunk))
469            .collect::<Result<Vec<_>, _>>()?;
470
471        if arg_arrows.is_empty() {
472            return Err(format!("Function '{}' requires at least one argument", name).into());
473        }
474
475        let num_rows = arg_arrows[0].size();
476
477        // Look up the function
478        let func = {
479            let reg = self.registry.lock().map_err(|e| format!("Lock poisoned: {e}"))?;
480            reg.get_scalar(name).cloned()
481        };
482        let func = match func {
483            Some(f) => f,
484            None => return Err(format!("Unknown function: '{}'", name).into()),
485        };
486
487        // SequenceOp needs the callback — delegate to evaluate
488        if matches!(func, ScalarFunction::SequenceOp { .. }) {
489            let legacy = self.evaluate_function_call(name, args, chunk)?;
490            return Ok(ArrowVector::from_legacy(&legacy));
491        }
492
493        let mut first_error: Option<String> = None;
494        let mut row_results: Vec<Value> = Vec::with_capacity(num_rows);
495
496        for row in 0..num_rows {
497            let arg_values: Vec<Value> = arg_arrows
498                .iter()
499                .map(|arr| {
500                    if row < arr.size() && !arr.is_null(row) {
501                        arr.get_value(row).unwrap_or(Value::Null)
502                    } else {
503                        Value::Null
504                    }
505                })
506                .collect();
507
508            if name != "coalesce" && name != "ifnull" && arg_values.iter().any(|v| matches!(v, Value::Null)) {
509                row_results.push(Value::Null);
510                continue;
511            }
512
513            match evaluate_scalar(&func, &arg_values) {
514                Ok(val) => row_results.push(val),
515                Err(e) => {
516                    row_results.push(Value::Null);
517                    if first_error.is_none() {
518                        first_error = Some(e);
519                    }
520                }
521            }
522        }
523
524        let result_type = row_results
525            .iter()
526            .find(|v| !matches!(v, Value::Null))
527            .map(|v| v.physical_type())
528            .unwrap_or(PhysicalTypeID::Int64);
529
530        // Build Arrow array directly from typed Vec<T> — avoids ValueVector allocation
531        let arrow_result = build_arrow_from_values(&row_results, result_type, num_rows)?;
532
533        if row_results.iter().all(|v| matches!(v, Value::Null))
534            && let Some(e) = first_error
535        {
536            return Err(e.into());
537        }
538
539        Ok(arrow_result)
540    }
541
542    /// Evaluate a constant expression — returns a vector filled with the constant value.
543    fn evaluate_constant(&self, c: &Constant, size: usize) -> Result<ValueVector, ProcessorError> {
544        let val: Value = match c {
545            Constant::Null => Value::Null,
546            Constant::Bool(b) => Value::Bool(*b),
547            Constant::Integer(i) => Value::Int64(*i),
548            Constant::Float(f) => Value::Double(*f),
549            Constant::String(s) => Value::String(s.clone()),
550        };
551
552        let physical_type = val.physical_type();
553        let mut v = ValueVector::new(physical_type, size);
554        v.resize(size);
555        for i in 0..size {
556            store_value_in_vector(&mut v, i, &val)?;
557        }
558        Ok(v)
559    }
560
561    /// Evaluate a variable expression — reads a field from the DataChunk by variable name.
562    /// The variable name is matched against:
563    /// 1. Numeric index (binder resolves names to positions, e.g., "0", "1")
564    /// 2. Chunk field names (e.g., "title", "d.title")
565    /// 3. Falls back to the first field (legacy compatibility) if no match found.
566    fn evaluate_variable(&self, name: &str, chunk: &DataChunk) -> Result<ValueVector, ProcessorError> {
567        if let Ok(idx) = name.parse::<usize>() {
568            if idx >= chunk.fields.len() {
569                return Err(format!(
570                    "Variable '{}' (index {}) not found in chunk with {} fields",
571                    name,
572                    idx,
573                    chunk.fields.len()
574                )
575                .into());
576            }
577            let phys_type = chunk.field_types[idx];
578            let mut v = ValueVector::new(phys_type, chunk.size);
579            v.resize(chunk.size);
580            for i in 0..chunk.size {
581                if let Some(val) = chunk.get_value(idx, i) {
582                    store_value_in_vector(&mut v, i, &val)?;
583                } else {
584                    v.set_null(i, true);
585                }
586            }
587            return Ok(v);
588        }
589
590        if !chunk.field_names.is_empty() {
591            if let Some(idx) = chunk.field_names.iter().position(|n| n == name) {
592                let phys_type = chunk.field_types[idx];
593                let mut v = ValueVector::new(phys_type, chunk.size);
594                v.resize(chunk.size);
595                for i in 0..chunk.size {
596                    if let Some(val) = chunk.get_value(idx, i) {
597                        store_value_in_vector(&mut v, i, &val)?;
598                    } else {
599                        v.set_null(i, true);
600                    }
601                }
602                return Ok(v);
603            }
604        }
605
606        if !chunk.fields.is_empty() {
607            let phys_type = chunk.field_types[0];
608            let mut v = ValueVector::new(phys_type, chunk.size);
609            v.resize(chunk.size);
610            for i in 0..chunk.size {
611                if let Some(val) = chunk.get_value(0, i) {
612                    store_value_in_vector(&mut v, i, &val)?;
613                } else {
614                    v.set_null(i, true);
615                }
616            }
617            Ok(v)
618        } else {
619            Ok(ValueVector::new(akar_common::types::PhysicalTypeID::Int64, 0))
620        }
621    }
622
623    /// Evaluate a property access expression — resolves the property name to a
624    /// column index using `chunk.field_names`, then returns that column's data.
625    ///
626    /// Falls back to evaluating the object expression (legacy behaviour) if no
627    /// `field_names` are available on the chunk.
628    fn evaluate_property_access(
629        &self,
630        obj: &Expression,
631        prop: &str,
632        chunk: &DataChunk,
633    ) -> Result<ValueVector, ProcessorError> {
634        // Build the qualified property name (e.g., "t.name")
635        let qualified_prop = if let Expression::Variable(var_name) = obj {
636            format!("{}.{}", var_name, prop)
637        } else {
638            prop.to_string()
639        };
640
641        // Fast path: look up the property by name in the chunk's field names.
642        if !chunk.field_names.is_empty()
643            && let Some(idx) = chunk.field_names.iter().position(|n| n == &qualified_prop || n == prop)
644        {
645            if chunk.fields.get(idx).is_none() {
646                return Err(format!("Column '{}' (index {}) not found in chunk", prop, idx).into());
647            }
648            let phys_type = chunk.field_types[idx];
649            let mut v = ValueVector::new(phys_type, chunk.size);
650            v.resize(chunk.size);
651            for i in 0..chunk.size {
652                if let Some(val) = chunk.get_value(idx, i) {
653                    store_value_in_vector(&mut v, i, &val)?;
654                } else {
655                    v.set_null(i, true);
656                }
657            }
658            return Ok(v);
659        }
660        // Fallback: evaluate the object expression (returns first column — legacy behaviour).
661        self.evaluate(obj, chunk)
662    }
663
664    /// Evaluate a function call expression.
665    fn evaluate_function_call(
666        &self,
667        name: &str,
668        args: &[&Expression],
669        chunk: &DataChunk,
670    ) -> Result<ValueVector, ProcessorError> {
671        // Handle lambda-based list functions at expression level.
672        // These cannot go through the normal scalar function pipeline because
673        // lambda expressions are not Values and must be evaluated per-element.
674        if let Some(lambda) = self.extract_lambda_arg(args) {
675            match name {
676                "list_transform" => return self.evaluate_list_transform(args, lambda, chunk),
677                "list_filter" => return self.evaluate_list_filter(args, lambda, chunk),
678                "list_reduce" => return self.evaluate_list_reduce(args, lambda, chunk),
679                _ => {}
680            }
681        }
682
683        // Evaluate all argument expressions first
684        let arg_vectors: Vec<ValueVector> = args
685            .iter()
686            .map(|arg| self.evaluate(arg, chunk))
687            .collect::<Result<Vec<_>, _>>()?;
688
689        if arg_vectors.is_empty() {
690            return Err(format!("Function '{}' requires at least one argument", name).into());
691        }
692
693        let num_rows = arg_vectors[0].size();
694
695        // Look up the function in the registry
696        let func = {
697            let reg = self.registry.lock().map_err(|e| format!("Lock poisoned: {e}"))?;
698            reg.get_scalar(name).cloned()
699        };
700
701        let func = match func {
702            Some(f) => f,
703            None => return Err(format!("Unknown function: '{}'", name).into()),
704        };
705
706        // Handle SequenceOp (nextval/currval) via callback with catalog access
707        if matches!(func, ScalarFunction::SequenceOp { .. }) {
708            return self.evaluate_sequence_op(name, &func, &arg_vectors, num_rows);
709        }
710
711        // Evaluate each row exactly once, then infer result type from cached values.
712        // This avoids double-invoking side-effecting functions (e.g., nextval custom scalar).
713        let mut row_results: Vec<Value> = Vec::with_capacity(num_rows);
714        let mut first_error: Option<String> = None;
715
716        for row in 0..num_rows {
717            let arg_values: Vec<Value> = arg_vectors
718                .iter()
719                .map(|vec| {
720                    if row < vec.size() && !vec.is_null(row) {
721                        vec.get_value(row).unwrap_or(Value::Null)
722                    } else {
723                        Value::Null
724                    }
725                })
726                .collect();
727
728            // Three-valued logic for AND/OR (short-circuit)
729            if name == "AND" || name == "OR" {
730                let l = &arg_values[0];
731                let r = &arg_values[1];
732                if name == "AND" {
733                    if matches!(l, Value::Bool(false)) || matches!(r, Value::Bool(false)) {
734                        row_results.push(Value::Bool(false));
735                        continue;
736                    }
737                    if matches!(l, Value::Null) || matches!(r, Value::Null) {
738                        row_results.push(Value::Null);
739                        continue;
740                    }
741                } else {
742                    if matches!(l, Value::Bool(true)) || matches!(r, Value::Bool(true)) {
743                        row_results.push(Value::Bool(true));
744                        continue;
745                    }
746                    if matches!(l, Value::Null) || matches!(r, Value::Null) {
747                        row_results.push(Value::Null);
748                        continue;
749                    }
750                }
751            }
752
753            // If any argument is null, the result is null (SQL NULL semantics)
754            // Coalesce/ifnull are exempt — they intentionally inspect null args
755            if name != "coalesce" && name != "ifnull" && arg_values.iter().any(|v| matches!(v, Value::Null)) {
756                row_results.push(Value::Null);
757                continue;
758            }
759
760            match evaluate_scalar(&func, &arg_values) {
761                Ok(val) => row_results.push(val),
762                Err(e) => {
763                    row_results.push(Value::Null);
764                    if first_error.is_none() {
765                        first_error = Some(e);
766                    }
767                }
768            }
769        }
770
771        let result_type = row_results
772            .iter()
773            .find(|v| !matches!(v, Value::Null))
774            .map(|v| v.physical_type())
775            .unwrap_or(akar_common::types::PhysicalTypeID::Int64);
776
777        let mut result_vec = ValueVector::new(result_type, num_rows);
778        result_vec.resize(num_rows);
779        for (row, val) in row_results.iter().enumerate() {
780            store_value_in_vector(&mut result_vec, row, val)?;
781        }
782
783        if row_results.iter().all(|v| matches!(v, Value::Null))
784            && let Some(e) = first_error
785        {
786            return Err(e.into());
787        }
788
789        Ok(result_vec)
790    }
791
792    /// Evaluate a binary operation.
793    fn evaluate_binary_op(
794        &self,
795        op: &BinaryOp,
796        left: &Expression,
797        right: &Expression,
798        chunk: &DataChunk,
799    ) -> Result<ValueVector, ProcessorError> {
800        // Map AST BinaryOp to a scalar function name
801        let func_name = match op {
802            BinaryOp::Add => "+",
803            BinaryOp::Subtract => "-",
804            BinaryOp::Multiply => "*",
805            BinaryOp::Divide => "/",
806            BinaryOp::Modulo => "%",
807            BinaryOp::Equal => "=",
808            BinaryOp::NotEqual => "<>",
809            BinaryOp::LessThan => "<",
810            BinaryOp::LessThanOrEqual => "<=",
811            BinaryOp::GreaterThan => ">",
812            BinaryOp::GreaterThanOrEqual => ">=",
813            BinaryOp::And => "AND",
814            BinaryOp::Or => "OR",
815            BinaryOp::Xor => "XOR",
816            BinaryOp::Concat => "concat",
817            // Handled inline — not mapped to scalar function
818            BinaryOp::In | BinaryOp::NotIn => {
819                return self.evaluate_in_op(op, left, right, chunk);
820            }
821            BinaryOp::StartsWith => "starts_with",
822            BinaryOp::EndsWith => "ends_with",
823            BinaryOp::Contains => "contains",
824            BinaryOp::Like => {
825                return self.evaluate_function_call("like", &[left, right], chunk);
826            }
827        };
828
829        // Treat as a function call with two arguments
830        self.evaluate_function_call(func_name, &[left, right], chunk)
831    }
832
833    /// Evaluate a unary operation.
834    fn evaluate_unary_op(
835        &self,
836        op: &UnaryOp,
837        inner: &Expression,
838        chunk: &DataChunk,
839    ) -> Result<ValueVector, ProcessorError> {
840        match op {
841            UnaryOp::Not => self.evaluate_function_call("NOT", std::slice::from_ref(&inner), chunk),
842            UnaryOp::Negate => self.evaluate_function_call("-", std::slice::from_ref(&inner), chunk),
843            UnaryOp::IsNull => {
844                let vec = self.evaluate(inner, chunk)?;
845                let num_rows = vec.size();
846                let mut result = ValueVector::new(akar_common::types::PhysicalTypeID::Bool, num_rows);
847                result.resize(num_rows);
848                for i in 0..num_rows {
849                    let is_null = vec.is_null(i) || matches!(vec.get_value(i), Some(Value::Null) | None);
850                    store_value_in_vector_simple(&mut result, i, &Value::Bool(is_null))?;
851                }
852                Ok(result)
853            }
854            UnaryOp::IsNotNull => {
855                let vec = self.evaluate(inner, chunk)?;
856                let num_rows = vec.size();
857                let mut result = ValueVector::new(akar_common::types::PhysicalTypeID::Bool, num_rows);
858                result.resize(num_rows);
859                for i in 0..num_rows {
860                    let is_null = vec.is_null(i) || matches!(vec.get_value(i), Some(Value::Null) | None);
861                    store_value_in_vector_simple(&mut result, i, &Value::Bool(!is_null))?;
862                }
863                Ok(result)
864            }
865        }
866    }
867
868    /// Evaluate `x IN list` and `x NOT IN list` operators.
869    fn evaluate_in_op(
870        &self,
871        op: &BinaryOp,
872        left: &Expression,
873        right: &Expression,
874        chunk: &DataChunk,
875    ) -> Result<ValueVector, ProcessorError> {
876        let left_arr = self.evaluate_arrow(left, chunk)?;
877        let num_rows = chunk.size;
878        let mut result = ValueVector::new(akar_common::types::PhysicalTypeID::Bool, num_rows);
879        result.resize(num_rows);
880        for row in 0..num_rows {
881            let lv = left_arr.get_value(row).unwrap_or(Value::Null);
882            if matches!(lv, Value::Null) {
883                result.set_null(row, true);
884                continue;
885            }
886            let (in_list, has_null) = match right {
887                Expression::List(items) => {
888                    let mut matched = false;
889                    let mut has_null_item = false;
890                    for item in items {
891                        let item_vec = self.evaluate(item, chunk)?;
892                        let iv = item_vec.get_value(row).unwrap_or(Value::Null);
893                        if matches!(iv, Value::Null) {
894                            has_null_item = true;
895                        } else if iv == lv {
896                            matched = true;
897                            break;
898                        }
899                    }
900                    (matched, has_null_item)
901                }
902                _ => {
903                    let right_arr = self.evaluate_arrow(right, chunk)?;
904                    let rv = right_arr.get_value(row).unwrap_or(Value::Null);
905                    match &rv {
906                        Value::List(ritems) => {
907                            let matched = ritems.contains(&lv);
908                            let has_null_item = if matched {
909                                false
910                            } else {
911                                ritems.iter().any(|item| matches!(item, Value::Null))
912                            };
913                            (matched, has_null_item)
914                        }
915                        _ => (lv == rv, false),
916                    }
917                }
918            };
919            let result_val = if *op == BinaryOp::NotIn { !in_list } else { in_list };
920            if !in_list && has_null {
921                result.set_null(row, true);
922            } else {
923                store_value_in_vector_simple(&mut result, row, &Value::Bool(result_val))?;
924            }
925        }
926        Ok(result)
927    }
928
929    /// Evaluate a `CASE [subject] WHEN ... THEN ... [ELSE ...] END` expression.
930    fn evaluate_case(
931        &self,
932        case_expr: &akar_parser::ast::CaseExpr,
933        chunk: &DataChunk,
934    ) -> Result<ValueVector, ProcessorError> {
935        let num_rows = chunk.size;
936        // Evaluate subject (if any)
937        let subject_vec = if let Some(subj) = &case_expr.subject {
938            Some(self.evaluate(subj, chunk)?)
939        } else {
940            None
941        };
942
943        // We need to find the result type first by speculatively checking THEN exprs
944        // Strategy: evaluate all WHEN/THEN in order per row
945        // Determine result type from first THEN expr evaluation
946        let result_type = {
947            let first_then = self.evaluate(&case_expr.alternatives[0].then, chunk)?;
948            first_then.physical_type()
949        };
950
951        let mut result = ValueVector::new(result_type, num_rows);
952        result.resize(num_rows);
953
954        // For each row, find the matching branch
955        for row in 0..num_rows {
956            let subject_val = subject_vec.as_ref().and_then(|sv| sv.get_value(row));
957
958            let mut matched = false;
959            for alt in &case_expr.alternatives {
960                let when_vec = self.evaluate(&alt.when, chunk)?;
961                let when_val = when_vec.get_value(row).unwrap_or(Value::Null);
962
963                // Simple CASE: compare subject == when_val
964                // Searched CASE: when_val is a boolean condition
965                let branch_taken = if let Some(ref sv) = subject_val {
966                    when_val != Value::Null && when_val == *sv
967                } else {
968                    matches!(when_val, Value::Bool(true))
969                };
970
971                if branch_taken {
972                    let then_vec = self.evaluate(&alt.then, chunk)?;
973                    let then_val = then_vec.get_value(row).unwrap_or(Value::Null);
974                    store_value_in_vector(&mut result, row, &then_val)?;
975                    matched = true;
976                    break;
977                }
978            }
979
980            if !matched {
981                if let Some(else_e) = &case_expr.else_expr {
982                    let else_vec = self.evaluate(else_e, chunk)?;
983                    let else_val = else_vec.get_value(row).unwrap_or(Value::Null);
984                    store_value_in_vector(&mut result, row, &else_val)?;
985                } else {
986                    result.set_null(row, true);
987                }
988            }
989        }
990
991        Ok(result)
992    }
993
994    /// Evaluate a list literal expression.
995    fn evaluate_list_literal(&self, items: &[Expression], chunk: &DataChunk) -> Result<ValueVector, ProcessorError> {
996        if items.is_empty() {
997            let mut v = ValueVector::new(akar_common::types::PhysicalTypeID::List, chunk.size);
998            v.resize(chunk.size);
999            for i in 0..chunk.size {
1000                store_value_in_vector(&mut v, i, &Value::List(vec![]))?;
1001            }
1002            return Ok(v);
1003        }
1004
1005        let num_rows = chunk.size;
1006        let mut result_vec = ValueVector::new(akar_common::types::PhysicalTypeID::List, num_rows);
1007        result_vec.resize(num_rows);
1008
1009        for row in 0..num_rows {
1010            // For each row, evaluate each item expression against the same chunk
1011            // (list literals reference the whole chunk, so we get the per-row values)
1012            let mut list_values = Vec::with_capacity(items.len());
1013            for item in items {
1014                let item_vec = self.evaluate(item, chunk)?;
1015                let val = if row < item_vec.size() {
1016                    item_vec.get_value(row).unwrap_or(Value::Null)
1017                } else {
1018                    Value::Null
1019                };
1020                list_values.push(val);
1021            }
1022            store_value_in_vector(&mut result_vec, row, &Value::List(list_values))?;
1023        }
1024
1025        Ok(result_vec)
1026    }
1027
1028    /// Evaluate a map literal expression.
1029    fn evaluate_map_literal(
1030        &self,
1031        items: &[(String, Expression)],
1032        chunk: &DataChunk,
1033    ) -> Result<ValueVector, ProcessorError> {
1034        let num_rows = chunk.size;
1035        let mut result_vec = ValueVector::new(akar_common::types::PhysicalTypeID::Struct, num_rows);
1036        result_vec.resize(num_rows);
1037
1038        for row in 0..num_rows {
1039            let mut map_values = Vec::with_capacity(items.len());
1040            for (key, item) in items {
1041                let item_vec = self.evaluate(item, chunk)?;
1042                let val = if row < item_vec.size() {
1043                    item_vec.get_value(row).unwrap_or(Value::Null)
1044                } else {
1045                    Value::Null
1046                };
1047                map_values.push((Value::String(key.clone()), val));
1048            }
1049            store_value_in_vector(&mut result_vec, row, &Value::Map(map_values))?;
1050        }
1051
1052        Ok(result_vec)
1053    }
1054
1055    /// Evaluate an ANY/ALL/NONE/SINGLE list predicate.
1056    /// Evaluates the list expression, then for each element evaluates the
1057    /// predicate and applies the quantifier logic.
1058    fn evaluate_list_predicate(
1059        &self,
1060        quantifier: &akar_parser::ast::Quantifier,
1061        list: &Expression,
1062        _var_name: &str,
1063        predicate: &Expression,
1064        chunk: &DataChunk,
1065    ) -> Result<ValueVector, ProcessorError> {
1066        // Evaluate the list expression to get a ValueVector
1067        let list_vec = self.evaluate(list, chunk)?;
1068        let num_rows = chunk.size;
1069        let mut result = ValueVector::new(akar_common::types::PhysicalTypeID::Bool, num_rows);
1070        result.resize(num_rows);
1071
1072        for row in 0..num_rows {
1073            let list_val = list_vec.get_value(row).unwrap_or(Value::Null);
1074            let items = match &list_val {
1075                Value::List(items) => items.as_slice(),
1076                _ => {
1077                    // Not a list → false for all quantifiers
1078                    store_value_in_vector(&mut result, row, &Value::Bool(false))?;
1079                    continue;
1080                }
1081            };
1082
1083            // For each element, create a mini-chunk with the variable bound
1084            // and evaluate the predicate
1085            let mut true_count = 0u64;
1086            for item in items {
1087                // Create a single-row chunk with the variable as first field
1088                let mut elem_vec = ValueVector::new(item.physical_type(), 1);
1089                elem_vec.resize(1);
1090                store_value_in_vector(&mut elem_vec, 0, item)?;
1091                let mini_chunk = {
1092                    let arrow_fields = vec![&elem_vec]
1093                        .into_iter()
1094                        .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
1095                        .collect::<Vec<_>>();
1096                    let arrow_field_types = vec![&elem_vec]
1097                        .into_iter()
1098                        .map(|v| v.physical_type())
1099                        .collect::<Vec<_>>();
1100                    DataChunk::new(arrow_fields, arrow_field_types)
1101                };
1102
1103                let pred_vec = self.evaluate(predicate, &mini_chunk)?;
1104                let pred_val = pred_vec.get_value(0).unwrap_or(Value::Null);
1105
1106                if matches!(pred_val, Value::Bool(true)) {
1107                    true_count += 1;
1108                }
1109            }
1110
1111            // Apply quantifier logic
1112            let elem_count = items.len() as u64;
1113            let bool_result = match quantifier {
1114                akar_parser::ast::Quantifier::Any => true_count > 0,
1115                akar_parser::ast::Quantifier::All => !items.is_empty() && true_count == elem_count,
1116                akar_parser::ast::Quantifier::None => true_count == 0,
1117                akar_parser::ast::Quantifier::Single => true_count == 1,
1118            };
1119            store_value_in_vector(&mut result, row, &Value::Bool(bool_result))?;
1120        }
1121
1122        Ok(result)
1123    }
1124
1125    /// Extract the Lambda expression from function call arguments, if present.
1126    fn extract_lambda_arg<'a>(&self, args: &'a [&Expression]) -> Option<&'a Expression> {
1127        args.iter().find(|a| matches!(a, Expression::Lambda { .. })).copied()
1128    }
1129
1130    /// Evaluate `list_transform(list, x -> body)` — apply lambda to each element.
1131    fn evaluate_list_transform(
1132        &self,
1133        args: &[&Expression],
1134        lambda: &Expression,
1135        chunk: &DataChunk,
1136    ) -> Result<ValueVector, ProcessorError> {
1137        let list_expr = args
1138            .iter()
1139            .find(|a| !matches!(a, Expression::Lambda { .. }))
1140            .ok_or("list_transform requires a list argument")?;
1141
1142        let (var_name, body) = match lambda {
1143            Expression::Lambda { var_name, body } => (var_name, body),
1144            _ => return Err("Expected lambda expression".into()),
1145        };
1146
1147        let list_vec = self.evaluate(list_expr, chunk)?;
1148        let num_rows = chunk.size;
1149        let mut result = ValueVector::new(akar_common::types::PhysicalTypeID::List, num_rows);
1150        result.resize(num_rows);
1151
1152        for row in 0..num_rows {
1153            let list_val = list_vec.get_value(row).unwrap_or(Value::Null);
1154            let items = match list_val {
1155                Value::List(items) => items,
1156                _ => {
1157                    store_value_in_vector(&mut result, row, &Value::List(vec![]))?;
1158                    continue;
1159                }
1160            };
1161
1162            let mut transformed: Vec<Value> = Vec::with_capacity(items.len());
1163            for item in items {
1164                let mut elem_vec = ValueVector::new(item.physical_type(), 1);
1165                elem_vec.resize(1);
1166                store_value_in_vector(&mut elem_vec, 0, &item)?;
1167                let mut mini_chunk = {
1168                    let arrow_fields = vec![&elem_vec]
1169                        .into_iter()
1170                        .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
1171                        .collect::<Vec<_>>();
1172                    let arrow_field_types = vec![&elem_vec]
1173                        .into_iter()
1174                        .map(|v| v.physical_type())
1175                        .collect::<Vec<_>>();
1176                    DataChunk::new(arrow_fields, arrow_field_types)
1177                };
1178                mini_chunk.field_names.push(var_name.clone());
1179
1180                let body_vec = self.evaluate(body, &mini_chunk)?;
1181                let body_val = body_vec.get_value(0).unwrap_or(Value::Null);
1182                transformed.push(body_val);
1183            }
1184            store_value_in_vector(&mut result, row, &Value::List(transformed))?;
1185        }
1186
1187        Ok(result)
1188    }
1189
1190    /// Evaluate `list_filter(list, x -> predicate)` — keep elements where predicate is true.
1191    fn evaluate_list_filter(
1192        &self,
1193        args: &[&Expression],
1194        lambda: &Expression,
1195        chunk: &DataChunk,
1196    ) -> Result<ValueVector, ProcessorError> {
1197        let list_expr = args
1198            .iter()
1199            .find(|a| !matches!(a, Expression::Lambda { .. }))
1200            .ok_or("list_filter requires a list argument")?;
1201
1202        let (var_name, body) = match lambda {
1203            Expression::Lambda { var_name, body } => (var_name, body),
1204            _ => return Err("Expected lambda expression".into()),
1205        };
1206
1207        let list_vec = self.evaluate(list_expr, chunk)?;
1208        let num_rows = chunk.size;
1209        let mut result = ValueVector::new(akar_common::types::PhysicalTypeID::List, num_rows);
1210        result.resize(num_rows);
1211
1212        for row in 0..num_rows {
1213            let list_val = list_vec.get_value(row).unwrap_or(Value::Null);
1214            let items = match list_val {
1215                Value::List(items) => items,
1216                _ => {
1217                    store_value_in_vector(&mut result, row, &Value::List(vec![]))?;
1218                    continue;
1219                }
1220            };
1221
1222            let mut filtered: Vec<Value> = Vec::with_capacity(items.len());
1223            for item in items {
1224                let mut elem_vec = ValueVector::new(item.physical_type(), 1);
1225                elem_vec.resize(1);
1226                store_value_in_vector(&mut elem_vec, 0, &item)?;
1227                let mut mini_chunk = {
1228                    let arrow_fields = vec![&elem_vec]
1229                        .into_iter()
1230                        .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
1231                        .collect::<Vec<_>>();
1232                    let arrow_field_types = vec![&elem_vec]
1233                        .into_iter()
1234                        .map(|v| v.physical_type())
1235                        .collect::<Vec<_>>();
1236                    DataChunk::new(arrow_fields, arrow_field_types)
1237                };
1238                mini_chunk.field_names.push(var_name.clone());
1239
1240                let pred_vec = self.evaluate(body, &mini_chunk)?;
1241                let pred_val = pred_vec.get_value(0).unwrap_or(Value::Null);
1242
1243                if matches!(pred_val, Value::Bool(true)) {
1244                    filtered.push(item);
1245                } else if let Value::Int64(x) = pred_val {
1246                    if x != 0 {
1247                        filtered.push(item);
1248                    }
1249                }
1250            }
1251            store_value_in_vector(&mut result, row, &Value::List(filtered))?;
1252        }
1253
1254        Ok(result)
1255    }
1256
1257    /// Evaluate `list_reduce(list, (acc, x) -> body, initial)` — fold over list.
1258    fn evaluate_list_reduce(
1259        &self,
1260        args: &[&Expression],
1261        lambda: &Expression,
1262        chunk: &DataChunk,
1263    ) -> Result<ValueVector, ProcessorError> {
1264        let list_expr = args
1265            .iter()
1266            .find(|a| !matches!(a, Expression::Lambda { .. }))
1267            .ok_or("list_reduce requires a list argument")?;
1268
1269        // Find initial value — the argument that is not the list and not the lambda
1270        let initial_expr = args
1271            .iter()
1272            .filter(|a| !matches!(a, Expression::Lambda { .. }))
1273            .nth(1) // Second non-lambda arg (first is list)
1274            .ok_or("list_reduce requires an initial value argument")?;
1275
1276        let (var_name, body) = match lambda {
1277            Expression::Lambda { var_name, body } => (var_name, body),
1278            _ => return Err("Expected lambda expression".into()),
1279        };
1280
1281        // list_reduce uses (acc, x) -> expr where acc is first var, x is second
1282        let acc_name = var_name.clone();
1283        let elem_name = match body.as_ref() {
1284            Expression::BinaryOp(_op, left, right) => {
1285                // Try to infer the element variable from the body pattern
1286                // Most common: acc + x where x is a Variable
1287                let left_var = if let Expression::Variable(v) = left.as_ref() {
1288                    Some(v.clone())
1289                } else {
1290                    None
1291                };
1292                let right_var = if let Expression::Variable(v) = right.as_ref() {
1293                    Some(v.clone())
1294                } else {
1295                    None
1296                };
1297
1298                if left_var.as_deref() == Some(&acc_name) {
1299                    right_var.unwrap_or_default()
1300                } else if right_var.as_deref() == Some(&acc_name) {
1301                    left_var.unwrap_or_default()
1302                } else {
1303                    String::new()
1304                }
1305            }
1306            _ => String::new(),
1307        };
1308
1309        let list_vec = self.evaluate(list_expr, chunk)?;
1310        let initial_vec = self.evaluate(initial_expr, chunk)?;
1311        let num_rows = chunk.size;
1312        let mut result = ValueVector::new(akar_common::types::PhysicalTypeID::Int64, num_rows);
1313        result.resize(num_rows);
1314
1315        // Pre-build field_names template (avoids N String clones per list element)
1316        let field_names_template = {
1317            let mut names = vec![acc_name.clone()];
1318            if !elem_name.is_empty() {
1319                names.push(elem_name.clone());
1320            }
1321            names
1322        };
1323
1324        for row in 0..num_rows {
1325            let list_val = list_vec.get_value(row).unwrap_or(Value::Null);
1326            let items = match list_val {
1327                Value::List(items) => items,
1328                _ => {
1329                    store_value_in_vector(&mut result, row, &Value::Null)?;
1330                    continue;
1331                }
1332            };
1333
1334            let mut acc = initial_vec.get_value(row).unwrap_or(Value::Null);
1335            for item in items {
1336                // Create mini-chunk with acc as field 0 and item as field 1
1337                let mut acc_vec = ValueVector::new(acc.physical_type(), 1);
1338                acc_vec.resize(1);
1339                store_value_in_vector(&mut acc_vec, 0, &acc)?;
1340                let mut elem_vec = ValueVector::new(item.physical_type(), 1);
1341                elem_vec.resize(1);
1342                store_value_in_vector(&mut elem_vec, 0, &item)?;
1343                let mut mini_chunk = {
1344                    let arrow_fields = vec![&acc_vec, &elem_vec]
1345                        .into_iter()
1346                        .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
1347                        .collect::<Vec<_>>();
1348                    let arrow_field_types = vec![&acc_vec, &elem_vec]
1349                        .into_iter()
1350                        .map(|v| v.physical_type())
1351                        .collect::<Vec<_>>();
1352                    DataChunk::new(arrow_fields, arrow_field_types)
1353                };
1354                mini_chunk.field_names = field_names_template.clone();
1355
1356                let body_vec = self.evaluate(body, &mini_chunk)?;
1357                acc = body_vec.get_value(0).unwrap_or(Value::Null);
1358            }
1359            store_value_in_vector(&mut result, row, &acc)?;
1360        }
1361
1362        Ok(result)
1363    }
1364}
1365
1366/// Simplified store_value_in_vector that accepts any Value type.
1367/// This is a copy adapted from physical_operator.rs.
1368fn store_value_in_vector_simple(v: &mut ValueVector, row: usize, val: &Value) -> Result<(), String> {
1369    match val {
1370        Value::Null => {
1371            v.set_null(row, true);
1372        }
1373        Value::Bool(x) => {
1374            if v.physical_type() == akar_common::types::PhysicalTypeID::Bool {
1375                v.data_mut()[row] = if *x { 1 } else { 0 };
1376                v.set_null(row, false);
1377            }
1378        }
1379        Value::Int64(x) => {
1380            let offset = row * 8;
1381            if offset + 8 <= v.data().len() {
1382                v.data_mut()[offset..offset + 8].copy_from_slice(&x.to_le_bytes());
1383                v.set_null(row, false);
1384            }
1385        }
1386        Value::UInt64(x) => {
1387            let offset = row * 8;
1388            if offset + 8 <= v.data().len() {
1389                v.data_mut()[offset..offset + 8].copy_from_slice(&x.to_le_bytes());
1390                v.set_null(row, false);
1391            }
1392        }
1393        Value::Double(x) => {
1394            let offset = row * 8;
1395            if offset + 8 <= v.data().len() {
1396                v.data_mut()[offset..offset + 8].copy_from_slice(&x.to_le_bytes());
1397                v.set_null(row, false);
1398            }
1399        }
1400        Value::String(s) => {
1401            let bytes = s.as_bytes();
1402            if bytes.len() > 255 {
1403                return Err(format!(
1404                    "Cannot store string of {} bytes: inline string storage limit is 255 bytes",
1405                    bytes.len()
1406                ));
1407            }
1408            let offset = row * 256;
1409            if offset < v.data().len() {
1410                v.data_mut()[offset] = bytes.len() as u8;
1411                if offset + 1 + bytes.len() <= v.data().len() {
1412                    v.data_mut()[offset + 1..offset + 1 + bytes.len()].copy_from_slice(bytes);
1413                }
1414                v.set_null(row, false);
1415            }
1416        }
1417        _ => {
1418            v.set_null(row, true);
1419        }
1420    }
1421    Ok(())
1422}
1423
1424/// Store a Value into a ValueVector at the given row index.
1425/// This is a copy of the helper from physical_operator.rs, kept here for independence.
1426fn store_value_in_vector(v: &mut ValueVector, row: usize, val: &Value) -> Result<(), String> {
1427    match val {
1428        Value::Null => {
1429            v.set_null(row, true);
1430        }
1431        Value::Bool(x) => {
1432            if v.physical_type() == akar_common::types::PhysicalTypeID::Bool {
1433                v.data_mut()[row] = if *x { 1 } else { 0 };
1434                v.set_null(row, false);
1435            }
1436        }
1437        Value::Int64(x) => {
1438            let offset = row * 8;
1439            if offset + 8 <= v.data().len() {
1440                v.data_mut()[offset..offset + 8].copy_from_slice(&x.to_le_bytes());
1441                v.set_null(row, false);
1442            }
1443        }
1444        Value::UInt64(x) => {
1445            let offset = row * 8;
1446            if offset + 8 <= v.data().len() {
1447                v.data_mut()[offset..offset + 8].copy_from_slice(&x.to_le_bytes());
1448                v.set_null(row, false);
1449            }
1450        }
1451        Value::Date(x) => {
1452            let offset = row * 8;
1453            if offset + 8 <= v.data().len() {
1454                v.data_mut()[offset..offset + 8].copy_from_slice(&(x.0 as i64).to_le_bytes());
1455                v.set_null(row, false);
1456            }
1457        }
1458        Value::Timestamp(x) | Value::TimestampNs(x) | Value::TimestampMs(x) | Value::TimestampSec(x) => {
1459            let offset = row * 8;
1460            if offset + 8 <= v.data().len() {
1461                v.data_mut()[offset..offset + 8].copy_from_slice(&x.0.to_le_bytes());
1462                v.set_null(row, false);
1463            }
1464        }
1465        Value::TimestampTz(x) => {
1466            let offset = row * 8;
1467            if offset + 8 <= v.data().len() {
1468                v.data_mut()[offset..offset + 8].copy_from_slice(&x.0.to_le_bytes());
1469                v.set_null(row, false);
1470            }
1471        }
1472        Value::DTime(x) => {
1473            let offset = row * 8;
1474            if offset + 8 <= v.data().len() {
1475                v.data_mut()[offset..offset + 8].copy_from_slice(&x.to_le_bytes());
1476                v.set_null(row, false);
1477            }
1478        }
1479        Value::Int32(x) => {
1480            let offset = row * 4;
1481            if offset + 4 <= v.data().len() {
1482                v.data_mut()[offset..offset + 4].copy_from_slice(&x.to_le_bytes());
1483                v.set_null(row, false);
1484            }
1485        }
1486        Value::Double(x) => {
1487            let offset = row * 8;
1488            if offset + 8 <= v.data().len() {
1489                v.data_mut()[offset..offset + 8].copy_from_slice(&x.to_le_bytes());
1490                v.set_null(row, false);
1491            }
1492        }
1493        Value::Float(x) => {
1494            let offset = row * 4;
1495            if offset + 4 <= v.data().len() {
1496                v.data_mut()[offset..offset + 4].copy_from_slice(&x.to_le_bytes());
1497                v.set_null(row, false);
1498            }
1499        }
1500        Value::String(s) => {
1501            let bytes = s.as_bytes();
1502            if bytes.len() > 255 {
1503                return Err(format!(
1504                    "Cannot store string of {} bytes: inline string storage limit is 255 bytes",
1505                    bytes.len()
1506                ));
1507            }
1508            let offset = row * 256;
1509            if offset < v.data().len() {
1510                v.data_mut()[offset] = bytes.len() as u8;
1511                if offset + 1 + bytes.len() <= v.data().len() {
1512                    v.data_mut()[offset + 1..offset + 1 + bytes.len()].copy_from_slice(bytes);
1513                }
1514                v.set_null(row, false);
1515            }
1516        }
1517        _ => {
1518            // For complex types (List, Struct, etc.), store as null
1519            v.set_null(row, true);
1520        }
1521    }
1522    Ok(())
1523}
1524
1525/// Build an ArrowVector from a Vec<Value>, using typed builders to
1526/// avoid the intermediate ValueVector allocation.
1527fn build_arrow_from_values(
1528    values: &[Value],
1529    phys_type: PhysicalTypeID,
1530    num_rows: usize,
1531) -> Result<ArrowVector, ProcessorError> {
1532    match phys_type {
1533        PhysicalTypeID::Bool => {
1534            let mut builder = arrow::array::BooleanBuilder::with_capacity(num_rows);
1535            for v in values {
1536                match v {
1537                    Value::Null => builder.append_null(),
1538                    Value::Bool(b) => builder.append_value(*b),
1539                    _ => builder.append_null(),
1540                }
1541            }
1542            Ok(ArrowVector::new(Arc::new(builder.finish()), phys_type))
1543        }
1544        PhysicalTypeID::Int64 => {
1545            let mut builder = arrow::array::Int64Builder::with_capacity(num_rows);
1546            for v in values {
1547                match v {
1548                    Value::Null => builder.append_null(),
1549                    Value::Int64(n) => builder.append_value(*n),
1550                    Value::Int32(n) => builder.append_value(*n as i64),
1551                    Value::Date(n) => builder.append_value(n.0 as i64),
1552                    Value::Timestamp(n) | Value::TimestampNs(n) | Value::TimestampMs(n) | Value::TimestampSec(n) => {
1553                        builder.append_value(n.0)
1554                    }
1555                    Value::TimestampTz(n) => builder.append_value(n.0),
1556                    Value::DTime(n) => builder.append_value(*n),
1557                    _ => builder.append_null(),
1558                }
1559            }
1560            Ok(ArrowVector::new(Arc::new(builder.finish()), phys_type))
1561        }
1562        PhysicalTypeID::Int32 => {
1563            let mut builder = arrow::array::Int32Builder::with_capacity(num_rows);
1564            for v in values {
1565                match v {
1566                    Value::Null => builder.append_null(),
1567                    Value::Int32(n) => builder.append_value(*n),
1568                    _ => builder.append_null(),
1569                }
1570            }
1571            Ok(ArrowVector::new(Arc::new(builder.finish()), phys_type))
1572        }
1573        PhysicalTypeID::Double => {
1574            let mut builder = arrow::array::Float64Builder::with_capacity(num_rows);
1575            for v in values {
1576                match v {
1577                    Value::Null => builder.append_null(),
1578                    Value::Double(n) => builder.append_value(*n),
1579                    _ => builder.append_null(),
1580                }
1581            }
1582            Ok(ArrowVector::new(Arc::new(builder.finish()), phys_type))
1583        }
1584        PhysicalTypeID::Float => {
1585            let mut builder = arrow::array::Float32Builder::with_capacity(num_rows);
1586            for v in values {
1587                match v {
1588                    Value::Null => builder.append_null(),
1589                    Value::Float(n) => builder.append_value(*n),
1590                    Value::Double(n) => builder.append_value(*n as f32),
1591                    _ => builder.append_null(),
1592                }
1593            }
1594            Ok(ArrowVector::new(Arc::new(builder.finish()), phys_type))
1595        }
1596        PhysicalTypeID::String => {
1597            let mut builder = arrow::array::StringBuilder::with_capacity(num_rows, num_rows * 16);
1598            for v in values {
1599                match v {
1600                    Value::Null => builder.append_null(),
1601                    Value::String(s) => builder.append_value(s),
1602                    _ => builder.append_null(),
1603                }
1604            }
1605            Ok(ArrowVector::new(Arc::new(builder.finish()), phys_type))
1606        }
1607        _ => {
1608            // Unsupported type — fall back to creating an Arrow array with all nulls
1609            let mut builder = arrow::array::Int64Builder::with_capacity(num_rows);
1610            builder.append_nulls(num_rows);
1611            Ok(ArrowVector::new(Arc::new(builder.finish()), PhysicalTypeID::Int64))
1612        }
1613    }
1614}
1615
1616impl ExpressionEvaluator {
1617    /// Evaluate a sequence operation (nextval/currval) using the sequence callback.
1618    /// Extracts the first string argument as the sequence name and delegates to the callback.
1619    fn evaluate_sequence_op(
1620        &self,
1621        name: &str,
1622        func: &ScalarFunction,
1623        arg_vectors: &[ValueVector],
1624        num_rows: usize,
1625    ) -> Result<ValueVector, ProcessorError> {
1626        let is_nextval = match func {
1627            ScalarFunction::SequenceOp { is_nextval } => *is_nextval,
1628            _ => return Err(format!("Internal error: expected SequenceOp for '{}'", name).into()),
1629        };
1630
1631        let seq_fn = self
1632            .sequence_fn
1633            .as_ref()
1634            .ok_or_else(|| format!("No sequence callback configured for '{}'", name))?;
1635
1636        let mut result_vec = ValueVector::new(akar_common::types::PhysicalTypeID::Int64, num_rows);
1637        result_vec.resize(num_rows);
1638
1639        for row in 0..num_rows {
1640            let seq_name = if row < arg_vectors[0].size() && !arg_vectors[0].is_null(row) {
1641                match arg_vectors[0].get_value(row) {
1642                    Some(Value::String(s)) => s,
1643                    _ => return Err("nextval/currval requires a string argument (sequence name)".into()),
1644                }
1645            } else {
1646                result_vec.set_null(row, true);
1647                continue;
1648            };
1649
1650            match seq_fn(&seq_name, is_nextval) {
1651                Ok(val) => {
1652                    store_value_in_vector(&mut result_vec, row, &val)?;
1653                }
1654                Err(e) => {
1655                    result_vec.set_null(row, true);
1656                    if row == 0 {
1657                        return Err(e);
1658                    }
1659                }
1660            }
1661        }
1662
1663        Ok(result_vec)
1664    }
1665}
1666
1667#[cfg(test)]
1668mod tests {
1669    use super::*;
1670    use akar_common::types::PhysicalTypeID;
1671    use akar_common::vector::ValueVector;
1672    use akar_function::registry::FunctionRegistry;
1673    use hashbrown::HashMap;
1674
1675    fn make_registry() -> Arc<Mutex<FunctionRegistry>> {
1676        Arc::new(Mutex::new(FunctionRegistry::new()))
1677    }
1678
1679    fn make_chunk(values: &[i64]) -> DataChunk {
1680        let mut v = ValueVector::new(PhysicalTypeID::Int64, values.len());
1681        v.resize(values.len());
1682        for (i, val) in values.iter().enumerate() {
1683            v.set_i64(i, *val);
1684        }
1685        {
1686            let arrow_fields = vec![akar_common::arrow_vector::ArrowVector::from_legacy(&v).array];
1687            let arrow_field_types = vec![v.physical_type()];
1688            DataChunk::new(arrow_fields, arrow_field_types)
1689        }
1690    }
1691
1692    #[test]
1693    fn test_evaluate_constant_int() {
1694        let eval = ExpressionEvaluator::new(make_registry());
1695        let expr = Expression::Constant(Constant::Integer(42));
1696        let chunk = make_chunk(&[]);
1697        let result = eval.evaluate(&expr, &chunk).unwrap();
1698        assert_eq!(result.size(), 0);
1699    }
1700
1701    #[test]
1702    fn test_evaluate_constant_bool() {
1703        let eval = ExpressionEvaluator::new(make_registry());
1704        let expr = Expression::Constant(Constant::Bool(true));
1705        let chunk = make_chunk(&[1, 2, 3]);
1706        let result = eval.evaluate(&expr, &chunk).unwrap();
1707        assert_eq!(result.size(), 3);
1708        // All rows should be true
1709        for i in 0..3 {
1710            assert!(!result.is_null(i));
1711        }
1712    }
1713
1714    #[test]
1715    fn test_evaluate_variable() {
1716        let eval = ExpressionEvaluator::new(make_registry());
1717        let chunk = make_chunk(&[10, 20, 30]);
1718        let expr = Expression::Variable("0".into());
1719        let result = eval.evaluate(&expr, &chunk).unwrap();
1720        assert_eq!(result.size(), 3);
1721        assert_eq!(result.get_i64(0), Some(10));
1722        assert_eq!(result.get_i64(1), Some(20));
1723        assert_eq!(result.get_i64(2), Some(30));
1724    }
1725
1726    #[test]
1727    fn test_evaluate_binary_equal() {
1728        let eval = ExpressionEvaluator::new(make_registry());
1729        // 0 = 0 → true, 1 = 0 → false, 2 = 0 → false
1730        let left = Box::new(Expression::Variable("0".into()));
1731        let right = Box::new(Expression::Constant(Constant::Integer(0)));
1732        let expr = Expression::BinaryOp(BinaryOp::Equal, left, right);
1733        let chunk = make_chunk(&[0, 1, 2]);
1734        let result = eval.evaluate(&expr, &chunk).unwrap();
1735        assert_eq!(result.size(), 3);
1736        assert_eq!(result.get_value(0), Some(Value::Bool(true)));
1737        assert_eq!(result.get_value(1), Some(Value::Bool(false)));
1738        assert_eq!(result.get_value(2), Some(Value::Bool(false)));
1739    }
1740
1741    #[test]
1742    fn test_evaluate_binary_greater_than() {
1743        let eval = ExpressionEvaluator::new(make_registry());
1744        // 3 > 2 → true, 1 > 2 → false, 5 > 2 → true
1745        let left = Box::new(Expression::Variable("0".into()));
1746        let right = Box::new(Expression::Constant(Constant::Integer(2)));
1747        let expr = Expression::BinaryOp(BinaryOp::GreaterThan, left, right);
1748        let chunk = make_chunk(&[3, 1, 5]);
1749        let result = eval.evaluate(&expr, &chunk).unwrap();
1750        assert_eq!(result.size(), 3);
1751        assert_eq!(result.get_value(0), Some(Value::Bool(true)));
1752        assert_eq!(result.get_value(1), Some(Value::Bool(false)));
1753        assert_eq!(result.get_value(2), Some(Value::Bool(true)));
1754    }
1755
1756    #[test]
1757    fn test_evaluate_binary_and() {
1758        let eval = ExpressionEvaluator::new(make_registry());
1759        // We need two columns: set up chunk with column 0 as bools and column 1 as bools
1760        let mut v0 = ValueVector::new(PhysicalTypeID::Bool, 2);
1761        v0.resize(2);
1762        store_value_in_vector(&mut v0, 0, &Value::Bool(true)).unwrap();
1763        store_value_in_vector(&mut v0, 1, &Value::Bool(false)).unwrap();
1764        let mut v1 = ValueVector::new(PhysicalTypeID::Bool, 2);
1765        v1.resize(2);
1766        store_value_in_vector(&mut v1, 0, &Value::Bool(true)).unwrap();
1767        store_value_in_vector(&mut v1, 1, &Value::Bool(true)).unwrap();
1768        let chunk = {
1769            let arrow_fields = vec![
1770                akar_common::arrow_vector::ArrowVector::from_legacy(&v0).array,
1771                akar_common::arrow_vector::ArrowVector::from_legacy(&v1).array,
1772            ];
1773            let arrow_field_types = vec![v0.physical_type(), v1.physical_type()];
1774            DataChunk::new(arrow_fields, arrow_field_types)
1775        };
1776
1777        // true AND true → true, false AND true → false
1778        let left = Box::new(Expression::Variable("0".into()));
1779        let right = Box::new(Expression::Variable("1".into()));
1780        let expr = Expression::BinaryOp(BinaryOp::And, left, right);
1781        let result = eval.evaluate(&expr, &chunk).unwrap();
1782        assert_eq!(result.size(), 2);
1783        assert_eq!(result.get_value(0), Some(Value::Bool(true)));
1784        assert_eq!(result.get_value(1), Some(Value::Bool(false)));
1785    }
1786
1787    #[test]
1788    fn test_evaluate_function_call_string_length() {
1789        let eval = ExpressionEvaluator::new(make_registry());
1790        let chunk = akar_common::vector::DataChunk::new(vec![], vec![]);
1791        let expr = Expression::FunctionCall(
1792            "length".into(),
1793            vec![Expression::Constant(Constant::String("hello".into()))],
1794        );
1795        let result = eval.evaluate(&expr, &chunk).unwrap();
1796        assert_eq!(result.size(), 0);
1797    }
1798
1799    #[test]
1800    fn test_evaluate_not() {
1801        let eval = ExpressionEvaluator::new(make_registry());
1802        let mut v = ValueVector::new(PhysicalTypeID::Bool, 3);
1803        v.resize(3);
1804        store_value_in_vector(&mut v, 0, &Value::Bool(true)).unwrap();
1805        store_value_in_vector(&mut v, 1, &Value::Bool(false)).unwrap();
1806        store_value_in_vector(&mut v, 2, &Value::Bool(true)).unwrap();
1807        let chunk = {
1808            let arrow_fields = vec![akar_common::arrow_vector::ArrowVector::from_legacy(&v).array];
1809            let arrow_field_types = vec![v.physical_type()];
1810            DataChunk::new(arrow_fields, arrow_field_types)
1811        };
1812        // NOT of column 0 — true→false, false→true, true→false
1813        let expr = Expression::UnaryOp(UnaryOp::Not, Box::new(Expression::Variable("0".into())));
1814        let result = eval.evaluate(&expr, &chunk).unwrap();
1815        assert_eq!(result.size(), 3);
1816        assert_eq!(result.get_value(0), Some(Value::Bool(false)));
1817        assert_eq!(result.get_value(1), Some(Value::Bool(true)));
1818        assert_eq!(result.get_value(2), Some(Value::Bool(false)));
1819    }
1820
1821    #[test]
1822    fn test_sequence_nextval_currval_with_callback() {
1823        let state = Arc::new(Mutex::new(HashMap::new()));
1824        state.lock().unwrap().insert("my_seq".to_string(), 10_i64);
1825
1826        let state_for_fn = state.clone();
1827        let seq_fn: Arc<dyn Fn(&str, bool) -> Result<Value, ProcessorError> + Send + Sync> =
1828            Arc::new(move |seq_name: &str, is_nextval: bool| {
1829                let mut map = state_for_fn.lock().map_err(|e| format!("Lock error: {e}"))?;
1830                let current = map
1831                    .get_mut(seq_name)
1832                    .ok_or_else(|| format!("Sequence '{}' not found", seq_name))?;
1833                if is_nextval {
1834                    let out = *current;
1835                    *current += 2;
1836                    Ok(Value::Int64(out))
1837                } else {
1838                    Ok(Value::Int64(*current))
1839                }
1840            });
1841
1842        let eval = ExpressionEvaluator::new(make_registry()).with_sequence_fn(seq_fn);
1843        let chunk = make_chunk(&[1, 2, 3]);
1844
1845        let nextval_expr = Expression::FunctionCall(
1846            "nextval".into(),
1847            vec![Expression::Constant(Constant::String("my_seq".into()))],
1848        );
1849        let nextvals = eval.evaluate(&nextval_expr, &chunk).unwrap();
1850        assert_eq!(nextvals.get_value(0), Some(Value::Int64(10)));
1851        assert_eq!(nextvals.get_value(1), Some(Value::Int64(12)));
1852        assert_eq!(nextvals.get_value(2), Some(Value::Int64(14)));
1853
1854        let currval_expr = Expression::FunctionCall(
1855            "currval".into(),
1856            vec![Expression::Constant(Constant::String("my_seq".into()))],
1857        );
1858        let curr = eval.evaluate(&currval_expr, &make_chunk(&[1])).unwrap();
1859        assert_eq!(curr.get_value(0), Some(Value::Int64(16)));
1860    }
1861
1862    #[test]
1863    fn test_sequence_requires_callback() {
1864        let eval = ExpressionEvaluator::new(make_registry());
1865        let expr = Expression::FunctionCall(
1866            "nextval".into(),
1867            vec![Expression::Constant(Constant::String("my_seq".into()))],
1868        );
1869        let err = eval.evaluate(&expr, &make_chunk(&[1])).unwrap_err();
1870        assert!(
1871            err.to_string().contains("No sequence callback configured"),
1872            "Unexpected error: {err}"
1873        );
1874    }
1875
1876    #[test]
1877    fn test_sequence_requires_string_arg() {
1878        let seq_fn: Arc<dyn Fn(&str, bool) -> Result<Value, ProcessorError> + Send + Sync> =
1879            Arc::new(|_seq_name: &str, _is_nextval: bool| Ok(Value::Int64(1)));
1880        let eval = ExpressionEvaluator::new(make_registry()).with_sequence_fn(seq_fn);
1881
1882        let expr = Expression::FunctionCall("nextval".into(), vec![Expression::Constant(Constant::Integer(42))]);
1883        let err = eval.evaluate(&expr, &make_chunk(&[1])).unwrap_err();
1884        assert!(
1885            err.to_string().contains("requires a string argument"),
1886            "Unexpected error: {err}"
1887        );
1888    }
1889}