1use std::sync::Arc;
64
65use super::ast::{BinOp, Expr, FieldRef, UnaryOp};
66use crate::storage::schema::coerce::coerce_via_catalog;
67use crate::storage::schema::coercion_spine;
68use crate::storage::schema::function_catalog::FUNCTION_CATALOG;
69use crate::storage::schema::operator_catalog::OperatorEntry;
70use crate::storage::schema::{DataType, Value};
71
72pub trait Row {
78 fn get(&self, field: &FieldRef) -> Option<Value>;
83}
84
85impl<F> Row for F
89where
90 F: Fn(&FieldRef) -> Option<Value>,
91{
92 fn get(&self, field: &FieldRef) -> Option<Value> {
93 self(field)
94 }
95}
96
97#[derive(Debug, Clone, PartialEq)]
101pub enum EvalError {
102 UnknownColumn(FieldRef),
104 UnboundParameter(usize),
108 OperatorMismatch {
111 op: BinOp,
112 lhs: DataType,
113 rhs: DataType,
114 },
115 UnaryMismatch { op: UnaryOp, operand: DataType },
117 UnknownFunction { name: String, args: Vec<DataType> },
121 ImplicitCastFailed {
126 from: DataType,
127 to: DataType,
128 reason: String,
129 },
130 CastFailed {
132 from: DataType,
133 to: DataType,
134 reason: String,
135 },
136 ArithmeticOverflow { op: BinOp },
138 DivisionByZero,
140 InvalidNumericResult { function: String, reason: String },
143 JsonParseFailed { reason: String },
145 EmptyInList,
149}
150
151impl std::fmt::Display for EvalError {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 match self {
154 EvalError::UnknownColumn(field) => write!(f, "unknown column: {field:?}"),
155 EvalError::UnboundParameter(idx) => {
156 write!(f, "unbound query parameter: ${idx}")
157 }
158 EvalError::OperatorMismatch { op, lhs, rhs } => {
159 write!(f, "operator {op:?} not defined for ({lhs:?}, {rhs:?})")
160 }
161 EvalError::UnaryMismatch { op, operand } => {
162 write!(f, "unary {op:?} not defined for {operand:?}")
163 }
164 EvalError::UnknownFunction { name, args } => {
165 write!(f, "unknown function: {name}({args:?})")
166 }
167 EvalError::ImplicitCastFailed { from, to, reason } => {
168 write!(f, "implicit cast {from:?} -> {to:?} failed: {reason}")
169 }
170 EvalError::CastFailed { from, to, reason } => {
171 write!(f, "cast {from:?} -> {to:?} failed: {reason}")
172 }
173 EvalError::ArithmeticOverflow { op } => {
174 write!(f, "arithmetic overflow in {op:?}")
175 }
176 EvalError::DivisionByZero => write!(f, "division by zero"),
177 EvalError::InvalidNumericResult { function, reason } => {
178 write!(f, "invalid numeric result in {function}: {reason}")
179 }
180 EvalError::JsonParseFailed { reason } => {
181 write!(f, "JSON_PARSE failed to parse JSON: {reason}")
182 }
183 EvalError::EmptyInList => write!(f, "IN list is empty"),
184 }
185 }
186}
187
188impl std::error::Error for EvalError {}
189
190pub fn evaluate(expr: &Expr, row: &dyn Row) -> Result<Value, EvalError> {
195 match expr {
196 Expr::Literal { value, .. } => Ok(value.clone()),
197 Expr::Column { field, .. } => row
198 .get(field)
199 .ok_or_else(|| EvalError::UnknownColumn(field.clone())),
200 Expr::Parameter { index, .. } => Err(EvalError::UnboundParameter(*index)),
201 Expr::UnaryOp { op, operand, .. } => eval_unary(*op, operand, row),
202 Expr::BinaryOp { op, lhs, rhs, .. } => eval_binary(*op, lhs, rhs, row),
203 Expr::Cast { inner, target, .. } => eval_cast(inner, *target, row),
204 Expr::FunctionCall { name, args, .. } => eval_function(name, args, row),
205 Expr::Case {
206 branches, else_, ..
207 } => eval_case(branches, else_.as_deref(), row),
208 Expr::IsNull {
209 operand, negated, ..
210 } => {
211 let v = evaluate(operand, row)?;
212 let is_null = v.is_null();
213 Ok(Value::Boolean(if *negated { !is_null } else { is_null }))
214 }
215 Expr::InList {
216 target,
217 values,
218 negated,
219 ..
220 } => eval_in_list(target, values, *negated, row),
221 Expr::Between {
222 target,
223 low,
224 high,
225 negated,
226 ..
227 } => eval_between(target, low, high, *negated, row),
228 Expr::Subquery { .. } => Err(EvalError::UnknownFunction {
229 name: "SUBQUERY".to_string(),
230 args: Vec::new(),
231 }),
232 Expr::WindowFunctionCall { name, .. } => Err(EvalError::UnknownFunction {
233 name: format!("{name} OVER (...)"),
234 args: Vec::new(),
235 }),
236 }
237}
238
239fn eval_unary(op: UnaryOp, operand: &Expr, row: &dyn Row) -> Result<Value, EvalError> {
240 let v = evaluate(operand, row)?;
241 if v.is_null() {
242 return Ok(Value::Null);
243 }
244 match op {
245 UnaryOp::Neg => match &v {
246 Value::Integer(n) => n
247 .checked_neg()
248 .map(Value::Integer)
249 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
250 Value::BigInt(n) => n
251 .checked_neg()
252 .map(Value::BigInt)
253 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
254 Value::Float(n) => Ok(Value::Float(-*n)),
255 Value::Decimal(n) => n
256 .checked_neg()
257 .map(Value::Decimal)
258 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
259 other => Err(EvalError::UnaryMismatch {
260 op,
261 operand: other.data_type(),
262 }),
263 },
264 UnaryOp::Not => match &v {
265 Value::Boolean(b) => Ok(Value::Boolean(!b)),
266 other => Err(EvalError::UnaryMismatch {
267 op,
268 operand: other.data_type(),
269 }),
270 },
271 }
272}
273
274fn eval_binary(op: BinOp, lhs: &Expr, rhs: &Expr, row: &dyn Row) -> Result<Value, EvalError> {
275 let l = evaluate(lhs, row)?;
279 let r = evaluate(rhs, row)?;
280
281 match op {
282 BinOp::And => return three_valued_and(&l, &r),
283 BinOp::Or => return three_valued_or(&l, &r),
284 _ => {}
285 }
286
287 if l.is_null() || r.is_null() {
288 return Ok(Value::Null);
289 }
290
291 let lhs_dt = l.data_type();
292 let rhs_dt = r.data_type();
293 let (entry, coercions) =
294 coercion_spine::resolve_binop(op, lhs_dt, rhs_dt).ok_or(EvalError::OperatorMismatch {
295 op,
296 lhs: lhs_dt,
297 rhs: rhs_dt,
298 })?;
299
300 let l = match coercions.at(0) {
301 Some(target) => apply_implicit_cast(&l, lhs_dt, target)?,
302 None => l,
303 };
304 let r = match coercions.at(1) {
305 Some(target) => apply_implicit_cast(&r, rhs_dt, target)?,
306 None => r,
307 };
308
309 dispatch_binop(op, entry, l, r)
310}
311
312fn dispatch_binop(
313 op: BinOp,
314 entry: &OperatorEntry,
315 l: Value,
316 r: Value,
317) -> Result<Value, EvalError> {
318 match op {
319 BinOp::Add => arith_add(entry, l, r),
320 BinOp::Sub => arith_sub(entry, l, r),
321 BinOp::Mul => arith_mul(entry, l, r),
322 BinOp::Div => arith_div(entry, l, r),
323 BinOp::Mod => arith_mod(entry, l, r),
324 BinOp::Concat => match (&l, &r) {
325 (Value::Text(a), Value::Text(b)) => {
326 let mut s = String::with_capacity(a.len() + b.len());
327 s.push_str(a);
328 s.push_str(b);
329 Ok(Value::Text(Arc::from(s)))
330 }
331 _ => Err(EvalError::OperatorMismatch {
332 op,
333 lhs: l.data_type(),
334 rhs: r.data_type(),
335 }),
336 },
337 BinOp::Eq => Ok(Value::Boolean(values_equal(&l, &r))),
338 BinOp::Ne => Ok(Value::Boolean(!values_equal(&l, &r))),
339 BinOp::Lt => cmp_op(op, l, r, |o| o == std::cmp::Ordering::Less),
340 BinOp::Le => cmp_op(op, l, r, |o| o != std::cmp::Ordering::Greater),
341 BinOp::Gt => cmp_op(op, l, r, |o| o == std::cmp::Ordering::Greater),
342 BinOp::Ge => cmp_op(op, l, r, |o| o != std::cmp::Ordering::Less),
343 BinOp::And | BinOp::Or => unreachable!("handled before dispatch"),
344 }
345}
346
347fn arith_add(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
348 match entry.return_type {
349 DataType::Integer => match (l, r) {
350 (Value::Integer(a), Value::Integer(b)) => a
351 .checked_add(b)
352 .map(Value::Integer)
353 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Add }),
354 _ => unreachable_after_coercion("Add", DataType::Integer),
355 },
356 DataType::BigInt => match (l, r) {
357 (Value::BigInt(a), Value::BigInt(b)) => a
358 .checked_add(b)
359 .map(Value::BigInt)
360 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Add }),
361 _ => unreachable_after_coercion("Add", DataType::BigInt),
362 },
363 DataType::Float => checked_float_binop(BinOp::Add, as_f64(&l) + as_f64(&r)),
364 DataType::Decimal => match (l, r) {
365 (Value::Decimal(a), Value::Decimal(b)) => a
366 .checked_add(b)
367 .map(Value::Decimal)
368 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Add }),
369 _ => unreachable_after_coercion("Add", DataType::Decimal),
370 },
371 other => Err(EvalError::OperatorMismatch {
372 op: BinOp::Add,
373 lhs: other,
374 rhs: other,
375 }),
376 }
377}
378
379fn arith_sub(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
380 match entry.return_type {
381 DataType::Integer => match (l, r) {
382 (Value::Integer(a), Value::Integer(b)) => a
383 .checked_sub(b)
384 .map(Value::Integer)
385 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
386 _ => unreachable_after_coercion("Sub", DataType::Integer),
387 },
388 DataType::BigInt => match (l, r) {
389 (Value::BigInt(a), Value::BigInt(b)) => a
390 .checked_sub(b)
391 .map(Value::BigInt)
392 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
393 _ => unreachable_after_coercion("Sub", DataType::BigInt),
394 },
395 DataType::Float => checked_float_binop(BinOp::Sub, as_f64(&l) - as_f64(&r)),
396 DataType::Decimal => match (l, r) {
397 (Value::Decimal(a), Value::Decimal(b)) => a
398 .checked_sub(b)
399 .map(Value::Decimal)
400 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
401 _ => unreachable_after_coercion("Sub", DataType::Decimal),
402 },
403 other => Err(EvalError::OperatorMismatch {
404 op: BinOp::Sub,
405 lhs: other,
406 rhs: other,
407 }),
408 }
409}
410
411fn arith_mul(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
412 match entry.return_type {
413 DataType::Integer => match (l, r) {
414 (Value::Integer(a), Value::Integer(b)) => a
415 .checked_mul(b)
416 .map(Value::Integer)
417 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Mul }),
418 _ => unreachable_after_coercion("Mul", DataType::Integer),
419 },
420 DataType::BigInt => match (l, r) {
421 (Value::BigInt(a), Value::BigInt(b)) => a
422 .checked_mul(b)
423 .map(Value::BigInt)
424 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Mul }),
425 _ => unreachable_after_coercion("Mul", DataType::BigInt),
426 },
427 DataType::Float => checked_float_binop(BinOp::Mul, as_f64(&l) * as_f64(&r)),
428 other => Err(EvalError::OperatorMismatch {
429 op: BinOp::Mul,
430 lhs: other,
431 rhs: other,
432 }),
433 }
434}
435
436fn arith_div(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
437 match entry.return_type {
440 DataType::Float => {
441 let denom = as_f64(&r);
442 if denom == 0.0 {
443 return Err(EvalError::DivisionByZero);
444 }
445 checked_float_binop(BinOp::Div, as_f64(&l) / denom)
446 }
447 other => Err(EvalError::OperatorMismatch {
448 op: BinOp::Div,
449 lhs: other,
450 rhs: other,
451 }),
452 }
453}
454
455fn arith_mod(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
456 match entry.return_type {
457 DataType::Integer => match (l, r) {
458 (Value::Integer(_), Value::Integer(0)) => Err(EvalError::DivisionByZero),
459 (Value::Integer(a), Value::Integer(b)) => a
460 .checked_rem(b)
461 .map(Value::Integer)
462 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Mod }),
463 _ => unreachable_after_coercion("Mod", DataType::Integer),
464 },
465 DataType::BigInt => match (l, r) {
466 (Value::BigInt(_), Value::BigInt(0)) => Err(EvalError::DivisionByZero),
467 (Value::BigInt(a), Value::BigInt(b)) => a
468 .checked_rem(b)
469 .map(Value::BigInt)
470 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Mod }),
471 _ => unreachable_after_coercion("Mod", DataType::BigInt),
472 },
473 other => Err(EvalError::OperatorMismatch {
474 op: BinOp::Mod,
475 lhs: other,
476 rhs: other,
477 }),
478 }
479}
480
481fn unreachable_after_coercion(op: &'static str, expected: DataType) -> Result<Value, EvalError> {
482 Err(EvalError::OperatorMismatch {
483 op: match op {
484 "Add" => BinOp::Add,
485 "Sub" => BinOp::Sub,
486 "Mul" => BinOp::Mul,
487 "Div" => BinOp::Div,
488 "Mod" => BinOp::Mod,
489 _ => BinOp::Add,
490 },
491 lhs: expected,
492 rhs: expected,
493 })
494}
495
496fn checked_float_binop(op: BinOp, value: f64) -> Result<Value, EvalError> {
497 if value.is_finite() {
498 Ok(Value::Float(value))
499 } else {
500 Err(EvalError::InvalidNumericResult {
501 function: format!("{op:?}"),
502 reason: "result is NaN or infinite".to_string(),
503 })
504 }
505}
506
507fn as_f64(v: &Value) -> f64 {
508 match v {
509 Value::Float(x) => *x,
510 Value::Integer(x) => *x as f64,
511 Value::BigInt(x) => *x as f64,
512 Value::UnsignedInteger(x) => *x as f64,
513 Value::Decimal(x) => crate::storage::schema::decimal_to_f64(*x),
514 _ => 0.0,
515 }
516}
517
518fn cmp_op<F>(op: BinOp, l: Value, r: Value, pick: F) -> Result<Value, EvalError>
519where
520 F: Fn(std::cmp::Ordering) -> bool,
521{
522 let ord = compare_values(&l, &r).ok_or(EvalError::OperatorMismatch {
523 op,
524 lhs: l.data_type(),
525 rhs: r.data_type(),
526 })?;
527 Ok(Value::Boolean(pick(ord)))
528}
529
530fn compare_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
535 use std::cmp::Ordering;
536 match (a, b) {
537 (Value::Integer(x), Value::Integer(y)) => Some(x.cmp(y)),
538 (Value::BigInt(x), Value::BigInt(y)) => Some(x.cmp(y)),
539 (Value::Float(x), Value::Float(y)) => x.partial_cmp(y),
540 (Value::Text(x), Value::Text(y)) => Some(x.as_ref().cmp(y.as_ref())),
541 (Value::Boolean(x), Value::Boolean(y)) => Some(x.cmp(y)),
542 (Value::Timestamp(x), Value::Timestamp(y)) => Some(x.cmp(y)),
543 (Value::TimestampMs(x), Value::TimestampMs(y)) => Some(x.cmp(y)),
544 (Value::Date(x), Value::Date(y)) => Some(x.cmp(y)),
545 (Value::Time(x), Value::Time(y)) => Some(x.cmp(y)),
546 (Value::Uuid(x), Value::Uuid(y)) => Some(x.cmp(y)),
547 (Value::Decimal(x), Value::Decimal(y)) => Some(x.cmp(y)),
548 (Value::Integer(_) | Value::Float(_) | Value::BigInt(_), _) => {
552 let l = as_f64(a);
553 let r = as_f64(b);
554 l.partial_cmp(&r)
555 }
556 _ => None,
557 }
558}
559
560fn values_equal(a: &Value, b: &Value) -> bool {
561 match (a, b) {
562 (Value::Float(x), Value::Float(y)) => x == y,
563 _ => a == b,
564 }
565}
566
567fn three_valued_and(l: &Value, r: &Value) -> Result<Value, EvalError> {
568 match (l, r) {
569 (Value::Boolean(false), _) | (_, Value::Boolean(false)) => Ok(Value::Boolean(false)),
570 (Value::Boolean(true), Value::Boolean(true)) => Ok(Value::Boolean(true)),
571 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
572 _ => Err(EvalError::OperatorMismatch {
573 op: BinOp::And,
574 lhs: l.data_type(),
575 rhs: r.data_type(),
576 }),
577 }
578}
579
580fn three_valued_or(l: &Value, r: &Value) -> Result<Value, EvalError> {
581 match (l, r) {
582 (Value::Boolean(true), _) | (_, Value::Boolean(true)) => Ok(Value::Boolean(true)),
583 (Value::Boolean(false), Value::Boolean(false)) => Ok(Value::Boolean(false)),
584 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
585 _ => Err(EvalError::OperatorMismatch {
586 op: BinOp::Or,
587 lhs: l.data_type(),
588 rhs: r.data_type(),
589 }),
590 }
591}
592
593fn apply_implicit_cast(value: &Value, src: DataType, target: DataType) -> Result<Value, EvalError> {
594 if src == target {
595 return Ok(value.clone());
596 }
597 coerce_via_catalog(value, src, target, None).map_err(|reason| EvalError::ImplicitCastFailed {
598 from: src,
599 to: target,
600 reason,
601 })
602}
603
604fn eval_cast(inner: &Expr, target: DataType, row: &dyn Row) -> Result<Value, EvalError> {
605 let v = evaluate(inner, row)?;
606 if v.is_null() {
607 return Ok(Value::Null);
608 }
609 let src = v.data_type();
610 if src == target {
611 return Ok(v);
612 }
613 coerce_via_catalog(&v, src, target, None).map_err(|reason| EvalError::CastFailed {
614 from: src,
615 to: target,
616 reason,
617 })
618}
619
620fn eval_function(name: &str, args: &[Expr], row: &dyn Row) -> Result<Value, EvalError> {
621 if name.eq_ignore_ascii_case("COALESCE") {
627 for arg in args {
628 let v = evaluate(arg, row)?;
629 if !v.is_null() {
630 return Ok(v);
631 }
632 }
633 return Ok(Value::Null);
634 }
635
636 let arg_values: Vec<Value> = args
637 .iter()
638 .map(|a| evaluate(a, row))
639 .collect::<Result<Vec<_>, _>>()?;
640 let arg_types: Vec<DataType> = arg_values.iter().map(|v| v.data_type()).collect();
641
642 if arg_values.iter().any(Value::is_null)
648 && FUNCTION_CATALOG
649 .iter()
650 .any(|e| e.name.eq_ignore_ascii_case(name))
651 {
652 return Ok(Value::Null);
653 }
654
655 let (entry, coercions) =
656 coercion_spine::resolve_function(name, &arg_types).ok_or_else(|| {
657 EvalError::UnknownFunction {
658 name: name.to_string(),
659 args: arg_types.clone(),
660 }
661 })?;
662
663 let mut coerced: Vec<Value> = Vec::with_capacity(arg_values.len());
665 for (idx, value) in arg_values.into_iter().enumerate() {
666 let src = arg_types[idx];
667 match coercions.at(idx) {
668 Some(target) if src != target => {
669 coerced.push(apply_implicit_cast(&value, src, target)?);
670 }
671 _ => coerced.push(value),
672 }
673 }
674
675 if !entry.variadic && coerced.iter().any(|v| v.is_null()) {
679 return Ok(Value::Null);
680 }
681
682 dispatch_function(entry.name, &coerced)
683}
684
685fn dispatch_function(name: &str, args: &[Value]) -> Result<Value, EvalError> {
686 match name {
687 "UPPER" => match &args[0] {
688 Value::Text(s) => Ok(Value::Text(Arc::from(s.to_uppercase()))),
689 other => Err(EvalError::UnknownFunction {
690 name: name.to_string(),
691 args: vec![other.data_type()],
692 }),
693 },
694 "LOWER" => match &args[0] {
695 Value::Text(s) => Ok(Value::Text(Arc::from(s.to_lowercase()))),
696 other => Err(EvalError::UnknownFunction {
697 name: name.to_string(),
698 args: vec![other.data_type()],
699 }),
700 },
701 "TRIM" => trim_function(name, args, true, true),
702 "LTRIM" => trim_function(name, args, true, false),
703 "RTRIM" => trim_function(name, args, false, true),
704 "LENGTH" | "CHAR_LENGTH" | "CHARACTER_LENGTH" => match &args[0] {
705 Value::Text(s) => Ok(Value::Integer(s.chars().count() as i64)),
706 other => Err(EvalError::UnknownFunction {
707 name: name.to_string(),
708 args: vec![other.data_type()],
709 }),
710 },
711 "OCTET_LENGTH" => match &args[0] {
712 Value::Text(s) => Ok(Value::Integer(s.len() as i64)),
713 Value::Blob(b) => Ok(Value::Integer(b.len() as i64)),
714 other => Err(EvalError::UnknownFunction {
715 name: name.to_string(),
716 args: vec![other.data_type()],
717 }),
718 },
719 "CONCAT" => {
720 let mut out = String::new();
721 for arg in args {
722 let Value::Text(value) = arg else {
723 return Err(EvalError::UnknownFunction {
724 name: name.to_string(),
725 args: args.iter().map(Value::data_type).collect(),
726 });
727 };
728 out.push_str(value);
729 }
730 Ok(Value::Text(Arc::from(out)))
731 }
732 "JSON_PARSE" => json_parse_value(&args[0]),
733 "JSON_EXTRACT" => Ok(json_extract_value(&args[0], &args[1], false)),
734 "JSON_EXTRACT_TEXT" => Ok(json_extract_value(&args[0], &args[1], true)),
735 "CONTAINS" => Ok(contains_value(&args[0], &args[1])),
736 "ABS" => match &args[0] {
737 Value::Integer(n) => n
738 .checked_abs()
739 .map(Value::Integer)
740 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
741 Value::BigInt(n) => n
742 .checked_abs()
743 .map(Value::BigInt)
744 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
745 Value::Float(n) => Ok(Value::Float(n.abs())),
746 Value::Decimal(n) => n
747 .checked_abs()
748 .map(Value::Decimal)
749 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
750 other => Err(EvalError::UnknownFunction {
751 name: name.to_string(),
752 args: vec![other.data_type()],
753 }),
754 },
755 "SQRT" => unary_math(name, args, |x| {
756 if x < 0.0 {
757 return Err("input must be greater than or equal to zero");
758 }
759 Ok(x.sqrt())
760 }),
761 "POWER" | "POW" => binary_math(name, args, |base, exp| Ok(base.powf(exp))),
762 "EXP" => unary_math(name, args, |x| Ok(x.exp())),
763 "LN" => unary_math(name, args, |x| {
764 if x <= 0.0 {
765 return Err("input must be greater than zero");
766 }
767 Ok(x.ln())
768 }),
769 "LOG" if args.len() == 1 => unary_math(name, args, |x| {
770 if x <= 0.0 {
771 return Err("input must be greater than zero");
772 }
773 Ok(x.log10())
774 }),
775 "LOG" => binary_math(name, args, |base, x| {
776 if base <= 0.0 {
777 return Err("base must be greater than zero");
778 }
779 if base == 1.0 {
780 return Err("base must not equal one");
781 }
782 if x <= 0.0 {
783 return Err("input must be greater than zero");
784 }
785 Ok(x.log(base))
786 }),
787 "LOG10" => unary_math(name, args, |x| {
788 if x <= 0.0 {
789 return Err("input must be greater than zero");
790 }
791 Ok(x.log10())
792 }),
793 "SIN" => unary_math(name, args, |x| Ok(x.sin())),
794 "COS" => unary_math(name, args, |x| Ok(x.cos())),
795 "TAN" => unary_math(name, args, |x| Ok(x.tan())),
796 "ASIN" | "ARCSIN" => unary_math(name, args, |x| {
797 if !(-1.0..=1.0).contains(&x) {
798 return Err("input must be between -1 and 1");
799 }
800 Ok(x.asin())
801 }),
802 "ACOS" | "ARCCOS" => unary_math(name, args, |x| {
803 if !(-1.0..=1.0).contains(&x) {
804 return Err("input must be between -1 and 1");
805 }
806 Ok(x.acos())
807 }),
808 "ATAN" | "ARCTAN" => unary_math(name, args, |x| Ok(x.atan())),
809 "ATAN2" => binary_math(name, args, |y, x| Ok(y.atan2(x))),
810 "COT" => unary_math(name, args, |x| {
811 let tan = x.tan();
812 if tan == 0.0 {
813 return Err("input must not produce zero tangent");
814 }
815 Ok(1.0 / tan)
816 }),
817 "DEGREES" => unary_math(name, args, |x| Ok(x.to_degrees())),
818 "RADIANS" => unary_math(name, args, |x| Ok(x.to_radians())),
819 "PI" => checked_math_result(name, std::f64::consts::PI),
820 other => Err(EvalError::UnknownFunction {
825 name: other.to_string(),
826 args: args.iter().map(|v| v.data_type()).collect(),
827 }),
828 }
829}
830
831fn trim_function(name: &str, args: &[Value], left: bool, right: bool) -> Result<Value, EvalError> {
832 if args.len() != 1 {
833 return Err(EvalError::UnknownFunction {
834 name: name.to_string(),
835 args: args.iter().map(|v| v.data_type()).collect(),
836 });
837 }
838
839 match &args[0] {
840 Value::Text(s) => {
841 let trimmed = match (left, right) {
842 (true, true) => s.trim(),
843 (true, false) => s.trim_start(),
844 (false, true) => s.trim_end(),
845 (false, false) => s,
846 };
847 Ok(Value::Text(Arc::from(trimmed)))
848 }
849 other => Err(EvalError::UnknownFunction {
850 name: name.to_string(),
851 args: vec![other.data_type()],
852 }),
853 }
854}
855
856fn unary_math<F>(name: &str, args: &[Value], op: F) -> Result<Value, EvalError>
857where
858 F: FnOnce(f64) -> Result<f64, &'static str>,
859{
860 let input = math_arg(name, args.first(), 0)?;
861 let value = op(input).map_err(|reason| EvalError::InvalidNumericResult {
862 function: name.to_string(),
863 reason: reason.to_string(),
864 })?;
865 checked_math_result(name, value)
866}
867
868fn binary_math<F>(name: &str, args: &[Value], op: F) -> Result<Value, EvalError>
869where
870 F: FnOnce(f64, f64) -> Result<f64, &'static str>,
871{
872 let left = math_arg(name, args.first(), 0)?;
873 let right = math_arg(name, args.get(1), 1)?;
874 let value = op(left, right).map_err(|reason| EvalError::InvalidNumericResult {
875 function: name.to_string(),
876 reason: reason.to_string(),
877 })?;
878 checked_math_result(name, value)
879}
880
881fn math_arg(name: &str, value: Option<&Value>, index: usize) -> Result<f64, EvalError> {
882 let value = value.ok_or_else(|| EvalError::UnknownFunction {
883 name: name.to_string(),
884 args: Vec::new(),
885 })?;
886 let numeric = as_f64(value);
887 if numeric.is_finite() {
888 Ok(numeric)
889 } else {
890 Err(EvalError::InvalidNumericResult {
891 function: name.to_string(),
892 reason: format!("argument {index} is NaN or infinite"),
893 })
894 }
895}
896
897fn checked_math_result(name: &str, value: f64) -> Result<Value, EvalError> {
898 if value.is_finite() {
899 Ok(Value::Float(value))
900 } else {
901 Err(EvalError::InvalidNumericResult {
902 function: name.to_string(),
903 reason: "result is NaN or infinite".to_string(),
904 })
905 }
906}
907
908fn json_extract_value(input: &Value, path: &Value, as_text: bool) -> Value {
909 let Value::Text(path) = path else {
910 return Value::Null;
911 };
912 let Some(json) = value_to_json(input) else {
913 return Value::Null;
914 };
915 let Some(steps) = parse_json_path(path) else {
916 return Value::Null;
917 };
918 let Some(target) = json_path_get(&json, &steps) else {
919 return Value::Null;
920 };
921
922 if as_text {
923 match target {
924 crate::serde_json::Value::String(value) => Value::text(value.clone()),
925 crate::serde_json::Value::Null => Value::Null,
926 crate::serde_json::Value::Bool(value) => Value::text(value.to_string()),
927 crate::serde_json::Value::Integer(value) => Value::text(value.to_string()),
928 crate::serde_json::Value::Number(value) => Value::text(value.to_string()),
929 other => Value::text(other.to_string_compact()),
930 }
931 } else {
932 Value::text(target.to_string_compact())
933 }
934}
935
936fn json_parse_value(input: &Value) -> Result<Value, EvalError> {
937 let Value::Text(text) = input else {
938 return Err(EvalError::UnknownFunction {
939 name: "JSON_PARSE".to_string(),
940 args: vec![input.data_type()],
941 });
942 };
943 crate::serde_json::from_str::<crate::serde_json::Value>(text)
944 .map(|json| Value::Json(json.to_string_compact().into_bytes()))
945 .map_err(|err| EvalError::JsonParseFailed {
946 reason: err.to_string(),
947 })
948}
949
950fn contains_value(input: &Value, needle: &Value) -> Value {
951 let Value::Text(needle) = needle else {
952 return Value::Null;
953 };
954 Value::Boolean(value_contains(input, needle))
955}
956
957fn value_contains(value: &Value, needle: &str) -> bool {
958 match value {
959 Value::Array(values) => values.iter().any(|value| value_contains(value, needle)),
960 Value::Json(_) => value_to_json(value)
961 .as_ref()
962 .is_some_and(|json| json_value_contains(json, needle)),
963 Value::Text(value) => value.contains(needle),
964 other => other.display_string().contains(needle),
965 }
966}
967
968fn json_value_contains(value: &crate::serde_json::Value, needle: &str) -> bool {
969 match value {
970 crate::serde_json::Value::Array(values) => values
971 .iter()
972 .any(|value| json_value_contains(value, needle)),
973 crate::serde_json::Value::Object(map) => {
979 map.values().any(|value| json_value_contains(value, needle))
980 }
981 crate::serde_json::Value::String(value) => value == needle,
982 crate::serde_json::Value::Integer(value) => value.to_string() == needle,
983 crate::serde_json::Value::Number(value) => value.to_string() == needle,
984 crate::serde_json::Value::Decimal(value) => value == needle,
985 crate::serde_json::Value::Bool(value) => value.to_string() == needle,
986 crate::serde_json::Value::Null => false,
987 }
988}
989
990fn value_to_json(value: &Value) -> Option<crate::serde_json::Value> {
991 match value {
992 Value::Null => Some(crate::serde_json::Value::Null),
993 Value::Json(bytes) => crate::document_body::decode_container_to_json(bytes)
997 .or_else(|| crate::serde_json::from_slice(bytes).ok()),
998 Value::Text(value) => crate::serde_json::from_str(value).ok(),
999 _ => None,
1000 }
1001}
1002
1003enum JsonPathStep<'a> {
1004 Field(&'a str),
1005 Index(usize),
1006}
1007
1008fn parse_json_path(path: &str) -> Option<Vec<JsonPathStep<'_>>> {
1009 let path = path.trim();
1010 let rest = path.strip_prefix('$').unwrap_or(path);
1011 let mut steps = Vec::new();
1012 let bytes = rest.as_bytes();
1013 let mut index = 0;
1014 while index < bytes.len() {
1015 match bytes[index] {
1016 b'.' => {
1017 index += 1;
1018 let start = index;
1019 while index < bytes.len() && bytes[index] != b'.' && bytes[index] != b'[' {
1020 index += 1;
1021 }
1022 if start == index {
1023 return None;
1024 }
1025 steps.push(JsonPathStep::Field(
1026 std::str::from_utf8(&bytes[start..index]).ok()?,
1027 ));
1028 }
1029 b'[' => {
1030 index += 1;
1031 let start = index;
1032 while index < bytes.len() && bytes[index] != b']' {
1033 index += 1;
1034 }
1035 if index >= bytes.len() {
1036 return None;
1037 }
1038 steps.push(JsonPathStep::Index(
1039 std::str::from_utf8(&bytes[start..index])
1040 .ok()?
1041 .parse()
1042 .ok()?,
1043 ));
1044 index += 1;
1045 }
1046 _ => return None,
1047 }
1048 }
1049 Some(steps)
1050}
1051
1052fn json_path_get<'a>(
1053 root: &'a crate::serde_json::Value,
1054 steps: &[JsonPathStep<'_>],
1055) -> Option<&'a crate::serde_json::Value> {
1056 let mut current = root;
1057 for step in steps {
1058 current = match (step, current) {
1059 (JsonPathStep::Field(name), crate::serde_json::Value::Object(map)) => map.get(*name)?,
1060 (JsonPathStep::Index(index), crate::serde_json::Value::Array(values)) => {
1061 values.get(*index)?
1062 }
1063 _ => return None,
1064 };
1065 }
1066 Some(current)
1067}
1068
1069fn eval_case(
1070 branches: &[(Expr, Expr)],
1071 else_: Option<&Expr>,
1072 row: &dyn Row,
1073) -> Result<Value, EvalError> {
1074 for (cond, value) in branches {
1075 let c = evaluate(cond, row)?;
1076 if matches!(c, Value::Boolean(true)) {
1077 return evaluate(value, row);
1078 }
1079 }
1080 match else_ {
1081 Some(e) => evaluate(e, row),
1082 None => Ok(Value::Null),
1083 }
1084}
1085
1086fn eval_in_list(
1087 target: &Expr,
1088 values: &[Expr],
1089 negated: bool,
1090 row: &dyn Row,
1091) -> Result<Value, EvalError> {
1092 if values.is_empty() {
1093 return Err(EvalError::EmptyInList);
1094 }
1095 let needle = evaluate(target, row)?;
1096 if needle.is_null() {
1097 return Ok(Value::Null);
1098 }
1099 let mut saw_null = false;
1100 for v in values {
1101 let candidate = evaluate(v, row)?;
1102 if candidate.is_null() {
1103 saw_null = true;
1104 continue;
1105 }
1106 if values_equal(&needle, &candidate) {
1107 return Ok(Value::Boolean(!negated));
1108 }
1109 }
1110 if saw_null {
1111 return Ok(Value::Null);
1112 }
1113 Ok(Value::Boolean(negated))
1114}
1115
1116fn eval_between(
1117 target: &Expr,
1118 low: &Expr,
1119 high: &Expr,
1120 negated: bool,
1121 row: &dyn Row,
1122) -> Result<Value, EvalError> {
1123 let v = evaluate(target, row)?;
1124 let lo = evaluate(low, row)?;
1125 let hi = evaluate(high, row)?;
1126 if v.is_null() || lo.is_null() || hi.is_null() {
1127 return Ok(Value::Null);
1128 }
1129 let lo_ok = compare_values(&v, &lo)
1130 .ok_or(EvalError::OperatorMismatch {
1131 op: BinOp::Ge,
1132 lhs: v.data_type(),
1133 rhs: lo.data_type(),
1134 })
1135 .map(|o| o != std::cmp::Ordering::Less)?;
1136 let hi_ok = compare_values(&v, &hi)
1137 .ok_or(EvalError::OperatorMismatch {
1138 op: BinOp::Le,
1139 lhs: v.data_type(),
1140 rhs: hi.data_type(),
1141 })
1142 .map(|o| o != std::cmp::Ordering::Greater)?;
1143 let inside = lo_ok && hi_ok;
1144 Ok(Value::Boolean(if negated { !inside } else { inside }))
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149 use super::*;
1150 use crate::storage::query::ast::Span;
1151
1152 fn lit(v: Value) -> Expr {
1153 Expr::Literal {
1154 value: v,
1155 span: Span::synthetic(),
1156 }
1157 }
1158
1159 fn binop(op: BinOp, l: Expr, r: Expr) -> Expr {
1160 Expr::BinaryOp {
1161 op,
1162 lhs: Box::new(l),
1163 rhs: Box::new(r),
1164 span: Span::synthetic(),
1165 }
1166 }
1167
1168 fn empty_row() -> impl Row {
1169 |_field: &FieldRef| -> Option<Value> { None }
1170 }
1171
1172 #[test]
1173 fn contains_descends_into_json_object_values() {
1174 let detections = br#"[{"label":"person","confidence":0.9,"bbox":[0,0,1,1]},
1178 {"label":"car","confidence":0.7,"bbox":[1,1,2,2]}]"#;
1179 let json = Value::Json(detections.to_vec());
1180 assert!(value_contains(&json, "person"));
1181 assert!(value_contains(&json, "car"));
1182 assert!(!value_contains(&json, "bicycle"));
1183 assert!(!value_contains(&json, "label"));
1185 }
1186
1187 #[test]
1188 fn integer_addition_overflow_surfaces_as_eval_error() {
1189 let expr = binop(
1190 BinOp::Add,
1191 lit(Value::Integer(i64::MAX)),
1192 lit(Value::Integer(1)),
1193 );
1194 let err = evaluate(&expr, &empty_row()).unwrap_err();
1195 assert_eq!(err, EvalError::ArithmeticOverflow { op: BinOp::Add });
1196 }
1197
1198 #[test]
1199 fn integer_multiplication_overflow_surfaces_as_eval_error() {
1200 let expr = binop(
1201 BinOp::Mul,
1202 lit(Value::Integer(i64::MAX)),
1203 lit(Value::Integer(2)),
1204 );
1205 let err = evaluate(&expr, &empty_row()).unwrap_err();
1206 assert_eq!(err, EvalError::ArithmeticOverflow { op: BinOp::Mul });
1207 }
1208
1209 #[test]
1210 fn integer_subtraction_overflow_surfaces_as_eval_error() {
1211 let expr = binop(
1212 BinOp::Sub,
1213 lit(Value::Integer(i64::MIN)),
1214 lit(Value::Integer(1)),
1215 );
1216 let err = evaluate(&expr, &empty_row()).unwrap_err();
1217 assert_eq!(err, EvalError::ArithmeticOverflow { op: BinOp::Sub });
1218 }
1219
1220 #[test]
1221 fn unary_neg_overflow_on_min_int_surfaces_as_eval_error() {
1222 let expr = Expr::UnaryOp {
1223 op: UnaryOp::Neg,
1224 operand: Box::new(lit(Value::Integer(i64::MIN))),
1225 span: Span::synthetic(),
1226 };
1227 let err = evaluate(&expr, &empty_row()).unwrap_err();
1228 assert_eq!(err, EvalError::ArithmeticOverflow { op: BinOp::Sub });
1229 }
1230
1231 #[test]
1232 fn null_propagates_through_arithmetic() {
1233 let expr = binop(BinOp::Add, lit(Value::Null), lit(Value::Integer(7)));
1234 let v = evaluate(&expr, &empty_row()).unwrap();
1235 assert_eq!(v, Value::Null);
1236 }
1237
1238 #[test]
1239 fn null_propagates_through_comparison() {
1240 let expr = binop(BinOp::Lt, lit(Value::Integer(1)), lit(Value::Null));
1241 let v = evaluate(&expr, &empty_row()).unwrap();
1242 assert_eq!(v, Value::Null);
1243 }
1244
1245 #[test]
1246 fn null_propagates_through_concat() {
1247 let expr = binop(
1248 BinOp::Concat,
1249 lit(Value::Text(Arc::from("hi"))),
1250 lit(Value::Null),
1251 );
1252 let v = evaluate(&expr, &empty_row()).unwrap();
1253 assert_eq!(v, Value::Null);
1254 }
1255
1256 #[test]
1257 fn three_valued_and_returns_false_when_one_side_false_even_with_null() {
1258 let expr = binop(BinOp::And, lit(Value::Null), lit(Value::Boolean(false)));
1259 let v = evaluate(&expr, &empty_row()).unwrap();
1260 assert_eq!(v, Value::Boolean(false));
1261 }
1262
1263 #[test]
1264 fn three_valued_or_returns_true_when_one_side_true_even_with_null() {
1265 let expr = binop(BinOp::Or, lit(Value::Null), lit(Value::Boolean(true)));
1266 let v = evaluate(&expr, &empty_row()).unwrap();
1267 assert_eq!(v, Value::Boolean(true));
1268 }
1269
1270 #[test]
1271 fn three_valued_and_returns_null_for_null_and_true() {
1272 let expr = binop(BinOp::And, lit(Value::Null), lit(Value::Boolean(true)));
1273 let v = evaluate(&expr, &empty_row()).unwrap();
1274 assert_eq!(v, Value::Null);
1275 }
1276
1277 #[test]
1278 fn implicit_cast_triggers_for_decimal_plus_integer() {
1279 let expr = binop(
1288 BinOp::Add,
1289 lit(Value::Decimal(10000)),
1290 lit(Value::Integer(2)),
1291 );
1292 let v = evaluate(&expr, &empty_row()).unwrap();
1293 assert_eq!(v, Value::Decimal(30000));
1294 }
1295
1296 #[test]
1297 fn integer_plus_bigint_resolves_to_preferred_float_overload() {
1298 let expr = binop(
1304 BinOp::Add,
1305 lit(Value::Integer(5)),
1306 lit(Value::BigInt(40_000_000_000)),
1307 );
1308 let v = evaluate(&expr, &empty_row()).unwrap();
1309 assert_eq!(v, Value::Float(40_000_000_005.0));
1310 }
1311
1312 #[test]
1313 fn implicit_cast_promotes_integer_to_float_for_float_addition() {
1314 let expr = binop(BinOp::Add, lit(Value::Integer(2)), lit(Value::Float(0.5)));
1318 let v = evaluate(&expr, &empty_row()).unwrap();
1319 assert_eq!(v, Value::Float(2.5));
1320 }
1321
1322 #[test]
1323 fn integer_division_promotes_to_float() {
1324 let expr = binop(BinOp::Div, lit(Value::Integer(7)), lit(Value::Integer(2)));
1325 let v = evaluate(&expr, &empty_row()).unwrap();
1326 assert_eq!(v, Value::Float(3.5));
1327 }
1328
1329 #[test]
1330 fn division_by_zero_is_eval_error() {
1331 let expr = binop(BinOp::Div, lit(Value::Integer(7)), lit(Value::Integer(0)));
1332 let err = evaluate(&expr, &empty_row()).unwrap_err();
1333 assert_eq!(err, EvalError::DivisionByZero);
1334 }
1335
1336 #[test]
1337 fn unknown_function_surfaces_as_eval_error() {
1338 let expr = Expr::FunctionCall {
1339 name: "no_such_fn".to_string(),
1340 args: vec![lit(Value::Integer(1))],
1341 span: Span::synthetic(),
1342 };
1343 let err = evaluate(&expr, &empty_row()).unwrap_err();
1344 match err {
1345 EvalError::UnknownFunction { name, args } => {
1346 assert_eq!(name, "no_such_fn");
1347 assert_eq!(args, vec![DataType::Integer]);
1348 }
1349 other => panic!("expected UnknownFunction, got {other:?}"),
1350 }
1351 }
1352
1353 #[test]
1354 fn coalesce_returns_first_non_null() {
1355 let expr = Expr::FunctionCall {
1356 name: "COALESCE".to_string(),
1357 args: vec![
1358 lit(Value::Null),
1359 lit(Value::Null),
1360 lit(Value::Integer(42)),
1361 lit(Value::Integer(99)),
1362 ],
1363 span: Span::synthetic(),
1364 };
1365 let v = evaluate(&expr, &empty_row()).unwrap();
1366 assert_eq!(v, Value::Integer(42));
1367 }
1368
1369 #[test]
1370 fn coalesce_all_null_returns_null() {
1371 let expr = Expr::FunctionCall {
1372 name: "COALESCE".to_string(),
1373 args: vec![lit(Value::Null), lit(Value::Null)],
1374 span: Span::synthetic(),
1375 };
1376 let v = evaluate(&expr, &empty_row()).unwrap();
1377 assert_eq!(v, Value::Null);
1378 }
1379
1380 #[test]
1381 fn upper_lower_dispatch_through_function_catalog() {
1382 let expr = Expr::FunctionCall {
1383 name: "UPPER".to_string(),
1384 args: vec![lit(Value::Text(Arc::from("hello")))],
1385 span: Span::synthetic(),
1386 };
1387 let v = evaluate(&expr, &empty_row()).unwrap();
1388 assert_eq!(v, Value::Text(Arc::from("HELLO")));
1389 }
1390
1391 #[test]
1392 fn length_of_null_propagates() {
1393 let expr = Expr::FunctionCall {
1394 name: "LENGTH".to_string(),
1395 args: vec![lit(Value::Null)],
1396 span: Span::synthetic(),
1397 };
1398 let v = evaluate(&expr, &empty_row()).unwrap();
1399 assert_eq!(v, Value::Null);
1400 }
1401
1402 #[test]
1403 fn case_when_picks_first_true_branch() {
1404 let expr = Expr::Case {
1405 branches: vec![
1406 (lit(Value::Boolean(false)), lit(Value::Integer(1))),
1407 (lit(Value::Boolean(true)), lit(Value::Integer(2))),
1408 (lit(Value::Boolean(true)), lit(Value::Integer(3))),
1409 ],
1410 else_: Some(Box::new(lit(Value::Integer(99)))),
1411 span: Span::synthetic(),
1412 };
1413 let v = evaluate(&expr, &empty_row()).unwrap();
1414 assert_eq!(v, Value::Integer(2));
1415 }
1416
1417 #[test]
1418 fn case_falls_through_to_else_when_no_branch_matches() {
1419 let expr = Expr::Case {
1420 branches: vec![(lit(Value::Boolean(false)), lit(Value::Integer(1)))],
1421 else_: Some(Box::new(lit(Value::Integer(99)))),
1422 span: Span::synthetic(),
1423 };
1424 let v = evaluate(&expr, &empty_row()).unwrap();
1425 assert_eq!(v, Value::Integer(99));
1426 }
1427
1428 #[test]
1429 fn case_returns_null_when_no_branch_matches_and_no_else() {
1430 let expr = Expr::Case {
1431 branches: vec![(lit(Value::Boolean(false)), lit(Value::Integer(1)))],
1432 else_: None,
1433 span: Span::synthetic(),
1434 };
1435 let v = evaluate(&expr, &empty_row()).unwrap();
1436 assert_eq!(v, Value::Null);
1437 }
1438
1439 #[test]
1440 fn is_null_handles_null_and_non_null() {
1441 let null_expr = Expr::IsNull {
1442 operand: Box::new(lit(Value::Null)),
1443 negated: false,
1444 span: Span::synthetic(),
1445 };
1446 assert_eq!(
1447 evaluate(&null_expr, &empty_row()).unwrap(),
1448 Value::Boolean(true)
1449 );
1450
1451 let non_null_expr = Expr::IsNull {
1452 operand: Box::new(lit(Value::Integer(7))),
1453 negated: false,
1454 span: Span::synthetic(),
1455 };
1456 assert_eq!(
1457 evaluate(&non_null_expr, &empty_row()).unwrap(),
1458 Value::Boolean(false)
1459 );
1460 }
1461
1462 #[test]
1463 fn between_inclusive_bounds() {
1464 let expr = Expr::Between {
1465 target: Box::new(lit(Value::Integer(5))),
1466 low: Box::new(lit(Value::Integer(1))),
1467 high: Box::new(lit(Value::Integer(10))),
1468 negated: false,
1469 span: Span::synthetic(),
1470 };
1471 assert_eq!(evaluate(&expr, &empty_row()).unwrap(), Value::Boolean(true));
1472 }
1473
1474 #[test]
1475 fn in_list_match_and_miss() {
1476 let hit = Expr::InList {
1477 target: Box::new(lit(Value::Integer(2))),
1478 values: vec![
1479 lit(Value::Integer(1)),
1480 lit(Value::Integer(2)),
1481 lit(Value::Integer(3)),
1482 ],
1483 negated: false,
1484 span: Span::synthetic(),
1485 };
1486 assert_eq!(evaluate(&hit, &empty_row()).unwrap(), Value::Boolean(true));
1487
1488 let miss = Expr::InList {
1489 target: Box::new(lit(Value::Integer(99))),
1490 values: vec![lit(Value::Integer(1)), lit(Value::Integer(2))],
1491 negated: false,
1492 span: Span::synthetic(),
1493 };
1494 assert_eq!(
1495 evaluate(&miss, &empty_row()).unwrap(),
1496 Value::Boolean(false)
1497 );
1498 }
1499
1500 #[test]
1501 fn column_lookup_walks_field_ref() {
1502 let row = |field: &FieldRef| -> Option<Value> {
1503 match field {
1504 FieldRef::TableColumn { table, column } if table == "t" && column == "x" => {
1505 Some(Value::Integer(11))
1506 }
1507 _ => None,
1508 }
1509 };
1510 let expr = Expr::Column {
1511 field: FieldRef::TableColumn {
1512 table: "t".to_string(),
1513 column: "x".to_string(),
1514 },
1515 span: Span::synthetic(),
1516 };
1517 assert_eq!(evaluate(&expr, &row).unwrap(), Value::Integer(11));
1518 }
1519
1520 #[test]
1521 fn missing_column_surfaces_unknown_column() {
1522 let row = |_: &FieldRef| -> Option<Value> { None };
1523 let expr = Expr::Column {
1524 field: FieldRef::TableColumn {
1525 table: "t".to_string(),
1526 column: "missing".to_string(),
1527 },
1528 span: Span::synthetic(),
1529 };
1530 let err = evaluate(&expr, &row).unwrap_err();
1531 match err {
1532 EvalError::UnknownColumn(_) => {}
1533 other => panic!("expected UnknownColumn, got {other:?}"),
1534 }
1535 }
1536
1537 #[test]
1538 fn parameter_without_bind_is_eval_error() {
1539 let expr = Expr::Parameter {
1540 index: 1,
1541 span: Span::synthetic(),
1542 };
1543 let err = evaluate(&expr, &empty_row()).unwrap_err();
1544 assert_eq!(err, EvalError::UnboundParameter(1));
1545 }
1546
1547 #[test]
1548 fn cast_integer_to_text_uses_explicit_path() {
1549 let expr = Expr::Cast {
1550 inner: Box::new(lit(Value::Integer(42))),
1551 target: DataType::Text,
1552 span: Span::synthetic(),
1553 };
1554 assert_eq!(
1555 evaluate(&expr, &empty_row()).unwrap(),
1556 Value::Text(Arc::from("42"))
1557 );
1558 }
1559
1560 #[test]
1561 fn concat_returns_text() {
1562 let expr = binop(
1563 BinOp::Concat,
1564 lit(Value::Text(Arc::from("foo"))),
1565 lit(Value::Text(Arc::from("bar"))),
1566 );
1567 assert_eq!(
1568 evaluate(&expr, &empty_row()).unwrap(),
1569 Value::Text(Arc::from("foobar"))
1570 );
1571 }
1572}