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