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