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