1use rustc_hash::FxHashMap;
4
5use crate::error::{Result, SqlError};
6use crate::parser::{BinOp, Expr, UnaryOp};
7use crate::types::{ColumnDef, CompactString, DataType, Value};
8
9#[derive(Debug)]
10pub struct ColumnMap {
11 exact: FxHashMap<String, usize>,
12 short: FxHashMap<String, ShortMatch>,
13 collations: Vec<crate::types::Collation>,
14 has_non_binary_collation: bool,
15}
16
17#[derive(Clone, Debug)]
18enum ShortMatch {
19 Unique(usize),
20 Ambiguous,
21}
22
23impl Clone for ColumnMap {
24 fn clone(&self) -> Self {
25 Self {
26 exact: self.exact.clone(),
27 short: self.short.clone(),
28 collations: self.collations.clone(),
29 has_non_binary_collation: self.has_non_binary_collation,
30 }
31 }
32}
33
34impl ColumnMap {
35 pub fn new(columns: &[ColumnDef]) -> Self {
36 let mut exact = FxHashMap::with_capacity_and_hasher(columns.len() * 2, Default::default());
37 let mut short: FxHashMap<String, ShortMatch> =
38 FxHashMap::with_capacity_and_hasher(columns.len(), Default::default());
39 let mut collations = Vec::with_capacity(columns.len());
40 let mut has_non_binary_collation = false;
41
42 for (i, col) in columns.iter().enumerate() {
43 let lower = col.name.to_ascii_lowercase();
44 exact.insert(lower.clone(), i);
45
46 let unqualified = if let Some(dot) = lower.rfind('.') {
47 &lower[dot + 1..]
48 } else {
49 &lower
50 };
51 short
52 .entry(unqualified.to_string())
53 .and_modify(|e| *e = ShortMatch::Ambiguous)
54 .or_insert(ShortMatch::Unique(i));
55 collations.push(col.collation);
56 if col.collation != crate::types::Collation::Binary {
57 has_non_binary_collation = true;
58 }
59 }
60
61 Self {
62 exact,
63 short,
64 collations,
65 has_non_binary_collation,
66 }
67 }
68
69 pub(crate) fn collation_at(&self, idx: usize) -> crate::types::Collation {
70 self.collations
71 .get(idx)
72 .copied()
73 .unwrap_or(crate::types::Collation::Binary)
74 }
75
76 #[inline]
77 pub(crate) fn has_non_binary_collation(&self) -> bool {
78 self.has_non_binary_collation
79 }
80
81 pub(crate) fn resolve(&self, name: &str) -> Result<usize> {
82 if let Some(&idx) = self.exact.get(name) {
83 return Ok(idx);
84 }
85 match self.short.get(name) {
86 Some(ShortMatch::Unique(idx)) => Ok(*idx),
87 Some(ShortMatch::Ambiguous) => Err(SqlError::AmbiguousColumn(name.to_string())),
88 None => Err(SqlError::ColumnNotFound(name.to_string())),
89 }
90 }
91
92 pub(crate) fn resolve_qualified(&self, table: &str, column: &str) -> Result<usize> {
93 let qualified = format!("{table}.{column}");
94 if let Some(&idx) = self.exact.get(&qualified) {
95 return Ok(idx);
96 }
97 match self.short.get(column) {
98 Some(ShortMatch::Unique(idx)) => Ok(*idx),
99 _ => Err(SqlError::ColumnNotFound(format!("{table}.{column}"))),
100 }
101 }
102}
103
104pub struct EvalCtx<'a> {
105 pub col_map: &'a ColumnMap,
106 pub row: &'a [Value],
107 pub params: &'a [Value],
108 pub excluded: Option<ExcludedRow<'a>>,
109 pub old_new: Option<OldNewRows<'a>>,
110 pub session_tz: Option<jiff::tz::TimeZone>,
111}
112
113pub struct ExcludedRow<'a> {
114 pub col_map: &'a ColumnMap,
115 pub row: &'a [Value],
116}
117
118pub struct OldNewRows<'a> {
119 pub col_map: &'a ColumnMap,
120 pub old_row: Option<&'a [Value]>,
121 pub new_row: Option<&'a [Value]>,
122}
123
124impl<'a> EvalCtx<'a> {
125 pub fn new(col_map: &'a ColumnMap, row: &'a [Value]) -> Self {
126 Self {
127 col_map,
128 row,
129 params: &[],
130 excluded: None,
131 old_new: None,
132 session_tz: None,
133 }
134 }
135
136 pub fn with_session_tz(mut self, tz: Option<jiff::tz::TimeZone>) -> Self {
137 self.session_tz = tz;
138 self
139 }
140
141 pub fn with_params(col_map: &'a ColumnMap, row: &'a [Value], params: &'a [Value]) -> Self {
142 Self {
143 col_map,
144 row,
145 params,
146 excluded: None,
147 old_new: None,
148 session_tz: None,
149 }
150 }
151
152 pub fn with_excluded(
153 col_map: &'a ColumnMap,
154 row: &'a [Value],
155 excluded_col_map: &'a ColumnMap,
156 excluded_row: &'a [Value],
157 ) -> Self {
158 Self {
159 col_map,
160 row,
161 params: &[],
162 excluded: Some(ExcludedRow {
163 col_map: excluded_col_map,
164 row: excluded_row,
165 }),
166 old_new: None,
167 session_tz: None,
168 }
169 }
170
171 pub fn with_old_new(
172 col_map: &'a ColumnMap,
173 row: &'a [Value],
174 old_row: Option<&'a [Value]>,
175 new_row: Option<&'a [Value]>,
176 ) -> Self {
177 Self {
178 col_map,
179 row,
180 params: &[],
181 excluded: None,
182 old_new: Some(OldNewRows {
183 col_map,
184 old_row,
185 new_row,
186 }),
187 session_tz: None,
188 }
189 }
190}
191
192thread_local! {
193 static SCOPED_PARAMS: std::cell::Cell<(*const Value, usize)> =
194 const { std::cell::Cell::new((std::ptr::null(), 0)) };
195}
196
197pub fn with_scoped_params<R>(params: &[Value], f: impl FnOnce() -> R) -> R {
198 struct Guard((*const Value, usize));
199 impl Drop for Guard {
200 fn drop(&mut self) {
201 SCOPED_PARAMS.with(|slot| slot.set(self.0));
202 }
203 }
204 SCOPED_PARAMS.with(|slot| {
205 let prev = slot.get();
206 slot.set((params.as_ptr(), params.len()));
207 let _guard = Guard(prev);
208 f()
209 })
210}
211
212fn resolve_parameter(n: usize, ctx_params: &[Value]) -> Result<Value> {
213 if !ctx_params.is_empty() {
214 if n == 0 || n > ctx_params.len() {
215 return Err(SqlError::ParameterCountMismatch {
216 expected: n,
217 got: ctx_params.len(),
218 });
219 }
220 return Ok(ctx_params[n - 1].clone());
221 }
222 resolve_scoped_param(n)
223}
224
225pub fn resolve_scoped_param(n: usize) -> Result<Value> {
226 SCOPED_PARAMS.with(|slot| {
227 let (ptr, len) = slot.get();
228 if n == 0 || n > len {
229 return Err(SqlError::ParameterCountMismatch {
230 expected: n,
231 got: len,
232 });
233 }
234 unsafe { Ok((*ptr.add(n - 1)).clone()) }
236 })
237}
238
239pub(crate) enum CompiledExpr<'a> {
240 Const(Value),
241 Slot(usize),
242 Param(usize),
243 BinaryOp {
244 left: Box<CompiledExpr<'a>>,
245 op: BinOp,
246 right: Box<CompiledExpr<'a>>,
247 collation: Option<crate::types::Collation>,
248 },
249 UnaryOp {
250 op: UnaryOp,
251 operand: Box<CompiledExpr<'a>>,
252 },
253 IsNull(Box<CompiledExpr<'a>>),
254 IsNotNull(Box<CompiledExpr<'a>>),
255 Dynamic(&'a Expr),
256}
257
258impl<'a> CompiledExpr<'a> {
259 pub(crate) fn compile(expr: &'a Expr, col_map: &ColumnMap) -> Self {
260 match expr {
261 Expr::Literal(v) => CompiledExpr::Const(v.clone()),
262 Expr::Parameter(n) => CompiledExpr::Param(*n),
263 Expr::Column(name) => match col_map.resolve(name) {
264 Ok(idx) => CompiledExpr::Slot(idx),
265 Err(_) => CompiledExpr::Dynamic(expr),
266 },
267 Expr::IsNull(e) => CompiledExpr::IsNull(Box::new(Self::compile(e, col_map))),
268 Expr::IsNotNull(e) => CompiledExpr::IsNotNull(Box::new(Self::compile(e, col_map))),
269 Expr::UnaryOp { op, expr: e } => CompiledExpr::UnaryOp {
270 op: *op,
271 operand: Box::new(Self::compile(e, col_map)),
272 },
273 Expr::BinaryOp { left, op, right } => CompiledExpr::BinaryOp {
274 collation: compile_collation(left, right, col_map),
275 left: Box::new(Self::compile(left, col_map)),
276 op: *op,
277 right: Box::new(Self::compile(right, col_map)),
278 },
279 _ => CompiledExpr::Dynamic(expr),
280 }
281 }
282
283 pub(crate) fn eval(&self, ctx: &EvalCtx) -> Result<Value> {
284 match self {
285 CompiledExpr::Const(v) => Ok(v.clone()),
286 CompiledExpr::Slot(i) => Ok(ctx.row[*i].clone()),
287 CompiledExpr::Param(n) => resolve_parameter(*n, ctx.params),
288 CompiledExpr::IsNull(e) => Ok(Value::Boolean(e.eval(ctx)?.is_null())),
289 CompiledExpr::IsNotNull(e) => Ok(Value::Boolean(!e.eval(ctx)?.is_null())),
290 CompiledExpr::UnaryOp { op, operand } => {
291 let val = operand.eval(ctx)?;
292 eval_unary_op(*op, &val)
293 }
294 CompiledExpr::BinaryOp {
295 left,
296 op,
297 right,
298 collation,
299 } => {
300 let lval = left.eval(ctx)?;
301 let rval = right.eval(ctx)?;
302 if let Some(coll) = collation {
303 if let Some(b) = eval_text_compare(&lval, *op, &rval, *coll) {
304 return Ok(Value::Boolean(b));
305 }
306 }
307 eval_binary_op(&lval, *op, &rval)
308 }
309 CompiledExpr::Dynamic(e) => eval_expr(e, ctx),
310 }
311 }
312}
313
314fn compile_collation(
315 left: &Expr,
316 right: &Expr,
317 col_map: &ColumnMap,
318) -> Option<crate::types::Collation> {
319 let needs_check = col_map.has_non_binary_collation()
320 || matches!(left, Expr::Collate { .. })
321 || matches!(right, Expr::Collate { .. });
322 if !needs_check {
323 return None;
324 }
325 let coll = collation_of(left)
326 .or_else(|| collation_of(right))
327 .or_else(|| column_collation(left, col_map))
328 .or_else(|| column_collation(right, col_map))?;
329 (coll != crate::types::Collation::Binary).then_some(coll)
330}
331
332pub fn eval_expr(expr: &Expr, ctx: &EvalCtx) -> Result<Value> {
333 match expr {
334 Expr::Literal(v) => Ok(v.clone()),
335
336 Expr::Column(name) => {
337 let idx = ctx.col_map.resolve(name)?;
338 Ok(ctx.row[idx].clone())
339 }
340
341 Expr::QualifiedColumn { table, column } => {
342 if let Some(excluded) = ctx.excluded.as_ref() {
343 if table.eq_ignore_ascii_case("excluded") {
344 let lowered = column.to_ascii_lowercase();
345 let idx = excluded.col_map.resolve(&lowered)?;
346 return Ok(excluded.row[idx].clone());
347 }
348 }
349 if let Some(on) = ctx.old_new.as_ref() {
350 if table.eq_ignore_ascii_case("old") {
351 let lowered = column.to_ascii_lowercase();
352 let idx = on.col_map.resolve(&lowered)?;
353 return Ok(on.old_row.map(|r| r[idx].clone()).unwrap_or(Value::Null));
354 }
355 if table.eq_ignore_ascii_case("new") {
356 let lowered = column.to_ascii_lowercase();
357 let idx = on.col_map.resolve(&lowered)?;
358 return Ok(on.new_row.map(|r| r[idx].clone()).unwrap_or(Value::Null));
359 }
360 }
361 if table.eq_ignore_ascii_case("old") || table.eq_ignore_ascii_case("new") {
363 if let Some(b) = crate::executor::triggers::current_bindings() {
364 let lowered = column.to_ascii_lowercase();
365 if table.eq_ignore_ascii_case("old") {
366 let cm = ColumnMap::new(&b.old_columns);
367 let idx = cm.resolve(&lowered)?;
368 return Ok(b.old_row.map(|r| r[idx].clone()).unwrap_or(Value::Null));
369 } else {
370 let cm = ColumnMap::new(&b.new_columns);
371 let idx = cm.resolve(&lowered)?;
372 return Ok(b.new_row.map(|r| r[idx].clone()).unwrap_or(Value::Null));
373 }
374 }
375 }
376 let idx = ctx.col_map.resolve_qualified(table, column)?;
377 Ok(ctx.row[idx].clone())
378 }
379
380 Expr::BinaryOp { left, op, right } => {
381 let lval = eval_expr(left, ctx)?;
382 let rval = eval_expr(right, ctx)?;
383 let needs_collation_check = ctx.col_map.has_non_binary_collation()
384 || matches!(left.as_ref(), Expr::Collate { .. })
385 || matches!(right.as_ref(), Expr::Collate { .. });
386 if needs_collation_check {
387 let coll = collation_of(left)
388 .or_else(|| collation_of(right))
389 .or_else(|| {
390 column_collation(left, ctx.col_map)
391 .or_else(|| column_collation(right, ctx.col_map))
392 });
393 if let Some(c) = coll {
394 if c != crate::types::Collation::Binary {
395 if let Some(b) = eval_text_compare(&lval, *op, &rval, c) {
396 return Ok(Value::Boolean(b));
397 }
398 }
399 }
400 }
401 eval_binary_op(&lval, *op, &rval)
402 }
403
404 Expr::UnaryOp { op, expr } => {
405 let val = eval_expr(expr, ctx)?;
406 eval_unary_op(*op, &val)
407 }
408
409 Expr::IsNull(e) => {
410 let val = eval_expr(e, ctx)?;
411 Ok(Value::Boolean(val.is_null()))
412 }
413
414 Expr::IsNotNull(e) => {
415 let val = eval_expr(e, ctx)?;
416 Ok(Value::Boolean(!val.is_null()))
417 }
418
419 Expr::Function { name, args, .. } => eval_scalar_function(name, args, ctx),
420
421 Expr::CountStar => Err(SqlError::Unsupported(
422 "COUNT(*) in non-aggregate context".into(),
423 )),
424
425 Expr::InList {
426 expr: e,
427 list,
428 negated,
429 } => {
430 let lhs = eval_expr(e, ctx)?;
431 eval_in_values(&lhs, list, ctx, *negated)
432 }
433
434 Expr::InSet {
435 expr: e,
436 values,
437 has_null,
438 negated,
439 } => {
440 let lhs = eval_expr(e, ctx)?;
441 eval_in_set(&lhs, values, *has_null, *negated)
442 }
443
444 Expr::Between {
445 expr: e,
446 low,
447 high,
448 negated,
449 } => {
450 let val = eval_expr(e, ctx)?;
451 let lo = eval_expr(low, ctx)?;
452 let hi = eval_expr(high, ctx)?;
453 eval_between(&val, &lo, &hi, *negated)
454 }
455
456 Expr::Like {
457 expr: e,
458 pattern,
459 escape,
460 negated,
461 } => {
462 let val = eval_expr(e, ctx)?;
463 let pat = eval_expr(pattern, ctx)?;
464 let esc = escape.as_ref().map(|e| eval_expr(e, ctx)).transpose()?;
465 eval_like(&val, &pat, esc.as_ref(), *negated)
466 }
467
468 Expr::Case {
469 operand,
470 conditions,
471 else_result,
472 } => eval_case(operand.as_deref(), conditions, else_result.as_deref(), ctx),
473
474 Expr::Coalesce(args) => {
475 for arg in args {
476 let val = eval_expr(arg, ctx)?;
477 if !val.is_null() {
478 return Ok(val);
479 }
480 }
481 Ok(Value::Null)
482 }
483
484 Expr::Cast { expr: e, data_type } => {
485 let val = eval_expr(e, ctx)?;
486 eval_cast(&val, *data_type)
487 }
488
489 Expr::Collate { expr: e, .. } => eval_expr(e, ctx),
490
491 Expr::InSubquery { .. } | Expr::Exists { .. } | Expr::ScalarSubquery(_) => Err(
492 SqlError::Unsupported("subquery not materialized (internal error)".into()),
493 ),
494
495 Expr::Parameter(n) => resolve_parameter(*n, ctx.params),
496
497 Expr::WindowFunction { .. } => Err(SqlError::Unsupported(
498 "window functions are only allowed in SELECT columns".into(),
499 )),
500
501 Expr::TypedNullRecord(_) => Ok(Value::Null),
502
503 Expr::ArrayLiteral(elems) => {
504 let mut out = Vec::with_capacity(elems.len());
505 for e in elems {
506 out.push(eval_expr(e, ctx)?);
507 }
508 Ok(Value::Array(std::sync::Arc::new(out)))
509 }
510
511 Expr::Quantified {
512 left,
513 op,
514 quantifier,
515 right,
516 } => eval_quantified(left, *op, *quantifier, right, ctx),
517 }
518}
519
520fn eval_quantified(
521 left: &Expr,
522 op: crate::parser::BinOp,
523 quantifier: crate::parser::Quantifier,
524 right: &crate::parser::QuantifiedRhs,
525 ctx: &EvalCtx,
526) -> Result<Value> {
527 use crate::parser::{QuantifiedRhs, Quantifier};
528 let lhs = eval_expr(left, ctx)?;
529 let elems: Vec<Value> = match right {
530 QuantifiedRhs::Array(e) => match eval_expr(e, ctx)? {
531 Value::Array(a) => (*a).clone(),
532 Value::Null => return Ok(Value::Null),
533 other => {
534 return Err(SqlError::TypeMismatch {
535 expected: "ARRAY".into(),
536 got: other.data_type().to_string(),
537 });
538 }
539 },
540 QuantifiedRhs::Subquery(_) => {
541 return Err(SqlError::Unsupported(
542 "ANY/ALL subquery not materialized (internal error)".into(),
543 ));
544 }
545 };
546
547 if lhs.is_null() {
548 return if elems.is_empty() {
549 match quantifier {
550 Quantifier::Any => Ok(Value::Boolean(false)),
551 Quantifier::All => Ok(Value::Boolean(true)),
552 }
553 } else {
554 Ok(Value::Null)
555 };
556 }
557
558 let mut any_unknown = false;
559 let mut any_match = false;
560 let mut any_mismatch = false;
561 for elem in &elems {
562 if elem.is_null() {
563 any_unknown = true;
564 continue;
565 }
566 let result = eval_binary_compare(&lhs, op, elem)?;
567 match result {
568 Value::Boolean(true) => any_match = true,
569 Value::Boolean(false) => any_mismatch = true,
570 Value::Null => any_unknown = true,
571 _ => {
572 return Err(SqlError::TypeMismatch {
573 expected: "BOOLEAN".into(),
574 got: result.data_type().to_string(),
575 });
576 }
577 }
578 }
579
580 match quantifier {
581 Quantifier::Any => {
582 if any_match {
583 Ok(Value::Boolean(true))
584 } else if any_unknown {
585 Ok(Value::Null)
586 } else {
587 Ok(Value::Boolean(false))
588 }
589 }
590 Quantifier::All => {
591 if any_mismatch {
592 Ok(Value::Boolean(false))
593 } else if any_unknown {
594 Ok(Value::Null)
595 } else {
596 Ok(Value::Boolean(true))
597 }
598 }
599 }
600}
601
602fn eval_binary_compare(left: &Value, op: crate::parser::BinOp, right: &Value) -> Result<Value> {
603 use crate::parser::BinOp;
604 if left.is_null() || right.is_null() {
605 return Ok(Value::Null);
606 }
607 let cmp = match (left, right) {
608 (Value::Text(a), Value::Text(b)) => Some(a.cmp(b)),
609 _ => left.partial_cmp(right),
610 };
611 let Some(cmp) = cmp else {
612 return Ok(Value::Null);
613 };
614 use std::cmp::Ordering;
615 let result = match op {
616 BinOp::Eq => cmp == Ordering::Equal,
617 BinOp::NotEq => cmp != Ordering::Equal,
618 BinOp::Lt => cmp == Ordering::Less,
619 BinOp::Gt => cmp == Ordering::Greater,
620 BinOp::LtEq => cmp != Ordering::Greater,
621 BinOp::GtEq => cmp != Ordering::Less,
622 _ => {
623 return Err(SqlError::Unsupported(format!(
624 "ANY/ALL comparison op {op:?}"
625 )));
626 }
627 };
628 Ok(Value::Boolean(result))
629}
630
631fn collation_of(expr: &Expr) -> Option<crate::types::Collation> {
632 match expr {
633 Expr::Collate { collation, .. } => Some(*collation),
634 _ => None,
635 }
636}
637
638fn column_collation(expr: &Expr, col_map: &ColumnMap) -> Option<crate::types::Collation> {
639 match expr {
640 Expr::Column(name) => col_map.resolve(name).ok().map(|i| col_map.collation_at(i)),
641 Expr::QualifiedColumn { table, column } => col_map
642 .resolve_qualified(table, column)
643 .ok()
644 .map(|i| col_map.collation_at(i)),
645 _ => None,
646 }
647}
648
649fn eval_text_compare(
650 left: &Value,
651 op: BinOp,
652 right: &Value,
653 coll: crate::types::Collation,
654) -> Option<bool> {
655 let (a, b) = match (left, right) {
656 (Value::Null, _) | (_, Value::Null) => return None,
657 (Value::Text(a), Value::Text(b)) => (a.as_str(), b.as_str()),
658 _ => return None,
659 };
660 let ord = coll.cmp_text(a, b);
661 Some(match op {
662 BinOp::Eq => ord == std::cmp::Ordering::Equal,
663 BinOp::NotEq => ord != std::cmp::Ordering::Equal,
664 BinOp::Lt => ord == std::cmp::Ordering::Less,
665 BinOp::Gt => ord == std::cmp::Ordering::Greater,
666 BinOp::LtEq => ord != std::cmp::Ordering::Greater,
667 BinOp::GtEq => ord != std::cmp::Ordering::Less,
668 _ => return None,
669 })
670}
671
672pub fn eval_binary_op_public(left: &Value, op: BinOp, right: &Value) -> Result<Value> {
673 eval_binary_op(left, op, right)
674}
675
676fn eval_binary_op(left: &Value, op: BinOp, right: &Value) -> Result<Value> {
677 match op {
678 BinOp::And => return eval_and(left, right),
679 BinOp::Or => return eval_or(left, right),
680 _ => {}
681 }
682
683 if left.is_null() || right.is_null() {
684 return Ok(Value::Null);
685 }
686
687 if let Some(res) = eval_temporal_op(left, op, right) {
688 return res;
689 }
690
691 match op {
692 BinOp::Eq => Ok(Value::Boolean(left == right)),
693 BinOp::NotEq => Ok(Value::Boolean(left != right)),
694 BinOp::Lt => Ok(Value::Boolean(left < right)),
695 BinOp::Gt => Ok(Value::Boolean(left > right)),
696 BinOp::LtEq => Ok(Value::Boolean(left <= right)),
697 BinOp::GtEq => Ok(Value::Boolean(left >= right)),
698 BinOp::Add => eval_arithmetic(left, right, i64::checked_add, |a, b| a + b),
699 BinOp::Sub => match left {
700 Value::Json(_) | Value::Jsonb(_) => crate::json::op_delete_one(left, right),
701 _ => eval_arithmetic(left, right, i64::checked_sub, |a, b| a - b),
702 },
703 BinOp::Mul => eval_arithmetic(left, right, i64::checked_mul, |a, b| a * b),
704 BinOp::Div => {
705 match right {
706 Value::Integer(0) => return Err(SqlError::DivisionByZero),
707 Value::Real(r) if *r == 0.0 => return Err(SqlError::DivisionByZero),
708 _ => {}
709 }
710 eval_arithmetic(left, right, i64::checked_div, |a, b| a / b)
711 }
712 BinOp::Mod => {
713 match right {
714 Value::Integer(0) => return Err(SqlError::DivisionByZero),
715 Value::Real(r) if *r == 0.0 => return Err(SqlError::DivisionByZero),
716 _ => {}
717 }
718 eval_arithmetic(left, right, i64::checked_rem, |a, b| a % b)
719 }
720 BinOp::Concat => match (left, right) {
721 (Value::TsVector(a), Value::TsVector(b)) => crate::fts::op_concat(a, b),
722 (Value::Json(_) | Value::Jsonb(_), _) | (_, Value::Json(_) | Value::Jsonb(_)) => {
723 crate::json::op_concat(left, right)
724 }
725 _ => {
726 let ls = value_to_text(left);
727 let rs = value_to_text(right);
728 Ok(Value::Text(format!("{ls}{rs}").into()))
729 }
730 },
731 BinOp::JsonGet
732 | BinOp::JsonGetText
733 | BinOp::JsonPath
734 | BinOp::JsonPathText
735 | BinOp::JsonContains
736 | BinOp::JsonContainedBy
737 | BinOp::JsonHasKey
738 | BinOp::JsonHasAnyKey
739 | BinOp::JsonHasAllKeys
740 | BinOp::JsonDeletePath
741 | BinOp::JsonPathExists
742 | BinOp::JsonPathMatch
743 | BinOp::JsonPathExistsTz
744 | BinOp::JsonPathMatchTz => eval_json_binary_op(left, op, right),
745 BinOp::VectorL2 => eval_vector_distance(left, right, VectorMetric::L2),
746 BinOp::VectorInner => eval_vector_distance(left, right, VectorMetric::Inner),
747 BinOp::VectorCosine => eval_vector_distance(left, right, VectorMetric::Cosine),
748 BinOp::And | BinOp::Or => unreachable!(),
749 }
750}
751
752#[cold]
753fn eval_json_binary_op(left: &Value, op: BinOp, right: &Value) -> Result<Value> {
754 match op {
755 BinOp::JsonGet => crate::json::op_get(left, right),
756 BinOp::JsonGetText => crate::json::op_get_text(left, right),
757 BinOp::JsonPath => crate::json::op_path(left, right),
758 BinOp::JsonPathText => crate::json::op_path_text(left, right),
759 BinOp::JsonContains => crate::json::op_contains(left, right),
760 BinOp::JsonContainedBy => crate::json::op_contained_by(left, right),
761 BinOp::JsonHasKey => crate::json::op_has_key(left, right),
762 BinOp::JsonHasAnyKey => crate::json::op_has_any_key(left, right),
763 BinOp::JsonHasAllKeys => crate::json::op_has_all_keys(left, right),
764 BinOp::JsonDeletePath => crate::json::op_delete_path(left, right),
765 BinOp::JsonPathExists => crate::json::op_path_exists(left, right),
766 BinOp::JsonPathMatch => eval_at_at(left, right),
767 BinOp::JsonPathExistsTz => {
768 crate::json::fn_jsonb_path_exists_tz(&[left.clone(), right.clone()])
769 }
770 BinOp::JsonPathMatchTz => {
771 crate::json::fn_jsonb_path_match_tz(&[left.clone(), right.clone()])
772 }
773 BinOp::VectorL2 => eval_vector_distance(left, right, VectorMetric::L2),
774 BinOp::VectorInner => eval_vector_distance(left, right, VectorMetric::Inner),
775 BinOp::VectorCosine => eval_vector_distance(left, right, VectorMetric::Cosine),
776 _ => unreachable!(),
777 }
778}
779
780#[derive(Copy, Clone)]
781enum VectorMetric {
782 L2,
783 Inner,
784 Cosine,
785}
786
787fn eval_vector_distance(left: &Value, right: &Value, metric: VectorMetric) -> Result<Value> {
788 if left.is_null() || right.is_null() {
789 return Ok(Value::Null);
790 }
791 let (a, b) = match (left, right) {
792 (Value::Vector(a), Value::Vector(b)) => (a.as_ref(), b.as_ref()),
793 _ => {
794 return Err(SqlError::TypeMismatch {
795 expected: "VECTOR".into(),
796 got: format!("{} vs {}", left.data_type(), right.data_type()),
797 });
798 }
799 };
800 if a.len() != b.len() {
801 return Err(SqlError::InvalidValue(format!(
802 "vector dimension mismatch: {} vs {}",
803 a.len(),
804 b.len()
805 )));
806 }
807 let d = match metric {
808 VectorMetric::L2 => {
809 let mut sum = 0.0f64;
810 for (x, y) in a.iter().zip(b.iter()) {
811 let diff = (*x as f64) - (*y as f64);
812 sum += diff * diff;
813 }
814 sum.sqrt()
815 }
816 VectorMetric::Inner => {
817 let mut sum = 0.0f64;
818 for (x, y) in a.iter().zip(b.iter()) {
819 sum += (*x as f64) * (*y as f64);
820 }
821 -sum
822 }
823 VectorMetric::Cosine => {
824 let mut dot = 0.0f64;
825 let mut na = 0.0f64;
826 let mut nb = 0.0f64;
827 for (x, y) in a.iter().zip(b.iter()) {
828 let xf = *x as f64;
829 let yf = *y as f64;
830 dot += xf * yf;
831 na += xf * xf;
832 nb += yf * yf;
833 }
834 let denom = na.sqrt() * nb.sqrt();
835 if denom == 0.0 {
836 return Ok(Value::Null);
837 }
838 1.0 - dot / denom
839 }
840 };
841 Ok(Value::Real(d))
842}
843
844fn eval_at_at(left: &Value, right: &Value) -> Result<Value> {
845 use crate::types::DataType as D;
846 if left.is_null() || right.is_null() {
847 return Ok(Value::Null);
848 }
849 match (left.data_type(), right.data_type()) {
850 (D::Json | D::Jsonb, D::Text) => crate::json::op_path_match(left, right),
851 (D::TsVector, D::TsQuery) => match (left, right) {
852 (Value::TsVector(v), Value::TsQuery(q)) => crate::fts::op_match(v, q),
853 _ => unreachable!(),
854 },
855 (D::TsQuery, D::TsVector) => match (left, right) {
856 (Value::TsQuery(q), Value::TsVector(v)) => crate::fts::op_match(v, q),
857 _ => unreachable!(),
858 },
859 (D::Text, D::TsQuery) => {
860 let s = match left {
861 Value::Text(s) => s.as_str(),
862 _ => unreachable!(),
863 };
864 let lhs = crate::fts::fn_to_tsvector(s)?;
865 eval_at_at(&lhs, right)
866 }
867 (D::TsVector, D::Text) => {
868 let s = match right {
869 Value::Text(s) => s.as_str(),
870 _ => unreachable!(),
871 };
872 let rhs = crate::fts::fn_plainto_tsquery(s)?;
873 eval_at_at(left, &rhs)
874 }
875 (D::Text, D::Text) => {
876 let ls = match left {
877 Value::Text(s) => s.as_str(),
878 _ => unreachable!(),
879 };
880 let rs = match right {
881 Value::Text(s) => s.as_str(),
882 _ => unreachable!(),
883 };
884 let lhs = crate::fts::fn_to_tsvector(ls)?;
885 let rhs = crate::fts::fn_plainto_tsquery(rs)?;
886 eval_at_at(&lhs, &rhs)
887 }
888 (lt, rt) => Err(SqlError::TypeMismatch {
889 expected: "JSONB @@ text, tsvector @@ tsquery".into(),
890 got: format!("{lt} @@ {rt}"),
891 }),
892 }
893}
894
895fn eval_temporal_op(left: &Value, op: BinOp, right: &Value) -> Option<Result<Value>> {
897 use crate::datetime as dt;
898 use std::cmp::Ordering;
899
900 let is_temporal = |v: &Value| {
901 matches!(
902 v,
903 Value::Date(_) | Value::Time(_) | Value::Timestamp(_) | Value::Interval { .. }
904 )
905 };
906 if !is_temporal(left) && !is_temporal(right) {
907 return None;
908 }
909 if matches!(op, BinOp::Add | BinOp::Sub)
910 && ((is_temporal(left) && matches!(right, Value::Real(_)))
911 || (matches!(left, Value::Real(_)) && is_temporal(right)))
912 {
913 return Some(Err(SqlError::TypeMismatch {
914 expected: "INTEGER or INTERVAL for date/time arithmetic (use CAST for REAL)".into(),
915 got: format!("{} and {}", left.data_type(), right.data_type()),
916 }));
917 }
918
919 match (left, op, right) {
920 (Value::Date(d), BinOp::Add, Value::Integer(n))
921 | (Value::Integer(n), BinOp::Add, Value::Date(d)) => {
922 Some(dt::add_days_to_date(*d, *n).map(Value::Date))
923 }
924 (Value::Date(d), BinOp::Sub, Value::Integer(n)) => {
925 Some(dt::add_days_to_date(*d, -*n).map(Value::Date))
926 }
927 (Value::Date(a), BinOp::Sub, Value::Date(b)) => {
928 Some(Ok(Value::Integer(*a as i64 - *b as i64)))
929 }
930 (
932 Value::Date(d),
933 BinOp::Add,
934 Value::Interval {
935 months,
936 days,
937 micros,
938 },
939 )
940 | (
941 Value::Interval {
942 months,
943 days,
944 micros,
945 },
946 BinOp::Add,
947 Value::Date(d),
948 ) => Some(dt::add_interval_to_date(*d, *months, *days, *micros).map(Value::Timestamp)),
949 (
950 Value::Date(d),
951 BinOp::Sub,
952 Value::Interval {
953 months,
954 days,
955 micros,
956 },
957 ) => Some(dt::add_interval_to_date(*d, -*months, -*days, -*micros).map(Value::Timestamp)),
958 (
959 Value::Timestamp(t),
960 BinOp::Add,
961 Value::Interval {
962 months,
963 days,
964 micros,
965 },
966 )
967 | (
968 Value::Interval {
969 months,
970 days,
971 micros,
972 },
973 BinOp::Add,
974 Value::Timestamp(t),
975 ) => Some(dt::add_interval_to_timestamp(*t, *months, *days, *micros).map(Value::Timestamp)),
976 (
977 Value::Timestamp(t),
978 BinOp::Sub,
979 Value::Interval {
980 months,
981 days,
982 micros,
983 },
984 ) => Some(
985 dt::add_interval_to_timestamp(*t, -*months, -*days, -*micros).map(Value::Timestamp),
986 ),
987 (Value::Timestamp(a), BinOp::Sub, Value::Timestamp(b)) => {
988 let (days, micros) = dt::subtract_timestamps(*a, *b);
989 Some(Ok(Value::Interval {
990 months: 0,
991 days,
992 micros,
993 }))
994 }
995 (
996 Value::Time(t),
997 BinOp::Add,
998 Value::Interval {
999 months,
1000 days,
1001 micros,
1002 },
1003 ) => Some(dt::add_interval_to_time(*t, *months, *days, *micros).map(Value::Time)),
1004 (
1005 Value::Time(t),
1006 BinOp::Sub,
1007 Value::Interval {
1008 months,
1009 days,
1010 micros,
1011 },
1012 ) => Some(dt::add_interval_to_time(*t, -*months, -*days, -*micros).map(Value::Time)),
1013 (Value::Time(a), BinOp::Sub, Value::Time(b)) => Some(Ok(Value::Interval {
1014 months: 0,
1015 days: 0,
1016 micros: *a - *b,
1017 })),
1018 (
1019 Value::Interval {
1020 months: am,
1021 days: ad,
1022 micros: au,
1023 },
1024 BinOp::Add,
1025 Value::Interval {
1026 months: bm,
1027 days: bd,
1028 micros: bu,
1029 },
1030 ) => Some(Ok(Value::Interval {
1031 months: am.saturating_add(*bm),
1032 days: ad.saturating_add(*bd),
1033 micros: au.saturating_add(*bu),
1034 })),
1035 (
1036 Value::Interval {
1037 months: am,
1038 days: ad,
1039 micros: au,
1040 },
1041 BinOp::Sub,
1042 Value::Interval {
1043 months: bm,
1044 days: bd,
1045 micros: bu,
1046 },
1047 ) => Some(Ok(Value::Interval {
1048 months: am.saturating_sub(*bm),
1049 days: ad.saturating_sub(*bd),
1050 micros: au.saturating_sub(*bu),
1051 })),
1052 (
1053 Value::Interval {
1054 months,
1055 days,
1056 micros,
1057 },
1058 BinOp::Mul,
1059 Value::Integer(n),
1060 )
1061 | (
1062 Value::Integer(n),
1063 BinOp::Mul,
1064 Value::Interval {
1065 months,
1066 days,
1067 micros,
1068 },
1069 ) => {
1070 let n32 = (*n).clamp(i32::MIN as i64, i32::MAX as i64) as i32;
1071 Some(Ok(Value::Interval {
1072 months: months.saturating_mul(n32),
1073 days: days.saturating_mul(n32),
1074 micros: micros.saturating_mul(*n),
1075 }))
1076 }
1077 (
1079 Value::Interval {
1080 months,
1081 days,
1082 micros,
1083 },
1084 BinOp::Mul,
1085 Value::Real(r),
1086 )
1087 | (
1088 Value::Real(r),
1089 BinOp::Mul,
1090 Value::Interval {
1091 months,
1092 days,
1093 micros,
1094 },
1095 ) => Some(Ok(scale_interval_by_real(*months, *days, *micros, *r))),
1096 (
1097 Value::Interval {
1098 months,
1099 days,
1100 micros,
1101 },
1102 BinOp::Div,
1103 Value::Integer(n),
1104 ) if *n != 0 => Some(Ok(Value::Interval {
1105 months: (*months as i64 / *n) as i32,
1106 days: (*days as i64 / *n) as i32,
1107 micros: *micros / *n,
1108 })),
1109 (
1110 Value::Interval {
1111 months,
1112 days,
1113 micros,
1114 },
1115 BinOp::Div,
1116 Value::Real(r),
1117 ) if *r != 0.0 => Some(Ok(scale_interval_by_real(*months, *days, *micros, 1.0 / r))),
1118 (
1120 Value::Interval {
1121 months: am,
1122 days: ad,
1123 micros: au,
1124 },
1125 op,
1126 Value::Interval {
1127 months: bm,
1128 days: bd,
1129 micros: bu,
1130 },
1131 ) if matches!(
1132 op,
1133 BinOp::Eq | BinOp::NotEq | BinOp::Lt | BinOp::Gt | BinOp::LtEq | BinOp::GtEq
1134 ) =>
1135 {
1136 let ord = dt::pg_normalized_interval_cmp((*am, *ad, *au), (*bm, *bd, *bu));
1137 let b = match op {
1138 BinOp::Eq => ord == Ordering::Equal,
1139 BinOp::NotEq => ord != Ordering::Equal,
1140 BinOp::Lt => ord == Ordering::Less,
1141 BinOp::Gt => ord == Ordering::Greater,
1142 BinOp::LtEq => ord != Ordering::Greater,
1143 BinOp::GtEq => ord != Ordering::Less,
1144 _ => unreachable!(),
1145 };
1146 Some(Ok(Value::Boolean(b)))
1147 }
1148 (Value::Timestamp(_), BinOp::Add | BinOp::Sub, Value::Integer(_))
1150 | (Value::Integer(_), BinOp::Add, Value::Timestamp(_)) => {
1151 Some(Err(SqlError::TypeMismatch {
1152 expected: "INTERVAL (use CAST or explicit unit)".into(),
1153 got: format!("{} and {}", left.data_type(), right.data_type()),
1154 }))
1155 }
1156 (l, op, r)
1158 if matches!(
1159 op,
1160 BinOp::Eq | BinOp::NotEq | BinOp::Lt | BinOp::Gt | BinOp::LtEq | BinOp::GtEq
1161 ) =>
1162 {
1163 temporal_compare(l, op, r)
1164 }
1165 _ => None,
1166 }
1167}
1168
1169fn temporal_compare(left: &Value, op: BinOp, right: &Value) -> Option<Result<Value>> {
1171 let (a, b) = coerce_temporal_pair(left, right)?;
1172 let ord = a.cmp(&b);
1173 use std::cmp::Ordering;
1174 let result = match op {
1175 BinOp::Eq => ord == Ordering::Equal,
1176 BinOp::NotEq => ord != Ordering::Equal,
1177 BinOp::Lt => ord == Ordering::Less,
1178 BinOp::Gt => ord == Ordering::Greater,
1179 BinOp::LtEq => ord != Ordering::Greater,
1180 BinOp::GtEq => ord != Ordering::Less,
1181 _ => return None,
1182 };
1183 Some(Ok(Value::Boolean(result)))
1184}
1185
1186fn coerce_temporal_pair(left: &Value, right: &Value) -> Option<(Value, Value)> {
1188 use crate::types::DataType;
1189 let temporal_type = |v: &Value| match v {
1190 Value::Date(_) => Some(DataType::Date),
1191 Value::Time(_) => Some(DataType::Time),
1192 Value::Timestamp(_) => Some(DataType::Timestamp),
1193 Value::Interval { .. } => Some(DataType::Interval),
1194 _ => None,
1195 };
1196 match (temporal_type(left), temporal_type(right)) {
1197 (Some(DataType::Date), Some(DataType::Timestamp))
1198 | (Some(DataType::Timestamp), Some(DataType::Date)) => Some((
1199 left.clone().coerce_into(DataType::Timestamp)?,
1200 right.clone().coerce_into(DataType::Timestamp)?,
1201 )),
1202 (Some(_), Some(_)) => None,
1203 (Some(t), None) => {
1204 let coerced = right.clone().coerce_into(t)?;
1205 Some((left.clone(), coerced))
1206 }
1207 (None, Some(t)) => {
1208 let coerced = left.clone().coerce_into(t)?;
1209 Some((coerced, right.clone()))
1210 }
1211 (None, None) => None,
1212 }
1213}
1214
1215fn scale_interval_by_real(months: i32, days: i32, micros: i64, factor: f64) -> Value {
1217 let raw_months = months as f64 * factor;
1218 let whole_months = raw_months.trunc() as i64;
1219 let frac_months = raw_months - whole_months as f64;
1220 let months_frac_as_days = frac_months * 30.0;
1221
1222 let raw_days = days as f64 * factor + months_frac_as_days;
1223 let whole_days = raw_days.trunc() as i64;
1224 let frac_days = raw_days - whole_days as f64;
1225 let days_frac_as_micros = (frac_days * crate::datetime::MICROS_PER_DAY as f64).round() as i64;
1226
1227 let raw_micros = (micros as f64 * factor).round() as i64;
1228 let total_micros = raw_micros.saturating_add(days_frac_as_micros);
1229
1230 let clamp_i32 = |n: i64| n.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
1231 Value::Interval {
1232 months: clamp_i32(whole_months),
1233 days: clamp_i32(whole_days),
1234 micros: total_micros,
1235 }
1236}
1237
1238fn eval_and(left: &Value, right: &Value) -> Result<Value> {
1240 let l = to_bool_or_null(left)?;
1241 let r = to_bool_or_null(right)?;
1242 match (l, r) {
1243 (Some(false), _) | (_, Some(false)) => Ok(Value::Boolean(false)),
1244 (Some(true), Some(true)) => Ok(Value::Boolean(true)),
1245 _ => Ok(Value::Null),
1246 }
1247}
1248
1249fn eval_or(left: &Value, right: &Value) -> Result<Value> {
1251 let l = to_bool_or_null(left)?;
1252 let r = to_bool_or_null(right)?;
1253 match (l, r) {
1254 (Some(true), _) | (_, Some(true)) => Ok(Value::Boolean(true)),
1255 (Some(false), Some(false)) => Ok(Value::Boolean(false)),
1256 _ => Ok(Value::Null),
1257 }
1258}
1259
1260fn to_bool_or_null(val: &Value) -> Result<Option<bool>> {
1261 match val {
1262 Value::Boolean(b) => Ok(Some(*b)),
1263 Value::Null => Ok(None),
1264 Value::Integer(i) => Ok(Some(*i != 0)),
1265 _ => Err(SqlError::TypeMismatch {
1266 expected: "BOOLEAN".into(),
1267 got: format!("{}", val.data_type()),
1268 }),
1269 }
1270}
1271
1272fn eval_arithmetic(
1273 left: &Value,
1274 right: &Value,
1275 int_op: fn(i64, i64) -> Option<i64>,
1276 real_op: fn(f64, f64) -> f64,
1277) -> Result<Value> {
1278 match (left, right) {
1279 (Value::Integer(a), Value::Integer(b)) => int_op(*a, *b)
1280 .map(Value::Integer)
1281 .ok_or(SqlError::IntegerOverflow),
1282 (Value::Real(a), Value::Real(b)) => Ok(Value::Real(real_op(*a, *b))),
1283 (Value::Integer(a), Value::Real(b)) => Ok(Value::Real(real_op(*a as f64, *b))),
1284 (Value::Real(a), Value::Integer(b)) => Ok(Value::Real(real_op(*a, *b as f64))),
1285 _ => Err(SqlError::TypeMismatch {
1286 expected: "numeric".into(),
1287 got: format!("{} and {}", left.data_type(), right.data_type()),
1288 }),
1289 }
1290}
1291
1292fn eval_in_values(lhs: &Value, list: &[Expr], ctx: &EvalCtx, negated: bool) -> Result<Value> {
1293 if list.is_empty() {
1294 return Ok(Value::Boolean(negated));
1295 }
1296 if lhs.is_null() {
1297 return Ok(Value::Null);
1298 }
1299 let mut has_null = false;
1300 for item in list {
1301 let rhs = eval_expr(item, ctx)?;
1302 if rhs.is_null() {
1303 has_null = true;
1304 } else if lhs == &rhs {
1305 return Ok(Value::Boolean(!negated));
1306 }
1307 }
1308 if has_null {
1309 Ok(Value::Null)
1310 } else {
1311 Ok(Value::Boolean(negated))
1312 }
1313}
1314
1315fn eval_in_set(
1316 lhs: &Value,
1317 values: &rustc_hash::FxHashSet<Value>,
1318 has_null: bool,
1319 negated: bool,
1320) -> Result<Value> {
1321 if values.is_empty() && !has_null {
1322 return Ok(Value::Boolean(negated));
1323 }
1324 if lhs.is_null() {
1325 return Ok(Value::Null);
1326 }
1327 if values.contains(lhs) {
1328 return Ok(Value::Boolean(!negated));
1329 }
1330 if has_null {
1331 Ok(Value::Null)
1332 } else {
1333 Ok(Value::Boolean(negated))
1334 }
1335}
1336
1337fn eval_unary_op(op: UnaryOp, val: &Value) -> Result<Value> {
1338 if val.is_null() {
1339 return Ok(Value::Null);
1340 }
1341 match op {
1342 UnaryOp::Neg => match val {
1343 Value::Integer(i) => i
1344 .checked_neg()
1345 .map(Value::Integer)
1346 .ok_or(SqlError::IntegerOverflow),
1347 Value::Real(r) => Ok(Value::Real(-r)),
1348 Value::Interval {
1349 months,
1350 days,
1351 micros,
1352 } => {
1353 let m = months.checked_neg().ok_or(SqlError::IntegerOverflow)?;
1354 let d = days.checked_neg().ok_or(SqlError::IntegerOverflow)?;
1355 let u = micros.checked_neg().ok_or(SqlError::IntegerOverflow)?;
1356 Ok(Value::Interval {
1357 months: m,
1358 days: d,
1359 micros: u,
1360 })
1361 }
1362 _ => Err(SqlError::TypeMismatch {
1363 expected: "numeric or INTERVAL".into(),
1364 got: format!("{}", val.data_type()),
1365 }),
1366 },
1367 UnaryOp::Not => match val {
1368 Value::Boolean(b) => Ok(Value::Boolean(!b)),
1369 Value::Integer(i) => Ok(Value::Boolean(*i == 0)),
1370 _ => Err(SqlError::TypeMismatch {
1371 expected: "BOOLEAN".into(),
1372 got: format!("{}", val.data_type()),
1373 }),
1374 },
1375 }
1376}
1377
1378fn value_to_text(val: &Value) -> String {
1379 match val {
1380 Value::Text(s) => s.to_string(),
1381 Value::Integer(i) => i.to_string(),
1382 Value::Real(r) => {
1383 if r.fract() == 0.0 && r.is_finite() {
1384 format!("{r:.1}")
1385 } else {
1386 format!("{r}")
1387 }
1388 }
1389 Value::Boolean(b) => if *b { "TRUE" } else { "FALSE" }.into(),
1390 Value::Null => String::new(),
1391 Value::Blob(b) => {
1392 let mut s = String::with_capacity(b.len() * 2);
1393 for byte in b {
1394 s.push_str(&format!("{byte:02X}"));
1395 }
1396 s
1397 }
1398 Value::Date(d) => crate::datetime::format_date(*d),
1399 Value::Time(t) => crate::datetime::format_time(*t),
1400 Value::Timestamp(t) => crate::datetime::format_timestamp(*t),
1401 Value::Interval {
1402 months,
1403 days,
1404 micros,
1405 } => crate::datetime::format_interval(*months, *days, *micros),
1406 Value::Json(s) => s.to_string(),
1407 Value::Jsonb(b) => crate::json::decode_to_text(b).unwrap_or_default(),
1408 Value::TsVector(b) => crate::fts::tsvector_display(b),
1409 Value::TsQuery(b) => crate::fts::tsquery_display(b),
1410 Value::Array(_) => val.to_string(),
1411 Value::Vector(_) => val.to_string(),
1412 }
1413}
1414
1415fn eval_between(val: &Value, low: &Value, high: &Value, negated: bool) -> Result<Value> {
1416 let ge = eval_binary_op(val, BinOp::GtEq, low)?;
1418 let le = eval_binary_op(val, BinOp::LtEq, high)?;
1419
1420 let result = match (as_bool(&ge), as_bool(&le)) {
1421 (Some(false), _) | (_, Some(false)) => Some(false),
1422 (Some(true), Some(true)) => Some(true),
1423 _ => None,
1424 };
1425
1426 match result {
1427 Some(b) => Ok(Value::Boolean(if negated { !b } else { b })),
1428 None => Ok(Value::Null),
1429 }
1430}
1431
1432fn as_bool(v: &Value) -> Option<bool> {
1433 match v {
1434 Value::Boolean(b) => Some(*b),
1435 _ => None,
1436 }
1437}
1438
1439const MAX_LIKE_PATTERN_LEN: usize = 10_000;
1440
1441fn eval_like(val: &Value, pattern: &Value, escape: Option<&Value>, negated: bool) -> Result<Value> {
1442 if val.is_null() || pattern.is_null() {
1443 return Ok(Value::Null);
1444 }
1445 let text = match val {
1446 Value::Text(s) => s.as_str(),
1447 _ => {
1448 return Err(SqlError::TypeMismatch {
1449 expected: "TEXT".into(),
1450 got: val.data_type().to_string(),
1451 })
1452 }
1453 };
1454 let pat = match pattern {
1455 Value::Text(s) => s.as_str(),
1456 _ => {
1457 return Err(SqlError::TypeMismatch {
1458 expected: "TEXT".into(),
1459 got: pattern.data_type().to_string(),
1460 })
1461 }
1462 };
1463
1464 if pat.len() > MAX_LIKE_PATTERN_LEN {
1465 return Err(SqlError::InvalidValue(format!(
1466 "LIKE pattern too long ({} chars, max {MAX_LIKE_PATTERN_LEN})",
1467 pat.len()
1468 )));
1469 }
1470
1471 let esc_char = match escape {
1472 Some(Value::Text(s)) => {
1473 let mut chars = s.chars();
1474 let c = chars.next().ok_or_else(|| {
1475 SqlError::InvalidValue("ESCAPE must be a single character".into())
1476 })?;
1477 if chars.next().is_some() {
1478 return Err(SqlError::InvalidValue(
1479 "ESCAPE must be a single character".into(),
1480 ));
1481 }
1482 Some(c)
1483 }
1484 Some(Value::Null) => return Ok(Value::Null),
1485 Some(_) => {
1486 return Err(SqlError::TypeMismatch {
1487 expected: "TEXT".into(),
1488 got: "non-text".into(),
1489 })
1490 }
1491 None => None,
1492 };
1493
1494 let matched = like_match(text, pat, esc_char);
1495 Ok(Value::Boolean(if negated { !matched } else { matched }))
1496}
1497
1498fn like_match(text: &str, pattern: &str, escape: Option<char>) -> bool {
1499 let t: Vec<char> = text.chars().collect();
1500 let p: Vec<char> = pattern.chars().collect();
1501 like_match_impl(&t, &p, 0, 0, escape)
1502}
1503
1504fn like_match_impl(
1505 t: &[char],
1506 p: &[char],
1507 mut ti: usize,
1508 mut pi: usize,
1509 esc: Option<char>,
1510) -> bool {
1511 let mut star_pi: Option<usize> = None;
1512 let mut star_ti: usize = 0;
1513
1514 while ti < t.len() {
1515 if pi < p.len() {
1516 if let Some(ec) = esc {
1517 if p[pi] == ec && pi + 1 < p.len() {
1518 pi += 1;
1519 let pc_lower = p[pi].to_ascii_lowercase();
1520 let tc_lower = t[ti].to_ascii_lowercase();
1521 if pc_lower == tc_lower {
1522 pi += 1;
1523 ti += 1;
1524 continue;
1525 } else if let Some(sp) = star_pi {
1526 pi = sp + 1;
1527 star_ti += 1;
1528 ti = star_ti;
1529 continue;
1530 } else {
1531 return false;
1532 }
1533 }
1534 }
1535 if p[pi] == '%' {
1536 star_pi = Some(pi);
1537 star_ti = ti;
1538 pi += 1;
1539 continue;
1540 }
1541 if p[pi] == '_' {
1542 pi += 1;
1543 ti += 1;
1544 continue;
1545 }
1546 if p[pi].eq_ignore_ascii_case(&t[ti]) {
1547 pi += 1;
1548 ti += 1;
1549 continue;
1550 }
1551 }
1552 if let Some(sp) = star_pi {
1553 pi = sp + 1;
1554 star_ti += 1;
1555 ti = star_ti;
1556 } else {
1557 return false;
1558 }
1559 }
1560
1561 while pi < p.len() && p[pi] == '%' {
1562 pi += 1;
1563 }
1564 pi == p.len()
1565}
1566
1567fn eval_case(
1568 operand: Option<&Expr>,
1569 conditions: &[(Expr, Expr)],
1570 else_result: Option<&Expr>,
1571 ctx: &EvalCtx,
1572) -> Result<Value> {
1573 if let Some(op_expr) = operand {
1574 let op_val = eval_expr(op_expr, ctx)?;
1575 for (cond, result) in conditions {
1576 let cond_val = eval_expr(cond, ctx)?;
1577 if !op_val.is_null() && !cond_val.is_null() && op_val == cond_val {
1578 return eval_expr(result, ctx);
1579 }
1580 }
1581 } else {
1582 for (cond, result) in conditions {
1583 let cond_val = eval_expr(cond, ctx)?;
1584 if is_truthy(&cond_val) {
1585 return eval_expr(result, ctx);
1586 }
1587 }
1588 }
1589 match else_result {
1590 Some(e) => eval_expr(e, ctx),
1591 None => Ok(Value::Null),
1592 }
1593}
1594
1595pub(crate) fn eval_cast(val: &Value, target: DataType) -> Result<Value> {
1596 if val.is_null() {
1597 return Ok(Value::Null);
1598 }
1599 match target {
1600 DataType::Integer => match val {
1601 Value::Integer(_) => Ok(val.clone()),
1602 Value::Real(r) => Ok(Value::Integer(*r as i64)),
1603 Value::Boolean(b) => Ok(Value::Integer(if *b { 1 } else { 0 })),
1604 Value::Text(s) => s
1605 .trim()
1606 .parse::<i64>()
1607 .map(Value::Integer)
1608 .or_else(|_| s.trim().parse::<f64>().map(|f| Value::Integer(f as i64)))
1609 .map_err(|_| SqlError::InvalidValue(format!("cannot cast '{s}' to INTEGER"))),
1610 _ => Err(SqlError::InvalidValue(format!(
1611 "cannot cast {} to INTEGER",
1612 val.data_type()
1613 ))),
1614 },
1615 DataType::Real => match val {
1616 Value::Real(_) => Ok(val.clone()),
1617 Value::Integer(i) => Ok(Value::Real(*i as f64)),
1618 Value::Boolean(b) => Ok(Value::Real(if *b { 1.0 } else { 0.0 })),
1619 Value::Text(s) => s
1620 .trim()
1621 .parse::<f64>()
1622 .map(Value::Real)
1623 .map_err(|_| SqlError::InvalidValue(format!("cannot cast '{s}' to REAL"))),
1624 _ => Err(SqlError::InvalidValue(format!(
1625 "cannot cast {} to REAL",
1626 val.data_type()
1627 ))),
1628 },
1629 DataType::Text => Ok(Value::Text(value_to_text(val).into())),
1630 DataType::Boolean => match val {
1631 Value::Boolean(_) => Ok(val.clone()),
1632 Value::Integer(i) => Ok(Value::Boolean(*i != 0)),
1633 Value::Text(s) => {
1634 let lower = s.trim().to_ascii_lowercase();
1635 match lower.as_str() {
1636 "true" | "1" | "yes" | "on" => Ok(Value::Boolean(true)),
1637 "false" | "0" | "no" | "off" => Ok(Value::Boolean(false)),
1638 _ => Err(SqlError::InvalidValue(format!(
1639 "cannot cast '{s}' to BOOLEAN"
1640 ))),
1641 }
1642 }
1643 _ => Err(SqlError::InvalidValue(format!(
1644 "cannot cast {} to BOOLEAN",
1645 val.data_type()
1646 ))),
1647 },
1648 DataType::Blob => match val {
1649 Value::Blob(_) => Ok(val.clone()),
1650 Value::Text(s) => Ok(Value::Blob(s.as_bytes().to_vec())),
1651 _ => Err(SqlError::InvalidValue(format!(
1652 "cannot cast {} to BLOB",
1653 val.data_type()
1654 ))),
1655 },
1656 DataType::Null => Ok(Value::Null),
1657 DataType::Date => val.clone().coerce_into(DataType::Date).ok_or_else(|| {
1658 SqlError::InvalidValue(format!("cannot cast {} to DATE", val.data_type()))
1659 }),
1660 DataType::Time => val.clone().coerce_into(DataType::Time).ok_or_else(|| {
1661 SqlError::InvalidValue(format!("cannot cast {} to TIME", val.data_type()))
1662 }),
1663 DataType::Timestamp => val.clone().coerce_into(DataType::Timestamp).ok_or_else(|| {
1664 SqlError::InvalidValue(format!("cannot cast {} to TIMESTAMP", val.data_type()))
1665 }),
1666 DataType::Interval => val.clone().coerce_into(DataType::Interval).ok_or_else(|| {
1667 SqlError::InvalidValue(format!("cannot cast {} to INTERVAL", val.data_type()))
1668 }),
1669 DataType::Json => val.clone().coerce_into(DataType::Json).ok_or_else(|| {
1670 SqlError::InvalidValue(format!("cannot cast {} to JSON", val.data_type()))
1671 }),
1672 DataType::Jsonb => val.clone().coerce_into(DataType::Jsonb).ok_or_else(|| {
1673 SqlError::InvalidValue(format!("cannot cast {} to JSONB", val.data_type()))
1674 }),
1675 DataType::TsVector => val.clone().coerce_into(DataType::TsVector).ok_or_else(|| {
1676 SqlError::InvalidValue(format!("cannot cast {} to TSVECTOR", val.data_type()))
1677 }),
1678 DataType::TsQuery => val.clone().coerce_into(DataType::TsQuery).ok_or_else(|| {
1679 SqlError::InvalidValue(format!("cannot cast {} to TSQUERY", val.data_type()))
1680 }),
1681 DataType::Array => val.clone().coerce_into(DataType::Array).ok_or_else(|| {
1682 SqlError::InvalidValue(format!("cannot cast {} to ARRAY", val.data_type()))
1683 }),
1684 DataType::Vector { dim } => match val {
1685 Value::Vector(v) if v.len() as u16 == dim => Ok(val.clone()),
1686 Value::Vector(_) => Err(SqlError::InvalidValue(format!(
1687 "cannot cast {} to VECTOR({dim}) (dim mismatch)",
1688 val.data_type()
1689 ))),
1690 Value::Text(s) => parse_vector_literal(s.as_str(), dim).map(Value::Vector),
1691 _ => Err(SqlError::InvalidValue(format!(
1692 "cannot cast {} to VECTOR({dim})",
1693 val.data_type()
1694 ))),
1695 },
1696 }
1697}
1698
1699fn parse_vector_literal(s: &str, expected_dim: u16) -> Result<std::sync::Arc<[f32]>> {
1700 let trimmed = s.trim();
1701 let inner = trimmed
1702 .strip_prefix('[')
1703 .and_then(|s| s.strip_suffix(']'))
1704 .unwrap_or(trimmed);
1705 let mut out: Vec<f32> = Vec::with_capacity(expected_dim as usize);
1706 for tok in inner.split(',') {
1707 let tok = tok.trim();
1708 if tok.is_empty() {
1709 continue;
1710 }
1711 let x: f32 = tok
1712 .parse()
1713 .map_err(|_| SqlError::InvalidValue(format!("invalid vector element: '{tok}'")))?;
1714 out.push(x);
1715 }
1716 if out.len() as u16 != expected_dim {
1717 return Err(SqlError::InvalidValue(format!(
1718 "vector literal has {} elements, expected {expected_dim}",
1719 out.len()
1720 )));
1721 }
1722 Ok(std::sync::Arc::from(out.into_boxed_slice()))
1723}
1724
1725fn eval_scalar_function(name: &str, args: &[Expr], ctx: &EvalCtx) -> Result<Value> {
1726 let evaluated: Vec<Value> = args
1727 .iter()
1728 .map(|a| eval_expr(a, ctx))
1729 .collect::<Result<Vec<_>>>()?;
1730
1731 match name {
1732 "LENGTH" => {
1733 check_args(name, &evaluated, 1)?;
1734 match &evaluated[0] {
1735 Value::Null => Ok(Value::Null),
1736 Value::Text(s) => Ok(Value::Integer(s.chars().count() as i64)),
1737 Value::Blob(b) => Ok(Value::Integer(b.len() as i64)),
1738 Value::TsVector(b) => crate::fts::fn_length_tsvector(b),
1739 _ => Ok(Value::Integer(
1740 value_to_text(&evaluated[0]).chars().count() as i64
1741 )),
1742 }
1743 }
1744 "UPPER" => {
1745 check_args(name, &evaluated, 1)?;
1746 match &evaluated[0] {
1747 Value::Null => Ok(Value::Null),
1748 Value::Text(s) => Ok(Value::Text(s.to_ascii_uppercase())),
1749 _ => Ok(Value::Text(
1750 value_to_text(&evaluated[0]).to_ascii_uppercase().into(),
1751 )),
1752 }
1753 }
1754 "LOWER" => {
1755 check_args(name, &evaluated, 1)?;
1756 match &evaluated[0] {
1757 Value::Null => Ok(Value::Null),
1758 Value::Text(s) => Ok(Value::Text(s.to_ascii_lowercase())),
1759 _ => Ok(Value::Text(
1760 value_to_text(&evaluated[0]).to_ascii_lowercase().into(),
1761 )),
1762 }
1763 }
1764 "SUBSTR" | "SUBSTRING" => {
1765 if evaluated.len() < 2 || evaluated.len() > 3 {
1766 return Err(SqlError::InvalidValue(format!(
1767 "{name} requires 2 or 3 arguments"
1768 )));
1769 }
1770 if evaluated.iter().any(|v| v.is_null()) {
1771 return Ok(Value::Null);
1772 }
1773 let s = value_to_text(&evaluated[0]);
1774 let chars: Vec<char> = s.chars().collect();
1775 let start = match &evaluated[1] {
1776 Value::Integer(i) => *i,
1777 _ => {
1778 return Err(SqlError::TypeMismatch {
1779 expected: "INTEGER".into(),
1780 got: evaluated[1].data_type().to_string(),
1781 })
1782 }
1783 };
1784 let len = chars.len() as i64;
1785
1786 let (begin, count) = if evaluated.len() == 3 {
1787 let cnt = match &evaluated[2] {
1788 Value::Integer(i) => *i,
1789 _ => {
1790 return Err(SqlError::TypeMismatch {
1791 expected: "INTEGER".into(),
1792 got: evaluated[2].data_type().to_string(),
1793 })
1794 }
1795 };
1796 if start >= 1 {
1797 let b = (start - 1).min(len) as usize;
1798 let c = cnt.max(0) as usize;
1799 (b, c)
1800 } else if start == 0 {
1801 let c = (cnt - 1).max(0) as usize;
1802 (0usize, c)
1803 } else {
1804 let adjusted_cnt = (cnt + start - 1).max(0) as usize;
1805 (0usize, adjusted_cnt)
1806 }
1807 } else if start >= 1 {
1808 let b = (start - 1).min(len) as usize;
1809 (b, chars.len() - b)
1810 } else if start == 0 {
1811 (0usize, chars.len())
1812 } else {
1813 let b = (len + start).max(0) as usize;
1814 (b, chars.len() - b)
1815 };
1816
1817 let result: String = chars.iter().skip(begin).take(count).collect();
1818 Ok(Value::Text(result.into()))
1819 }
1820 "TRIM" | "LTRIM" | "RTRIM" => {
1821 if evaluated.is_empty() || evaluated.len() > 2 {
1822 return Err(SqlError::InvalidValue(format!(
1823 "{name} requires 1 or 2 arguments"
1824 )));
1825 }
1826 if evaluated[0].is_null() {
1827 return Ok(Value::Null);
1828 }
1829 let s = value_to_text(&evaluated[0]);
1830 let trim_chars: Vec<char> = if evaluated.len() == 2 {
1831 if evaluated[1].is_null() {
1832 return Ok(Value::Null);
1833 }
1834 value_to_text(&evaluated[1]).chars().collect()
1835 } else {
1836 vec![' ']
1837 };
1838 let result = match name {
1839 "TRIM" => s
1840 .trim_matches(|c: char| trim_chars.contains(&c))
1841 .to_string(),
1842 "LTRIM" => s
1843 .trim_start_matches(|c: char| trim_chars.contains(&c))
1844 .to_string(),
1845 "RTRIM" => s
1846 .trim_end_matches(|c: char| trim_chars.contains(&c))
1847 .to_string(),
1848 _ => unreachable!(),
1849 };
1850 Ok(Value::Text(result.into()))
1851 }
1852 "REPLACE" => {
1853 check_args(name, &evaluated, 3)?;
1854 if evaluated.iter().any(|v| v.is_null()) {
1855 return Ok(Value::Null);
1856 }
1857 let s = value_to_text(&evaluated[0]);
1858 let from = value_to_text(&evaluated[1]);
1859 let to = value_to_text(&evaluated[2]);
1860 if from.is_empty() {
1861 return Ok(Value::Text(s.into()));
1862 }
1863 Ok(Value::Text(s.replace(&from, &to).into()))
1864 }
1865 "INSTR" => {
1866 check_args(name, &evaluated, 2)?;
1867 if evaluated.iter().any(|v| v.is_null()) {
1868 return Ok(Value::Null);
1869 }
1870 let haystack = value_to_text(&evaluated[0]);
1871 let needle = value_to_text(&evaluated[1]);
1872 let pos = haystack
1873 .find(&needle)
1874 .map(|i| haystack[..i].chars().count() as i64 + 1)
1875 .unwrap_or(0);
1876 Ok(Value::Integer(pos))
1877 }
1878 "CONCAT" => {
1879 if evaluated.is_empty() {
1880 return Ok(Value::Text(CompactString::default()));
1881 }
1882 let mut result = String::new();
1883 for v in &evaluated {
1884 match v {
1885 Value::Null => {}
1886 _ => result.push_str(&value_to_text(v)),
1887 }
1888 }
1889 Ok(Value::Text(result.into()))
1890 }
1891 "ABS" => {
1892 check_args(name, &evaluated, 1)?;
1893 match &evaluated[0] {
1894 Value::Null => Ok(Value::Null),
1895 Value::Integer(i) => i
1896 .checked_abs()
1897 .map(Value::Integer)
1898 .ok_or(SqlError::IntegerOverflow),
1899 Value::Real(r) => Ok(Value::Real(r.abs())),
1900 _ => Err(SqlError::TypeMismatch {
1901 expected: "numeric".into(),
1902 got: evaluated[0].data_type().to_string(),
1903 }),
1904 }
1905 }
1906 "ROUND" => {
1907 if evaluated.is_empty() || evaluated.len() > 2 {
1908 return Err(SqlError::InvalidValue(
1909 "ROUND requires 1 or 2 arguments".into(),
1910 ));
1911 }
1912 if evaluated[0].is_null() {
1913 return Ok(Value::Null);
1914 }
1915 let val = match &evaluated[0] {
1916 Value::Integer(i) => *i as f64,
1917 Value::Real(r) => *r,
1918 _ => {
1919 return Err(SqlError::TypeMismatch {
1920 expected: "numeric".into(),
1921 got: evaluated[0].data_type().to_string(),
1922 })
1923 }
1924 };
1925 let places = if evaluated.len() == 2 {
1926 match &evaluated[1] {
1927 Value::Null => return Ok(Value::Null),
1928 Value::Integer(i) => *i,
1929 _ => {
1930 return Err(SqlError::TypeMismatch {
1931 expected: "INTEGER".into(),
1932 got: evaluated[1].data_type().to_string(),
1933 })
1934 }
1935 }
1936 } else {
1937 0
1938 };
1939 let factor = 10f64.powi(places as i32);
1940 let rounded = (val * factor).round() / factor;
1941 Ok(Value::Real(rounded))
1942 }
1943 "CEIL" | "CEILING" => {
1944 check_args(name, &evaluated, 1)?;
1945 match &evaluated[0] {
1946 Value::Null => Ok(Value::Null),
1947 Value::Integer(i) => Ok(Value::Integer(*i)),
1948 Value::Real(r) => Ok(Value::Integer(r.ceil() as i64)),
1949 _ => Err(SqlError::TypeMismatch {
1950 expected: "numeric".into(),
1951 got: evaluated[0].data_type().to_string(),
1952 }),
1953 }
1954 }
1955 "FLOOR" => {
1956 check_args(name, &evaluated, 1)?;
1957 match &evaluated[0] {
1958 Value::Null => Ok(Value::Null),
1959 Value::Integer(i) => Ok(Value::Integer(*i)),
1960 Value::Real(r) => Ok(Value::Integer(r.floor() as i64)),
1961 _ => Err(SqlError::TypeMismatch {
1962 expected: "numeric".into(),
1963 got: evaluated[0].data_type().to_string(),
1964 }),
1965 }
1966 }
1967 "SIGN" => {
1968 check_args(name, &evaluated, 1)?;
1969 match &evaluated[0] {
1970 Value::Null => Ok(Value::Null),
1971 Value::Integer(i) => Ok(Value::Integer(i.signum())),
1972 Value::Real(r) => {
1973 if *r > 0.0 {
1974 Ok(Value::Integer(1))
1975 } else if *r < 0.0 {
1976 Ok(Value::Integer(-1))
1977 } else {
1978 Ok(Value::Integer(0))
1979 }
1980 }
1981 _ => Err(SqlError::TypeMismatch {
1982 expected: "numeric".into(),
1983 got: evaluated[0].data_type().to_string(),
1984 }),
1985 }
1986 }
1987 "SQRT" => {
1988 check_args(name, &evaluated, 1)?;
1989 match &evaluated[0] {
1990 Value::Null => Ok(Value::Null),
1991 Value::Integer(i) => {
1992 if *i < 0 {
1993 Ok(Value::Null)
1994 } else {
1995 Ok(Value::Real((*i as f64).sqrt()))
1996 }
1997 }
1998 Value::Real(r) => {
1999 if *r < 0.0 {
2000 Ok(Value::Null)
2001 } else {
2002 Ok(Value::Real(r.sqrt()))
2003 }
2004 }
2005 _ => Err(SqlError::TypeMismatch {
2006 expected: "numeric".into(),
2007 got: evaluated[0].data_type().to_string(),
2008 }),
2009 }
2010 }
2011 "RANDOM" => {
2012 check_args(name, &evaluated, 0)?;
2013 use std::collections::hash_map::DefaultHasher;
2014 use std::hash::{Hash, Hasher};
2015 let mut hasher = DefaultHasher::new();
2016 crate::datetime::now_micros().hash(&mut hasher);
2017 std::thread::current().id().hash(&mut hasher);
2018 let mut val = hasher.finish() as i64;
2019 if val == i64::MIN {
2020 val = i64::MAX;
2021 }
2022 Ok(Value::Integer(val))
2023 }
2024 "TYPEOF" => {
2025 check_args(name, &evaluated, 1)?;
2026 let type_name = match &evaluated[0] {
2027 Value::Null => "null",
2028 Value::Integer(_) => "integer",
2029 Value::Real(_) => "real",
2030 Value::Text(_) => "text",
2031 Value::Blob(_) => "blob",
2032 Value::Boolean(_) => "boolean",
2033 Value::Date(_) => "date",
2034 Value::Time(_) => "time",
2035 Value::Timestamp(_) => "timestamp",
2036 Value::Interval { .. } => "interval",
2037 Value::Json(_) => "json",
2038 Value::Jsonb(_) => "jsonb",
2039 Value::TsVector(_) => "tsvector",
2040 Value::TsQuery(_) => "tsquery",
2041 Value::Array(_) => "array",
2042 Value::Vector(_) => "vector",
2043 };
2044 Ok(Value::Text(type_name.into()))
2045 }
2046 "MIN" => {
2047 check_args(name, &evaluated, 2)?;
2048 if evaluated[0].is_null() {
2049 return Ok(evaluated[1].clone());
2050 }
2051 if evaluated[1].is_null() {
2052 return Ok(evaluated[0].clone());
2053 }
2054 if evaluated[0] <= evaluated[1] {
2055 Ok(evaluated[0].clone())
2056 } else {
2057 Ok(evaluated[1].clone())
2058 }
2059 }
2060 "MAX" => {
2061 check_args(name, &evaluated, 2)?;
2062 if evaluated[0].is_null() {
2063 return Ok(evaluated[1].clone());
2064 }
2065 if evaluated[1].is_null() {
2066 return Ok(evaluated[0].clone());
2067 }
2068 if evaluated[0] >= evaluated[1] {
2069 Ok(evaluated[0].clone())
2070 } else {
2071 Ok(evaluated[1].clone())
2072 }
2073 }
2074 "HEX" => {
2075 check_args(name, &evaluated, 1)?;
2076 match &evaluated[0] {
2077 Value::Null => Ok(Value::Null),
2078 Value::Blob(b) => {
2079 let mut s = String::with_capacity(b.len() * 2);
2080 for byte in b {
2081 s.push_str(&format!("{byte:02X}"));
2082 }
2083 Ok(Value::Text(s.into()))
2084 }
2085 Value::Text(s) => {
2086 let mut r = String::with_capacity(s.len() * 2);
2087 for byte in s.as_bytes() {
2088 r.push_str(&format!("{byte:02X}"));
2089 }
2090 Ok(Value::Text(r.into()))
2091 }
2092 _ => Ok(Value::Text(value_to_text(&evaluated[0]).into())),
2093 }
2094 }
2095 "NOW" | "CURRENT_TIMESTAMP" | "LOCALTIMESTAMP" => {
2096 check_args(name, &evaluated, 0)?;
2097 Ok(Value::Timestamp(crate::datetime::txn_or_clock_micros()))
2098 }
2099 "CURRENT_DATE" => {
2100 check_args(name, &evaluated, 0)?;
2101 Ok(Value::Date(crate::datetime::ts_to_date_floor(
2102 crate::datetime::txn_or_clock_micros(),
2103 )))
2104 }
2105 "CURRENT_TIME" | "LOCALTIME" => {
2106 check_args(name, &evaluated, 0)?;
2107 Ok(Value::Time(
2108 crate::datetime::ts_split(crate::datetime::txn_or_clock_micros()).1,
2109 ))
2110 }
2111 "CLOCK_TIMESTAMP" | "STATEMENT_TIMESTAMP" | "TRANSACTION_TIMESTAMP" => {
2112 check_args(name, &evaluated, 0)?;
2113 let ts = match name {
2114 "CLOCK_TIMESTAMP" => crate::datetime::now_micros(),
2115 _ => crate::datetime::txn_or_clock_micros(),
2116 };
2117 Ok(Value::Timestamp(ts))
2118 }
2119 "EXTRACT" | "DATE_PART" | "DATEPART" => {
2120 check_args(name, &evaluated, 2)?;
2121 let field: &str = match &evaluated[0] {
2122 Value::Null => return Ok(Value::Null),
2123 Value::Text(s) => s.as_str(),
2124 _ => {
2125 return Err(SqlError::TypeMismatch {
2126 expected: "TEXT field name".into(),
2127 got: evaluated[0].data_type().to_string(),
2128 })
2129 }
2130 };
2131 if evaluated[1].is_null() {
2132 return Ok(Value::Null);
2133 }
2134 crate::datetime::extract(field, &evaluated[1])
2135 }
2136 "DATE_TRUNC" => {
2137 if evaluated.len() < 2 || evaluated.len() > 3 {
2138 return Err(SqlError::InvalidValue(
2139 "DATE_TRUNC requires 2 or 3 arguments".into(),
2140 ));
2141 }
2142 let unit = match &evaluated[0] {
2143 Value::Null => return Ok(Value::Null),
2144 Value::Text(s) => s.to_string(),
2145 _ => {
2146 return Err(SqlError::TypeMismatch {
2147 expected: "TEXT unit name".into(),
2148 got: evaluated[0].data_type().to_string(),
2149 })
2150 }
2151 };
2152 if evaluated[1].is_null() {
2153 return Ok(Value::Null);
2154 }
2155 if evaluated.len() == 3 {
2157 if let Value::Text(tz) = &evaluated[2] {
2158 if !tz.eq_ignore_ascii_case("UTC") {
2159 if let Value::Timestamp(ts) = &evaluated[1] {
2160 return date_trunc_in_zone(&unit, *ts, tz);
2161 }
2162 }
2163 }
2164 }
2165 crate::datetime::date_trunc(&unit, &evaluated[1])
2166 }
2167 "DATE_BIN" => {
2168 check_args(name, &evaluated, 3)?;
2169 if evaluated.iter().any(|v| v.is_null()) {
2170 return Ok(Value::Null);
2171 }
2172 let stride = match &evaluated[0] {
2173 Value::Interval {
2174 months: _,
2175 days,
2176 micros,
2177 } => *days as i64 * crate::datetime::MICROS_PER_DAY + *micros,
2178 _ => {
2179 return Err(SqlError::TypeMismatch {
2180 expected: "INTERVAL stride".into(),
2181 got: evaluated[0].data_type().to_string(),
2182 })
2183 }
2184 };
2185 if stride <= 0 {
2186 return Err(SqlError::InvalidValue(
2187 "DATE_BIN stride must be positive".into(),
2188 ));
2189 }
2190 let (src, origin) = match (&evaluated[1], &evaluated[2]) {
2191 (Value::Timestamp(s), Value::Timestamp(o)) => (*s, *o),
2192 _ => {
2193 return Err(SqlError::TypeMismatch {
2194 expected: "TIMESTAMP, TIMESTAMP".into(),
2195 got: format!("{}, {}", evaluated[1].data_type(), evaluated[2].data_type()),
2196 })
2197 }
2198 };
2199 let diff = src - origin;
2200 let binned = origin + (diff.div_euclid(stride)) * stride;
2201 Ok(Value::Timestamp(binned))
2202 }
2203 "AGE" => {
2204 if evaluated.len() == 1 {
2205 if evaluated[0].is_null() {
2206 return Ok(Value::Null);
2207 }
2208 let ts = match &evaluated[0] {
2209 Value::Timestamp(t) => *t,
2210 Value::Date(d) => crate::datetime::date_to_ts(*d),
2211 _ => {
2212 return Err(SqlError::TypeMismatch {
2213 expected: "TIMESTAMP or DATE".into(),
2214 got: evaluated[0].data_type().to_string(),
2215 })
2216 }
2217 };
2218 let today = crate::datetime::today_days();
2220 let midnight = crate::datetime::date_to_ts(today);
2221 let (m, d, u) = crate::datetime::age(midnight, ts)?;
2222 return Ok(Value::Interval {
2223 months: m,
2224 days: d,
2225 micros: u,
2226 });
2227 }
2228 check_args(name, &evaluated, 2)?;
2229 if evaluated.iter().any(|v| v.is_null()) {
2230 return Ok(Value::Null);
2231 }
2232 let a = ts_of(&evaluated[0])?;
2233 let b = ts_of(&evaluated[1])?;
2234 let (m, d, u) = crate::datetime::age(a, b)?;
2235 Ok(Value::Interval {
2236 months: m,
2237 days: d,
2238 micros: u,
2239 })
2240 }
2241 "MAKE_DATE" => {
2242 check_args(name, &evaluated, 3)?;
2243 if evaluated.iter().any(|v| v.is_null()) {
2244 return Ok(Value::Null);
2245 }
2246 let y = int_arg(&evaluated[0], "MAKE_DATE year")? as i32;
2247 let m = int_arg(&evaluated[1], "MAKE_DATE month")? as u8;
2248 let d = int_arg(&evaluated[2], "MAKE_DATE day")? as u8;
2249 crate::datetime::ymd_to_days(y, m, d)
2250 .map(Value::Date)
2251 .ok_or_else(|| SqlError::InvalidDateLiteral(format!("make_date({y}, {m}, {d})")))
2252 }
2253 "MAKE_TIME" => {
2254 check_args(name, &evaluated, 3)?;
2255 if evaluated.iter().any(|v| v.is_null()) {
2256 return Ok(Value::Null);
2257 }
2258 let h = int_arg(&evaluated[0], "MAKE_TIME hour")? as u8;
2259 let mi = int_arg(&evaluated[1], "MAKE_TIME minute")? as u8;
2260 let (s, us) = real_sec_arg(&evaluated[2])?;
2261 crate::datetime::hmsn_to_micros(h, mi, s, us)
2262 .map(Value::Time)
2263 .ok_or_else(|| SqlError::InvalidTimeLiteral(format!("make_time({h}, {mi}, ...)")))
2264 }
2265 "MAKE_TIMESTAMP" => {
2266 check_args(name, &evaluated, 6)?;
2267 if evaluated.iter().any(|v| v.is_null()) {
2268 return Ok(Value::Null);
2269 }
2270 let y = int_arg(&evaluated[0], "MAKE_TIMESTAMP year")? as i32;
2271 let mo = int_arg(&evaluated[1], "MAKE_TIMESTAMP month")? as u8;
2272 let d = int_arg(&evaluated[2], "MAKE_TIMESTAMP day")? as u8;
2273 let h = int_arg(&evaluated[3], "MAKE_TIMESTAMP hour")? as u8;
2274 let mi = int_arg(&evaluated[4], "MAKE_TIMESTAMP min")? as u8;
2275 let (s, us) = real_sec_arg(&evaluated[5])?;
2276 let days = crate::datetime::ymd_to_days(y, mo, d).ok_or_else(|| {
2277 SqlError::InvalidTimestampLiteral(format!("make_timestamp year={y}"))
2278 })?;
2279 let tmicros = crate::datetime::hmsn_to_micros(h, mi, s, us)
2280 .ok_or_else(|| SqlError::InvalidTimestampLiteral("time out of range".into()))?;
2281 Ok(Value::Timestamp(crate::datetime::ts_combine(days, tmicros)))
2282 }
2283 "MAKE_INTERVAL" => {
2284 if evaluated.len() > 7 {
2286 return Err(SqlError::InvalidValue(
2287 "MAKE_INTERVAL accepts at most 7 arguments".into(),
2288 ));
2289 }
2290 let mut months: i64 = 0;
2291 let mut days: i64 = 0;
2292 let mut micros: i64 = 0;
2293 for (i, v) in evaluated.iter().enumerate() {
2294 if v.is_null() {
2295 continue;
2296 }
2297 let n = match v {
2298 Value::Integer(n) => *n,
2299 Value::Real(r) => *r as i64,
2300 _ => {
2301 return Err(SqlError::TypeMismatch {
2302 expected: "numeric".into(),
2303 got: v.data_type().to_string(),
2304 })
2305 }
2306 };
2307 match i {
2308 0 => months = months.saturating_add(n.saturating_mul(12)),
2309 1 => months = months.saturating_add(n),
2310 2 => days = days.saturating_add(n.saturating_mul(7)),
2311 3 => days = days.saturating_add(n),
2312 4 => {
2313 micros = micros
2314 .saturating_add(n.saturating_mul(crate::datetime::MICROS_PER_HOUR))
2315 }
2316 5 => {
2317 micros =
2318 micros.saturating_add(n.saturating_mul(crate::datetime::MICROS_PER_MIN))
2319 }
2320 6 => {
2321 if let Value::Real(r) = v {
2323 micros = micros.saturating_add(
2324 (*r * crate::datetime::MICROS_PER_SEC as f64) as i64,
2325 );
2326 } else {
2327 micros = micros
2328 .saturating_add(n.saturating_mul(crate::datetime::MICROS_PER_SEC));
2329 }
2330 }
2331 _ => unreachable!(),
2332 }
2333 }
2334 Ok(Value::Interval {
2335 months: months.clamp(i32::MIN as i64, i32::MAX as i64) as i32,
2336 days: days.clamp(i32::MIN as i64, i32::MAX as i64) as i32,
2337 micros,
2338 })
2339 }
2340 "JUSTIFY_DAYS" => {
2341 check_args(name, &evaluated, 1)?;
2342 match &evaluated[0] {
2343 Value::Null => Ok(Value::Null),
2344 Value::Interval {
2345 months,
2346 days,
2347 micros,
2348 } => {
2349 let (m, d, u) = crate::datetime::justify_days(*months, *days, *micros);
2350 Ok(Value::Interval {
2351 months: m,
2352 days: d,
2353 micros: u,
2354 })
2355 }
2356 other => Err(SqlError::TypeMismatch {
2357 expected: "INTERVAL".into(),
2358 got: other.data_type().to_string(),
2359 }),
2360 }
2361 }
2362 "JUSTIFY_HOURS" => {
2363 check_args(name, &evaluated, 1)?;
2364 match &evaluated[0] {
2365 Value::Null => Ok(Value::Null),
2366 Value::Interval {
2367 months,
2368 days,
2369 micros,
2370 } => {
2371 let (m, d, u) = crate::datetime::justify_hours(*months, *days, *micros);
2372 Ok(Value::Interval {
2373 months: m,
2374 days: d,
2375 micros: u,
2376 })
2377 }
2378 other => Err(SqlError::TypeMismatch {
2379 expected: "INTERVAL".into(),
2380 got: other.data_type().to_string(),
2381 }),
2382 }
2383 }
2384 "JUSTIFY_INTERVAL" => {
2385 check_args(name, &evaluated, 1)?;
2386 match &evaluated[0] {
2387 Value::Null => Ok(Value::Null),
2388 Value::Interval {
2389 months,
2390 days,
2391 micros,
2392 } => {
2393 let (m, d, u) = crate::datetime::justify_interval(*months, *days, *micros);
2394 Ok(Value::Interval {
2395 months: m,
2396 days: d,
2397 micros: u,
2398 })
2399 }
2400 other => Err(SqlError::TypeMismatch {
2401 expected: "INTERVAL".into(),
2402 got: other.data_type().to_string(),
2403 }),
2404 }
2405 }
2406 "ISFINITE" => {
2407 check_args(name, &evaluated, 1)?;
2408 if evaluated[0].is_null() {
2409 return Ok(Value::Null);
2410 }
2411 Ok(Value::Boolean(evaluated[0].is_finite_temporal()))
2412 }
2413 "DATE" => {
2414 if evaluated.is_empty() {
2415 return Err(SqlError::InvalidValue(
2416 "DATE requires at least 1 argument".into(),
2417 ));
2418 }
2419 if evaluated[0].is_null() {
2420 return Ok(Value::Null);
2421 }
2422 let d = match &evaluated[0] {
2423 Value::Date(d) => *d,
2424 Value::Timestamp(t) => crate::datetime::ts_to_date_floor(*t),
2425 Value::Text(s) if s.eq_ignore_ascii_case("now") => crate::datetime::today_days(),
2426 Value::Text(s) => crate::datetime::parse_date(s)?,
2427 Value::Integer(n) => {
2428 crate::datetime::ts_to_date_floor(*n * crate::datetime::MICROS_PER_SEC)
2429 }
2430 other => {
2431 return Err(SqlError::TypeMismatch {
2432 expected: "TIMESTAMP, DATE, TEXT, or INTEGER".into(),
2433 got: other.data_type().to_string(),
2434 })
2435 }
2436 };
2437 Ok(Value::Date(d))
2438 }
2439 "TIME" => {
2440 if evaluated.is_empty() {
2441 return Err(SqlError::InvalidValue(
2442 "TIME requires at least 1 argument".into(),
2443 ));
2444 }
2445 if evaluated[0].is_null() {
2446 return Ok(Value::Null);
2447 }
2448 let t = match &evaluated[0] {
2449 Value::Time(t) => *t,
2450 Value::Timestamp(t) => crate::datetime::ts_split(*t).1,
2451 Value::Text(s) if s.eq_ignore_ascii_case("now") => {
2452 crate::datetime::current_time_micros()
2453 }
2454 Value::Text(s) => crate::datetime::parse_time(s)?,
2455 other => {
2456 return Err(SqlError::TypeMismatch {
2457 expected: "TIMESTAMP, TIME, or TEXT".into(),
2458 got: other.data_type().to_string(),
2459 })
2460 }
2461 };
2462 Ok(Value::Time(t))
2463 }
2464 "DATETIME" => {
2465 if evaluated.is_empty() {
2466 return Err(SqlError::InvalidValue(
2467 "DATETIME requires at least 1 argument".into(),
2468 ));
2469 }
2470 if evaluated[0].is_null() {
2471 return Ok(Value::Null);
2472 }
2473 let t = match &evaluated[0] {
2474 Value::Timestamp(t) => *t,
2475 Value::Date(d) => crate::datetime::date_to_ts(*d),
2476 Value::Text(s) if s.eq_ignore_ascii_case("now") => crate::datetime::now_micros(),
2477 Value::Text(s) => crate::datetime::parse_timestamp(s)?,
2478 Value::Integer(n) => n * crate::datetime::MICROS_PER_SEC,
2479 other => {
2480 return Err(SqlError::TypeMismatch {
2481 expected: "TIMESTAMP, DATE, TEXT, or INTEGER".into(),
2482 got: other.data_type().to_string(),
2483 })
2484 }
2485 };
2486 Ok(Value::Timestamp(t))
2487 }
2488 "STRFTIME" => {
2489 if evaluated.len() < 2 {
2490 return Err(SqlError::InvalidValue(
2491 "STRFTIME requires format + value".into(),
2492 ));
2493 }
2494 if evaluated.iter().take(2).any(|v| v.is_null()) {
2495 return Ok(Value::Null);
2496 }
2497 let fmt = match &evaluated[0] {
2498 Value::Text(s) => s.to_string(),
2499 _ => {
2500 return Err(SqlError::TypeMismatch {
2501 expected: "TEXT format".into(),
2502 got: evaluated[0].data_type().to_string(),
2503 })
2504 }
2505 };
2506 let out = crate::datetime::strftime(&fmt, &evaluated[1])?;
2507 Ok(Value::Text(out.into()))
2508 }
2509 "JULIANDAY" => {
2510 if evaluated.is_empty() {
2511 return Err(SqlError::InvalidValue(
2512 "JULIANDAY requires at least 1 argument".into(),
2513 ));
2514 }
2515 if evaluated[0].is_null() {
2516 return Ok(Value::Null);
2517 }
2518 let micros = ts_of(&evaluated[0])?;
2519 let (days, tmicros) = crate::datetime::ts_split(micros);
2520 let julian =
2522 days as f64 + 2_440_587.5 + tmicros as f64 / crate::datetime::MICROS_PER_DAY as f64;
2523 Ok(Value::Real(julian))
2524 }
2525 "UNIXEPOCH" => {
2526 if evaluated.is_empty() {
2527 return Err(SqlError::InvalidValue(
2528 "UNIXEPOCH requires at least 1 argument".into(),
2529 ));
2530 }
2531 if evaluated[0].is_null() {
2532 return Ok(Value::Null);
2533 }
2534 let micros = ts_of(&evaluated[0])?;
2535 let subsec = evaluated
2536 .get(1)
2537 .and_then(|v| {
2538 if let Value::Text(s) = v {
2539 Some(s.to_string())
2540 } else {
2541 None
2542 }
2543 })
2544 .map(|s| s.eq_ignore_ascii_case("subsec") || s.eq_ignore_ascii_case("subsecond"))
2545 .unwrap_or(false);
2546 if subsec {
2547 Ok(Value::Real(
2548 micros as f64 / crate::datetime::MICROS_PER_SEC as f64,
2549 ))
2550 } else {
2551 Ok(Value::Integer(micros / crate::datetime::MICROS_PER_SEC))
2552 }
2553 }
2554 "TIMEDIFF" => {
2555 check_args(name, &evaluated, 2)?;
2556 if evaluated.iter().any(|v| v.is_null()) {
2557 return Ok(Value::Null);
2558 }
2559 let a = ts_of(&evaluated[0])?;
2560 let b = ts_of(&evaluated[1])?;
2561 let (days, micros) = crate::datetime::subtract_timestamps(a, b);
2562 let sign = if days < 0 || (days == 0 && micros < 0) {
2563 "-"
2564 } else {
2565 "+"
2566 };
2567 let abs_days = days.unsigned_abs() as i64;
2568 let abs_us = micros.unsigned_abs() as i64;
2569 let (h, m, s, us) = crate::datetime::micros_to_hmsn(abs_us);
2571 Ok(Value::Text(
2572 format!("{sign}{abs_days:04}-00-00 {h:02}:{m:02}:{s:02}.{us:06}").into(),
2573 ))
2574 }
2575 "AT_TIMEZONE" => {
2576 check_args(name, &evaluated, 2)?;
2577 if evaluated.iter().any(|v| v.is_null()) {
2578 return Ok(Value::Null);
2579 }
2580 let ts = match &evaluated[0] {
2581 Value::Timestamp(t) => *t,
2582 Value::Date(d) => crate::datetime::date_to_ts(*d),
2583 other => {
2584 return Err(SqlError::TypeMismatch {
2585 expected: "TIMESTAMP or DATE".into(),
2586 got: other.data_type().to_string(),
2587 })
2588 }
2589 };
2590 let zone = match &evaluated[1] {
2591 Value::Text(s) => s.to_string(),
2592 _ => {
2593 return Err(SqlError::TypeMismatch {
2594 expected: "TEXT time zone".into(),
2595 got: evaluated[1].data_type().to_string(),
2596 })
2597 }
2598 };
2599 let upper = zone.to_ascii_uppercase();
2601 if (upper.starts_with("UTC+") || upper.starts_with("UTC-")) && zone.len() > 3 {
2602 return Err(SqlError::InvalidTimezone(format!(
2603 "'{zone}' is ambiguous — use ISO-8601 offset like '+05:00' or named zone like 'Etc/GMT-5'"
2604 )));
2605 }
2606 let formatted = crate::datetime::format_timestamp_in_zone(ts, &zone)?;
2607 Ok(Value::Text(formatted.into()))
2608 }
2609 "JSONB_TYPEOF" | "JSON_TYPEOF" => {
2610 check_args(name, &evaluated, 1)?;
2611 if evaluated[0].is_null() {
2612 return Ok(Value::Null);
2613 }
2614 crate::json::fn_typeof(&evaluated[0])
2615 }
2616 "JSONB_ARRAY_LENGTH" | "JSON_ARRAY_LENGTH" => {
2617 check_args(name, &evaluated, 1)?;
2618 if evaluated[0].is_null() {
2619 return Ok(Value::Null);
2620 }
2621 crate::json::fn_array_length(&evaluated[0])
2622 }
2623 "JSONB_OBJECT_LENGTH" | "JSON_OBJECT_LENGTH" => {
2624 check_args(name, &evaluated, 1)?;
2625 if evaluated[0].is_null() {
2626 return Ok(Value::Null);
2627 }
2628 crate::json::fn_object_length(&evaluated[0])
2629 }
2630 "JSONB_EXTRACT_PATH" | "JSON_EXTRACT_PATH" => {
2631 if evaluated.is_empty() {
2632 return Err(SqlError::InvalidValue(format!(
2633 "{name} requires at least 1 argument"
2634 )));
2635 }
2636 if evaluated[0].is_null() {
2637 return Ok(Value::Null);
2638 }
2639 let target = if name.eq_ignore_ascii_case("JSONB_EXTRACT_PATH") {
2640 crate::types::DataType::Jsonb
2641 } else {
2642 crate::types::DataType::Json
2643 };
2644 crate::json::fn_extract_path(&evaluated, target, false)
2645 }
2646 "JSONB_EXTRACT_PATH_TEXT" | "JSON_EXTRACT_PATH_TEXT" => {
2647 if evaluated.is_empty() {
2648 return Err(SqlError::InvalidValue(format!(
2649 "{name} requires at least 1 argument"
2650 )));
2651 }
2652 if evaluated[0].is_null() {
2653 return Ok(Value::Null);
2654 }
2655 crate::json::fn_extract_path(&evaluated, crate::types::DataType::Text, true)
2656 }
2657 "JSON_EXTRACT" => {
2658 check_args(name, &evaluated, 2)?;
2659 if evaluated[0].is_null() || evaluated[1].is_null() {
2660 return Ok(Value::Null);
2661 }
2662 crate::json::fn_sqlite_extract(&evaluated[0], &evaluated[1])
2663 }
2664 "JSON_VALID" => {
2665 check_args(name, &evaluated, 1)?;
2666 if evaluated[0].is_null() {
2667 return Ok(Value::Null);
2668 }
2669 crate::json::fn_valid(&evaluated[0])
2670 }
2671 "JSONB_STRIP_NULLS" | "JSON_STRIP_NULLS" => {
2672 check_args(name, &evaluated, 1)?;
2673 if evaluated[0].is_null() {
2674 return Ok(Value::Null);
2675 }
2676 let target = if name.eq_ignore_ascii_case("JSONB_STRIP_NULLS") {
2677 crate::types::DataType::Jsonb
2678 } else {
2679 crate::types::DataType::Json
2680 };
2681 crate::json::fn_strip_nulls(&evaluated[0], target)
2682 }
2683 "JSONB_PRETTY" | "JSON_PRETTY" => {
2684 check_args(name, &evaluated, 1)?;
2685 if evaluated[0].is_null() {
2686 return Ok(Value::Null);
2687 }
2688 crate::json::fn_pretty(&evaluated[0])
2689 }
2690 "JSONB_BUILD_OBJECT" | "JSON_BUILD_OBJECT" => {
2691 let target = if name.eq_ignore_ascii_case("JSONB_BUILD_OBJECT") {
2692 crate::types::DataType::Jsonb
2693 } else {
2694 crate::types::DataType::Json
2695 };
2696 crate::json::fn_build_object(&evaluated, target)
2697 }
2698 "JSONB_BUILD_ARRAY" | "JSON_BUILD_ARRAY" => {
2699 let target = if name.eq_ignore_ascii_case("JSONB_BUILD_ARRAY") {
2700 crate::types::DataType::Jsonb
2701 } else {
2702 crate::types::DataType::Json
2703 };
2704 crate::json::fn_build_array(&evaluated, target)
2705 }
2706 "JSONB_SET" | "JSON_SET" => {
2707 if !(3..=4).contains(&evaluated.len()) {
2708 return Err(SqlError::InvalidValue(format!(
2709 "{name} requires 3 or 4 arguments"
2710 )));
2711 }
2712 if evaluated[0].is_null() {
2713 return Ok(Value::Null);
2714 }
2715 let target = if name.eq_ignore_ascii_case("JSONB_SET") {
2716 crate::types::DataType::Jsonb
2717 } else {
2718 crate::types::DataType::Json
2719 };
2720 let create_missing = evaluated
2721 .get(3)
2722 .map(|v| matches!(v, Value::Boolean(true)))
2723 .unwrap_or(true);
2724 crate::json::fn_set(
2725 &evaluated[0],
2726 &evaluated[1],
2727 &evaluated[2],
2728 create_missing,
2729 target,
2730 )
2731 }
2732 "JSONB_INSERT" | "JSON_INSERT" => {
2733 if !(3..=4).contains(&evaluated.len()) {
2734 return Err(SqlError::InvalidValue(format!(
2735 "{name} requires 3 or 4 arguments"
2736 )));
2737 }
2738 if evaluated[0].is_null() {
2739 return Ok(Value::Null);
2740 }
2741 let target = if name.eq_ignore_ascii_case("JSONB_INSERT") {
2742 crate::types::DataType::Jsonb
2743 } else {
2744 crate::types::DataType::Json
2745 };
2746 let insert_after = evaluated
2747 .get(3)
2748 .map(|v| matches!(v, Value::Boolean(true)))
2749 .unwrap_or(false);
2750 crate::json::fn_insert(
2751 &evaluated[0],
2752 &evaluated[1],
2753 &evaluated[2],
2754 insert_after,
2755 target,
2756 )
2757 }
2758 "TO_JSONB" | "TO_JSON" => {
2759 check_args(name, &evaluated, 1)?;
2760 let target = if name.eq_ignore_ascii_case("TO_JSONB") {
2761 crate::types::DataType::Jsonb
2762 } else {
2763 crate::types::DataType::Json
2764 };
2765 crate::json::fn_to_json(&evaluated[0], target)
2766 }
2767 "ROW_TO_JSON" | "ROW_TO_JSONB" => {
2768 check_args(name, &evaluated, 1)?;
2769 let target = if name.eq_ignore_ascii_case("ROW_TO_JSONB") {
2770 crate::types::DataType::Jsonb
2771 } else {
2772 crate::types::DataType::Json
2773 };
2774 crate::json::fn_to_json(&evaluated[0], target)
2775 }
2776 "JSON_OBJECT" => crate::json::fn_json_object(&evaluated),
2777 "JSON_EXISTS" => {
2778 check_args(name, &evaluated, 2)?;
2779 if evaluated[0].is_null() || evaluated[1].is_null() {
2780 return Ok(Value::Null);
2781 }
2782 crate::json::fn_json_exists(&evaluated[0], &evaluated[1])
2783 }
2784 "JSON_VALUE" => {
2785 check_args(name, &evaluated, 2)?;
2786 if evaluated[0].is_null() || evaluated[1].is_null() {
2787 return Ok(Value::Null);
2788 }
2789 crate::json::fn_json_value(&evaluated[0], &evaluated[1])
2790 }
2791 "JSON_QUERY" => {
2792 check_args(name, &evaluated, 2)?;
2793 if evaluated[0].is_null() || evaluated[1].is_null() {
2794 return Ok(Value::Null);
2795 }
2796 crate::json::fn_json_query(&evaluated[0], &evaluated[1], crate::types::DataType::Jsonb)
2797 }
2798 "JSONB_PATH_EXISTS" => {
2799 if evaluated[0].is_null() || evaluated[1].is_null() {
2800 return Ok(Value::Null);
2801 }
2802 crate::json::fn_jsonb_path_exists(&evaluated)
2803 }
2804 "JSONB_PATH_MATCH" => {
2805 if evaluated[0].is_null() || evaluated[1].is_null() {
2806 return Ok(Value::Null);
2807 }
2808 crate::json::fn_jsonb_path_match(&evaluated)
2809 }
2810 "JSONB_PATH_QUERY_FIRST" => {
2811 if evaluated[0].is_null() || evaluated[1].is_null() {
2812 return Ok(Value::Null);
2813 }
2814 crate::json::fn_jsonb_path_query_first(&evaluated)
2815 }
2816 "JSONB_PATH_QUERY_ARRAY" => {
2817 if evaluated[0].is_null() || evaluated[1].is_null() {
2818 return Ok(Value::Null);
2819 }
2820 crate::json::fn_jsonb_path_query_array(&evaluated)
2821 }
2822 "JSONB_PATH_EXISTS_TZ" => {
2823 if evaluated[0].is_null() || evaluated[1].is_null() {
2824 return Ok(Value::Null);
2825 }
2826 crate::json::fn_jsonb_path_exists_tz(&evaluated)
2827 }
2828 "JSONB_PATH_MATCH_TZ" => {
2829 if evaluated[0].is_null() || evaluated[1].is_null() {
2830 return Ok(Value::Null);
2831 }
2832 crate::json::fn_jsonb_path_match_tz(&evaluated)
2833 }
2834 "JSONB_PATH_QUERY_TZ" => {
2835 if evaluated[0].is_null() || evaluated[1].is_null() {
2836 return Ok(Value::Null);
2837 }
2838 crate::json::fn_jsonb_path_query_tz(&evaluated)
2839 }
2840 "JSONB_PATH_QUERY_FIRST_TZ" => {
2841 if evaluated[0].is_null() || evaluated[1].is_null() {
2842 return Ok(Value::Null);
2843 }
2844 crate::json::fn_jsonb_path_query_first_tz(&evaluated)
2845 }
2846 "JSONB_PATH_QUERY_ARRAY_TZ" => {
2847 if evaluated[0].is_null() || evaluated[1].is_null() {
2848 return Ok(Value::Null);
2849 }
2850 crate::json::fn_jsonb_path_query_array_tz(&evaluated)
2851 }
2852 "JSONB_HAS_KEY" | "JSON_HAS_KEY" => {
2853 check_args(name, &evaluated, 2)?;
2854 if evaluated[0].is_null() || evaluated[1].is_null() {
2855 return Ok(Value::Null);
2856 }
2857 crate::json::op_has_key(&evaluated[0], &evaluated[1])
2858 }
2859 "JSONB_HAS_ANY_KEY" | "JSON_HAS_ANY_KEY" => {
2860 check_args(name, &evaluated, 2)?;
2861 if evaluated[0].is_null() || evaluated[1].is_null() {
2862 return Ok(Value::Null);
2863 }
2864 crate::json::op_has_any_key(&evaluated[0], &evaluated[1])
2865 }
2866 "JSONB_HAS_ALL_KEYS" | "JSON_HAS_ALL_KEYS" => {
2867 check_args(name, &evaluated, 2)?;
2868 if evaluated[0].is_null() || evaluated[1].is_null() {
2869 return Ok(Value::Null);
2870 }
2871 crate::json::op_has_all_keys(&evaluated[0], &evaluated[1])
2872 }
2873 "TO_TSVECTOR" => fts_to_tsvector(&evaluated),
2874 "TO_TSQUERY" => fts_to_tsquery(&evaluated),
2875 "PLAINTO_TSQUERY" => fts_plainto_tsquery(&evaluated),
2876 "PHRASETO_TSQUERY" => fts_phraseto_tsquery(&evaluated),
2877 "WEBSEARCH_TO_TSQUERY" => fts_websearch_to_tsquery(&evaluated),
2878 "TS_RANK" => fts_ts_rank(&evaluated, false),
2879 "TS_RANK_CD" => fts_ts_rank(&evaluated, true),
2880 "TS_HEADLINE" => fts_ts_headline(&evaluated),
2881 "TS_LEXIZE" => fts_ts_lexize(&evaluated),
2882 "NUMNODE" => fts_numnode(&evaluated),
2883 "SETWEIGHT" => fts_setweight(&evaluated),
2884 "STRIP" => fts_strip(&evaluated),
2885 _ => Err(SqlError::Unsupported(format!("scalar function: {name}"))),
2886 }
2887}
2888
2889fn fts_resolve_config_and_text(
2890 args: &[Value],
2891 fname: &str,
2892) -> Result<(crate::fts::TokenizerKind, String)> {
2893 if args.is_empty() || args.len() > 2 {
2894 return Err(SqlError::InvalidValue(format!(
2895 "{fname} requires 1 or 2 arguments"
2896 )));
2897 }
2898 let (config_name, text) = if args.len() == 2 {
2899 let cfg = match &args[0] {
2900 Value::Text(s) => Some(s.as_str().to_string()),
2901 v => {
2902 return Err(SqlError::TypeMismatch {
2903 expected: "TEXT (config)".into(),
2904 got: v.data_type().to_string(),
2905 })
2906 }
2907 };
2908 let txt = match &args[1] {
2909 Value::Text(s) => s.as_str().to_string(),
2910 v => {
2911 return Err(SqlError::TypeMismatch {
2912 expected: "TEXT".into(),
2913 got: v.data_type().to_string(),
2914 })
2915 }
2916 };
2917 (cfg, txt)
2918 } else {
2919 let txt = match &args[0] {
2920 Value::Text(s) => s.as_str().to_string(),
2921 v => {
2922 return Err(SqlError::TypeMismatch {
2923 expected: "TEXT".into(),
2924 got: v.data_type().to_string(),
2925 })
2926 }
2927 };
2928 (None, txt)
2929 };
2930 let kind = match config_name {
2931 Some(name) => crate::fts::TokenizerKind::from_name(&name)?,
2932 None => crate::fts::TokenizerKind::English,
2933 };
2934 Ok((kind, text))
2935}
2936
2937fn fts_to_tsvector(args: &[Value]) -> Result<Value> {
2938 if args.iter().any(|v| v.is_null()) {
2939 return Ok(Value::Null);
2940 }
2941 let (kind, text) = fts_resolve_config_and_text(args, "to_tsvector")?;
2942 crate::fts::fn_to_tsvector_with(kind, &text)
2943}
2944
2945fn fts_to_tsquery(args: &[Value]) -> Result<Value> {
2946 if args.iter().any(|v| v.is_null()) {
2947 return Ok(Value::Null);
2948 }
2949 let (kind, text) = fts_resolve_config_and_text(args, "to_tsquery")?;
2950 crate::fts::fn_to_tsquery_with(kind, &text)
2951}
2952
2953fn fts_plainto_tsquery(args: &[Value]) -> Result<Value> {
2954 if args.iter().any(|v| v.is_null()) {
2955 return Ok(Value::Null);
2956 }
2957 let (kind, text) = fts_resolve_config_and_text(args, "plainto_tsquery")?;
2958 crate::fts::fn_plainto_tsquery_with(kind, &text)
2959}
2960
2961fn fts_phraseto_tsquery(args: &[Value]) -> Result<Value> {
2962 if args.iter().any(|v| v.is_null()) {
2963 return Ok(Value::Null);
2964 }
2965 let (kind, text) = fts_resolve_config_and_text(args, "phraseto_tsquery")?;
2966 crate::fts::fn_phraseto_tsquery_with(kind, &text)
2967}
2968
2969fn fts_websearch_to_tsquery(args: &[Value]) -> Result<Value> {
2970 if args.iter().any(|v| v.is_null()) {
2971 return Ok(Value::Null);
2972 }
2973 let (kind, text) = fts_resolve_config_and_text(args, "websearch_to_tsquery")?;
2974 crate::fts::fn_websearch_to_tsquery_with(kind, &text)
2975}
2976
2977fn fts_ts_rank(args: &[Value], cover_density: bool) -> Result<Value> {
2978 let fname = if cover_density {
2979 "ts_rank_cd"
2980 } else {
2981 "ts_rank"
2982 };
2983 if args.len() != 2 && args.len() != 3 {
2984 return Err(SqlError::InvalidValue(format!(
2985 "{fname} requires 2 or 3 arguments"
2986 )));
2987 }
2988 if args[0].is_null() || args[1].is_null() {
2989 return Ok(Value::Null);
2990 }
2991 let tsv = match &args[0] {
2992 Value::TsVector(b) => b,
2993 v => {
2994 return Err(SqlError::TypeMismatch {
2995 expected: "TSVECTOR".into(),
2996 got: v.data_type().to_string(),
2997 })
2998 }
2999 };
3000 let tsq = match &args[1] {
3001 Value::TsQuery(b) => b,
3002 v => {
3003 return Err(SqlError::TypeMismatch {
3004 expected: "TSQUERY".into(),
3005 got: v.data_type().to_string(),
3006 })
3007 }
3008 };
3009 let norm = if args.len() == 3 {
3010 match &args[2] {
3011 Value::Integer(n) => *n,
3012 Value::Null => return Ok(Value::Null),
3013 v => {
3014 return Err(SqlError::TypeMismatch {
3015 expected: "INTEGER (norm)".into(),
3016 got: v.data_type().to_string(),
3017 })
3018 }
3019 }
3020 } else {
3021 0
3022 };
3023 if cover_density {
3024 crate::fts::fn_ts_rank_cd(tsv, tsq, norm)
3025 } else {
3026 crate::fts::fn_ts_rank(tsv, tsq, norm)
3027 }
3028}
3029
3030fn fts_ts_headline(args: &[Value]) -> Result<Value> {
3031 if args.len() < 2 || args.len() > 4 {
3032 return Err(SqlError::InvalidValue(
3033 "ts_headline requires 2 to 4 arguments".into(),
3034 ));
3035 }
3036 if args.iter().any(|v| v.is_null()) {
3037 return Ok(Value::Null);
3038 }
3039 let kind = if args.len() >= 3 {
3040 match &args[0] {
3041 Value::Text(s) => crate::fts::TokenizerKind::from_name(s.as_str())?,
3042 v => {
3043 return Err(SqlError::TypeMismatch {
3044 expected: "TEXT (config)".into(),
3045 got: v.data_type().to_string(),
3046 })
3047 }
3048 }
3049 } else {
3050 crate::fts::TokenizerKind::English
3051 };
3052 let text_idx = if args.len() >= 3 { 1 } else { 0 };
3053 let tsq_idx = if args.len() >= 3 { 2 } else { 1 };
3054 let text = match args.get(text_idx) {
3055 Some(Value::Text(s)) => s.as_str(),
3056 _ => {
3057 return Err(SqlError::TypeMismatch {
3058 expected: "TEXT".into(),
3059 got: "non-text".into(),
3060 })
3061 }
3062 };
3063 let tsq = match args.get(tsq_idx) {
3064 Some(Value::TsQuery(b)) => b.as_ref(),
3065 _ => {
3066 return Err(SqlError::TypeMismatch {
3067 expected: "TSQUERY".into(),
3068 got: "non-tsquery".into(),
3069 })
3070 }
3071 };
3072 crate::fts::fn_ts_headline_with(kind, text, tsq)
3073}
3074
3075fn fts_ts_lexize(args: &[Value]) -> Result<Value> {
3076 if args.len() != 2 {
3077 return Err(SqlError::InvalidValue(
3078 "ts_lexize requires 2 arguments (config, word)".into(),
3079 ));
3080 }
3081 if args.iter().any(|v| v.is_null()) {
3082 return Ok(Value::Null);
3083 }
3084 let kind = match &args[0] {
3085 Value::Text(s) => crate::fts::TokenizerKind::from_name(s.as_str())?,
3086 v => {
3087 return Err(SqlError::TypeMismatch {
3088 expected: "TEXT (config)".into(),
3089 got: v.data_type().to_string(),
3090 })
3091 }
3092 };
3093 let word = match &args[1] {
3094 Value::Text(s) => s.as_str(),
3095 v => {
3096 return Err(SqlError::TypeMismatch {
3097 expected: "TEXT (word)".into(),
3098 got: v.data_type().to_string(),
3099 })
3100 }
3101 };
3102 crate::fts::fn_ts_lexize_with(kind, word)
3103}
3104
3105fn fts_numnode(args: &[Value]) -> Result<Value> {
3106 check_args("numnode", args, 1)?;
3107 if args[0].is_null() {
3108 return Ok(Value::Null);
3109 }
3110 let tsq = match &args[0] {
3111 Value::TsQuery(b) => b,
3112 v => {
3113 return Err(SqlError::TypeMismatch {
3114 expected: "TSQUERY".into(),
3115 got: v.data_type().to_string(),
3116 })
3117 }
3118 };
3119 crate::fts::fn_numnode(tsq)
3120}
3121
3122fn fts_setweight(args: &[Value]) -> Result<Value> {
3123 if args.len() == 3 {
3124 return fts_setweight_selective(args);
3125 }
3126 check_args("setweight", args, 2)?;
3127 if args[0].is_null() || args[1].is_null() {
3128 return Ok(Value::Null);
3129 }
3130 let tsv = match &args[0] {
3131 Value::TsVector(b) => b,
3132 v => {
3133 return Err(SqlError::TypeMismatch {
3134 expected: "TSVECTOR".into(),
3135 got: v.data_type().to_string(),
3136 })
3137 }
3138 };
3139 let weight_text = match &args[1] {
3140 Value::Text(s) => s.as_str(),
3141 v => {
3142 return Err(SqlError::TypeMismatch {
3143 expected: "TEXT".into(),
3144 got: v.data_type().to_string(),
3145 })
3146 }
3147 };
3148 let weight = crate::fts::parse_weight_char(weight_text)?;
3149 crate::fts::fn_setweight(tsv, weight)
3150}
3151
3152fn fts_setweight_selective(args: &[Value]) -> Result<Value> {
3153 check_args("setweight", args, 3)?;
3154 if args[0].is_null() || args[1].is_null() || args[2].is_null() {
3155 return Ok(Value::Null);
3156 }
3157 let tsv = match &args[0] {
3158 Value::TsVector(b) => b,
3159 v => {
3160 return Err(SqlError::TypeMismatch {
3161 expected: "TSVECTOR".into(),
3162 got: v.data_type().to_string(),
3163 })
3164 }
3165 };
3166 let weight_text = match &args[1] {
3167 Value::Text(s) => s.as_str(),
3168 v => {
3169 return Err(SqlError::TypeMismatch {
3170 expected: "TEXT".into(),
3171 got: v.data_type().to_string(),
3172 })
3173 }
3174 };
3175 let weight = crate::fts::parse_weight_char(weight_text)?;
3176 let filter = match &args[2] {
3177 Value::Array(a) => a.as_ref().as_slice(),
3178 v => {
3179 return Err(SqlError::TypeMismatch {
3180 expected: "TEXT[]".into(),
3181 got: v.data_type().to_string(),
3182 })
3183 }
3184 };
3185 crate::fts::fn_setweight_selective(tsv, weight, filter)
3186}
3187
3188fn fts_strip(args: &[Value]) -> Result<Value> {
3189 check_args("strip", args, 1)?;
3190 if args[0].is_null() {
3191 return Ok(Value::Null);
3192 }
3193 let tsv = match &args[0] {
3194 Value::TsVector(b) => b,
3195 v => {
3196 return Err(SqlError::TypeMismatch {
3197 expected: "TSVECTOR".into(),
3198 got: v.data_type().to_string(),
3199 })
3200 }
3201 };
3202 crate::fts::fn_strip(tsv)
3203}
3204
3205fn ts_of(v: &Value) -> Result<i64> {
3207 match v {
3208 Value::Timestamp(t) => Ok(*t),
3209 Value::Date(d) => Ok(crate::datetime::date_to_ts(*d)),
3210 _ => Err(SqlError::TypeMismatch {
3211 expected: "TIMESTAMP or DATE".into(),
3212 got: v.data_type().to_string(),
3213 }),
3214 }
3215}
3216
3217fn int_arg(v: &Value, label: &str) -> Result<i64> {
3218 match v {
3219 Value::Integer(n) => Ok(*n),
3220 _ => Err(SqlError::TypeMismatch {
3221 expected: format!("INTEGER ({label})"),
3222 got: v.data_type().to_string(),
3223 }),
3224 }
3225}
3226
3227fn real_sec_arg(v: &Value) -> Result<(u8, u32)> {
3229 match v {
3230 Value::Integer(n) => {
3231 if !(0..=60).contains(n) {
3232 return Err(SqlError::InvalidValue(format!("second out of range: {n}")));
3233 }
3234 Ok((*n as u8, 0))
3235 }
3236 Value::Real(r) => {
3237 let whole = r.trunc() as i64;
3238 if !(0..=60).contains(&whole) {
3239 return Err(SqlError::InvalidValue(format!("second out of range: {r}")));
3240 }
3241 let frac = ((r - whole as f64) * 1_000_000.0).round() as i64;
3242 Ok((whole as u8, frac.max(0) as u32))
3243 }
3244 _ => Err(SqlError::TypeMismatch {
3245 expected: "numeric seconds".into(),
3246 got: v.data_type().to_string(),
3247 }),
3248 }
3249}
3250
3251fn date_trunc_in_zone(unit: &str, ts_utc: i64, tz: &str) -> Result<Value> {
3253 use jiff::{tz::TimeZone, Timestamp as JTimestamp};
3254 let zone = TimeZone::get(tz).map_err(|e| SqlError::InvalidTimezone(format!("{tz}: {e}")))?;
3255 let ts = JTimestamp::from_microsecond(ts_utc)
3256 .map_err(|e| SqlError::InvalidValue(format!("ts: {e}")))?;
3257 let zoned = ts.to_zoned(zone.clone());
3258 let unit_lower = unit.to_ascii_lowercase();
3259 let rounded = match unit_lower.as_str() {
3260 "microseconds" => return Ok(Value::Timestamp(ts_utc)),
3261 "second" => zoned
3262 .start_of_day()
3263 .map_err(|e| SqlError::InvalidValue(format!("{e}")))?,
3264 _ => {
3265 let naive_ts = zoned.timestamp().as_microsecond();
3266 return crate::datetime::date_trunc(unit, &Value::Timestamp(naive_ts));
3267 }
3268 };
3269 Ok(Value::Timestamp(rounded.timestamp().as_microsecond()))
3270}
3271
3272fn check_args(name: &str, args: &[Value], expected: usize) -> Result<()> {
3273 if args.len() != expected {
3274 Err(SqlError::InvalidValue(format!(
3275 "{name} requires {expected} argument(s), got {}",
3276 args.len()
3277 )))
3278 } else {
3279 Ok(())
3280 }
3281}
3282
3283pub fn referenced_columns(expr: &Expr, columns: &[ColumnDef]) -> Vec<usize> {
3284 let mut indices = Vec::new();
3285 collect_column_refs(expr, columns, &mut indices);
3286 indices.sort_unstable();
3287 indices.dedup();
3288 indices
3289}
3290
3291fn collect_column_refs(expr: &Expr, columns: &[ColumnDef], out: &mut Vec<usize>) {
3292 match expr {
3293 Expr::Column(name) => {
3294 for (i, c) in columns.iter().enumerate() {
3295 if c.name == *name
3296 || (c.name.len() > name.len()
3297 && c.name.as_bytes()[c.name.len() - name.len() - 1] == b'.'
3298 && c.name.ends_with(name.as_str()))
3299 {
3300 out.push(i);
3301 break;
3302 }
3303 }
3304 }
3305 Expr::QualifiedColumn { table, column } => {
3306 let mut found: Option<usize> = None;
3307 let mut bare_match: Option<usize> = None;
3308 let mut bare_count = 0usize;
3309 for (i, c) in columns.iter().enumerate() {
3310 if c.name.len() == table.len() + 1 + column.len()
3311 && c.name.as_bytes()[table.len()] == b'.'
3312 && c.name.starts_with(table.as_str())
3313 && c.name.ends_with(column.as_str())
3314 {
3315 found = Some(i);
3316 break;
3317 }
3318 if c.name == *column {
3319 bare_match = Some(i);
3320 bare_count += 1;
3321 }
3322 }
3323 if let Some(idx) = found {
3324 out.push(idx);
3325 } else if bare_count == 1 {
3326 out.push(bare_match.unwrap());
3327 }
3328 }
3329 Expr::BinaryOp { left, right, .. } => {
3330 collect_column_refs(left, columns, out);
3331 collect_column_refs(right, columns, out);
3332 }
3333 Expr::UnaryOp { expr, .. } => {
3334 collect_column_refs(expr, columns, out);
3335 }
3336 Expr::IsNull(e) | Expr::IsNotNull(e) => {
3337 collect_column_refs(e, columns, out);
3338 }
3339 Expr::Function { args, .. } => {
3340 for arg in args {
3341 collect_column_refs(arg, columns, out);
3342 }
3343 }
3344 Expr::InSubquery { expr, .. } => {
3345 collect_column_refs(expr, columns, out);
3346 }
3347 Expr::InList { expr, list, .. } => {
3348 collect_column_refs(expr, columns, out);
3349 for item in list {
3350 collect_column_refs(item, columns, out);
3351 }
3352 }
3353 Expr::InSet { expr, .. } => {
3354 collect_column_refs(expr, columns, out);
3355 }
3356 Expr::Between {
3357 expr, low, high, ..
3358 } => {
3359 collect_column_refs(expr, columns, out);
3360 collect_column_refs(low, columns, out);
3361 collect_column_refs(high, columns, out);
3362 }
3363 Expr::Like {
3364 expr,
3365 pattern,
3366 escape,
3367 ..
3368 } => {
3369 collect_column_refs(expr, columns, out);
3370 collect_column_refs(pattern, columns, out);
3371 if let Some(esc) = escape {
3372 collect_column_refs(esc, columns, out);
3373 }
3374 }
3375 Expr::Case {
3376 operand,
3377 conditions,
3378 else_result,
3379 } => {
3380 if let Some(op) = operand {
3381 collect_column_refs(op, columns, out);
3382 }
3383 for (when, then) in conditions {
3384 collect_column_refs(when, columns, out);
3385 collect_column_refs(then, columns, out);
3386 }
3387 if let Some(e) = else_result {
3388 collect_column_refs(e, columns, out);
3389 }
3390 }
3391 Expr::Coalesce(args) => {
3392 for arg in args {
3393 collect_column_refs(arg, columns, out);
3394 }
3395 }
3396 Expr::Cast { expr, .. } => {
3397 collect_column_refs(expr, columns, out);
3398 }
3399 Expr::Collate { expr, .. } => {
3400 collect_column_refs(expr, columns, out);
3401 }
3402 Expr::WindowFunction { args, spec, .. } => {
3403 for arg in args {
3404 collect_column_refs(arg, columns, out);
3405 }
3406 for pb in &spec.partition_by {
3407 collect_column_refs(pb, columns, out);
3408 }
3409 for ob in &spec.order_by {
3410 collect_column_refs(&ob.expr, columns, out);
3411 }
3412 }
3413 Expr::ArrayLiteral(elems) => {
3414 for e in elems {
3415 collect_column_refs(e, columns, out);
3416 }
3417 }
3418 Expr::Quantified { left, right, .. } => {
3419 collect_column_refs(left, columns, out);
3420 if let crate::parser::QuantifiedRhs::Array(e) = right {
3421 collect_column_refs(e, columns, out);
3422 }
3423 }
3424 Expr::Literal(_)
3425 | Expr::Parameter(_)
3426 | Expr::CountStar
3427 | Expr::Exists { .. }
3428 | Expr::ScalarSubquery(_)
3429 | Expr::TypedNullRecord(_) => {}
3430 }
3431}
3432
3433pub fn is_truthy(val: &Value) -> bool {
3434 match val {
3435 Value::Boolean(b) => *b,
3436 Value::Integer(i) => *i != 0,
3437 Value::Null => false,
3438 _ => true,
3439 }
3440}
3441
3442#[cfg(test)]
3443#[path = "eval_tests.rs"]
3444mod tests;