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