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 EmptyInList,
144}
145
146impl std::fmt::Display for EvalError {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 match self {
149 EvalError::UnknownColumn(field) => write!(f, "unknown column: {field:?}"),
150 EvalError::UnboundParameter(idx) => {
151 write!(f, "unbound query parameter: ${idx}")
152 }
153 EvalError::OperatorMismatch { op, lhs, rhs } => {
154 write!(f, "operator {op:?} not defined for ({lhs:?}, {rhs:?})")
155 }
156 EvalError::UnaryMismatch { op, operand } => {
157 write!(f, "unary {op:?} not defined for {operand:?}")
158 }
159 EvalError::UnknownFunction { name, args } => {
160 write!(f, "unknown function: {name}({args:?})")
161 }
162 EvalError::ImplicitCastFailed { from, to, reason } => {
163 write!(f, "implicit cast {from:?} -> {to:?} failed: {reason}")
164 }
165 EvalError::CastFailed { from, to, reason } => {
166 write!(f, "cast {from:?} -> {to:?} failed: {reason}")
167 }
168 EvalError::ArithmeticOverflow { op } => {
169 write!(f, "arithmetic overflow in {op:?}")
170 }
171 EvalError::DivisionByZero => write!(f, "division by zero"),
172 EvalError::EmptyInList => write!(f, "IN list is empty"),
173 }
174 }
175}
176
177impl std::error::Error for EvalError {}
178
179pub fn evaluate(expr: &Expr, row: &dyn Row) -> Result<Value, EvalError> {
184 match expr {
185 Expr::Literal { value, .. } => Ok(value.clone()),
186 Expr::Column { field, .. } => row
187 .get(field)
188 .ok_or_else(|| EvalError::UnknownColumn(field.clone())),
189 Expr::Parameter { index, .. } => Err(EvalError::UnboundParameter(*index)),
190 Expr::UnaryOp { op, operand, .. } => eval_unary(*op, operand, row),
191 Expr::BinaryOp { op, lhs, rhs, .. } => eval_binary(*op, lhs, rhs, row),
192 Expr::Cast { inner, target, .. } => eval_cast(inner, *target, row),
193 Expr::FunctionCall { name, args, .. } => eval_function(name, args, row),
194 Expr::Case {
195 branches, else_, ..
196 } => eval_case(branches, else_.as_deref(), row),
197 Expr::IsNull {
198 operand, negated, ..
199 } => {
200 let v = evaluate(operand, row)?;
201 let is_null = v.is_null();
202 Ok(Value::Boolean(if *negated { !is_null } else { is_null }))
203 }
204 Expr::InList {
205 target,
206 values,
207 negated,
208 ..
209 } => eval_in_list(target, values, *negated, row),
210 Expr::Between {
211 target,
212 low,
213 high,
214 negated,
215 ..
216 } => eval_between(target, low, high, *negated, row),
217 }
218}
219
220fn eval_unary(op: UnaryOp, operand: &Expr, row: &dyn Row) -> Result<Value, EvalError> {
221 let v = evaluate(operand, row)?;
222 if v.is_null() {
223 return Ok(Value::Null);
224 }
225 match op {
226 UnaryOp::Neg => match &v {
227 Value::Integer(n) => n
228 .checked_neg()
229 .map(Value::Integer)
230 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
231 Value::BigInt(n) => n
232 .checked_neg()
233 .map(Value::BigInt)
234 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
235 Value::Float(n) => Ok(Value::Float(-*n)),
236 Value::Decimal(n) => n
237 .checked_neg()
238 .map(Value::Decimal)
239 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
240 other => Err(EvalError::UnaryMismatch {
241 op,
242 operand: other.data_type(),
243 }),
244 },
245 UnaryOp::Not => match &v {
246 Value::Boolean(b) => Ok(Value::Boolean(!b)),
247 other => Err(EvalError::UnaryMismatch {
248 op,
249 operand: other.data_type(),
250 }),
251 },
252 }
253}
254
255fn eval_binary(op: BinOp, lhs: &Expr, rhs: &Expr, row: &dyn Row) -> Result<Value, EvalError> {
256 let l = evaluate(lhs, row)?;
260 let r = evaluate(rhs, row)?;
261
262 match op {
263 BinOp::And => return three_valued_and(&l, &r),
264 BinOp::Or => return three_valued_or(&l, &r),
265 _ => {}
266 }
267
268 if l.is_null() || r.is_null() {
269 return Ok(Value::Null);
270 }
271
272 let lhs_dt = l.data_type();
273 let rhs_dt = r.data_type();
274 let (entry, coercions) =
275 coercion_spine::resolve_binop(op, lhs_dt, rhs_dt).ok_or(EvalError::OperatorMismatch {
276 op,
277 lhs: lhs_dt,
278 rhs: rhs_dt,
279 })?;
280
281 let l = match coercions.at(0) {
282 Some(target) => apply_implicit_cast(&l, lhs_dt, target)?,
283 None => l,
284 };
285 let r = match coercions.at(1) {
286 Some(target) => apply_implicit_cast(&r, rhs_dt, target)?,
287 None => r,
288 };
289
290 dispatch_binop(op, entry, l, r)
291}
292
293fn dispatch_binop(
294 op: BinOp,
295 entry: &OperatorEntry,
296 l: Value,
297 r: Value,
298) -> Result<Value, EvalError> {
299 match op {
300 BinOp::Add => arith_add(entry, l, r),
301 BinOp::Sub => arith_sub(entry, l, r),
302 BinOp::Mul => arith_mul(entry, l, r),
303 BinOp::Div => arith_div(entry, l, r),
304 BinOp::Mod => arith_mod(entry, l, r),
305 BinOp::Concat => match (&l, &r) {
306 (Value::Text(a), Value::Text(b)) => {
307 let mut s = String::with_capacity(a.len() + b.len());
308 s.push_str(a);
309 s.push_str(b);
310 Ok(Value::Text(Arc::from(s)))
311 }
312 _ => Err(EvalError::OperatorMismatch {
313 op,
314 lhs: l.data_type(),
315 rhs: r.data_type(),
316 }),
317 },
318 BinOp::Eq => Ok(Value::Boolean(values_equal(&l, &r))),
319 BinOp::Ne => Ok(Value::Boolean(!values_equal(&l, &r))),
320 BinOp::Lt => cmp_op(op, l, r, |o| o == std::cmp::Ordering::Less),
321 BinOp::Le => cmp_op(op, l, r, |o| o != std::cmp::Ordering::Greater),
322 BinOp::Gt => cmp_op(op, l, r, |o| o == std::cmp::Ordering::Greater),
323 BinOp::Ge => cmp_op(op, l, r, |o| o != std::cmp::Ordering::Less),
324 BinOp::And | BinOp::Or => unreachable!("handled before dispatch"),
325 }
326}
327
328fn arith_add(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
329 match entry.return_type {
330 DataType::Integer => match (l, r) {
331 (Value::Integer(a), Value::Integer(b)) => a
332 .checked_add(b)
333 .map(Value::Integer)
334 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Add }),
335 _ => unreachable_after_coercion("Add", DataType::Integer),
336 },
337 DataType::BigInt => match (l, r) {
338 (Value::BigInt(a), Value::BigInt(b)) => a
339 .checked_add(b)
340 .map(Value::BigInt)
341 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Add }),
342 _ => unreachable_after_coercion("Add", DataType::BigInt),
343 },
344 DataType::Float => Ok(Value::Float(as_f64(&l) + as_f64(&r))),
345 DataType::Decimal => match (l, r) {
346 (Value::Decimal(a), Value::Decimal(b)) => a
347 .checked_add(b)
348 .map(Value::Decimal)
349 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Add }),
350 _ => unreachable_after_coercion("Add", DataType::Decimal),
351 },
352 other => Err(EvalError::OperatorMismatch {
353 op: BinOp::Add,
354 lhs: other,
355 rhs: other,
356 }),
357 }
358}
359
360fn arith_sub(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
361 match entry.return_type {
362 DataType::Integer => match (l, r) {
363 (Value::Integer(a), Value::Integer(b)) => a
364 .checked_sub(b)
365 .map(Value::Integer)
366 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
367 _ => unreachable_after_coercion("Sub", DataType::Integer),
368 },
369 DataType::BigInt => match (l, r) {
370 (Value::BigInt(a), Value::BigInt(b)) => a
371 .checked_sub(b)
372 .map(Value::BigInt)
373 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
374 _ => unreachable_after_coercion("Sub", DataType::BigInt),
375 },
376 DataType::Float => Ok(Value::Float(as_f64(&l) - as_f64(&r))),
377 DataType::Decimal => match (l, r) {
378 (Value::Decimal(a), Value::Decimal(b)) => a
379 .checked_sub(b)
380 .map(Value::Decimal)
381 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
382 _ => unreachable_after_coercion("Sub", DataType::Decimal),
383 },
384 other => Err(EvalError::OperatorMismatch {
385 op: BinOp::Sub,
386 lhs: other,
387 rhs: other,
388 }),
389 }
390}
391
392fn arith_mul(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
393 match entry.return_type {
394 DataType::Integer => match (l, r) {
395 (Value::Integer(a), Value::Integer(b)) => a
396 .checked_mul(b)
397 .map(Value::Integer)
398 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Mul }),
399 _ => unreachable_after_coercion("Mul", DataType::Integer),
400 },
401 DataType::BigInt => match (l, r) {
402 (Value::BigInt(a), Value::BigInt(b)) => a
403 .checked_mul(b)
404 .map(Value::BigInt)
405 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Mul }),
406 _ => unreachable_after_coercion("Mul", DataType::BigInt),
407 },
408 DataType::Float => Ok(Value::Float(as_f64(&l) * as_f64(&r))),
409 other => Err(EvalError::OperatorMismatch {
410 op: BinOp::Mul,
411 lhs: other,
412 rhs: other,
413 }),
414 }
415}
416
417fn arith_div(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
418 match entry.return_type {
421 DataType::Float => {
422 let denom = as_f64(&r);
423 if denom == 0.0 {
424 return Err(EvalError::DivisionByZero);
425 }
426 Ok(Value::Float(as_f64(&l) / denom))
427 }
428 other => Err(EvalError::OperatorMismatch {
429 op: BinOp::Div,
430 lhs: other,
431 rhs: other,
432 }),
433 }
434}
435
436fn arith_mod(entry: &OperatorEntry, l: Value, r: Value) -> Result<Value, EvalError> {
437 match entry.return_type {
438 DataType::Integer => match (l, r) {
439 (Value::Integer(_), Value::Integer(0)) => Err(EvalError::DivisionByZero),
440 (Value::Integer(a), Value::Integer(b)) => a
441 .checked_rem(b)
442 .map(Value::Integer)
443 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Mod }),
444 _ => unreachable_after_coercion("Mod", DataType::Integer),
445 },
446 DataType::BigInt => match (l, r) {
447 (Value::BigInt(_), Value::BigInt(0)) => Err(EvalError::DivisionByZero),
448 (Value::BigInt(a), Value::BigInt(b)) => a
449 .checked_rem(b)
450 .map(Value::BigInt)
451 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Mod }),
452 _ => unreachable_after_coercion("Mod", DataType::BigInt),
453 },
454 other => Err(EvalError::OperatorMismatch {
455 op: BinOp::Mod,
456 lhs: other,
457 rhs: other,
458 }),
459 }
460}
461
462fn unreachable_after_coercion(op: &'static str, expected: DataType) -> Result<Value, EvalError> {
463 Err(EvalError::OperatorMismatch {
464 op: match op {
465 "Add" => BinOp::Add,
466 "Sub" => BinOp::Sub,
467 "Mul" => BinOp::Mul,
468 "Div" => BinOp::Div,
469 "Mod" => BinOp::Mod,
470 _ => BinOp::Add,
471 },
472 lhs: expected,
473 rhs: expected,
474 })
475}
476
477fn as_f64(v: &Value) -> f64 {
478 match v {
479 Value::Float(x) => *x,
480 Value::Integer(x) => *x as f64,
481 Value::BigInt(x) => *x as f64,
482 Value::UnsignedInteger(x) => *x as f64,
483 Value::Decimal(x) => *x as f64,
484 _ => 0.0,
485 }
486}
487
488fn cmp_op<F>(op: BinOp, l: Value, r: Value, pick: F) -> Result<Value, EvalError>
489where
490 F: Fn(std::cmp::Ordering) -> bool,
491{
492 let ord = compare_values(&l, &r).ok_or(EvalError::OperatorMismatch {
493 op,
494 lhs: l.data_type(),
495 rhs: r.data_type(),
496 })?;
497 Ok(Value::Boolean(pick(ord)))
498}
499
500fn compare_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
505 use std::cmp::Ordering;
506 match (a, b) {
507 (Value::Integer(x), Value::Integer(y)) => Some(x.cmp(y)),
508 (Value::BigInt(x), Value::BigInt(y)) => Some(x.cmp(y)),
509 (Value::Float(x), Value::Float(y)) => x.partial_cmp(y),
510 (Value::Text(x), Value::Text(y)) => Some(x.as_ref().cmp(y.as_ref())),
511 (Value::Boolean(x), Value::Boolean(y)) => Some(x.cmp(y)),
512 (Value::Timestamp(x), Value::Timestamp(y)) => Some(x.cmp(y)),
513 (Value::TimestampMs(x), Value::TimestampMs(y)) => Some(x.cmp(y)),
514 (Value::Date(x), Value::Date(y)) => Some(x.cmp(y)),
515 (Value::Time(x), Value::Time(y)) => Some(x.cmp(y)),
516 (Value::Uuid(x), Value::Uuid(y)) => Some(x.cmp(y)),
517 (Value::Decimal(x), Value::Decimal(y)) => Some(x.cmp(y)),
518 (Value::Integer(_) | Value::Float(_) | Value::BigInt(_), _) => {
522 let l = as_f64(a);
523 let r = as_f64(b);
524 l.partial_cmp(&r)
525 }
526 _ => None,
527 }
528}
529
530fn values_equal(a: &Value, b: &Value) -> bool {
531 match (a, b) {
532 (Value::Float(x), Value::Float(y)) => x == y,
533 _ => a == b,
534 }
535}
536
537fn three_valued_and(l: &Value, r: &Value) -> Result<Value, EvalError> {
538 match (l, r) {
539 (Value::Boolean(false), _) | (_, Value::Boolean(false)) => Ok(Value::Boolean(false)),
540 (Value::Boolean(true), Value::Boolean(true)) => Ok(Value::Boolean(true)),
541 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
542 _ => Err(EvalError::OperatorMismatch {
543 op: BinOp::And,
544 lhs: l.data_type(),
545 rhs: r.data_type(),
546 }),
547 }
548}
549
550fn three_valued_or(l: &Value, r: &Value) -> Result<Value, EvalError> {
551 match (l, r) {
552 (Value::Boolean(true), _) | (_, Value::Boolean(true)) => Ok(Value::Boolean(true)),
553 (Value::Boolean(false), Value::Boolean(false)) => Ok(Value::Boolean(false)),
554 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
555 _ => Err(EvalError::OperatorMismatch {
556 op: BinOp::Or,
557 lhs: l.data_type(),
558 rhs: r.data_type(),
559 }),
560 }
561}
562
563fn apply_implicit_cast(value: &Value, src: DataType, target: DataType) -> Result<Value, EvalError> {
564 if src == target {
565 return Ok(value.clone());
566 }
567 coerce_via_catalog(value, src, target, None).map_err(|reason| EvalError::ImplicitCastFailed {
568 from: src,
569 to: target,
570 reason,
571 })
572}
573
574fn eval_cast(inner: &Expr, target: DataType, row: &dyn Row) -> Result<Value, EvalError> {
575 let v = evaluate(inner, row)?;
576 if v.is_null() {
577 return Ok(Value::Null);
578 }
579 let src = v.data_type();
580 if src == target {
581 return Ok(v);
582 }
583 coerce_via_catalog(&v, src, target, None).map_err(|reason| EvalError::CastFailed {
584 from: src,
585 to: target,
586 reason,
587 })
588}
589
590fn eval_function(name: &str, args: &[Expr], row: &dyn Row) -> Result<Value, EvalError> {
591 if name.eq_ignore_ascii_case("COALESCE") {
597 for arg in args {
598 let v = evaluate(arg, row)?;
599 if !v.is_null() {
600 return Ok(v);
601 }
602 }
603 return Ok(Value::Null);
604 }
605
606 let arg_values: Vec<Value> = args
607 .iter()
608 .map(|a| evaluate(a, row))
609 .collect::<Result<Vec<_>, _>>()?;
610 let arg_types: Vec<DataType> = arg_values.iter().map(|v| v.data_type()).collect();
611
612 if arg_values.iter().any(Value::is_null)
618 && FUNCTION_CATALOG
619 .iter()
620 .any(|e| e.name.eq_ignore_ascii_case(name))
621 {
622 return Ok(Value::Null);
623 }
624
625 let (entry, coercions) =
626 coercion_spine::resolve_function(name, &arg_types).ok_or_else(|| {
627 EvalError::UnknownFunction {
628 name: name.to_string(),
629 args: arg_types.clone(),
630 }
631 })?;
632
633 let mut coerced: Vec<Value> = Vec::with_capacity(arg_values.len());
635 for (idx, value) in arg_values.into_iter().enumerate() {
636 let src = arg_types[idx];
637 match coercions.at(idx) {
638 Some(target) if src != target => {
639 coerced.push(apply_implicit_cast(&value, src, target)?);
640 }
641 _ => coerced.push(value),
642 }
643 }
644
645 if !entry.variadic && coerced.iter().any(|v| v.is_null()) {
649 return Ok(Value::Null);
650 }
651
652 dispatch_function(entry.name, &coerced)
653}
654
655fn dispatch_function(name: &str, args: &[Value]) -> Result<Value, EvalError> {
656 match name {
657 "UPPER" => match &args[0] {
658 Value::Text(s) => Ok(Value::Text(Arc::from(s.to_uppercase()))),
659 other => Err(EvalError::UnknownFunction {
660 name: name.to_string(),
661 args: vec![other.data_type()],
662 }),
663 },
664 "LOWER" => match &args[0] {
665 Value::Text(s) => Ok(Value::Text(Arc::from(s.to_lowercase()))),
666 other => Err(EvalError::UnknownFunction {
667 name: name.to_string(),
668 args: vec![other.data_type()],
669 }),
670 },
671 "LENGTH" | "CHAR_LENGTH" | "CHARACTER_LENGTH" => match &args[0] {
672 Value::Text(s) => Ok(Value::Integer(s.chars().count() as i64)),
673 other => Err(EvalError::UnknownFunction {
674 name: name.to_string(),
675 args: vec![other.data_type()],
676 }),
677 },
678 "OCTET_LENGTH" => match &args[0] {
679 Value::Text(s) => Ok(Value::Integer(s.len() as i64)),
680 Value::Blob(b) => Ok(Value::Integer(b.len() as i64)),
681 other => Err(EvalError::UnknownFunction {
682 name: name.to_string(),
683 args: vec![other.data_type()],
684 }),
685 },
686 "ABS" => match &args[0] {
687 Value::Integer(n) => n
688 .checked_abs()
689 .map(Value::Integer)
690 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
691 Value::BigInt(n) => n
692 .checked_abs()
693 .map(Value::BigInt)
694 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
695 Value::Float(n) => Ok(Value::Float(n.abs())),
696 Value::Decimal(n) => n
697 .checked_abs()
698 .map(Value::Decimal)
699 .ok_or(EvalError::ArithmeticOverflow { op: BinOp::Sub }),
700 other => Err(EvalError::UnknownFunction {
701 name: name.to_string(),
702 args: vec![other.data_type()],
703 }),
704 },
705 other => Err(EvalError::UnknownFunction {
711 name: other.to_string(),
712 args: args.iter().map(|v| v.data_type()).collect(),
713 }),
714 }
715}
716
717fn eval_case(
718 branches: &[(Expr, Expr)],
719 else_: Option<&Expr>,
720 row: &dyn Row,
721) -> Result<Value, EvalError> {
722 for (cond, value) in branches {
723 let c = evaluate(cond, row)?;
724 if matches!(c, Value::Boolean(true)) {
725 return evaluate(value, row);
726 }
727 }
728 match else_ {
729 Some(e) => evaluate(e, row),
730 None => Ok(Value::Null),
731 }
732}
733
734fn eval_in_list(
735 target: &Expr,
736 values: &[Expr],
737 negated: bool,
738 row: &dyn Row,
739) -> Result<Value, EvalError> {
740 if values.is_empty() {
741 return Err(EvalError::EmptyInList);
742 }
743 let needle = evaluate(target, row)?;
744 if needle.is_null() {
745 return Ok(Value::Null);
746 }
747 let mut saw_null = false;
748 for v in values {
749 let candidate = evaluate(v, row)?;
750 if candidate.is_null() {
751 saw_null = true;
752 continue;
753 }
754 if values_equal(&needle, &candidate) {
755 return Ok(Value::Boolean(!negated));
756 }
757 }
758 if saw_null {
759 return Ok(Value::Null);
760 }
761 Ok(Value::Boolean(negated))
762}
763
764fn eval_between(
765 target: &Expr,
766 low: &Expr,
767 high: &Expr,
768 negated: bool,
769 row: &dyn Row,
770) -> Result<Value, EvalError> {
771 let v = evaluate(target, row)?;
772 let lo = evaluate(low, row)?;
773 let hi = evaluate(high, row)?;
774 if v.is_null() || lo.is_null() || hi.is_null() {
775 return Ok(Value::Null);
776 }
777 let lo_ok = compare_values(&v, &lo)
778 .ok_or(EvalError::OperatorMismatch {
779 op: BinOp::Ge,
780 lhs: v.data_type(),
781 rhs: lo.data_type(),
782 })
783 .map(|o| o != std::cmp::Ordering::Less)?;
784 let hi_ok = compare_values(&v, &hi)
785 .ok_or(EvalError::OperatorMismatch {
786 op: BinOp::Le,
787 lhs: v.data_type(),
788 rhs: hi.data_type(),
789 })
790 .map(|o| o != std::cmp::Ordering::Greater)?;
791 let inside = lo_ok && hi_ok;
792 Ok(Value::Boolean(if negated { !inside } else { inside }))
793}
794
795#[cfg(test)]
796mod tests {
797 use super::*;
798 use crate::storage::query::ast::Span;
799
800 fn lit(v: Value) -> Expr {
801 Expr::Literal {
802 value: v,
803 span: Span::synthetic(),
804 }
805 }
806
807 fn binop(op: BinOp, l: Expr, r: Expr) -> Expr {
808 Expr::BinaryOp {
809 op,
810 lhs: Box::new(l),
811 rhs: Box::new(r),
812 span: Span::synthetic(),
813 }
814 }
815
816 fn empty_row() -> impl Row {
817 |_field: &FieldRef| -> Option<Value> { None }
818 }
819
820 #[test]
821 fn integer_addition_overflow_surfaces_as_eval_error() {
822 let expr = binop(
823 BinOp::Add,
824 lit(Value::Integer(i64::MAX)),
825 lit(Value::Integer(1)),
826 );
827 let err = evaluate(&expr, &empty_row()).unwrap_err();
828 assert_eq!(err, EvalError::ArithmeticOverflow { op: BinOp::Add });
829 }
830
831 #[test]
832 fn integer_multiplication_overflow_surfaces_as_eval_error() {
833 let expr = binop(
834 BinOp::Mul,
835 lit(Value::Integer(i64::MAX)),
836 lit(Value::Integer(2)),
837 );
838 let err = evaluate(&expr, &empty_row()).unwrap_err();
839 assert_eq!(err, EvalError::ArithmeticOverflow { op: BinOp::Mul });
840 }
841
842 #[test]
843 fn integer_subtraction_overflow_surfaces_as_eval_error() {
844 let expr = binop(
845 BinOp::Sub,
846 lit(Value::Integer(i64::MIN)),
847 lit(Value::Integer(1)),
848 );
849 let err = evaluate(&expr, &empty_row()).unwrap_err();
850 assert_eq!(err, EvalError::ArithmeticOverflow { op: BinOp::Sub });
851 }
852
853 #[test]
854 fn unary_neg_overflow_on_min_int_surfaces_as_eval_error() {
855 let expr = Expr::UnaryOp {
856 op: UnaryOp::Neg,
857 operand: Box::new(lit(Value::Integer(i64::MIN))),
858 span: Span::synthetic(),
859 };
860 let err = evaluate(&expr, &empty_row()).unwrap_err();
861 assert_eq!(err, EvalError::ArithmeticOverflow { op: BinOp::Sub });
862 }
863
864 #[test]
865 fn null_propagates_through_arithmetic() {
866 let expr = binop(BinOp::Add, lit(Value::Null), lit(Value::Integer(7)));
867 let v = evaluate(&expr, &empty_row()).unwrap();
868 assert_eq!(v, Value::Null);
869 }
870
871 #[test]
872 fn null_propagates_through_comparison() {
873 let expr = binop(BinOp::Lt, lit(Value::Integer(1)), lit(Value::Null));
874 let v = evaluate(&expr, &empty_row()).unwrap();
875 assert_eq!(v, Value::Null);
876 }
877
878 #[test]
879 fn null_propagates_through_concat() {
880 let expr = binop(
881 BinOp::Concat,
882 lit(Value::Text(Arc::from("hi"))),
883 lit(Value::Null),
884 );
885 let v = evaluate(&expr, &empty_row()).unwrap();
886 assert_eq!(v, Value::Null);
887 }
888
889 #[test]
890 fn three_valued_and_returns_false_when_one_side_false_even_with_null() {
891 let expr = binop(BinOp::And, lit(Value::Null), lit(Value::Boolean(false)));
892 let v = evaluate(&expr, &empty_row()).unwrap();
893 assert_eq!(v, Value::Boolean(false));
894 }
895
896 #[test]
897 fn three_valued_or_returns_true_when_one_side_true_even_with_null() {
898 let expr = binop(BinOp::Or, lit(Value::Null), lit(Value::Boolean(true)));
899 let v = evaluate(&expr, &empty_row()).unwrap();
900 assert_eq!(v, Value::Boolean(true));
901 }
902
903 #[test]
904 fn three_valued_and_returns_null_for_null_and_true() {
905 let expr = binop(BinOp::And, lit(Value::Null), lit(Value::Boolean(true)));
906 let v = evaluate(&expr, &empty_row()).unwrap();
907 assert_eq!(v, Value::Null);
908 }
909
910 #[test]
911 fn implicit_cast_triggers_for_decimal_plus_integer() {
912 let expr = binop(
921 BinOp::Add,
922 lit(Value::Decimal(10000)),
923 lit(Value::Integer(2)),
924 );
925 let v = evaluate(&expr, &empty_row()).unwrap();
926 assert_eq!(v, Value::Decimal(30000));
927 }
928
929 #[test]
930 fn integer_plus_bigint_resolves_to_preferred_float_overload() {
931 let expr = binop(
937 BinOp::Add,
938 lit(Value::Integer(5)),
939 lit(Value::BigInt(40_000_000_000)),
940 );
941 let v = evaluate(&expr, &empty_row()).unwrap();
942 assert_eq!(v, Value::Float(40_000_000_005.0));
943 }
944
945 #[test]
946 fn implicit_cast_promotes_integer_to_float_for_float_addition() {
947 let expr = binop(BinOp::Add, lit(Value::Integer(2)), lit(Value::Float(0.5)));
951 let v = evaluate(&expr, &empty_row()).unwrap();
952 assert_eq!(v, Value::Float(2.5));
953 }
954
955 #[test]
956 fn integer_division_promotes_to_float() {
957 let expr = binop(BinOp::Div, lit(Value::Integer(7)), lit(Value::Integer(2)));
958 let v = evaluate(&expr, &empty_row()).unwrap();
959 assert_eq!(v, Value::Float(3.5));
960 }
961
962 #[test]
963 fn division_by_zero_is_eval_error() {
964 let expr = binop(BinOp::Div, lit(Value::Integer(7)), lit(Value::Integer(0)));
965 let err = evaluate(&expr, &empty_row()).unwrap_err();
966 assert_eq!(err, EvalError::DivisionByZero);
967 }
968
969 #[test]
970 fn unknown_function_surfaces_as_eval_error() {
971 let expr = Expr::FunctionCall {
972 name: "no_such_fn".to_string(),
973 args: vec![lit(Value::Integer(1))],
974 span: Span::synthetic(),
975 };
976 let err = evaluate(&expr, &empty_row()).unwrap_err();
977 match err {
978 EvalError::UnknownFunction { name, args } => {
979 assert_eq!(name, "no_such_fn");
980 assert_eq!(args, vec![DataType::Integer]);
981 }
982 other => panic!("expected UnknownFunction, got {other:?}"),
983 }
984 }
985
986 #[test]
987 fn coalesce_returns_first_non_null() {
988 let expr = Expr::FunctionCall {
989 name: "COALESCE".to_string(),
990 args: vec![
991 lit(Value::Null),
992 lit(Value::Null),
993 lit(Value::Integer(42)),
994 lit(Value::Integer(99)),
995 ],
996 span: Span::synthetic(),
997 };
998 let v = evaluate(&expr, &empty_row()).unwrap();
999 assert_eq!(v, Value::Integer(42));
1000 }
1001
1002 #[test]
1003 fn coalesce_all_null_returns_null() {
1004 let expr = Expr::FunctionCall {
1005 name: "COALESCE".to_string(),
1006 args: vec![lit(Value::Null), lit(Value::Null)],
1007 span: Span::synthetic(),
1008 };
1009 let v = evaluate(&expr, &empty_row()).unwrap();
1010 assert_eq!(v, Value::Null);
1011 }
1012
1013 #[test]
1014 fn upper_lower_dispatch_through_function_catalog() {
1015 let expr = Expr::FunctionCall {
1016 name: "UPPER".to_string(),
1017 args: vec![lit(Value::Text(Arc::from("hello")))],
1018 span: Span::synthetic(),
1019 };
1020 let v = evaluate(&expr, &empty_row()).unwrap();
1021 assert_eq!(v, Value::Text(Arc::from("HELLO")));
1022 }
1023
1024 #[test]
1025 fn length_of_null_propagates() {
1026 let expr = Expr::FunctionCall {
1027 name: "LENGTH".to_string(),
1028 args: vec![lit(Value::Null)],
1029 span: Span::synthetic(),
1030 };
1031 let v = evaluate(&expr, &empty_row()).unwrap();
1032 assert_eq!(v, Value::Null);
1033 }
1034
1035 #[test]
1036 fn case_when_picks_first_true_branch() {
1037 let expr = Expr::Case {
1038 branches: vec![
1039 (lit(Value::Boolean(false)), lit(Value::Integer(1))),
1040 (lit(Value::Boolean(true)), lit(Value::Integer(2))),
1041 (lit(Value::Boolean(true)), lit(Value::Integer(3))),
1042 ],
1043 else_: Some(Box::new(lit(Value::Integer(99)))),
1044 span: Span::synthetic(),
1045 };
1046 let v = evaluate(&expr, &empty_row()).unwrap();
1047 assert_eq!(v, Value::Integer(2));
1048 }
1049
1050 #[test]
1051 fn case_falls_through_to_else_when_no_branch_matches() {
1052 let expr = Expr::Case {
1053 branches: vec![(lit(Value::Boolean(false)), lit(Value::Integer(1)))],
1054 else_: Some(Box::new(lit(Value::Integer(99)))),
1055 span: Span::synthetic(),
1056 };
1057 let v = evaluate(&expr, &empty_row()).unwrap();
1058 assert_eq!(v, Value::Integer(99));
1059 }
1060
1061 #[test]
1062 fn case_returns_null_when_no_branch_matches_and_no_else() {
1063 let expr = Expr::Case {
1064 branches: vec![(lit(Value::Boolean(false)), lit(Value::Integer(1)))],
1065 else_: None,
1066 span: Span::synthetic(),
1067 };
1068 let v = evaluate(&expr, &empty_row()).unwrap();
1069 assert_eq!(v, Value::Null);
1070 }
1071
1072 #[test]
1073 fn is_null_handles_null_and_non_null() {
1074 let null_expr = Expr::IsNull {
1075 operand: Box::new(lit(Value::Null)),
1076 negated: false,
1077 span: Span::synthetic(),
1078 };
1079 assert_eq!(
1080 evaluate(&null_expr, &empty_row()).unwrap(),
1081 Value::Boolean(true)
1082 );
1083
1084 let non_null_expr = Expr::IsNull {
1085 operand: Box::new(lit(Value::Integer(7))),
1086 negated: false,
1087 span: Span::synthetic(),
1088 };
1089 assert_eq!(
1090 evaluate(&non_null_expr, &empty_row()).unwrap(),
1091 Value::Boolean(false)
1092 );
1093 }
1094
1095 #[test]
1096 fn between_inclusive_bounds() {
1097 let expr = Expr::Between {
1098 target: Box::new(lit(Value::Integer(5))),
1099 low: Box::new(lit(Value::Integer(1))),
1100 high: Box::new(lit(Value::Integer(10))),
1101 negated: false,
1102 span: Span::synthetic(),
1103 };
1104 assert_eq!(evaluate(&expr, &empty_row()).unwrap(), Value::Boolean(true));
1105 }
1106
1107 #[test]
1108 fn in_list_match_and_miss() {
1109 let hit = Expr::InList {
1110 target: Box::new(lit(Value::Integer(2))),
1111 values: vec![
1112 lit(Value::Integer(1)),
1113 lit(Value::Integer(2)),
1114 lit(Value::Integer(3)),
1115 ],
1116 negated: false,
1117 span: Span::synthetic(),
1118 };
1119 assert_eq!(evaluate(&hit, &empty_row()).unwrap(), Value::Boolean(true));
1120
1121 let miss = Expr::InList {
1122 target: Box::new(lit(Value::Integer(99))),
1123 values: vec![lit(Value::Integer(1)), lit(Value::Integer(2))],
1124 negated: false,
1125 span: Span::synthetic(),
1126 };
1127 assert_eq!(
1128 evaluate(&miss, &empty_row()).unwrap(),
1129 Value::Boolean(false)
1130 );
1131 }
1132
1133 #[test]
1134 fn column_lookup_walks_field_ref() {
1135 let row = |field: &FieldRef| -> Option<Value> {
1136 match field {
1137 FieldRef::TableColumn { table, column } if table == "t" && column == "x" => {
1138 Some(Value::Integer(11))
1139 }
1140 _ => None,
1141 }
1142 };
1143 let expr = Expr::Column {
1144 field: FieldRef::TableColumn {
1145 table: "t".to_string(),
1146 column: "x".to_string(),
1147 },
1148 span: Span::synthetic(),
1149 };
1150 assert_eq!(evaluate(&expr, &row).unwrap(), Value::Integer(11));
1151 }
1152
1153 #[test]
1154 fn missing_column_surfaces_unknown_column() {
1155 let row = |_: &FieldRef| -> Option<Value> { None };
1156 let expr = Expr::Column {
1157 field: FieldRef::TableColumn {
1158 table: "t".to_string(),
1159 column: "missing".to_string(),
1160 },
1161 span: Span::synthetic(),
1162 };
1163 let err = evaluate(&expr, &row).unwrap_err();
1164 match err {
1165 EvalError::UnknownColumn(_) => {}
1166 other => panic!("expected UnknownColumn, got {other:?}"),
1167 }
1168 }
1169
1170 #[test]
1171 fn parameter_without_bind_is_eval_error() {
1172 let expr = Expr::Parameter {
1173 index: 1,
1174 span: Span::synthetic(),
1175 };
1176 let err = evaluate(&expr, &empty_row()).unwrap_err();
1177 assert_eq!(err, EvalError::UnboundParameter(1));
1178 }
1179
1180 #[test]
1181 fn cast_integer_to_text_uses_explicit_path() {
1182 let expr = Expr::Cast {
1183 inner: Box::new(lit(Value::Integer(42))),
1184 target: DataType::Text,
1185 span: Span::synthetic(),
1186 };
1187 assert_eq!(
1188 evaluate(&expr, &empty_row()).unwrap(),
1189 Value::Text(Arc::from("42"))
1190 );
1191 }
1192
1193 #[test]
1194 fn concat_returns_text() {
1195 let expr = binop(
1196 BinOp::Concat,
1197 lit(Value::Text(Arc::from("foo"))),
1198 lit(Value::Text(Arc::from("bar"))),
1199 );
1200 assert_eq!(
1201 evaluate(&expr, &empty_row()).unwrap(),
1202 Value::Text(Arc::from("foobar"))
1203 );
1204 }
1205}