1use rustc_hash::FxHashMap;
4
5use crate::error::{Result, SqlError};
6use crate::parser::{BinOp, Expr, UnaryOp};
7use crate::types::{ColumnDef, CompactString, DataType, Value};
8
9pub struct ColumnMap {
10 exact: FxHashMap<String, usize>,
11 short: FxHashMap<String, ShortMatch>,
12}
13
14#[derive(Clone)]
15enum ShortMatch {
16 Unique(usize),
17 Ambiguous,
18}
19
20impl Clone for ColumnMap {
21 fn clone(&self) -> Self {
22 Self {
23 exact: self.exact.clone(),
24 short: self.short.clone(),
25 }
26 }
27}
28
29impl ColumnMap {
30 pub fn new(columns: &[ColumnDef]) -> Self {
31 let mut exact = FxHashMap::with_capacity_and_hasher(columns.len() * 2, Default::default());
32 let mut short: FxHashMap<String, ShortMatch> =
33 FxHashMap::with_capacity_and_hasher(columns.len(), Default::default());
34
35 for (i, col) in columns.iter().enumerate() {
36 let lower = col.name.to_ascii_lowercase();
37 exact.insert(lower.clone(), i);
38
39 let unqualified = if let Some(dot) = lower.rfind('.') {
40 &lower[dot + 1..]
41 } else {
42 &lower
43 };
44 short
45 .entry(unqualified.to_string())
46 .and_modify(|e| *e = ShortMatch::Ambiguous)
47 .or_insert(ShortMatch::Unique(i));
48 }
49
50 Self { exact, short }
51 }
52
53 pub(crate) fn resolve(&self, name: &str) -> Result<usize> {
54 if let Some(&idx) = self.exact.get(name) {
55 return Ok(idx);
56 }
57 match self.short.get(name) {
58 Some(ShortMatch::Unique(idx)) => Ok(*idx),
59 Some(ShortMatch::Ambiguous) => Err(SqlError::AmbiguousColumn(name.to_string())),
60 None => Err(SqlError::ColumnNotFound(name.to_string())),
61 }
62 }
63
64 pub(crate) fn resolve_qualified(&self, table: &str, column: &str) -> Result<usize> {
65 let qualified = format!("{table}.{column}");
66 if let Some(&idx) = self.exact.get(&qualified) {
67 return Ok(idx);
68 }
69 match self.short.get(column) {
70 Some(ShortMatch::Unique(idx)) => Ok(*idx),
71 _ => Err(SqlError::ColumnNotFound(format!("{table}.{column}"))),
72 }
73 }
74}
75
76pub struct EvalCtx<'a> {
77 pub col_map: &'a ColumnMap,
78 pub row: &'a [Value],
79 pub params: &'a [Value],
80 pub excluded: Option<ExcludedRow<'a>>,
81 pub old_new: Option<OldNewRows<'a>>,
82}
83
84pub struct ExcludedRow<'a> {
85 pub col_map: &'a ColumnMap,
86 pub row: &'a [Value],
87}
88
89pub struct OldNewRows<'a> {
90 pub col_map: &'a ColumnMap,
91 pub old_row: Option<&'a [Value]>,
92 pub new_row: Option<&'a [Value]>,
93}
94
95impl<'a> EvalCtx<'a> {
96 pub fn new(col_map: &'a ColumnMap, row: &'a [Value]) -> Self {
97 Self {
98 col_map,
99 row,
100 params: &[],
101 excluded: None,
102 old_new: None,
103 }
104 }
105
106 pub fn with_params(col_map: &'a ColumnMap, row: &'a [Value], params: &'a [Value]) -> Self {
107 Self {
108 col_map,
109 row,
110 params,
111 excluded: None,
112 old_new: None,
113 }
114 }
115
116 pub fn with_excluded(
117 col_map: &'a ColumnMap,
118 row: &'a [Value],
119 excluded_col_map: &'a ColumnMap,
120 excluded_row: &'a [Value],
121 ) -> Self {
122 Self {
123 col_map,
124 row,
125 params: &[],
126 excluded: Some(ExcludedRow {
127 col_map: excluded_col_map,
128 row: excluded_row,
129 }),
130 old_new: None,
131 }
132 }
133
134 pub fn with_old_new(
135 col_map: &'a ColumnMap,
136 row: &'a [Value],
137 old_row: Option<&'a [Value]>,
138 new_row: Option<&'a [Value]>,
139 ) -> Self {
140 Self {
141 col_map,
142 row,
143 params: &[],
144 excluded: None,
145 old_new: Some(OldNewRows {
146 col_map,
147 old_row,
148 new_row,
149 }),
150 }
151 }
152}
153
154thread_local! {
155 static SCOPED_PARAMS: std::cell::Cell<(*const Value, usize)> =
156 const { std::cell::Cell::new((std::ptr::null(), 0)) };
157}
158
159pub fn with_scoped_params<R>(params: &[Value], f: impl FnOnce() -> R) -> R {
161 struct Guard((*const Value, usize));
162 impl Drop for Guard {
163 fn drop(&mut self) {
164 SCOPED_PARAMS.with(|slot| slot.set(self.0));
165 }
166 }
167 SCOPED_PARAMS.with(|slot| {
168 let prev = slot.get();
169 slot.set((params.as_ptr(), params.len()));
170 let _guard = Guard(prev);
171 f()
172 })
173}
174
175fn resolve_parameter(n: usize, ctx_params: &[Value]) -> Result<Value> {
176 if !ctx_params.is_empty() {
177 if n == 0 || n > ctx_params.len() {
178 return Err(SqlError::ParameterCountMismatch {
179 expected: n,
180 got: ctx_params.len(),
181 });
182 }
183 return Ok(ctx_params[n - 1].clone());
184 }
185 resolve_scoped_param(n)
186}
187
188pub fn resolve_scoped_param(n: usize) -> Result<Value> {
189 SCOPED_PARAMS.with(|slot| {
190 let (ptr, len) = slot.get();
191 if n == 0 || n > len {
192 return Err(SqlError::ParameterCountMismatch {
193 expected: n,
194 got: len,
195 });
196 }
197 unsafe { Ok((*ptr.add(n - 1)).clone()) }
200 })
201}
202
203pub fn eval_expr(expr: &Expr, ctx: &EvalCtx) -> Result<Value> {
204 match expr {
205 Expr::Literal(v) => Ok(v.clone()),
206
207 Expr::Column(name) => {
208 let idx = ctx.col_map.resolve(name)?;
209 Ok(ctx.row[idx].clone())
210 }
211
212 Expr::QualifiedColumn { table, column } => {
213 if let Some(excluded) = ctx.excluded.as_ref() {
214 if table.eq_ignore_ascii_case("excluded") {
215 let lowered = column.to_ascii_lowercase();
216 let idx = excluded.col_map.resolve(&lowered)?;
217 return Ok(excluded.row[idx].clone());
218 }
219 }
220 if let Some(on) = ctx.old_new.as_ref() {
221 if table.eq_ignore_ascii_case("old") {
222 let lowered = column.to_ascii_lowercase();
223 let idx = on.col_map.resolve(&lowered)?;
224 return Ok(on.old_row.map(|r| r[idx].clone()).unwrap_or(Value::Null));
225 }
226 if table.eq_ignore_ascii_case("new") {
227 let lowered = column.to_ascii_lowercase();
228 let idx = on.col_map.resolve(&lowered)?;
229 return Ok(on.new_row.map(|r| r[idx].clone()).unwrap_or(Value::Null));
230 }
231 }
232 let idx = ctx.col_map.resolve_qualified(table, column)?;
233 Ok(ctx.row[idx].clone())
234 }
235
236 Expr::BinaryOp { left, op, right } => {
237 let lval = eval_expr(left, ctx)?;
238 let rval = eval_expr(right, ctx)?;
239 eval_binary_op(&lval, *op, &rval)
240 }
241
242 Expr::UnaryOp { op, expr } => {
243 let val = eval_expr(expr, ctx)?;
244 eval_unary_op(*op, &val)
245 }
246
247 Expr::IsNull(e) => {
248 let val = eval_expr(e, ctx)?;
249 Ok(Value::Boolean(val.is_null()))
250 }
251
252 Expr::IsNotNull(e) => {
253 let val = eval_expr(e, ctx)?;
254 Ok(Value::Boolean(!val.is_null()))
255 }
256
257 Expr::Function { name, args, .. } => eval_scalar_function(name, args, ctx),
258
259 Expr::CountStar => Err(SqlError::Unsupported(
260 "COUNT(*) in non-aggregate context".into(),
261 )),
262
263 Expr::InList {
264 expr: e,
265 list,
266 negated,
267 } => {
268 let lhs = eval_expr(e, ctx)?;
269 eval_in_values(&lhs, list, ctx, *negated)
270 }
271
272 Expr::InSet {
273 expr: e,
274 values,
275 has_null,
276 negated,
277 } => {
278 let lhs = eval_expr(e, ctx)?;
279 eval_in_set(&lhs, values, *has_null, *negated)
280 }
281
282 Expr::Between {
283 expr: e,
284 low,
285 high,
286 negated,
287 } => {
288 let val = eval_expr(e, ctx)?;
289 let lo = eval_expr(low, ctx)?;
290 let hi = eval_expr(high, ctx)?;
291 eval_between(&val, &lo, &hi, *negated)
292 }
293
294 Expr::Like {
295 expr: e,
296 pattern,
297 escape,
298 negated,
299 } => {
300 let val = eval_expr(e, ctx)?;
301 let pat = eval_expr(pattern, ctx)?;
302 let esc = escape.as_ref().map(|e| eval_expr(e, ctx)).transpose()?;
303 eval_like(&val, &pat, esc.as_ref(), *negated)
304 }
305
306 Expr::Case {
307 operand,
308 conditions,
309 else_result,
310 } => eval_case(operand.as_deref(), conditions, else_result.as_deref(), ctx),
311
312 Expr::Coalesce(args) => {
313 for arg in args {
314 let val = eval_expr(arg, ctx)?;
315 if !val.is_null() {
316 return Ok(val);
317 }
318 }
319 Ok(Value::Null)
320 }
321
322 Expr::Cast { expr: e, data_type } => {
323 let val = eval_expr(e, ctx)?;
324 eval_cast(&val, *data_type)
325 }
326
327 Expr::InSubquery { .. } | Expr::Exists { .. } | Expr::ScalarSubquery(_) => Err(
328 SqlError::Unsupported("subquery not materialized (internal error)".into()),
329 ),
330
331 Expr::Parameter(n) => resolve_parameter(*n, ctx.params),
332
333 Expr::WindowFunction { .. } => Err(SqlError::Unsupported(
334 "window functions are only allowed in SELECT columns".into(),
335 )),
336 }
337}
338
339pub fn eval_binary_op_public(left: &Value, op: BinOp, right: &Value) -> Result<Value> {
341 eval_binary_op(left, op, right)
342}
343
344fn eval_binary_op(left: &Value, op: BinOp, right: &Value) -> Result<Value> {
345 match op {
346 BinOp::And => return eval_and(left, right),
347 BinOp::Or => return eval_or(left, right),
348 _ => {}
349 }
350
351 if left.is_null() || right.is_null() {
352 return Ok(Value::Null);
353 }
354
355 if let Some(res) = eval_temporal_op(left, op, right) {
356 return res;
357 }
358
359 match op {
360 BinOp::Eq => Ok(Value::Boolean(left == right)),
361 BinOp::NotEq => Ok(Value::Boolean(left != right)),
362 BinOp::Lt => Ok(Value::Boolean(left < right)),
363 BinOp::Gt => Ok(Value::Boolean(left > right)),
364 BinOp::LtEq => Ok(Value::Boolean(left <= right)),
365 BinOp::GtEq => Ok(Value::Boolean(left >= right)),
366 BinOp::Add => eval_arithmetic(left, right, i64::checked_add, |a, b| a + b),
367 BinOp::Sub => eval_arithmetic(left, right, i64::checked_sub, |a, b| a - b),
368 BinOp::Mul => eval_arithmetic(left, right, i64::checked_mul, |a, b| a * b),
369 BinOp::Div => {
370 match right {
371 Value::Integer(0) => return Err(SqlError::DivisionByZero),
372 Value::Real(r) if *r == 0.0 => return Err(SqlError::DivisionByZero),
373 _ => {}
374 }
375 eval_arithmetic(left, right, i64::checked_div, |a, b| a / b)
376 }
377 BinOp::Mod => {
378 match right {
379 Value::Integer(0) => return Err(SqlError::DivisionByZero),
380 Value::Real(r) if *r == 0.0 => return Err(SqlError::DivisionByZero),
381 _ => {}
382 }
383 eval_arithmetic(left, right, i64::checked_rem, |a, b| a % b)
384 }
385 BinOp::Concat => {
386 let ls = value_to_text(left);
387 let rs = value_to_text(right);
388 Ok(Value::Text(format!("{ls}{rs}").into()))
389 }
390 BinOp::And | BinOp::Or => unreachable!(),
391 }
392}
393
394fn eval_temporal_op(left: &Value, op: BinOp, right: &Value) -> Option<Result<Value>> {
396 use crate::datetime as dt;
397 use std::cmp::Ordering;
398
399 let is_temporal = |v: &Value| {
400 matches!(
401 v,
402 Value::Date(_) | Value::Time(_) | Value::Timestamp(_) | Value::Interval { .. }
403 )
404 };
405 if matches!(op, BinOp::Add | BinOp::Sub)
406 && ((is_temporal(left) && matches!(right, Value::Real(_)))
407 || (matches!(left, Value::Real(_)) && is_temporal(right)))
408 {
409 return Some(Err(SqlError::TypeMismatch {
410 expected: "INTEGER or INTERVAL for date/time arithmetic (use CAST for REAL)".into(),
411 got: format!("{} and {}", left.data_type(), right.data_type()),
412 }));
413 }
414
415 match (left, op, right) {
416 (Value::Date(d), BinOp::Add, Value::Integer(n))
417 | (Value::Integer(n), BinOp::Add, Value::Date(d)) => {
418 Some(dt::add_days_to_date(*d, *n).map(Value::Date))
419 }
420 (Value::Date(d), BinOp::Sub, Value::Integer(n)) => {
421 Some(dt::add_days_to_date(*d, -*n).map(Value::Date))
422 }
423 (Value::Date(a), BinOp::Sub, Value::Date(b)) => {
424 Some(Ok(Value::Integer(*a as i64 - *b as i64)))
425 }
426 (
428 Value::Date(d),
429 BinOp::Add,
430 Value::Interval {
431 months,
432 days,
433 micros,
434 },
435 )
436 | (
437 Value::Interval {
438 months,
439 days,
440 micros,
441 },
442 BinOp::Add,
443 Value::Date(d),
444 ) => Some(dt::add_interval_to_date(*d, *months, *days, *micros).map(Value::Timestamp)),
445 (
446 Value::Date(d),
447 BinOp::Sub,
448 Value::Interval {
449 months,
450 days,
451 micros,
452 },
453 ) => Some(dt::add_interval_to_date(*d, -*months, -*days, -*micros).map(Value::Timestamp)),
454 (
455 Value::Timestamp(t),
456 BinOp::Add,
457 Value::Interval {
458 months,
459 days,
460 micros,
461 },
462 )
463 | (
464 Value::Interval {
465 months,
466 days,
467 micros,
468 },
469 BinOp::Add,
470 Value::Timestamp(t),
471 ) => Some(dt::add_interval_to_timestamp(*t, *months, *days, *micros).map(Value::Timestamp)),
472 (
473 Value::Timestamp(t),
474 BinOp::Sub,
475 Value::Interval {
476 months,
477 days,
478 micros,
479 },
480 ) => Some(
481 dt::add_interval_to_timestamp(*t, -*months, -*days, -*micros).map(Value::Timestamp),
482 ),
483 (Value::Timestamp(a), BinOp::Sub, Value::Timestamp(b)) => {
484 let (days, micros) = dt::subtract_timestamps(*a, *b);
485 Some(Ok(Value::Interval {
486 months: 0,
487 days,
488 micros,
489 }))
490 }
491 (
492 Value::Time(t),
493 BinOp::Add,
494 Value::Interval {
495 months,
496 days,
497 micros,
498 },
499 ) => Some(dt::add_interval_to_time(*t, *months, *days, *micros).map(Value::Time)),
500 (
501 Value::Time(t),
502 BinOp::Sub,
503 Value::Interval {
504 months,
505 days,
506 micros,
507 },
508 ) => Some(dt::add_interval_to_time(*t, -*months, -*days, -*micros).map(Value::Time)),
509 (Value::Time(a), BinOp::Sub, Value::Time(b)) => Some(Ok(Value::Interval {
510 months: 0,
511 days: 0,
512 micros: *a - *b,
513 })),
514 (
515 Value::Interval {
516 months: am,
517 days: ad,
518 micros: au,
519 },
520 BinOp::Add,
521 Value::Interval {
522 months: bm,
523 days: bd,
524 micros: bu,
525 },
526 ) => Some(Ok(Value::Interval {
527 months: am.saturating_add(*bm),
528 days: ad.saturating_add(*bd),
529 micros: au.saturating_add(*bu),
530 })),
531 (
532 Value::Interval {
533 months: am,
534 days: ad,
535 micros: au,
536 },
537 BinOp::Sub,
538 Value::Interval {
539 months: bm,
540 days: bd,
541 micros: bu,
542 },
543 ) => Some(Ok(Value::Interval {
544 months: am.saturating_sub(*bm),
545 days: ad.saturating_sub(*bd),
546 micros: au.saturating_sub(*bu),
547 })),
548 (
549 Value::Interval {
550 months,
551 days,
552 micros,
553 },
554 BinOp::Mul,
555 Value::Integer(n),
556 )
557 | (
558 Value::Integer(n),
559 BinOp::Mul,
560 Value::Interval {
561 months,
562 days,
563 micros,
564 },
565 ) => {
566 let n32 = (*n).clamp(i32::MIN as i64, i32::MAX as i64) as i32;
567 Some(Ok(Value::Interval {
568 months: months.saturating_mul(n32),
569 days: days.saturating_mul(n32),
570 micros: micros.saturating_mul(*n),
571 }))
572 }
573 (
575 Value::Interval {
576 months,
577 days,
578 micros,
579 },
580 BinOp::Mul,
581 Value::Real(r),
582 )
583 | (
584 Value::Real(r),
585 BinOp::Mul,
586 Value::Interval {
587 months,
588 days,
589 micros,
590 },
591 ) => Some(Ok(scale_interval_by_real(*months, *days, *micros, *r))),
592 (
593 Value::Interval {
594 months,
595 days,
596 micros,
597 },
598 BinOp::Div,
599 Value::Integer(n),
600 ) if *n != 0 => Some(Ok(Value::Interval {
601 months: (*months as i64 / *n) as i32,
602 days: (*days as i64 / *n) as i32,
603 micros: *micros / *n,
604 })),
605 (
606 Value::Interval {
607 months,
608 days,
609 micros,
610 },
611 BinOp::Div,
612 Value::Real(r),
613 ) if *r != 0.0 => Some(Ok(scale_interval_by_real(*months, *days, *micros, 1.0 / r))),
614 (
616 Value::Interval {
617 months: am,
618 days: ad,
619 micros: au,
620 },
621 op,
622 Value::Interval {
623 months: bm,
624 days: bd,
625 micros: bu,
626 },
627 ) if matches!(
628 op,
629 BinOp::Eq | BinOp::NotEq | BinOp::Lt | BinOp::Gt | BinOp::LtEq | BinOp::GtEq
630 ) =>
631 {
632 let ord = dt::pg_normalized_interval_cmp((*am, *ad, *au), (*bm, *bd, *bu));
633 let b = match op {
634 BinOp::Eq => ord == Ordering::Equal,
635 BinOp::NotEq => ord != Ordering::Equal,
636 BinOp::Lt => ord == Ordering::Less,
637 BinOp::Gt => ord == Ordering::Greater,
638 BinOp::LtEq => ord != Ordering::Greater,
639 BinOp::GtEq => ord != Ordering::Less,
640 _ => unreachable!(),
641 };
642 Some(Ok(Value::Boolean(b)))
643 }
644 (Value::Timestamp(_), BinOp::Add | BinOp::Sub, Value::Integer(_))
646 | (Value::Integer(_), BinOp::Add, Value::Timestamp(_)) => {
647 Some(Err(SqlError::TypeMismatch {
648 expected: "INTERVAL (use CAST or explicit unit)".into(),
649 got: format!("{} and {}", left.data_type(), right.data_type()),
650 }))
651 }
652 _ => None,
653 }
654}
655
656fn scale_interval_by_real(months: i32, days: i32, micros: i64, factor: f64) -> Value {
658 let raw_months = months as f64 * factor;
659 let whole_months = raw_months.trunc() as i64;
660 let frac_months = raw_months - whole_months as f64;
661 let months_frac_as_days = frac_months * 30.0;
662
663 let raw_days = days as f64 * factor + months_frac_as_days;
664 let whole_days = raw_days.trunc() as i64;
665 let frac_days = raw_days - whole_days as f64;
666 let days_frac_as_micros = (frac_days * crate::datetime::MICROS_PER_DAY as f64).round() as i64;
667
668 let raw_micros = (micros as f64 * factor).round() as i64;
669 let total_micros = raw_micros.saturating_add(days_frac_as_micros);
670
671 let clamp_i32 = |n: i64| n.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
672 Value::Interval {
673 months: clamp_i32(whole_months),
674 days: clamp_i32(whole_days),
675 micros: total_micros,
676 }
677}
678
679fn eval_and(left: &Value, right: &Value) -> Result<Value> {
681 let l = to_bool_or_null(left)?;
682 let r = to_bool_or_null(right)?;
683 match (l, r) {
684 (Some(false), _) | (_, Some(false)) => Ok(Value::Boolean(false)),
685 (Some(true), Some(true)) => Ok(Value::Boolean(true)),
686 _ => Ok(Value::Null),
687 }
688}
689
690fn eval_or(left: &Value, right: &Value) -> Result<Value> {
692 let l = to_bool_or_null(left)?;
693 let r = to_bool_or_null(right)?;
694 match (l, r) {
695 (Some(true), _) | (_, Some(true)) => Ok(Value::Boolean(true)),
696 (Some(false), Some(false)) => Ok(Value::Boolean(false)),
697 _ => Ok(Value::Null),
698 }
699}
700
701fn to_bool_or_null(val: &Value) -> Result<Option<bool>> {
702 match val {
703 Value::Boolean(b) => Ok(Some(*b)),
704 Value::Null => Ok(None),
705 Value::Integer(i) => Ok(Some(*i != 0)),
706 _ => Err(SqlError::TypeMismatch {
707 expected: "BOOLEAN".into(),
708 got: format!("{}", val.data_type()),
709 }),
710 }
711}
712
713fn eval_arithmetic(
714 left: &Value,
715 right: &Value,
716 int_op: fn(i64, i64) -> Option<i64>,
717 real_op: fn(f64, f64) -> f64,
718) -> Result<Value> {
719 match (left, right) {
720 (Value::Integer(a), Value::Integer(b)) => int_op(*a, *b)
721 .map(Value::Integer)
722 .ok_or(SqlError::IntegerOverflow),
723 (Value::Real(a), Value::Real(b)) => Ok(Value::Real(real_op(*a, *b))),
724 (Value::Integer(a), Value::Real(b)) => Ok(Value::Real(real_op(*a as f64, *b))),
725 (Value::Real(a), Value::Integer(b)) => Ok(Value::Real(real_op(*a, *b as f64))),
726 _ => Err(SqlError::TypeMismatch {
727 expected: "numeric".into(),
728 got: format!("{} and {}", left.data_type(), right.data_type()),
729 }),
730 }
731}
732
733fn eval_in_values(lhs: &Value, list: &[Expr], ctx: &EvalCtx, negated: bool) -> Result<Value> {
734 if list.is_empty() {
735 return Ok(Value::Boolean(negated));
736 }
737 if lhs.is_null() {
738 return Ok(Value::Null);
739 }
740 let mut has_null = false;
741 for item in list {
742 let rhs = eval_expr(item, ctx)?;
743 if rhs.is_null() {
744 has_null = true;
745 } else if lhs == &rhs {
746 return Ok(Value::Boolean(!negated));
747 }
748 }
749 if has_null {
750 Ok(Value::Null)
751 } else {
752 Ok(Value::Boolean(negated))
753 }
754}
755
756fn eval_in_set(
757 lhs: &Value,
758 values: &rustc_hash::FxHashSet<Value>,
759 has_null: bool,
760 negated: bool,
761) -> Result<Value> {
762 if values.is_empty() && !has_null {
763 return Ok(Value::Boolean(negated));
764 }
765 if lhs.is_null() {
766 return Ok(Value::Null);
767 }
768 if values.contains(lhs) {
769 return Ok(Value::Boolean(!negated));
770 }
771 if has_null {
772 Ok(Value::Null)
773 } else {
774 Ok(Value::Boolean(negated))
775 }
776}
777
778fn eval_unary_op(op: UnaryOp, val: &Value) -> Result<Value> {
779 if val.is_null() {
780 return Ok(Value::Null);
781 }
782 match op {
783 UnaryOp::Neg => match val {
784 Value::Integer(i) => i
785 .checked_neg()
786 .map(Value::Integer)
787 .ok_or(SqlError::IntegerOverflow),
788 Value::Real(r) => Ok(Value::Real(-r)),
789 Value::Interval {
790 months,
791 days,
792 micros,
793 } => {
794 let m = months.checked_neg().ok_or(SqlError::IntegerOverflow)?;
795 let d = days.checked_neg().ok_or(SqlError::IntegerOverflow)?;
796 let u = micros.checked_neg().ok_or(SqlError::IntegerOverflow)?;
797 Ok(Value::Interval {
798 months: m,
799 days: d,
800 micros: u,
801 })
802 }
803 _ => Err(SqlError::TypeMismatch {
804 expected: "numeric or INTERVAL".into(),
805 got: format!("{}", val.data_type()),
806 }),
807 },
808 UnaryOp::Not => match val {
809 Value::Boolean(b) => Ok(Value::Boolean(!b)),
810 Value::Integer(i) => Ok(Value::Boolean(*i == 0)),
811 _ => Err(SqlError::TypeMismatch {
812 expected: "BOOLEAN".into(),
813 got: format!("{}", val.data_type()),
814 }),
815 },
816 }
817}
818
819fn value_to_text(val: &Value) -> String {
820 match val {
821 Value::Text(s) => s.to_string(),
822 Value::Integer(i) => i.to_string(),
823 Value::Real(r) => {
824 if r.fract() == 0.0 && r.is_finite() {
825 format!("{r:.1}")
826 } else {
827 format!("{r}")
828 }
829 }
830 Value::Boolean(b) => if *b { "TRUE" } else { "FALSE" }.into(),
831 Value::Null => String::new(),
832 Value::Blob(b) => {
833 let mut s = String::with_capacity(b.len() * 2);
834 for byte in b {
835 s.push_str(&format!("{byte:02X}"));
836 }
837 s
838 }
839 Value::Date(d) => crate::datetime::format_date(*d),
840 Value::Time(t) => crate::datetime::format_time(*t),
841 Value::Timestamp(t) => crate::datetime::format_timestamp(*t),
842 Value::Interval {
843 months,
844 days,
845 micros,
846 } => crate::datetime::format_interval(*months, *days, *micros),
847 }
848}
849
850fn eval_between(val: &Value, low: &Value, high: &Value, negated: bool) -> Result<Value> {
851 if val.is_null() || low.is_null() || high.is_null() {
852 let ge = if val.is_null() || low.is_null() {
853 None
854 } else {
855 Some(*val >= *low)
856 };
857 let le = if val.is_null() || high.is_null() {
858 None
859 } else {
860 Some(*val <= *high)
861 };
862
863 let result = match (ge, le) {
864 (Some(false), _) | (_, Some(false)) => Some(false),
865 (Some(true), Some(true)) => Some(true),
866 _ => None,
867 };
868
869 return match result {
870 Some(b) => Ok(Value::Boolean(if negated { !b } else { b })),
871 None => Ok(Value::Null),
872 };
873 }
874
875 let in_range = *val >= *low && *val <= *high;
876 Ok(Value::Boolean(if negated { !in_range } else { in_range }))
877}
878
879const MAX_LIKE_PATTERN_LEN: usize = 10_000;
880
881fn eval_like(val: &Value, pattern: &Value, escape: Option<&Value>, negated: bool) -> Result<Value> {
882 if val.is_null() || pattern.is_null() {
883 return Ok(Value::Null);
884 }
885 let text = match val {
886 Value::Text(s) => s.as_str(),
887 _ => {
888 return Err(SqlError::TypeMismatch {
889 expected: "TEXT".into(),
890 got: val.data_type().to_string(),
891 })
892 }
893 };
894 let pat = match pattern {
895 Value::Text(s) => s.as_str(),
896 _ => {
897 return Err(SqlError::TypeMismatch {
898 expected: "TEXT".into(),
899 got: pattern.data_type().to_string(),
900 })
901 }
902 };
903
904 if pat.len() > MAX_LIKE_PATTERN_LEN {
905 return Err(SqlError::InvalidValue(format!(
906 "LIKE pattern too long ({} chars, max {MAX_LIKE_PATTERN_LEN})",
907 pat.len()
908 )));
909 }
910
911 let esc_char = match escape {
912 Some(Value::Text(s)) => {
913 let mut chars = s.chars();
914 let c = chars.next().ok_or_else(|| {
915 SqlError::InvalidValue("ESCAPE must be a single character".into())
916 })?;
917 if chars.next().is_some() {
918 return Err(SqlError::InvalidValue(
919 "ESCAPE must be a single character".into(),
920 ));
921 }
922 Some(c)
923 }
924 Some(Value::Null) => return Ok(Value::Null),
925 Some(_) => {
926 return Err(SqlError::TypeMismatch {
927 expected: "TEXT".into(),
928 got: "non-text".into(),
929 })
930 }
931 None => None,
932 };
933
934 let matched = like_match(text, pat, esc_char);
935 Ok(Value::Boolean(if negated { !matched } else { matched }))
936}
937
938fn like_match(text: &str, pattern: &str, escape: Option<char>) -> bool {
939 let t: Vec<char> = text.chars().collect();
940 let p: Vec<char> = pattern.chars().collect();
941 like_match_impl(&t, &p, 0, 0, escape)
942}
943
944fn like_match_impl(
945 t: &[char],
946 p: &[char],
947 mut ti: usize,
948 mut pi: usize,
949 esc: Option<char>,
950) -> bool {
951 let mut star_pi: Option<usize> = None;
952 let mut star_ti: usize = 0;
953
954 while ti < t.len() {
955 if pi < p.len() {
956 if let Some(ec) = esc {
957 if p[pi] == ec && pi + 1 < p.len() {
958 pi += 1;
959 let pc_lower = p[pi].to_ascii_lowercase();
960 let tc_lower = t[ti].to_ascii_lowercase();
961 if pc_lower == tc_lower {
962 pi += 1;
963 ti += 1;
964 continue;
965 } else if let Some(sp) = star_pi {
966 pi = sp + 1;
967 star_ti += 1;
968 ti = star_ti;
969 continue;
970 } else {
971 return false;
972 }
973 }
974 }
975 if p[pi] == '%' {
976 star_pi = Some(pi);
977 star_ti = ti;
978 pi += 1;
979 continue;
980 }
981 if p[pi] == '_' {
982 pi += 1;
983 ti += 1;
984 continue;
985 }
986 if p[pi].eq_ignore_ascii_case(&t[ti]) {
987 pi += 1;
988 ti += 1;
989 continue;
990 }
991 }
992 if let Some(sp) = star_pi {
993 pi = sp + 1;
994 star_ti += 1;
995 ti = star_ti;
996 } else {
997 return false;
998 }
999 }
1000
1001 while pi < p.len() && p[pi] == '%' {
1002 pi += 1;
1003 }
1004 pi == p.len()
1005}
1006
1007fn eval_case(
1008 operand: Option<&Expr>,
1009 conditions: &[(Expr, Expr)],
1010 else_result: Option<&Expr>,
1011 ctx: &EvalCtx,
1012) -> Result<Value> {
1013 if let Some(op_expr) = operand {
1014 let op_val = eval_expr(op_expr, ctx)?;
1015 for (cond, result) in conditions {
1016 let cond_val = eval_expr(cond, ctx)?;
1017 if !op_val.is_null() && !cond_val.is_null() && op_val == cond_val {
1018 return eval_expr(result, ctx);
1019 }
1020 }
1021 } else {
1022 for (cond, result) in conditions {
1023 let cond_val = eval_expr(cond, ctx)?;
1024 if is_truthy(&cond_val) {
1025 return eval_expr(result, ctx);
1026 }
1027 }
1028 }
1029 match else_result {
1030 Some(e) => eval_expr(e, ctx),
1031 None => Ok(Value::Null),
1032 }
1033}
1034
1035fn eval_cast(val: &Value, target: DataType) -> Result<Value> {
1036 if val.is_null() {
1037 return Ok(Value::Null);
1038 }
1039 match target {
1040 DataType::Integer => match val {
1041 Value::Integer(_) => Ok(val.clone()),
1042 Value::Real(r) => Ok(Value::Integer(*r as i64)),
1043 Value::Boolean(b) => Ok(Value::Integer(if *b { 1 } else { 0 })),
1044 Value::Text(s) => s
1045 .trim()
1046 .parse::<i64>()
1047 .map(Value::Integer)
1048 .or_else(|_| s.trim().parse::<f64>().map(|f| Value::Integer(f as i64)))
1049 .map_err(|_| SqlError::InvalidValue(format!("cannot cast '{s}' to INTEGER"))),
1050 _ => Err(SqlError::InvalidValue(format!(
1051 "cannot cast {} to INTEGER",
1052 val.data_type()
1053 ))),
1054 },
1055 DataType::Real => match val {
1056 Value::Real(_) => Ok(val.clone()),
1057 Value::Integer(i) => Ok(Value::Real(*i as f64)),
1058 Value::Boolean(b) => Ok(Value::Real(if *b { 1.0 } else { 0.0 })),
1059 Value::Text(s) => s
1060 .trim()
1061 .parse::<f64>()
1062 .map(Value::Real)
1063 .map_err(|_| SqlError::InvalidValue(format!("cannot cast '{s}' to REAL"))),
1064 _ => Err(SqlError::InvalidValue(format!(
1065 "cannot cast {} to REAL",
1066 val.data_type()
1067 ))),
1068 },
1069 DataType::Text => Ok(Value::Text(value_to_text(val).into())),
1070 DataType::Boolean => match val {
1071 Value::Boolean(_) => Ok(val.clone()),
1072 Value::Integer(i) => Ok(Value::Boolean(*i != 0)),
1073 Value::Text(s) => {
1074 let lower = s.trim().to_ascii_lowercase();
1075 match lower.as_str() {
1076 "true" | "1" | "yes" | "on" => Ok(Value::Boolean(true)),
1077 "false" | "0" | "no" | "off" => Ok(Value::Boolean(false)),
1078 _ => Err(SqlError::InvalidValue(format!(
1079 "cannot cast '{s}' to BOOLEAN"
1080 ))),
1081 }
1082 }
1083 _ => Err(SqlError::InvalidValue(format!(
1084 "cannot cast {} to BOOLEAN",
1085 val.data_type()
1086 ))),
1087 },
1088 DataType::Blob => match val {
1089 Value::Blob(_) => Ok(val.clone()),
1090 Value::Text(s) => Ok(Value::Blob(s.as_bytes().to_vec())),
1091 _ => Err(SqlError::InvalidValue(format!(
1092 "cannot cast {} to BLOB",
1093 val.data_type()
1094 ))),
1095 },
1096 DataType::Null => Ok(Value::Null),
1097 DataType::Date => val.clone().coerce_into(DataType::Date).ok_or_else(|| {
1098 SqlError::InvalidValue(format!("cannot cast {} to DATE", val.data_type()))
1099 }),
1100 DataType::Time => val.clone().coerce_into(DataType::Time).ok_or_else(|| {
1101 SqlError::InvalidValue(format!("cannot cast {} to TIME", val.data_type()))
1102 }),
1103 DataType::Timestamp => val.clone().coerce_into(DataType::Timestamp).ok_or_else(|| {
1104 SqlError::InvalidValue(format!("cannot cast {} to TIMESTAMP", val.data_type()))
1105 }),
1106 DataType::Interval => val.clone().coerce_into(DataType::Interval).ok_or_else(|| {
1107 SqlError::InvalidValue(format!("cannot cast {} to INTERVAL", val.data_type()))
1108 }),
1109 }
1110}
1111
1112fn eval_scalar_function(name: &str, args: &[Expr], ctx: &EvalCtx) -> Result<Value> {
1113 let evaluated: Vec<Value> = args
1114 .iter()
1115 .map(|a| eval_expr(a, ctx))
1116 .collect::<Result<Vec<_>>>()?;
1117
1118 match name {
1119 "LENGTH" => {
1120 check_args(name, &evaluated, 1)?;
1121 match &evaluated[0] {
1122 Value::Null => Ok(Value::Null),
1123 Value::Text(s) => Ok(Value::Integer(s.chars().count() as i64)),
1124 Value::Blob(b) => Ok(Value::Integer(b.len() as i64)),
1125 _ => Ok(Value::Integer(
1126 value_to_text(&evaluated[0]).chars().count() as i64
1127 )),
1128 }
1129 }
1130 "UPPER" => {
1131 check_args(name, &evaluated, 1)?;
1132 match &evaluated[0] {
1133 Value::Null => Ok(Value::Null),
1134 Value::Text(s) => Ok(Value::Text(s.to_ascii_uppercase())),
1135 _ => Ok(Value::Text(
1136 value_to_text(&evaluated[0]).to_ascii_uppercase().into(),
1137 )),
1138 }
1139 }
1140 "LOWER" => {
1141 check_args(name, &evaluated, 1)?;
1142 match &evaluated[0] {
1143 Value::Null => Ok(Value::Null),
1144 Value::Text(s) => Ok(Value::Text(s.to_ascii_lowercase())),
1145 _ => Ok(Value::Text(
1146 value_to_text(&evaluated[0]).to_ascii_lowercase().into(),
1147 )),
1148 }
1149 }
1150 "SUBSTR" | "SUBSTRING" => {
1151 if evaluated.len() < 2 || evaluated.len() > 3 {
1152 return Err(SqlError::InvalidValue(format!(
1153 "{name} requires 2 or 3 arguments"
1154 )));
1155 }
1156 if evaluated.iter().any(|v| v.is_null()) {
1157 return Ok(Value::Null);
1158 }
1159 let s = value_to_text(&evaluated[0]);
1160 let chars: Vec<char> = s.chars().collect();
1161 let start = match &evaluated[1] {
1162 Value::Integer(i) => *i,
1163 _ => {
1164 return Err(SqlError::TypeMismatch {
1165 expected: "INTEGER".into(),
1166 got: evaluated[1].data_type().to_string(),
1167 })
1168 }
1169 };
1170 let len = chars.len() as i64;
1171
1172 let (begin, count) = if evaluated.len() == 3 {
1173 let cnt = match &evaluated[2] {
1174 Value::Integer(i) => *i,
1175 _ => {
1176 return Err(SqlError::TypeMismatch {
1177 expected: "INTEGER".into(),
1178 got: evaluated[2].data_type().to_string(),
1179 })
1180 }
1181 };
1182 if start >= 1 {
1183 let b = (start - 1).min(len) as usize;
1184 let c = cnt.max(0) as usize;
1185 (b, c)
1186 } else if start == 0 {
1187 let c = (cnt - 1).max(0) as usize;
1188 (0usize, c)
1189 } else {
1190 let adjusted_cnt = (cnt + start - 1).max(0) as usize;
1191 (0usize, adjusted_cnt)
1192 }
1193 } else if start >= 1 {
1194 let b = (start - 1).min(len) as usize;
1195 (b, chars.len() - b)
1196 } else if start == 0 {
1197 (0usize, chars.len())
1198 } else {
1199 let b = (len + start).max(0) as usize;
1200 (b, chars.len() - b)
1201 };
1202
1203 let result: String = chars.iter().skip(begin).take(count).collect();
1204 Ok(Value::Text(result.into()))
1205 }
1206 "TRIM" | "LTRIM" | "RTRIM" => {
1207 if evaluated.is_empty() || evaluated.len() > 2 {
1208 return Err(SqlError::InvalidValue(format!(
1209 "{name} requires 1 or 2 arguments"
1210 )));
1211 }
1212 if evaluated[0].is_null() {
1213 return Ok(Value::Null);
1214 }
1215 let s = value_to_text(&evaluated[0]);
1216 let trim_chars: Vec<char> = if evaluated.len() == 2 {
1217 if evaluated[1].is_null() {
1218 return Ok(Value::Null);
1219 }
1220 value_to_text(&evaluated[1]).chars().collect()
1221 } else {
1222 vec![' ']
1223 };
1224 let result = match name {
1225 "TRIM" => s
1226 .trim_matches(|c: char| trim_chars.contains(&c))
1227 .to_string(),
1228 "LTRIM" => s
1229 .trim_start_matches(|c: char| trim_chars.contains(&c))
1230 .to_string(),
1231 "RTRIM" => s
1232 .trim_end_matches(|c: char| trim_chars.contains(&c))
1233 .to_string(),
1234 _ => unreachable!(),
1235 };
1236 Ok(Value::Text(result.into()))
1237 }
1238 "REPLACE" => {
1239 check_args(name, &evaluated, 3)?;
1240 if evaluated.iter().any(|v| v.is_null()) {
1241 return Ok(Value::Null);
1242 }
1243 let s = value_to_text(&evaluated[0]);
1244 let from = value_to_text(&evaluated[1]);
1245 let to = value_to_text(&evaluated[2]);
1246 if from.is_empty() {
1247 return Ok(Value::Text(s.into()));
1248 }
1249 Ok(Value::Text(s.replace(&from, &to).into()))
1250 }
1251 "INSTR" => {
1252 check_args(name, &evaluated, 2)?;
1253 if evaluated.iter().any(|v| v.is_null()) {
1254 return Ok(Value::Null);
1255 }
1256 let haystack = value_to_text(&evaluated[0]);
1257 let needle = value_to_text(&evaluated[1]);
1258 let pos = haystack
1259 .find(&needle)
1260 .map(|i| haystack[..i].chars().count() as i64 + 1)
1261 .unwrap_or(0);
1262 Ok(Value::Integer(pos))
1263 }
1264 "CONCAT" => {
1265 if evaluated.is_empty() {
1266 return Ok(Value::Text(CompactString::default()));
1267 }
1268 let mut result = String::new();
1269 for v in &evaluated {
1270 match v {
1271 Value::Null => {}
1272 _ => result.push_str(&value_to_text(v)),
1273 }
1274 }
1275 Ok(Value::Text(result.into()))
1276 }
1277 "ABS" => {
1278 check_args(name, &evaluated, 1)?;
1279 match &evaluated[0] {
1280 Value::Null => Ok(Value::Null),
1281 Value::Integer(i) => i
1282 .checked_abs()
1283 .map(Value::Integer)
1284 .ok_or(SqlError::IntegerOverflow),
1285 Value::Real(r) => Ok(Value::Real(r.abs())),
1286 _ => Err(SqlError::TypeMismatch {
1287 expected: "numeric".into(),
1288 got: evaluated[0].data_type().to_string(),
1289 }),
1290 }
1291 }
1292 "ROUND" => {
1293 if evaluated.is_empty() || evaluated.len() > 2 {
1294 return Err(SqlError::InvalidValue(
1295 "ROUND requires 1 or 2 arguments".into(),
1296 ));
1297 }
1298 if evaluated[0].is_null() {
1299 return Ok(Value::Null);
1300 }
1301 let val = match &evaluated[0] {
1302 Value::Integer(i) => *i as f64,
1303 Value::Real(r) => *r,
1304 _ => {
1305 return Err(SqlError::TypeMismatch {
1306 expected: "numeric".into(),
1307 got: evaluated[0].data_type().to_string(),
1308 })
1309 }
1310 };
1311 let places = if evaluated.len() == 2 {
1312 match &evaluated[1] {
1313 Value::Null => return Ok(Value::Null),
1314 Value::Integer(i) => *i,
1315 _ => {
1316 return Err(SqlError::TypeMismatch {
1317 expected: "INTEGER".into(),
1318 got: evaluated[1].data_type().to_string(),
1319 })
1320 }
1321 }
1322 } else {
1323 0
1324 };
1325 let factor = 10f64.powi(places as i32);
1326 let rounded = (val * factor).round() / factor;
1327 Ok(Value::Real(rounded))
1328 }
1329 "CEIL" | "CEILING" => {
1330 check_args(name, &evaluated, 1)?;
1331 match &evaluated[0] {
1332 Value::Null => Ok(Value::Null),
1333 Value::Integer(i) => Ok(Value::Integer(*i)),
1334 Value::Real(r) => Ok(Value::Integer(r.ceil() as i64)),
1335 _ => Err(SqlError::TypeMismatch {
1336 expected: "numeric".into(),
1337 got: evaluated[0].data_type().to_string(),
1338 }),
1339 }
1340 }
1341 "FLOOR" => {
1342 check_args(name, &evaluated, 1)?;
1343 match &evaluated[0] {
1344 Value::Null => Ok(Value::Null),
1345 Value::Integer(i) => Ok(Value::Integer(*i)),
1346 Value::Real(r) => Ok(Value::Integer(r.floor() as i64)),
1347 _ => Err(SqlError::TypeMismatch {
1348 expected: "numeric".into(),
1349 got: evaluated[0].data_type().to_string(),
1350 }),
1351 }
1352 }
1353 "SIGN" => {
1354 check_args(name, &evaluated, 1)?;
1355 match &evaluated[0] {
1356 Value::Null => Ok(Value::Null),
1357 Value::Integer(i) => Ok(Value::Integer(i.signum())),
1358 Value::Real(r) => {
1359 if *r > 0.0 {
1360 Ok(Value::Integer(1))
1361 } else if *r < 0.0 {
1362 Ok(Value::Integer(-1))
1363 } else {
1364 Ok(Value::Integer(0))
1365 }
1366 }
1367 _ => Err(SqlError::TypeMismatch {
1368 expected: "numeric".into(),
1369 got: evaluated[0].data_type().to_string(),
1370 }),
1371 }
1372 }
1373 "SQRT" => {
1374 check_args(name, &evaluated, 1)?;
1375 match &evaluated[0] {
1376 Value::Null => Ok(Value::Null),
1377 Value::Integer(i) => {
1378 if *i < 0 {
1379 Ok(Value::Null)
1380 } else {
1381 Ok(Value::Real((*i as f64).sqrt()))
1382 }
1383 }
1384 Value::Real(r) => {
1385 if *r < 0.0 {
1386 Ok(Value::Null)
1387 } else {
1388 Ok(Value::Real(r.sqrt()))
1389 }
1390 }
1391 _ => Err(SqlError::TypeMismatch {
1392 expected: "numeric".into(),
1393 got: evaluated[0].data_type().to_string(),
1394 }),
1395 }
1396 }
1397 "RANDOM" => {
1398 check_args(name, &evaluated, 0)?;
1399 use std::collections::hash_map::DefaultHasher;
1400 use std::hash::{Hash, Hasher};
1401 use std::time::SystemTime;
1402 let mut hasher = DefaultHasher::new();
1403 SystemTime::now().hash(&mut hasher);
1404 std::thread::current().id().hash(&mut hasher);
1405 let mut val = hasher.finish() as i64;
1406 if val == i64::MIN {
1407 val = i64::MAX;
1408 }
1409 Ok(Value::Integer(val))
1410 }
1411 "TYPEOF" => {
1412 check_args(name, &evaluated, 1)?;
1413 let type_name = match &evaluated[0] {
1414 Value::Null => "null",
1415 Value::Integer(_) => "integer",
1416 Value::Real(_) => "real",
1417 Value::Text(_) => "text",
1418 Value::Blob(_) => "blob",
1419 Value::Boolean(_) => "boolean",
1420 Value::Date(_) => "date",
1421 Value::Time(_) => "time",
1422 Value::Timestamp(_) => "timestamp",
1423 Value::Interval { .. } => "interval",
1424 };
1425 Ok(Value::Text(type_name.into()))
1426 }
1427 "MIN" => {
1428 check_args(name, &evaluated, 2)?;
1429 if evaluated[0].is_null() {
1430 return Ok(evaluated[1].clone());
1431 }
1432 if evaluated[1].is_null() {
1433 return Ok(evaluated[0].clone());
1434 }
1435 if evaluated[0] <= evaluated[1] {
1436 Ok(evaluated[0].clone())
1437 } else {
1438 Ok(evaluated[1].clone())
1439 }
1440 }
1441 "MAX" => {
1442 check_args(name, &evaluated, 2)?;
1443 if evaluated[0].is_null() {
1444 return Ok(evaluated[1].clone());
1445 }
1446 if evaluated[1].is_null() {
1447 return Ok(evaluated[0].clone());
1448 }
1449 if evaluated[0] >= evaluated[1] {
1450 Ok(evaluated[0].clone())
1451 } else {
1452 Ok(evaluated[1].clone())
1453 }
1454 }
1455 "HEX" => {
1456 check_args(name, &evaluated, 1)?;
1457 match &evaluated[0] {
1458 Value::Null => Ok(Value::Null),
1459 Value::Blob(b) => {
1460 let mut s = String::with_capacity(b.len() * 2);
1461 for byte in b {
1462 s.push_str(&format!("{byte:02X}"));
1463 }
1464 Ok(Value::Text(s.into()))
1465 }
1466 Value::Text(s) => {
1467 let mut r = String::with_capacity(s.len() * 2);
1468 for byte in s.as_bytes() {
1469 r.push_str(&format!("{byte:02X}"));
1470 }
1471 Ok(Value::Text(r.into()))
1472 }
1473 _ => Ok(Value::Text(value_to_text(&evaluated[0]).into())),
1474 }
1475 }
1476 "NOW" | "CURRENT_TIMESTAMP" | "LOCALTIMESTAMP" => {
1477 check_args(name, &evaluated, 0)?;
1478 Ok(Value::Timestamp(crate::datetime::txn_or_clock_micros()))
1479 }
1480 "CURRENT_DATE" => {
1481 check_args(name, &evaluated, 0)?;
1482 Ok(Value::Date(crate::datetime::ts_to_date_floor(
1483 crate::datetime::txn_or_clock_micros(),
1484 )))
1485 }
1486 "CURRENT_TIME" | "LOCALTIME" => {
1487 check_args(name, &evaluated, 0)?;
1488 Ok(Value::Time(
1489 crate::datetime::ts_split(crate::datetime::txn_or_clock_micros()).1,
1490 ))
1491 }
1492 "CLOCK_TIMESTAMP" | "STATEMENT_TIMESTAMP" | "TRANSACTION_TIMESTAMP" => {
1493 check_args(name, &evaluated, 0)?;
1494 let ts = match name {
1495 "CLOCK_TIMESTAMP" => crate::datetime::now_micros(),
1496 _ => crate::datetime::txn_or_clock_micros(),
1497 };
1498 Ok(Value::Timestamp(ts))
1499 }
1500 "EXTRACT" | "DATE_PART" | "DATEPART" => {
1501 check_args(name, &evaluated, 2)?;
1502 let field: &str = match &evaluated[0] {
1504 Value::Null => return Ok(Value::Null),
1505 Value::Text(s) => s.as_str(),
1506 _ => {
1507 return Err(SqlError::TypeMismatch {
1508 expected: "TEXT field name".into(),
1509 got: evaluated[0].data_type().to_string(),
1510 })
1511 }
1512 };
1513 if evaluated[1].is_null() {
1514 return Ok(Value::Null);
1515 }
1516 crate::datetime::extract(field, &evaluated[1])
1517 }
1518 "DATE_TRUNC" => {
1519 if evaluated.len() < 2 || evaluated.len() > 3 {
1520 return Err(SqlError::InvalidValue(
1521 "DATE_TRUNC requires 2 or 3 arguments".into(),
1522 ));
1523 }
1524 let unit = match &evaluated[0] {
1525 Value::Null => return Ok(Value::Null),
1526 Value::Text(s) => s.to_string(),
1527 _ => {
1528 return Err(SqlError::TypeMismatch {
1529 expected: "TEXT unit name".into(),
1530 got: evaluated[0].data_type().to_string(),
1531 })
1532 }
1533 };
1534 if evaluated[1].is_null() {
1535 return Ok(Value::Null);
1536 }
1537 if evaluated.len() == 3 {
1539 if let Value::Text(tz) = &evaluated[2] {
1540 if !tz.eq_ignore_ascii_case("UTC") {
1541 if let Value::Timestamp(ts) = &evaluated[1] {
1542 return date_trunc_in_zone(&unit, *ts, tz);
1543 }
1544 }
1545 }
1546 }
1547 crate::datetime::date_trunc(&unit, &evaluated[1])
1548 }
1549 "DATE_BIN" => {
1550 check_args(name, &evaluated, 3)?;
1551 if evaluated.iter().any(|v| v.is_null()) {
1552 return Ok(Value::Null);
1553 }
1554 let stride = match &evaluated[0] {
1555 Value::Interval {
1556 months: _,
1557 days,
1558 micros,
1559 } => *days as i64 * crate::datetime::MICROS_PER_DAY + *micros,
1560 _ => {
1561 return Err(SqlError::TypeMismatch {
1562 expected: "INTERVAL stride".into(),
1563 got: evaluated[0].data_type().to_string(),
1564 })
1565 }
1566 };
1567 if stride <= 0 {
1568 return Err(SqlError::InvalidValue(
1569 "DATE_BIN stride must be positive".into(),
1570 ));
1571 }
1572 let (src, origin) = match (&evaluated[1], &evaluated[2]) {
1573 (Value::Timestamp(s), Value::Timestamp(o)) => (*s, *o),
1574 _ => {
1575 return Err(SqlError::TypeMismatch {
1576 expected: "TIMESTAMP, TIMESTAMP".into(),
1577 got: format!("{}, {}", evaluated[1].data_type(), evaluated[2].data_type()),
1578 })
1579 }
1580 };
1581 let diff = src - origin;
1582 let binned = origin + (diff.div_euclid(stride)) * stride;
1583 Ok(Value::Timestamp(binned))
1584 }
1585 "AGE" => {
1586 if evaluated.len() == 1 {
1587 if evaluated[0].is_null() {
1588 return Ok(Value::Null);
1589 }
1590 let ts = match &evaluated[0] {
1591 Value::Timestamp(t) => *t,
1592 Value::Date(d) => crate::datetime::date_to_ts(*d),
1593 _ => {
1594 return Err(SqlError::TypeMismatch {
1595 expected: "TIMESTAMP or DATE".into(),
1596 got: evaluated[0].data_type().to_string(),
1597 })
1598 }
1599 };
1600 let today = crate::datetime::today_days();
1602 let midnight = crate::datetime::date_to_ts(today);
1603 let (m, d, u) = crate::datetime::age(midnight, ts)?;
1604 return Ok(Value::Interval {
1605 months: m,
1606 days: d,
1607 micros: u,
1608 });
1609 }
1610 check_args(name, &evaluated, 2)?;
1611 if evaluated.iter().any(|v| v.is_null()) {
1612 return Ok(Value::Null);
1613 }
1614 let a = ts_of(&evaluated[0])?;
1615 let b = ts_of(&evaluated[1])?;
1616 let (m, d, u) = crate::datetime::age(a, b)?;
1617 Ok(Value::Interval {
1618 months: m,
1619 days: d,
1620 micros: u,
1621 })
1622 }
1623 "MAKE_DATE" => {
1624 check_args(name, &evaluated, 3)?;
1625 if evaluated.iter().any(|v| v.is_null()) {
1626 return Ok(Value::Null);
1627 }
1628 let y = int_arg(&evaluated[0], "MAKE_DATE year")? as i32;
1629 let m = int_arg(&evaluated[1], "MAKE_DATE month")? as u8;
1630 let d = int_arg(&evaluated[2], "MAKE_DATE day")? as u8;
1631 crate::datetime::ymd_to_days(y, m, d)
1632 .map(Value::Date)
1633 .ok_or_else(|| SqlError::InvalidDateLiteral(format!("make_date({y}, {m}, {d})")))
1634 }
1635 "MAKE_TIME" => {
1636 check_args(name, &evaluated, 3)?;
1637 if evaluated.iter().any(|v| v.is_null()) {
1638 return Ok(Value::Null);
1639 }
1640 let h = int_arg(&evaluated[0], "MAKE_TIME hour")? as u8;
1641 let mi = int_arg(&evaluated[1], "MAKE_TIME minute")? as u8;
1642 let (s, us) = real_sec_arg(&evaluated[2])?;
1643 crate::datetime::hmsn_to_micros(h, mi, s, us)
1644 .map(Value::Time)
1645 .ok_or_else(|| SqlError::InvalidTimeLiteral(format!("make_time({h}, {mi}, ...)")))
1646 }
1647 "MAKE_TIMESTAMP" => {
1648 check_args(name, &evaluated, 6)?;
1649 if evaluated.iter().any(|v| v.is_null()) {
1650 return Ok(Value::Null);
1651 }
1652 let y = int_arg(&evaluated[0], "MAKE_TIMESTAMP year")? as i32;
1653 let mo = int_arg(&evaluated[1], "MAKE_TIMESTAMP month")? as u8;
1654 let d = int_arg(&evaluated[2], "MAKE_TIMESTAMP day")? as u8;
1655 let h = int_arg(&evaluated[3], "MAKE_TIMESTAMP hour")? as u8;
1656 let mi = int_arg(&evaluated[4], "MAKE_TIMESTAMP min")? as u8;
1657 let (s, us) = real_sec_arg(&evaluated[5])?;
1658 let days = crate::datetime::ymd_to_days(y, mo, d).ok_or_else(|| {
1659 SqlError::InvalidTimestampLiteral(format!("make_timestamp year={y}"))
1660 })?;
1661 let tmicros = crate::datetime::hmsn_to_micros(h, mi, s, us)
1662 .ok_or_else(|| SqlError::InvalidTimestampLiteral("time out of range".into()))?;
1663 Ok(Value::Timestamp(crate::datetime::ts_combine(days, tmicros)))
1664 }
1665 "MAKE_INTERVAL" => {
1666 if evaluated.len() > 7 {
1668 return Err(SqlError::InvalidValue(
1669 "MAKE_INTERVAL accepts at most 7 arguments".into(),
1670 ));
1671 }
1672 let mut months: i64 = 0;
1673 let mut days: i64 = 0;
1674 let mut micros: i64 = 0;
1675 for (i, v) in evaluated.iter().enumerate() {
1676 if v.is_null() {
1677 continue;
1678 }
1679 let n = match v {
1680 Value::Integer(n) => *n,
1681 Value::Real(r) => *r as i64,
1682 _ => {
1683 return Err(SqlError::TypeMismatch {
1684 expected: "numeric".into(),
1685 got: v.data_type().to_string(),
1686 })
1687 }
1688 };
1689 match i {
1690 0 => months = months.saturating_add(n.saturating_mul(12)),
1691 1 => months = months.saturating_add(n),
1692 2 => days = days.saturating_add(n.saturating_mul(7)),
1693 3 => days = days.saturating_add(n),
1694 4 => {
1695 micros = micros
1696 .saturating_add(n.saturating_mul(crate::datetime::MICROS_PER_HOUR))
1697 }
1698 5 => {
1699 micros =
1700 micros.saturating_add(n.saturating_mul(crate::datetime::MICROS_PER_MIN))
1701 }
1702 6 => {
1703 if let Value::Real(r) = v {
1705 micros = micros.saturating_add(
1706 (*r * crate::datetime::MICROS_PER_SEC as f64) as i64,
1707 );
1708 } else {
1709 micros = micros
1710 .saturating_add(n.saturating_mul(crate::datetime::MICROS_PER_SEC));
1711 }
1712 }
1713 _ => unreachable!(),
1714 }
1715 }
1716 Ok(Value::Interval {
1717 months: months.clamp(i32::MIN as i64, i32::MAX as i64) as i32,
1718 days: days.clamp(i32::MIN as i64, i32::MAX as i64) as i32,
1719 micros,
1720 })
1721 }
1722 "JUSTIFY_DAYS" => {
1723 check_args(name, &evaluated, 1)?;
1724 match &evaluated[0] {
1725 Value::Null => Ok(Value::Null),
1726 Value::Interval {
1727 months,
1728 days,
1729 micros,
1730 } => {
1731 let (m, d, u) = crate::datetime::justify_days(*months, *days, *micros);
1732 Ok(Value::Interval {
1733 months: m,
1734 days: d,
1735 micros: u,
1736 })
1737 }
1738 other => Err(SqlError::TypeMismatch {
1739 expected: "INTERVAL".into(),
1740 got: other.data_type().to_string(),
1741 }),
1742 }
1743 }
1744 "JUSTIFY_HOURS" => {
1745 check_args(name, &evaluated, 1)?;
1746 match &evaluated[0] {
1747 Value::Null => Ok(Value::Null),
1748 Value::Interval {
1749 months,
1750 days,
1751 micros,
1752 } => {
1753 let (m, d, u) = crate::datetime::justify_hours(*months, *days, *micros);
1754 Ok(Value::Interval {
1755 months: m,
1756 days: d,
1757 micros: u,
1758 })
1759 }
1760 other => Err(SqlError::TypeMismatch {
1761 expected: "INTERVAL".into(),
1762 got: other.data_type().to_string(),
1763 }),
1764 }
1765 }
1766 "JUSTIFY_INTERVAL" => {
1767 check_args(name, &evaluated, 1)?;
1768 match &evaluated[0] {
1769 Value::Null => Ok(Value::Null),
1770 Value::Interval {
1771 months,
1772 days,
1773 micros,
1774 } => {
1775 let (m, d, u) = crate::datetime::justify_interval(*months, *days, *micros);
1776 Ok(Value::Interval {
1777 months: m,
1778 days: d,
1779 micros: u,
1780 })
1781 }
1782 other => Err(SqlError::TypeMismatch {
1783 expected: "INTERVAL".into(),
1784 got: other.data_type().to_string(),
1785 }),
1786 }
1787 }
1788 "ISFINITE" => {
1789 check_args(name, &evaluated, 1)?;
1790 if evaluated[0].is_null() {
1791 return Ok(Value::Null);
1792 }
1793 Ok(Value::Boolean(evaluated[0].is_finite_temporal()))
1794 }
1795 "DATE" => {
1796 if evaluated.is_empty() {
1797 return Err(SqlError::InvalidValue(
1798 "DATE requires at least 1 argument".into(),
1799 ));
1800 }
1801 if evaluated[0].is_null() {
1802 return Ok(Value::Null);
1803 }
1804 let d = match &evaluated[0] {
1805 Value::Date(d) => *d,
1806 Value::Timestamp(t) => crate::datetime::ts_to_date_floor(*t),
1807 Value::Text(s) if s.eq_ignore_ascii_case("now") => crate::datetime::today_days(),
1808 Value::Text(s) => crate::datetime::parse_date(s)?,
1809 Value::Integer(n) => {
1810 crate::datetime::ts_to_date_floor(*n * crate::datetime::MICROS_PER_SEC)
1811 }
1812 other => {
1813 return Err(SqlError::TypeMismatch {
1814 expected: "TIMESTAMP, DATE, TEXT, or INTEGER".into(),
1815 got: other.data_type().to_string(),
1816 })
1817 }
1818 };
1819 Ok(Value::Date(d))
1820 }
1821 "TIME" => {
1822 if evaluated.is_empty() {
1823 return Err(SqlError::InvalidValue(
1824 "TIME requires at least 1 argument".into(),
1825 ));
1826 }
1827 if evaluated[0].is_null() {
1828 return Ok(Value::Null);
1829 }
1830 let t = match &evaluated[0] {
1831 Value::Time(t) => *t,
1832 Value::Timestamp(t) => crate::datetime::ts_split(*t).1,
1833 Value::Text(s) if s.eq_ignore_ascii_case("now") => {
1834 crate::datetime::current_time_micros()
1835 }
1836 Value::Text(s) => crate::datetime::parse_time(s)?,
1837 other => {
1838 return Err(SqlError::TypeMismatch {
1839 expected: "TIMESTAMP, TIME, or TEXT".into(),
1840 got: other.data_type().to_string(),
1841 })
1842 }
1843 };
1844 Ok(Value::Time(t))
1845 }
1846 "DATETIME" => {
1847 if evaluated.is_empty() {
1848 return Err(SqlError::InvalidValue(
1849 "DATETIME requires at least 1 argument".into(),
1850 ));
1851 }
1852 if evaluated[0].is_null() {
1853 return Ok(Value::Null);
1854 }
1855 let t = match &evaluated[0] {
1856 Value::Timestamp(t) => *t,
1857 Value::Date(d) => crate::datetime::date_to_ts(*d),
1858 Value::Text(s) if s.eq_ignore_ascii_case("now") => crate::datetime::now_micros(),
1859 Value::Text(s) => crate::datetime::parse_timestamp(s)?,
1860 Value::Integer(n) => n * crate::datetime::MICROS_PER_SEC,
1861 other => {
1862 return Err(SqlError::TypeMismatch {
1863 expected: "TIMESTAMP, DATE, TEXT, or INTEGER".into(),
1864 got: other.data_type().to_string(),
1865 })
1866 }
1867 };
1868 Ok(Value::Timestamp(t))
1869 }
1870 "STRFTIME" => {
1871 if evaluated.len() < 2 {
1872 return Err(SqlError::InvalidValue(
1873 "STRFTIME requires format + value".into(),
1874 ));
1875 }
1876 if evaluated.iter().take(2).any(|v| v.is_null()) {
1877 return Ok(Value::Null);
1878 }
1879 let fmt = match &evaluated[0] {
1880 Value::Text(s) => s.to_string(),
1881 _ => {
1882 return Err(SqlError::TypeMismatch {
1883 expected: "TEXT format".into(),
1884 got: evaluated[0].data_type().to_string(),
1885 })
1886 }
1887 };
1888 let out = crate::datetime::strftime(&fmt, &evaluated[1])?;
1889 Ok(Value::Text(out.into()))
1890 }
1891 "JULIANDAY" => {
1892 if evaluated.is_empty() {
1893 return Err(SqlError::InvalidValue(
1894 "JULIANDAY requires at least 1 argument".into(),
1895 ));
1896 }
1897 if evaluated[0].is_null() {
1898 return Ok(Value::Null);
1899 }
1900 let micros = ts_of(&evaluated[0])?;
1901 let (days, tmicros) = crate::datetime::ts_split(micros);
1902 let julian =
1904 days as f64 + 2_440_587.5 + tmicros as f64 / crate::datetime::MICROS_PER_DAY as f64;
1905 Ok(Value::Real(julian))
1906 }
1907 "UNIXEPOCH" => {
1908 if evaluated.is_empty() {
1909 return Err(SqlError::InvalidValue(
1910 "UNIXEPOCH requires at least 1 argument".into(),
1911 ));
1912 }
1913 if evaluated[0].is_null() {
1914 return Ok(Value::Null);
1915 }
1916 let micros = ts_of(&evaluated[0])?;
1917 let subsec = evaluated
1918 .get(1)
1919 .and_then(|v| {
1920 if let Value::Text(s) = v {
1921 Some(s.to_string())
1922 } else {
1923 None
1924 }
1925 })
1926 .map(|s| s.eq_ignore_ascii_case("subsec") || s.eq_ignore_ascii_case("subsecond"))
1927 .unwrap_or(false);
1928 if subsec {
1929 Ok(Value::Real(
1930 micros as f64 / crate::datetime::MICROS_PER_SEC as f64,
1931 ))
1932 } else {
1933 Ok(Value::Integer(micros / crate::datetime::MICROS_PER_SEC))
1934 }
1935 }
1936 "TIMEDIFF" => {
1937 check_args(name, &evaluated, 2)?;
1938 if evaluated.iter().any(|v| v.is_null()) {
1939 return Ok(Value::Null);
1940 }
1941 let a = ts_of(&evaluated[0])?;
1942 let b = ts_of(&evaluated[1])?;
1943 let (days, micros) = crate::datetime::subtract_timestamps(a, b);
1944 let sign = if days < 0 || (days == 0 && micros < 0) {
1945 "-"
1946 } else {
1947 "+"
1948 };
1949 let abs_days = days.unsigned_abs() as i64;
1950 let abs_us = micros.unsigned_abs() as i64;
1951 let (h, m, s, us) = crate::datetime::micros_to_hmsn(abs_us);
1953 Ok(Value::Text(
1954 format!("{sign}{abs_days:04}-00-00 {h:02}:{m:02}:{s:02}.{us:06}").into(),
1955 ))
1956 }
1957 "AT_TIMEZONE" => {
1958 check_args(name, &evaluated, 2)?;
1959 if evaluated.iter().any(|v| v.is_null()) {
1960 return Ok(Value::Null);
1961 }
1962 let ts = match &evaluated[0] {
1963 Value::Timestamp(t) => *t,
1964 Value::Date(d) => crate::datetime::date_to_ts(*d),
1965 other => {
1966 return Err(SqlError::TypeMismatch {
1967 expected: "TIMESTAMP or DATE".into(),
1968 got: other.data_type().to_string(),
1969 })
1970 }
1971 };
1972 let zone = match &evaluated[1] {
1973 Value::Text(s) => s.to_string(),
1974 _ => {
1975 return Err(SqlError::TypeMismatch {
1976 expected: "TEXT time zone".into(),
1977 got: evaluated[1].data_type().to_string(),
1978 })
1979 }
1980 };
1981 let upper = zone.to_ascii_uppercase();
1983 if (upper.starts_with("UTC+") || upper.starts_with("UTC-")) && zone.len() > 3 {
1984 return Err(SqlError::InvalidTimezone(format!(
1985 "'{zone}' is ambiguous — use ISO-8601 offset like '+05:00' or named zone like 'Etc/GMT-5'"
1986 )));
1987 }
1988 let formatted = crate::datetime::format_timestamp_in_zone(ts, &zone)?;
1989 Ok(Value::Text(formatted.into()))
1990 }
1991 _ => Err(SqlError::Unsupported(format!("scalar function: {name}"))),
1992 }
1993}
1994
1995fn ts_of(v: &Value) -> Result<i64> {
1997 match v {
1998 Value::Timestamp(t) => Ok(*t),
1999 Value::Date(d) => Ok(crate::datetime::date_to_ts(*d)),
2000 _ => Err(SqlError::TypeMismatch {
2001 expected: "TIMESTAMP or DATE".into(),
2002 got: v.data_type().to_string(),
2003 }),
2004 }
2005}
2006
2007fn int_arg(v: &Value, label: &str) -> Result<i64> {
2008 match v {
2009 Value::Integer(n) => Ok(*n),
2010 _ => Err(SqlError::TypeMismatch {
2011 expected: format!("INTEGER ({label})"),
2012 got: v.data_type().to_string(),
2013 }),
2014 }
2015}
2016
2017fn real_sec_arg(v: &Value) -> Result<(u8, u32)> {
2019 match v {
2020 Value::Integer(n) => {
2021 if !(0..=60).contains(n) {
2022 return Err(SqlError::InvalidValue(format!("second out of range: {n}")));
2023 }
2024 Ok((*n as u8, 0))
2025 }
2026 Value::Real(r) => {
2027 let whole = r.trunc() as i64;
2028 if !(0..=60).contains(&whole) {
2029 return Err(SqlError::InvalidValue(format!("second out of range: {r}")));
2030 }
2031 let frac = ((r - whole as f64) * 1_000_000.0).round() as i64;
2032 Ok((whole as u8, frac.max(0) as u32))
2033 }
2034 _ => Err(SqlError::TypeMismatch {
2035 expected: "numeric seconds".into(),
2036 got: v.data_type().to_string(),
2037 }),
2038 }
2039}
2040
2041fn date_trunc_in_zone(unit: &str, ts_utc: i64, tz: &str) -> Result<Value> {
2043 use jiff::{tz::TimeZone, Timestamp as JTimestamp};
2044 let zone = TimeZone::get(tz).map_err(|e| SqlError::InvalidTimezone(format!("{tz}: {e}")))?;
2045 let ts = JTimestamp::from_microsecond(ts_utc)
2046 .map_err(|e| SqlError::InvalidValue(format!("ts: {e}")))?;
2047 let zoned = ts.to_zoned(zone.clone());
2048 let unit_lower = unit.to_ascii_lowercase();
2049 let rounded = match unit_lower.as_str() {
2050 "microseconds" => return Ok(Value::Timestamp(ts_utc)),
2051 "second" => zoned
2052 .start_of_day()
2053 .map_err(|e| SqlError::InvalidValue(format!("{e}")))?,
2054 _ => {
2055 let naive_ts = zoned.timestamp().as_microsecond();
2056 return crate::datetime::date_trunc(unit, &Value::Timestamp(naive_ts));
2057 }
2058 };
2059 Ok(Value::Timestamp(rounded.timestamp().as_microsecond()))
2060}
2061
2062fn check_args(name: &str, args: &[Value], expected: usize) -> Result<()> {
2063 if args.len() != expected {
2064 Err(SqlError::InvalidValue(format!(
2065 "{name} requires {expected} argument(s), got {}",
2066 args.len()
2067 )))
2068 } else {
2069 Ok(())
2070 }
2071}
2072
2073pub fn referenced_columns(expr: &Expr, columns: &[ColumnDef]) -> Vec<usize> {
2074 let mut indices = Vec::new();
2075 collect_column_refs(expr, columns, &mut indices);
2076 indices.sort_unstable();
2077 indices.dedup();
2078 indices
2079}
2080
2081fn collect_column_refs(expr: &Expr, columns: &[ColumnDef], out: &mut Vec<usize>) {
2082 match expr {
2083 Expr::Column(name) => {
2084 for (i, c) in columns.iter().enumerate() {
2085 if c.name == *name || c.name.ends_with(&format!(".{name}")) {
2086 out.push(i);
2087 break;
2088 }
2089 }
2090 }
2091 Expr::QualifiedColumn { table, column } => {
2092 let qualified = format!("{table}.{column}");
2093 if let Some(idx) = columns.iter().position(|c| c.name == qualified) {
2094 out.push(idx);
2095 } else {
2096 let matches: Vec<usize> = columns
2097 .iter()
2098 .enumerate()
2099 .filter(|(_, c)| c.name == *column)
2100 .map(|(i, _)| i)
2101 .collect();
2102 if matches.len() == 1 {
2103 out.push(matches[0]);
2104 }
2105 }
2106 }
2107 Expr::BinaryOp { left, right, .. } => {
2108 collect_column_refs(left, columns, out);
2109 collect_column_refs(right, columns, out);
2110 }
2111 Expr::UnaryOp { expr, .. } => {
2112 collect_column_refs(expr, columns, out);
2113 }
2114 Expr::IsNull(e) | Expr::IsNotNull(e) => {
2115 collect_column_refs(e, columns, out);
2116 }
2117 Expr::Function { args, .. } => {
2118 for arg in args {
2119 collect_column_refs(arg, columns, out);
2120 }
2121 }
2122 Expr::InSubquery { expr, .. } => {
2123 collect_column_refs(expr, columns, out);
2124 }
2125 Expr::InList { expr, list, .. } => {
2126 collect_column_refs(expr, columns, out);
2127 for item in list {
2128 collect_column_refs(item, columns, out);
2129 }
2130 }
2131 Expr::InSet { expr, .. } => {
2132 collect_column_refs(expr, columns, out);
2133 }
2134 Expr::Between {
2135 expr, low, high, ..
2136 } => {
2137 collect_column_refs(expr, columns, out);
2138 collect_column_refs(low, columns, out);
2139 collect_column_refs(high, columns, out);
2140 }
2141 Expr::Like {
2142 expr,
2143 pattern,
2144 escape,
2145 ..
2146 } => {
2147 collect_column_refs(expr, columns, out);
2148 collect_column_refs(pattern, columns, out);
2149 if let Some(esc) = escape {
2150 collect_column_refs(esc, columns, out);
2151 }
2152 }
2153 Expr::Case {
2154 operand,
2155 conditions,
2156 else_result,
2157 } => {
2158 if let Some(op) = operand {
2159 collect_column_refs(op, columns, out);
2160 }
2161 for (when, then) in conditions {
2162 collect_column_refs(when, columns, out);
2163 collect_column_refs(then, columns, out);
2164 }
2165 if let Some(e) = else_result {
2166 collect_column_refs(e, columns, out);
2167 }
2168 }
2169 Expr::Coalesce(args) => {
2170 for arg in args {
2171 collect_column_refs(arg, columns, out);
2172 }
2173 }
2174 Expr::Cast { expr, .. } => {
2175 collect_column_refs(expr, columns, out);
2176 }
2177 Expr::WindowFunction { args, spec, .. } => {
2178 for arg in args {
2179 collect_column_refs(arg, columns, out);
2180 }
2181 for pb in &spec.partition_by {
2182 collect_column_refs(pb, columns, out);
2183 }
2184 for ob in &spec.order_by {
2185 collect_column_refs(&ob.expr, columns, out);
2186 }
2187 }
2188 Expr::Literal(_)
2189 | Expr::Parameter(_)
2190 | Expr::CountStar
2191 | Expr::Exists { .. }
2192 | Expr::ScalarSubquery(_) => {}
2193 }
2194}
2195
2196pub fn is_truthy(val: &Value) -> bool {
2198 match val {
2199 Value::Boolean(b) => *b,
2200 Value::Integer(i) => *i != 0,
2201 Value::Null => false,
2202 _ => true,
2203 }
2204}
2205
2206#[cfg(test)]
2207#[path = "eval_tests.rs"]
2208mod tests;