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