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