1use anyhow::{Result, anyhow};
10use std::cmp::Ordering;
11use uni_common::{TemporalValue, Value};
12
13use crate::query::datetime::{
14 CypherDuration, TemporalType, add_cypher_duration_to_date, add_cypher_duration_to_datetime,
15 add_cypher_duration_to_localdatetime, add_cypher_duration_to_localtime,
16 add_cypher_duration_to_time, classify_temporal, eval_datetime_function, is_duration_value,
17 parse_datetime_utc, parse_duration_from_value, parse_duration_to_cypher,
18};
19use crate::query::spatial::eval_spatial_function;
20use uni_cypher::ast::BinaryOp;
21
22pub fn eval_binary_op(left: &Value, op: &BinaryOp, right: &Value) -> Result<Value> {
27 if !matches!(op, BinaryOp::And | BinaryOp::Or) && (left.is_null() || right.is_null()) {
29 return Ok(Value::Null);
30 }
31
32 match op {
33 BinaryOp::Eq => Ok(match cypher_eq(left, right) {
34 Some(b) => Value::Bool(b),
35 None => Value::Null,
36 }),
37 BinaryOp::NotEq => Ok(match cypher_eq(left, right) {
38 Some(b) => Value::Bool(!b),
39 None => Value::Null,
40 }),
41 BinaryOp::And => {
42 match (left.as_bool(), right.as_bool()) {
44 (Some(false), _) | (_, Some(false)) => Ok(Value::Bool(false)),
45 (Some(true), Some(true)) => Ok(Value::Bool(true)),
46 _ if left.is_null() || right.is_null() => Ok(Value::Null),
47 _ => Err(anyhow!(
48 "InvalidArgumentType: Expected bool for AND operands"
49 )),
50 }
51 }
52 BinaryOp::Or => {
53 match (left.as_bool(), right.as_bool()) {
55 (Some(true), _) | (_, Some(true)) => Ok(Value::Bool(true)),
56 (Some(false), Some(false)) => Ok(Value::Bool(false)),
57 _ if left.is_null() || right.is_null() => Ok(Value::Null),
58 _ => Err(anyhow!(
59 "InvalidArgumentType: Expected bool for OR operands"
60 )),
61 }
62 }
63 BinaryOp::Xor => {
64 match (left.as_bool(), right.as_bool()) {
66 (Some(l), Some(r)) => Ok(Value::Bool(l ^ r)),
67 _ if left.is_null() || right.is_null() => Ok(Value::Null),
68 _ => Err(anyhow!(
69 "InvalidArgumentType: Expected bool for XOR operands"
70 )),
71 }
72 }
73 BinaryOp::Gt => eval_comparison(left, right, |ordering| ordering.is_gt()),
74 BinaryOp::Lt => eval_comparison(left, right, |ordering| ordering.is_lt()),
75 BinaryOp::GtEq => eval_comparison(left, right, |ordering| ordering.is_ge()),
76 BinaryOp::LtEq => eval_comparison(left, right, |ordering| ordering.is_le()),
77 BinaryOp::Contains => eval_string_predicate(left, right, "CONTAINS", |l, r| l.contains(r)),
78 BinaryOp::StartsWith => {
79 eval_string_predicate(left, right, "STARTS WITH", |l, r| l.starts_with(r))
80 }
81 BinaryOp::EndsWith => {
82 eval_string_predicate(left, right, "ENDS WITH", |l, r| l.ends_with(r))
83 }
84 BinaryOp::Add => eval_add(left, right),
85 BinaryOp::Sub => eval_sub(left, right),
86 BinaryOp::Mul => eval_mul(left, right),
87 BinaryOp::Div => eval_div(left, right),
88 BinaryOp::Mod => eval_numeric_op(left, right, |a, b| a % b),
89 BinaryOp::Pow => eval_numeric_op(left, right, |a, b| a.powf(b)),
90 BinaryOp::Regex => {
91 let l = left
92 .as_str()
93 .ok_or_else(|| anyhow!("Left operand of =~ must be a string"))?;
94 let pattern = right
95 .as_str()
96 .ok_or_else(|| anyhow!("Right operand of =~ must be a regex pattern string"))?;
97 let re = regex::Regex::new(pattern)
98 .map_err(|e| anyhow!("Invalid regex pattern '{}': {}", pattern, e))?;
99 Ok(Value::Bool(re.is_match(l)))
100 }
101 BinaryOp::ApproxEq => eval_vector_similarity(left, right),
102 }
103}
104
105pub fn cypher_eq(left: &Value, right: &Value) -> Option<bool> {
108 if left.is_null() || right.is_null() {
109 return None;
110 }
111
112 if let (Some(l), Some(r)) = (left.as_i64(), right.as_i64()) {
114 return Some(l == r);
115 }
116
117 if let (Some(l), Some(r)) = (left.as_f64(), right.as_f64()) {
119 if l.is_nan() || r.is_nan() {
120 return Some(false);
121 }
122 return Some(l == r);
123 }
124
125 if let (Value::List(l), Value::List(r)) = (left, right) {
127 if l.len() != r.len() {
128 return Some(false);
129 }
130 let mut has_null = false;
131 for (lv, rv) in l.iter().zip(r.iter()) {
132 match cypher_eq(lv, rv) {
133 Some(false) => return Some(false),
134 None => has_null = true,
135 Some(true) => {}
136 }
137 }
138 return if has_null { None } else { Some(true) };
139 }
140
141 if let (Value::Map(l), Value::Map(r)) = (left, right) {
143 if let (Some(vid_l), Some(vid_r)) = (l.get("_vid"), r.get("_vid")) {
145 return Some(vid_l == vid_r);
146 }
147 if let (Some(eid_l), Some(eid_r)) = (l.get("_eid"), r.get("_eid")) {
149 return Some(eid_l == eid_r);
150 }
151
152 if l.len() != r.len() {
153 return Some(false);
154 }
155
156 let mut has_null = false;
157 for (k, lv) in l {
158 if let Some(rv) = r.get(k) {
159 match cypher_eq(lv, rv) {
160 Some(false) => return Some(false),
161 None => has_null = true,
162 Some(true) => {}
163 }
164 } else {
165 return Some(false);
166 }
167 }
168 return if has_null { None } else { Some(true) };
169 }
170
171 Some(left == right)
173}
174
175pub fn eval_in_op(left: &Value, right: &Value) -> Result<Value> {
177 if let Value::List(arr) = right {
178 let mut has_null = false;
179 for item in arr {
181 match cypher_eq(left, item) {
182 Some(true) => return Ok(Value::Bool(true)),
183 None => has_null = true,
184 _ => {}
185 }
186 }
187
188 if let Value::Map(map) = left
192 && let Some(vid_val) = map.get("_vid")
193 && let Some(vid_u64) = vid_val.as_u64()
194 {
195 let vid = uni_common::core::id::Vid::from(vid_u64);
196 let vid_str = vid.to_string();
197 for item in arr {
198 match item {
199 Value::String(s) if s == &vid_str => return Ok(Value::Bool(true)),
200 Value::Int(n) if *n as u64 == vid_u64 => return Ok(Value::Bool(true)),
201 _ => {}
202 }
203 }
204 }
205
206 if has_null {
207 Ok(Value::Null)
208 } else {
209 Ok(Value::Bool(false))
210 }
211 } else {
212 Err(anyhow!("Right side of IN must be a list"))
213 }
214}
215
216fn eval_string_predicate(
217 left: &Value,
218 right: &Value,
219 op_name: &str,
220 check: fn(&str, &str) -> bool,
221) -> Result<Value> {
222 let l = left
223 .as_str()
224 .ok_or_else(|| anyhow!("Left side of {} must be a string", op_name))?;
225 let r = right
226 .as_str()
227 .ok_or_else(|| anyhow!("Right side of {} must be a string", op_name))?;
228 Ok(Value::Bool(check(l, r)))
229}
230
231fn eval_numeric_op<F>(left: &Value, right: &Value, op: F) -> Result<Value>
232where
233 F: Fn(f64, f64) -> f64,
234{
235 if left.is_null() || right.is_null() {
237 return Ok(Value::Null);
238 }
239 let (l, r) = match (left.as_f64(), right.as_f64()) {
240 (Some(l), Some(r)) => (l, r),
241 _ => return Err(anyhow!("Arithmetic operation requires numbers")),
242 };
243 let result = op(l, r);
244 if !result.is_nan()
246 && !result.is_infinite()
247 && result.fract() == 0.0
248 && left.is_i64()
249 && right.is_i64()
250 {
251 Ok(Value::Int(result as i64))
252 } else {
253 Ok(Value::Float(result))
254 }
255}
256
257fn add_temporal_duration_to_value(val: &Value, dur: &CypherDuration) -> Result<Value> {
264 match val {
265 Value::Temporal(tv) => add_temporal_duration_typed(tv, dur),
266 Value::Map(map) => {
267 if let Some(tv) = temporal_from_map_wrapper(map) {
268 add_temporal_duration_typed(&tv, dur)
269 } else {
270 Err(anyhow!("Expected temporal value for duration arithmetic"))
271 }
272 }
273 Value::String(s) => {
274 if let Some(tv) = temporal_from_json_wrapper_str(s) {
275 return add_temporal_duration_typed(&tv, dur);
276 }
277 let ttype = classify_temporal(s)
278 .ok_or_else(|| anyhow!("Cannot classify temporal value: {}", s))?;
279 let result_str = match ttype {
280 TemporalType::Date => add_cypher_duration_to_date(s, dur)?,
281 TemporalType::LocalTime => add_cypher_duration_to_localtime(s, dur)?,
282 TemporalType::Time => add_cypher_duration_to_time(s, dur)?,
283 TemporalType::LocalDateTime => add_cypher_duration_to_localdatetime(s, dur)?,
284 TemporalType::DateTime => add_cypher_duration_to_datetime(s, dur)?,
285 TemporalType::Duration | TemporalType::Btic => {
286 return Err(anyhow!("Cannot add duration to {:?} this way", ttype));
287 }
288 };
289 Ok(Value::String(result_str))
290 }
291 _ => Err(anyhow!("Expected temporal value for duration arithmetic")),
292 }
293}
294
295fn add_temporal_duration_typed(tv: &TemporalValue, dur: &CypherDuration) -> Result<Value> {
297 let s = tv.to_string();
300 let ttype = tv.temporal_type();
301 let result_str = match ttype {
302 TemporalType::Date => add_cypher_duration_to_date(&s, dur)?,
303 TemporalType::LocalTime => add_cypher_duration_to_localtime(&s, dur)?,
304 TemporalType::Time => add_cypher_duration_to_time(&s, dur)?,
305 TemporalType::LocalDateTime => add_cypher_duration_to_localdatetime(&s, dur)?,
306 TemporalType::DateTime => add_cypher_duration_to_datetime(&s, dur)?,
307 TemporalType::Duration | TemporalType::Btic => {
308 return Err(anyhow!("Cannot add duration to {:?} this way", ttype));
309 }
310 };
311 let args = [Value::String(result_str)];
313 match ttype {
314 TemporalType::Date => eval_datetime_function("DATE", &args),
315 TemporalType::LocalTime => eval_datetime_function("LOCALTIME", &args),
316 TemporalType::Time => eval_datetime_function("TIME", &args),
317 TemporalType::LocalDateTime => eval_datetime_function("LOCALDATETIME", &args),
318 TemporalType::DateTime => eval_datetime_function("DATETIME", &args),
319 TemporalType::Duration | TemporalType::Btic => unreachable!(),
320 }
321}
322
323fn eval_add(left: &Value, right: &Value) -> Result<Value> {
325 if left.is_null() || right.is_null() {
327 return Ok(Value::Null);
328 }
329
330 match (left, right) {
332 (Value::List(l), Value::List(r)) => {
333 let mut result = l.clone();
334 result.extend(r.iter().cloned());
335 return Ok(Value::List(result));
336 }
337 (Value::List(l), _) => {
338 let mut result = l.clone();
339 result.push(right.clone());
340 return Ok(Value::List(result));
341 }
342 (_, Value::List(r)) => {
343 let mut result = vec![left.clone()];
344 result.extend(r.iter().cloned());
345 return Ok(Value::List(result));
346 }
347 _ => {}
348 }
349
350 if let (Some(l), Some(r)) = (left.as_f64(), right.as_f64()) {
352 if left.is_i64() && right.is_i64() {
353 return Ok(Value::Int(left.as_i64().unwrap() + right.as_i64().unwrap()));
354 }
355 return Ok(Value::Float(l + r));
356 }
357
358 if let Value::String(s) = left
360 && classify_temporal(s).is_some_and(|t| t != TemporalType::Duration)
361 && let Ok(dur) = parse_duration_from_value(right)
362 {
363 return add_temporal_duration_to_value(left, &dur);
364 }
365 if let Value::String(s) = right
366 && classify_temporal(s).is_some_and(|t| t != TemporalType::Duration)
367 && let Ok(dur) = parse_duration_from_value(left)
368 {
369 return add_temporal_duration_to_value(right, &dur);
370 }
371
372 if let Some(tv) = temporal_from_value(left)
374 && !matches!(tv, TemporalValue::Duration { .. })
375 && (is_duration_value(right) || right.is_number())
376 {
377 let dur = parse_duration_from_value(right)?;
378 return add_temporal_duration_typed(&tv, &dur);
379 }
380 if let Some(tv) = temporal_from_value(right)
382 && !matches!(tv, TemporalValue::Duration { .. })
383 && (is_duration_value(left) || left.is_number())
384 {
385 let dur = parse_duration_from_value(left)?;
386 return add_temporal_duration_typed(&tv, &dur);
387 }
388 if let (
390 Some(TemporalValue::Duration {
391 months: m1,
392 days: d1,
393 nanos: n1,
394 }),
395 Some(TemporalValue::Duration {
396 months: m2,
397 days: d2,
398 nanos: n2,
399 }),
400 ) = (temporal_from_value(left), temporal_from_value(right))
401 {
402 return Ok(Value::Temporal(TemporalValue::Duration {
403 months: m1 + m2,
404 days: d1 + d2,
405 nanos: n1 + n2,
406 }));
407 }
408
409 if let (Value::String(l), Value::String(r)) = (left, right) {
411 let l_type = classify_temporal(l);
412 let r_type = classify_temporal(r);
413
414 match (l_type, r_type) {
415 (Some(lt), Some(TemporalType::Duration)) if lt != TemporalType::Duration => {
417 let dur = parse_duration_to_cypher(r)?;
418 return add_temporal_duration_to_value(left, &dur);
419 }
420 (Some(TemporalType::Duration), Some(rt)) if rt != TemporalType::Duration => {
422 let dur = parse_duration_to_cypher(l)?;
423 return add_temporal_duration_to_value(right, &dur);
424 }
425 (Some(TemporalType::Duration), Some(TemporalType::Duration)) => {
427 let d1 = parse_duration_to_cypher(l)?;
428 let d2 = parse_duration_to_cypher(r)?;
429 return Ok(Value::String(d1.add(&d2).to_iso8601()));
430 }
431 _ => return Ok(Value::String(format!("{}{}", l, r))),
433 }
434 }
435
436 if let Value::String(_) = left
438 && right.is_number()
439 && classify_value_temporal(left).is_some_and(|t| t != TemporalType::Duration)
440 {
441 let dur = parse_duration_from_value(right)?;
442 return add_temporal_duration_to_value(left, &dur);
443 }
444 if let Value::String(_) = right
446 && left.is_number()
447 && classify_value_temporal(right).is_some_and(|t| t != TemporalType::Duration)
448 {
449 let dur = parse_duration_from_value(left)?;
450 return add_temporal_duration_to_value(right, &dur);
451 }
452
453 Err(anyhow!(
454 "Invalid types for addition: left={:?}, right={:?}",
455 left,
456 right
457 ))
458}
459
460fn classify_value_temporal(val: &Value) -> Option<TemporalType> {
462 match val {
463 Value::Temporal(tv) => Some(tv.temporal_type()),
464 Value::String(s) => classify_temporal(s),
465 _ => None,
466 }
467}
468
469fn eval_sub(left: &Value, right: &Value) -> Result<Value> {
471 if left.is_null() || right.is_null() {
473 return Ok(Value::Null);
474 }
475
476 if let Value::Temporal(tv) = left
478 && !matches!(tv, TemporalValue::Duration { .. })
479 {
480 if let Value::Temporal(TemporalValue::Duration {
481 months,
482 days,
483 nanos,
484 }) = right
485 {
486 let dur = CypherDuration::new(-months, -days, -nanos);
487 return add_temporal_duration_typed(tv, &dur);
488 }
489 if is_duration_value(right) || right.is_number() {
490 let dur = parse_duration_from_value(right)?.negate();
491 return add_temporal_duration_typed(tv, &dur);
492 }
493 }
494 if let (
496 Value::Temporal(TemporalValue::Duration {
497 months: m1,
498 days: d1,
499 nanos: n1,
500 }),
501 Value::Temporal(TemporalValue::Duration {
502 months: m2,
503 days: d2,
504 nanos: n2,
505 }),
506 ) = (left, right)
507 {
508 return Ok(Value::Temporal(TemporalValue::Duration {
509 months: m1 - m2,
510 days: d1 - d2,
511 nanos: n1 - n2,
512 }));
513 }
514 if let (Value::Temporal(l), Value::Temporal(r)) = (left, right)
516 && l.temporal_type() == r.temporal_type()
517 && l.temporal_type() != TemporalType::Duration
518 {
519 let args = [left.clone(), right.clone()];
520 return crate::query::datetime::eval_datetime_function("DURATION.BETWEEN", &args);
521 }
522
523 if let (Value::String(l), Value::String(r)) = (left, right) {
525 let l_type = classify_temporal(l);
526 let r_type = classify_temporal(r);
527
528 match (l_type, r_type) {
529 (Some(lt), Some(TemporalType::Duration)) if lt != TemporalType::Duration => {
530 let dur = parse_duration_to_cypher(r)?.negate();
531 return add_temporal_duration_to_value(left, &dur);
532 }
533 (Some(TemporalType::Duration), Some(TemporalType::Duration)) => {
534 let d1 = parse_duration_to_cypher(l)?;
535 let d2 = parse_duration_to_cypher(r)?;
536 return Ok(Value::String(d1.sub(&d2).to_iso8601()));
537 }
538 (Some(lt), Some(rt))
539 if lt != TemporalType::Duration && rt != TemporalType::Duration && lt == rt =>
540 {
541 let args = [left.clone(), right.clone()];
542 return crate::query::datetime::eval_datetime_function("DURATION.BETWEEN", &args);
543 }
544 _ => {}
545 }
546 }
547
548 if let Value::String(_) = left
550 && right.is_number()
551 && classify_value_temporal(left).is_some_and(|t| t != TemporalType::Duration)
552 {
553 let dur = parse_duration_from_value(right)?.negate();
554 return add_temporal_duration_to_value(left, &dur);
555 }
556
557 eval_numeric_op(left, right, |a, b| a - b)
558}
559
560fn extract_cypher_duration(val: &Value) -> Option<Result<(CypherDuration, bool)>> {
564 match val {
565 Value::Temporal(TemporalValue::Duration {
566 months,
567 days,
568 nanos,
569 }) => Some(Ok((CypherDuration::new(*months, *days, *nanos), true))),
570 Value::String(s) if is_duration_value(val) => {
571 Some(parse_duration_to_cypher(s).map(|d| (d, false)))
572 }
573 _ => None,
574 }
575}
576
577fn duration_to_value(result: CypherDuration, is_temporal: bool) -> Value {
582 if is_temporal {
583 result.to_temporal_value()
584 } else {
585 Value::String(result.to_iso8601())
586 }
587}
588
589fn eval_mul(left: &Value, right: &Value) -> Result<Value> {
591 if left.is_null() || right.is_null() {
592 return Ok(Value::Null);
593 }
594
595 if let Some(dur_result) = extract_cypher_duration(left)
597 && let Some(factor) = right.as_f64()
598 {
599 let (dur, is_temporal) = dur_result?;
600 return Ok(duration_to_value(dur.multiply(factor), is_temporal));
601 }
602 if let Some(dur_result) = extract_cypher_duration(right)
603 && let Some(factor) = left.as_f64()
604 {
605 let (dur, is_temporal) = dur_result?;
606 return Ok(duration_to_value(dur.multiply(factor), is_temporal));
607 }
608
609 eval_numeric_op(left, right, |a, b| a * b)
610}
611
612fn eval_div(left: &Value, right: &Value) -> Result<Value> {
614 if left.is_null() || right.is_null() {
615 return Ok(Value::Null);
616 }
617
618 if let Some(dur_result) = extract_cypher_duration(left)
620 && let Some(divisor) = right.as_f64()
621 {
622 let (dur, is_temporal) = dur_result?;
623 return Ok(duration_to_value(dur.divide(divisor), is_temporal));
624 }
625
626 if let (Value::Int(l), Value::Int(r)) = (left, right) {
628 return if *r == 0 {
629 Err(anyhow!("Division by zero"))
630 } else {
631 Ok(Value::Int(l / r))
632 };
633 }
634
635 eval_numeric_op(left, right, |a, b| a / b)
636}
637
638fn eval_comparison<F>(left: &Value, right: &Value, check: F) -> Result<Value>
644where
645 F: Fn(Ordering) -> bool,
646{
647 if left.is_null() || right.is_null() {
649 return Ok(Value::Null);
650 }
651
652 let left_nan = left.as_f64().is_some_and(|f| f.is_nan());
654 let right_nan = right.as_f64().is_some_and(|f| f.is_nan());
655 if left_nan || right_nan {
656 if left_nan && right_nan {
657 return Ok(Value::Bool(false));
658 }
659 let other = if left_nan { right } else { left };
660 if other.as_f64().is_some() {
661 return Ok(Value::Bool(false)); }
663 return Ok(Value::Null); }
665
666 let ord = cypher_partial_cmp(left, right);
667 match ord {
668 Some(o) => Ok(Value::Bool(check(o))),
669 None => Ok(Value::Null),
670 }
671}
672
673fn cypher_partial_cmp(left: &Value, right: &Value) -> Option<Ordering> {
675 if left.is_null() || right.is_null() {
676 return None;
677 }
678
679 let left_temporal = temporal_from_value(left);
680 let right_temporal = temporal_from_value(right);
681 if let (Some(l), Some(r)) = (&left_temporal, &right_temporal) {
682 return temporal_partial_cmp(l, r);
683 }
684 if let (Some(_), Value::String(rs)) = (&left_temporal, right) {
685 let ls = left.to_string();
686 if let (Some(lt), Some(rt)) = (classify_temporal(&ls), classify_temporal(rs))
687 && lt == rt
688 {
689 return temporal_string_cmp(&ls, rs, lt);
690 }
691 return None;
692 }
693 if let (Value::String(ls), Some(_)) = (left, &right_temporal) {
694 let rs = right.to_string();
695 if let (Some(lt), Some(rt)) = (classify_temporal(ls), classify_temporal(&rs))
696 && lt == rt
697 {
698 return temporal_string_cmp(ls, &rs, lt);
699 }
700 return None;
701 }
702
703 if let (Some(l), Some(r)) = (left.as_i64(), right.as_i64()) {
705 return Some(l.cmp(&r));
706 }
707
708 if let (Some(l), Some(r)) = (left.as_f64(), right.as_f64()) {
710 return l.partial_cmp(&r);
711 }
712
713 if let (Some(l), Some(r)) = (left.as_str(), right.as_str()) {
715 if let (Some(lt), Some(rt)) = (classify_temporal(l), classify_temporal(r))
717 && lt == rt
718 {
719 let res = temporal_string_cmp(l, r, lt);
720 if res.is_some() {
721 return res;
722 }
723 }
724 return l.partial_cmp(r);
725 }
726
727 if let (Some(l), Some(r)) = (left.as_bool(), right.as_bool()) {
729 return l.partial_cmp(&r);
730 }
731
732 if let (Value::List(l), Value::List(r)) = (left, right) {
734 for (lv, rv) in l.iter().zip(r.iter()) {
735 match cypher_partial_cmp(lv, rv) {
736 Some(Ordering::Equal) => continue,
737 other => return other,
738 }
739 }
740 return l.len().partial_cmp(&r.len());
741 }
742
743 None
745}
746
747fn temporal_partial_cmp(left: &TemporalValue, right: &TemporalValue) -> Option<Ordering> {
749 match (left, right) {
750 (
751 TemporalValue::Date {
752 days_since_epoch: l,
753 },
754 TemporalValue::Date {
755 days_since_epoch: r,
756 },
757 ) => Some(l.cmp(r)),
758 (
759 TemporalValue::LocalTime {
760 nanos_since_midnight: l,
761 },
762 TemporalValue::LocalTime {
763 nanos_since_midnight: r,
764 },
765 ) => Some(l.cmp(r)),
766 (
767 TemporalValue::Time {
768 nanos_since_midnight: lm,
769 offset_seconds: lo,
770 },
771 TemporalValue::Time {
772 nanos_since_midnight: rm,
773 offset_seconds: ro,
774 },
775 ) => {
776 let l_utc = *lm as i128 - (*lo as i128) * 1_000_000_000;
778 let r_utc = *rm as i128 - (*ro as i128) * 1_000_000_000;
779 Some(l_utc.cmp(&r_utc))
780 }
781 (
782 TemporalValue::LocalDateTime {
783 nanos_since_epoch: l,
784 },
785 TemporalValue::LocalDateTime {
786 nanos_since_epoch: r,
787 },
788 ) => Some(l.cmp(r)),
789 (
790 TemporalValue::DateTime {
791 nanos_since_epoch: l,
792 ..
793 },
794 TemporalValue::DateTime {
795 nanos_since_epoch: r,
796 ..
797 },
798 ) => {
799 Some(l.cmp(r))
801 }
802 (TemporalValue::Duration { .. }, TemporalValue::Duration { .. }) => None,
804 _ => None,
806 }
807}
808
809pub(crate) fn temporal_from_value(v: &Value) -> Option<TemporalValue> {
816 match v {
817 Value::Temporal(tv) => Some(tv.clone()),
818 Value::Map(map) => temporal_from_map_wrapper(map),
819 Value::String(s) => {
820 temporal_from_json_wrapper_str(s).or_else(|| temporal_from_human_readable_str(s))
821 }
822 _ => None,
823 }
824}
825
826pub(crate) fn temporal_from_human_readable_str(s: &str) -> Option<TemporalValue> {
829 let fn_name = match classify_temporal(s)? {
830 TemporalType::Date => "DATE",
831 TemporalType::LocalTime => "LOCALTIME",
832 TemporalType::Time => "TIME",
833 TemporalType::LocalDateTime => "LOCALDATETIME",
834 TemporalType::DateTime => "DATETIME",
835 TemporalType::Duration => "DURATION",
836 TemporalType::Btic => return None, };
838 match eval_datetime_function(fn_name, &[Value::String(s.to_string())]).ok()? {
839 Value::Temporal(tv) => Some(tv),
840 _ => None,
841 }
842}
843
844pub(crate) fn temporal_from_map_wrapper(
850 map: &std::collections::HashMap<String, Value>,
851) -> Option<TemporalValue> {
852 if map.len() != 1 {
853 return None;
854 }
855
856 let as_i32 = |v: &Value| v.as_i64().and_then(|n| i32::try_from(n).ok());
857 let as_i64 = |v: &Value| v.as_i64();
858
859 if let Some(Value::Map(inner)) = map.get("Date") {
860 let days = inner.get("days_since_epoch").and_then(as_i32)?;
861 return Some(TemporalValue::Date {
862 days_since_epoch: days,
863 });
864 }
865 if let Some(Value::Map(inner)) = map.get("LocalTime") {
866 let nanos = inner.get("nanos_since_midnight").and_then(as_i64)?;
867 return Some(TemporalValue::LocalTime {
868 nanos_since_midnight: nanos,
869 });
870 }
871 if let Some(Value::Map(inner)) = map.get("Time") {
872 let nanos = inner.get("nanos_since_midnight").and_then(as_i64)?;
873 let offset = inner.get("offset_seconds").and_then(as_i32)?;
874 return Some(TemporalValue::Time {
875 nanos_since_midnight: nanos,
876 offset_seconds: offset,
877 });
878 }
879 if let Some(Value::Map(inner)) = map.get("LocalDateTime") {
880 let nanos = inner.get("nanos_since_epoch").and_then(as_i64)?;
881 return Some(TemporalValue::LocalDateTime {
882 nanos_since_epoch: nanos,
883 });
884 }
885 if let Some(Value::Map(inner)) = map.get("DateTime") {
886 let nanos = inner.get("nanos_since_epoch").and_then(as_i64)?;
887 let offset = inner.get("offset_seconds").and_then(as_i32)?;
888 let timezone_name = match inner.get("timezone_name") {
889 Some(Value::String(s)) => Some(s.clone()),
890 _ => None,
891 };
892 return Some(TemporalValue::DateTime {
893 nanos_since_epoch: nanos,
894 offset_seconds: offset,
895 timezone_name,
896 });
897 }
898 if let Some(Value::Map(inner)) = map.get("Duration") {
899 let months = inner.get("months").and_then(as_i64)?;
900 let days = inner.get("days").and_then(as_i64)?;
901 let nanos = inner.get("nanos").and_then(as_i64)?;
902 return Some(TemporalValue::Duration {
903 months,
904 days,
905 nanos,
906 });
907 }
908 None
909}
910
911fn temporal_from_json_wrapper_str(s: &str) -> Option<TemporalValue> {
912 let parsed: serde_json::Value = serde_json::from_str(s).ok()?;
913 let obj = parsed.as_object()?;
914 if obj.len() != 1 {
915 return None;
916 }
917
918 let as_i32 = |o: &serde_json::Map<String, serde_json::Value>, key: &str| {
919 o.get(key)
920 .and_then(serde_json::Value::as_i64)
921 .and_then(|n| i32::try_from(n).ok())
922 };
923 let as_i64 = |o: &serde_json::Map<String, serde_json::Value>, key: &str| {
924 o.get(key).and_then(serde_json::Value::as_i64)
925 };
926
927 if let Some(inner) = obj.get("Date").and_then(serde_json::Value::as_object) {
928 return Some(TemporalValue::Date {
929 days_since_epoch: as_i32(inner, "days_since_epoch")?,
930 });
931 }
932 if let Some(inner) = obj.get("LocalTime").and_then(serde_json::Value::as_object) {
933 return Some(TemporalValue::LocalTime {
934 nanos_since_midnight: as_i64(inner, "nanos_since_midnight")?,
935 });
936 }
937 if let Some(inner) = obj.get("Time").and_then(serde_json::Value::as_object) {
938 return Some(TemporalValue::Time {
939 nanos_since_midnight: as_i64(inner, "nanos_since_midnight")?,
940 offset_seconds: as_i32(inner, "offset_seconds")?,
941 });
942 }
943 if let Some(inner) = obj
944 .get("LocalDateTime")
945 .and_then(serde_json::Value::as_object)
946 {
947 return Some(TemporalValue::LocalDateTime {
948 nanos_since_epoch: as_i64(inner, "nanos_since_epoch")?,
949 });
950 }
951 if let Some(inner) = obj.get("DateTime").and_then(serde_json::Value::as_object) {
952 return Some(TemporalValue::DateTime {
953 nanos_since_epoch: as_i64(inner, "nanos_since_epoch")?,
954 offset_seconds: as_i32(inner, "offset_seconds")?,
955 timezone_name: inner
956 .get("timezone_name")
957 .and_then(serde_json::Value::as_str)
958 .map(str::to_string),
959 });
960 }
961 if let Some(inner) = obj.get("Duration").and_then(serde_json::Value::as_object) {
962 return Some(TemporalValue::Duration {
963 months: as_i64(inner, "months")?,
964 days: as_i64(inner, "days")?,
965 nanos: as_i64(inner, "nanos")?,
966 });
967 }
968 None
969}
970
971fn temporal_string_cmp(l: &str, r: &str, ttype: TemporalType) -> Option<Ordering> {
973 match ttype {
974 TemporalType::Date => {
975 let ld = chrono::NaiveDate::parse_from_str(l, "%Y-%m-%d").ok();
976 let rd = chrono::NaiveDate::parse_from_str(r, "%Y-%m-%d").ok();
977 ld.and_then(|l| rd.map(|r| l.cmp(&r)))
978 }
979 TemporalType::LocalTime => {
980 let lt = parse_time_for_cmp(l).ok();
981 let rt = parse_time_for_cmp(r).ok();
982 lt.and_then(|l| rt.map(|r| l.cmp(&r)))
983 }
984 TemporalType::Time => {
985 let ln = time_with_tz_to_utc_nanos(l).ok();
986 let rn = time_with_tz_to_utc_nanos(r).ok();
987 ln.and_then(|l| rn.map(|r| l.cmp(&r)))
988 }
989 TemporalType::LocalDateTime => {
990 let ldt = parse_local_datetime_for_cmp(l).ok();
991 let rdt = parse_local_datetime_for_cmp(r).ok();
992 ldt.and_then(|l| rdt.map(|r| l.cmp(&r)))
993 }
994 TemporalType::DateTime => {
995 let ldt = parse_datetime_utc(l).ok();
996 let rdt = parse_datetime_utc(r).ok();
997 ldt.and_then(|l| rdt.map(|r| l.cmp(&r)))
998 }
999 TemporalType::Duration => None, TemporalType::Btic => None, }
1002}
1003
1004fn parse_time_for_cmp(s: &str) -> Result<chrono::NaiveTime> {
1006 chrono::NaiveTime::parse_from_str(s, "%H:%M:%S%.f")
1007 .or_else(|_| chrono::NaiveTime::parse_from_str(s, "%H:%M:%S"))
1008 .or_else(|_| chrono::NaiveTime::parse_from_str(s, "%H:%M"))
1009 .map_err(|_| anyhow!("Cannot parse time: {}", s))
1010}
1011
1012fn parse_local_datetime_for_cmp(s: &str) -> Result<chrono::NaiveDateTime> {
1014 chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")
1015 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S"))
1016 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M"))
1017 .map_err(|_| anyhow!("Cannot parse localdatetime: {}", s))
1018}
1019
1020const NANOS_PER_SECOND_CMP: i64 = 1_000_000_000;
1021
1022fn time_with_tz_to_utc_nanos(s: &str) -> Result<i64> {
1024 use chrono::Timelike;
1025 let (_, time, tz_info) = crate::query::datetime::parse_datetime_with_tz(s)?;
1026 let local_nanos = time.hour() as i64 * 3_600 * NANOS_PER_SECOND_CMP
1027 + time.minute() as i64 * 60 * NANOS_PER_SECOND_CMP
1028 + time.second() as i64 * NANOS_PER_SECOND_CMP
1029 + time.nanosecond() as i64;
1030
1031 let offset_secs: i64 = match tz_info {
1033 Some(ref tz) => {
1034 let today = chrono::NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
1035 let ndt = chrono::NaiveDateTime::new(today, time);
1036 tz.offset_for_local(&ndt)?.local_minus_utc() as i64
1037 }
1038 None => 0,
1039 };
1040
1041 Ok(local_nanos - offset_secs * NANOS_PER_SECOND_CMP)
1042}
1043
1044fn eval_size(arg: &Value) -> Result<Value> {
1049 match arg {
1050 Value::List(arr) => Ok(Value::Int(arr.len() as i64)),
1051 Value::Map(map) => Ok(Value::Int(map.len() as i64)),
1052 Value::String(s) => Ok(Value::Int(s.len() as i64)),
1053 Value::Null => Ok(Value::Null),
1054 _ => Err(anyhow!("size() expects a List, Map, or String")),
1055 }
1056}
1057
1058fn eval_keys(arg: &Value) -> Result<Value> {
1059 match arg {
1060 Value::Map(map) => {
1061 let is_entity =
1065 map.contains_key("_vid") || map.contains_key("_eid") || map.contains_key("_labels");
1066 let mut keys: Vec<&String> = map
1067 .iter()
1068 .filter(|(k, v)| !k.starts_with('_') && (!is_entity || !v.is_null()))
1069 .map(|(k, _)| k)
1070 .collect();
1071 keys.sort();
1072 Ok(Value::List(
1073 keys.into_iter().map(|k| Value::String(k.clone())).collect(),
1074 ))
1075 }
1076 Value::Null => Ok(Value::Null),
1077 _ => Err(anyhow!("keys() expects a Map")),
1078 }
1079}
1080
1081fn eval_head(arg: &Value) -> Result<Value> {
1082 match arg {
1083 Value::List(arr) => Ok(arr.first().cloned().unwrap_or(Value::Null)),
1084 Value::Null => Ok(Value::Null),
1085 _ => Err(anyhow!("head() expects a List")),
1086 }
1087}
1088
1089fn eval_tail(arg: &Value) -> Result<Value> {
1090 match arg {
1091 Value::List(arr) => Ok(Value::List(arr.get(1..).unwrap_or_default().to_vec())),
1092 Value::Null => Ok(Value::Null),
1093 _ => Err(anyhow!("tail() expects a List")),
1094 }
1095}
1096
1097fn eval_last(arg: &Value) -> Result<Value> {
1098 match arg {
1099 Value::List(arr) => Ok(arr.last().cloned().unwrap_or(Value::Null)),
1100 Value::Null => Ok(Value::Null),
1101 _ => Err(anyhow!("last() expects a List")),
1102 }
1103}
1104
1105fn eval_length(arg: &Value) -> Result<Value> {
1106 match arg {
1107 Value::List(arr) => Ok(Value::Int(arr.len() as i64)),
1108 Value::String(s) => Ok(Value::Int(s.len() as i64)),
1109 Value::Path(p) => Ok(Value::Int(p.edges.len() as i64)),
1110 Value::Map(map) => {
1111 if map.contains_key("nodes")
1113 && map.contains_key("relationships")
1114 && let Some(Value::List(rels)) = map.get("relationships")
1115 {
1116 return Ok(Value::Int(rels.len() as i64));
1117 }
1118 Ok(Value::Null)
1119 }
1120 Value::Null => Ok(Value::Null),
1121 _ => Err(anyhow!("length() expects a List, String, or Path")),
1122 }
1123}
1124
1125fn eval_nodes(arg: &Value) -> Result<Value> {
1126 match arg {
1127 Value::Path(p) => Ok(Value::List(
1128 p.nodes.iter().map(|n| Value::Node(n.clone())).collect(),
1129 )),
1130 Value::Map(map) => {
1131 if let Some(nodes) = map.get("nodes") {
1132 Ok(nodes.clone())
1133 } else {
1134 Ok(Value::Null)
1135 }
1136 }
1137 Value::Null => Ok(Value::Null),
1138 _ => Err(anyhow!("nodes() expects a Path")),
1139 }
1140}
1141
1142fn eval_relationships(arg: &Value) -> Result<Value> {
1143 match arg {
1144 Value::Path(p) => Ok(Value::List(
1145 p.edges.iter().map(|e| Value::Edge(e.clone())).collect(),
1146 )),
1147 Value::Map(map) => {
1148 if let Some(rels) = map.get("relationships") {
1149 Ok(rels.clone())
1150 } else {
1151 Ok(Value::Null)
1152 }
1153 }
1154 Value::Null => Ok(Value::Null),
1155 _ => Err(anyhow!("relationships() expects a Path")),
1156 }
1157}
1158
1159fn eval_list_function(name: &str, args: &[Value]) -> Result<Value> {
1161 if args.len() != 1 {
1162 return Err(anyhow!("{}() requires 1 argument", name));
1163 }
1164 match name {
1165 "SIZE" => eval_size(&args[0]),
1166 "KEYS" => eval_keys(&args[0]),
1167 "HEAD" => eval_head(&args[0]),
1168 "TAIL" => eval_tail(&args[0]),
1169 "LAST" => eval_last(&args[0]),
1170 "LENGTH" => eval_length(&args[0]),
1171 "NODES" => eval_nodes(&args[0]),
1172 "RELATIONSHIPS" => eval_relationships(&args[0]),
1173 _ => Err(anyhow!("Unknown list function: {}", name)),
1174 }
1175}
1176
1177fn eval_tointeger(arg: &Value) -> Result<Value> {
1182 match arg {
1183 Value::Int(i) => Ok(Value::Int(*i)),
1184 Value::Float(f) => Ok(Value::Int(*f as i64)),
1185 Value::String(s) => Ok(s.parse::<i64>().map(Value::Int).unwrap_or(Value::Null)),
1186 Value::Null => Ok(Value::Null),
1187 _ => Err(anyhow!(
1188 "InvalidArgumentValue: toInteger() cannot convert type"
1189 )),
1190 }
1191}
1192
1193fn eval_tofloat(arg: &Value) -> Result<Value> {
1194 match arg {
1195 Value::Int(i) => Ok(Value::Float(*i as f64)),
1196 Value::Float(f) => Ok(Value::Float(*f)),
1197 Value::String(s) => Ok(s.parse::<f64>().map(Value::Float).unwrap_or(Value::Null)),
1198 Value::Null => Ok(Value::Null),
1199 _ => Err(anyhow!(
1200 "InvalidArgumentValue: toFloat() cannot convert type"
1201 )),
1202 }
1203}
1204
1205fn eval_tostring(arg: &Value) -> Result<Value> {
1206 match arg {
1207 Value::String(s) => Ok(Value::String(s.clone())),
1208 Value::Int(i) => Ok(Value::String(i.to_string())),
1209 Value::Float(f) => {
1210 if f.fract() == 0.0 && f.is_finite() {
1212 Ok(Value::String(format!("{f:.1}")))
1213 } else {
1214 Ok(Value::String(f.to_string()))
1215 }
1216 }
1217 Value::Bool(b) => Ok(Value::String(b.to_string())),
1218 Value::Null => Ok(Value::Null),
1219 other => Ok(Value::String(other.to_string())),
1220 }
1221}
1222
1223fn eval_toboolean(arg: &Value) -> Result<Value> {
1224 match arg {
1225 Value::Bool(b) => Ok(Value::Bool(*b)),
1226 Value::String(s) => {
1227 let lower = s.to_lowercase();
1228 if lower == "true" {
1229 Ok(Value::Bool(true))
1230 } else if lower == "false" {
1231 Ok(Value::Bool(false))
1232 } else {
1233 Ok(Value::Null)
1234 }
1235 }
1236 Value::Null => Ok(Value::Null),
1237 _ => Err(anyhow!(
1238 "InvalidArgumentValue: toBoolean() cannot convert type"
1239 )),
1240 }
1241}
1242
1243fn eval_type_function(name: &str, args: &[Value]) -> Result<Value> {
1245 if args.len() != 1 {
1246 return Err(anyhow!("{}() requires 1 argument", name));
1247 }
1248 match name {
1249 "TOINTEGER" | "TOINT" => eval_tointeger(&args[0]),
1250 "TOFLOAT" => eval_tofloat(&args[0]),
1251 "TOSTRING" => eval_tostring(&args[0]),
1252 "TOBOOLEAN" | "TOBOOL" => eval_toboolean(&args[0]),
1253 _ => Err(anyhow!("Unknown type function: {}", name)),
1254 }
1255}
1256
1257fn eval_abs(arg: &Value) -> Result<Value> {
1262 match arg {
1263 Value::Int(i) => Ok(Value::Int(i.abs())),
1264 Value::Float(f) => Ok(Value::Float(f.abs())),
1265 Value::Null => Ok(Value::Null),
1266 _ => Err(anyhow!("abs() expects a number")),
1267 }
1268}
1269
1270fn eval_sqrt(arg: &Value) -> Result<Value> {
1271 match arg {
1272 v if v.is_number() => {
1273 let f = v.as_f64().unwrap();
1274 if f < 0.0 {
1275 Ok(Value::Null)
1276 } else {
1277 Ok(Value::Float(f.sqrt()))
1278 }
1279 }
1280 Value::Null => Ok(Value::Null),
1281 _ => Err(anyhow!("sqrt() expects a number")),
1282 }
1283}
1284
1285fn eval_sign(arg: &Value) -> Result<Value> {
1286 match arg {
1287 Value::Int(i) => Ok(Value::Int(i.signum())),
1288 Value::Float(f) => Ok(Value::Int(f.signum() as i64)),
1289 Value::Null => Ok(Value::Null),
1290 _ => Err(anyhow!("sign() expects a number")),
1291 }
1292}
1293
1294fn eval_power(args: &[Value]) -> Result<Value> {
1295 if args.len() != 2 {
1296 return Err(anyhow!("power() requires 2 arguments"));
1297 }
1298 match (&args[0], &args[1]) {
1299 (a, b) if a.is_number() && b.is_number() => {
1300 let base = a.as_f64().unwrap();
1301 let exp = b.as_f64().unwrap();
1302 Ok(Value::Float(base.powf(exp)))
1303 }
1304 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
1305 _ => Err(anyhow!("power() expects numeric arguments")),
1306 }
1307}
1308
1309fn eval_unary_numeric_op<F>(arg: &Value, func_name: &str, op: F) -> Result<Value>
1311where
1312 F: Fn(f64) -> f64,
1313{
1314 match arg {
1315 Value::Int(i) => Ok(Value::Float(op(*i as f64))),
1316 Value::Float(f) => Ok(Value::Float(op(*f))),
1317 Value::Null => Ok(Value::Null),
1318 _ => Err(anyhow!("{}() expects a number", func_name)),
1319 }
1320}
1321
1322fn eval_atan2(args: &[Value]) -> Result<Value> {
1323 if args.len() != 2 {
1324 return Err(anyhow!("atan2() requires 2 arguments"));
1325 }
1326 match (&args[0], &args[1]) {
1327 (a, b) if a.is_number() && b.is_number() => {
1328 let y_val = a.as_f64().unwrap();
1329 let x_val = b.as_f64().unwrap();
1330 Ok(Value::Float(y_val.atan2(x_val)))
1331 }
1332 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
1333 _ => Err(anyhow!("atan2() expects numeric arguments")),
1334 }
1335}
1336
1337fn require_one_arg<'a>(name: &str, args: &'a [Value]) -> Result<&'a Value> {
1339 if args.len() != 1 {
1340 return Err(anyhow!("{} requires 1 argument", name));
1341 }
1342 Ok(&args[0])
1343}
1344
1345fn eval_math_function(name: &str, args: &[Value]) -> Result<Value> {
1350 match name {
1351 "ABS" => eval_abs(require_one_arg(name, args)?),
1353 "CEIL" => eval_unary_numeric_op(require_one_arg(name, args)?, "ceil", f64::ceil),
1354 "FLOOR" => eval_unary_numeric_op(require_one_arg(name, args)?, "floor", f64::floor),
1355 "ROUND" => eval_unary_numeric_op(require_one_arg(name, args)?, "round", f64::round),
1356 "SQRT" => eval_sqrt(require_one_arg(name, args)?),
1357 "SIGN" => eval_sign(require_one_arg(name, args)?),
1358 "LOG" => eval_unary_numeric_op(require_one_arg(name, args)?, "log", f64::ln),
1359 "LOG10" => eval_unary_numeric_op(require_one_arg(name, args)?, "log10", f64::log10),
1360 "EXP" => eval_unary_numeric_op(require_one_arg(name, args)?, "exp", f64::exp),
1361 "SIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "sin", f64::sin),
1362 "COS" => eval_unary_numeric_op(require_one_arg(name, args)?, "cos", f64::cos),
1363 "TAN" => eval_unary_numeric_op(require_one_arg(name, args)?, "tan", f64::tan),
1364 "ASIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "asin", f64::asin),
1365 "ACOS" => eval_unary_numeric_op(require_one_arg(name, args)?, "acos", f64::acos),
1366 "ATAN" => eval_unary_numeric_op(require_one_arg(name, args)?, "atan", f64::atan),
1367 "DEGREES" => {
1368 eval_unary_numeric_op(require_one_arg(name, args)?, "degrees", f64::to_degrees)
1369 }
1370 "RADIANS" => {
1371 eval_unary_numeric_op(require_one_arg(name, args)?, "radians", f64::to_radians)
1372 }
1373 "HAVERSIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "haversin", |f| {
1374 (1.0 - f.cos()) / 2.0
1375 }),
1376 "POWER" | "POW" => eval_power(args),
1378 "ATAN2" => eval_atan2(args),
1379 "PI" => {
1381 if !args.is_empty() {
1382 return Err(anyhow!("PI takes no arguments"));
1383 }
1384 Ok(Value::Float(std::f64::consts::PI))
1385 }
1386 "E" => {
1387 if !args.is_empty() {
1388 return Err(anyhow!("E takes no arguments"));
1389 }
1390 Ok(Value::Float(std::f64::consts::E))
1391 }
1392 "RAND" => {
1393 if !args.is_empty() {
1394 return Err(anyhow!("RAND takes no arguments"));
1395 }
1396 use rand::Rng;
1397 let mut rng = rand::thread_rng();
1398 Ok(Value::Float(rng.gen_range(0.0..1.0)))
1399 }
1400 _ => Err(anyhow!("Unknown math function: {}", name)),
1401 }
1402}
1403
1404fn eval_unary_string_op<F>(arg: &Value, func_name: &str, op: F) -> Result<Value>
1410where
1411 F: FnOnce(&str) -> String,
1412{
1413 match arg {
1414 Value::String(s) => Ok(Value::String(op(s))),
1415 Value::Null => Ok(Value::Null),
1416 _ => Err(anyhow!("{}() expects a string", func_name)),
1417 }
1418}
1419
1420fn eval_toupper(args: &[Value]) -> Result<Value> {
1421 let arg = require_one_arg("toUpper", args)?;
1422 eval_unary_string_op(arg, "toUpper", |s| s.to_uppercase())
1423}
1424
1425fn eval_tolower(args: &[Value]) -> Result<Value> {
1426 let arg = require_one_arg("toLower", args)?;
1427 eval_unary_string_op(arg, "toLower", |s| s.to_lowercase())
1428}
1429
1430fn eval_trim(args: &[Value]) -> Result<Value> {
1431 let arg = require_one_arg("trim", args)?;
1432 eval_unary_string_op(arg, "trim", |s| s.trim().to_string())
1433}
1434
1435fn eval_ltrim(args: &[Value]) -> Result<Value> {
1436 let arg = require_one_arg("ltrim", args)?;
1437 eval_unary_string_op(arg, "ltrim", |s| s.trim_start().to_string())
1438}
1439
1440fn eval_rtrim(args: &[Value]) -> Result<Value> {
1441 let arg = require_one_arg("rtrim", args)?;
1442 eval_unary_string_op(arg, "rtrim", |s| s.trim_end().to_string())
1443}
1444
1445fn eval_reverse(args: &[Value]) -> Result<Value> {
1446 let arg = require_one_arg("reverse", args)?;
1447 match arg {
1448 Value::String(s) => Ok(Value::String(s.chars().rev().collect())),
1449 Value::List(arr) => Ok(Value::List(arr.iter().rev().cloned().collect())),
1450 Value::Null => Ok(Value::Null),
1451 _ => Err(anyhow!("reverse() expects a string or list")),
1452 }
1453}
1454
1455fn eval_replace(args: &[Value]) -> Result<Value> {
1456 if args.len() != 3 {
1457 return Err(anyhow!("replace() requires 3 arguments"));
1458 }
1459 match (&args[0], &args[1], &args[2]) {
1460 (Value::String(s), Value::String(search), Value::String(replacement)) => Ok(Value::String(
1461 s.replace(search.as_str(), replacement.as_str()),
1462 )),
1463 (Value::Null, _, _) => Ok(Value::Null),
1464 _ => Err(anyhow!("replace() expects string arguments")),
1465 }
1466}
1467
1468pub(crate) fn eval_split(args: &[Value]) -> Result<Value> {
1469 if args.len() != 2 {
1470 return Err(anyhow!("split() requires 2 arguments"));
1471 }
1472 match (&args[0], &args[1]) {
1473 (Value::String(s), Value::String(delimiter)) => {
1474 let parts: Vec<Value> = s
1475 .split(delimiter.as_str())
1476 .map(|p| Value::String(p.to_string()))
1477 .collect();
1478 Ok(Value::List(parts))
1479 }
1480 (Value::Null, _) => Ok(Value::Null),
1481 _ => Err(anyhow!("split() expects string arguments")),
1482 }
1483}
1484
1485fn eval_substring(args: &[Value]) -> Result<Value> {
1486 if args.len() < 2 || args.len() > 3 {
1487 return Err(anyhow!("substring() requires 2 or 3 arguments"));
1488 }
1489 match &args[0] {
1490 Value::String(s) => {
1491 let start = args[1]
1492 .as_i64()
1493 .ok_or_else(|| anyhow!("substring() start must be an integer"))?
1494 as usize;
1495 let len = if args.len() == 3 {
1496 args[2]
1497 .as_i64()
1498 .ok_or_else(|| anyhow!("substring() length must be an integer"))?
1499 as usize
1500 } else {
1501 s.len().saturating_sub(start)
1502 };
1503 let chars: Vec<char> = s.chars().collect();
1504 let end = (start + len).min(chars.len());
1505 let result: String = chars[start.min(chars.len())..end].iter().collect();
1506 Ok(Value::String(result))
1507 }
1508 Value::Null => Ok(Value::Null),
1509 _ => Err(anyhow!("substring() expects a string")),
1510 }
1511}
1512
1513fn eval_left(args: &[Value]) -> Result<Value> {
1514 if args.len() != 2 {
1515 return Err(anyhow!("left() requires 2 arguments"));
1516 }
1517 match (&args[0], &args[1]) {
1518 (Value::String(s), n) if n.is_number() => {
1519 let len = n.as_i64().unwrap_or(0) as usize;
1520 Ok(Value::String(s.chars().take(len).collect()))
1521 }
1522 (Value::Null, _) => Ok(Value::Null),
1523 _ => Err(anyhow!("left() expects a string and integer")),
1524 }
1525}
1526
1527fn eval_right(args: &[Value]) -> Result<Value> {
1528 if args.len() != 2 {
1529 return Err(anyhow!("right() requires 2 arguments"));
1530 }
1531 match (&args[0], &args[1]) {
1532 (Value::String(s), n) if n.is_number() => {
1533 let len = n.as_i64().unwrap_or(0) as usize;
1534 let chars: Vec<char> = s.chars().collect();
1535 let start = chars.len().saturating_sub(len);
1536 Ok(Value::String(chars[start..].iter().collect()))
1537 }
1538 (Value::Null, _) => Ok(Value::Null),
1539 _ => Err(anyhow!("right() expects a string and integer")),
1540 }
1541}
1542
1543fn eval_pad(func_name: &str, args: &[Value], pad_left: bool) -> Result<Value> {
1545 if args.len() < 2 || args.len() > 3 {
1546 return Err(anyhow!("{}() requires 2 or 3 arguments", func_name));
1547 }
1548 let s = match &args[0] {
1549 Value::String(s) => s,
1550 Value::Null => return Ok(Value::Null),
1551 _ => {
1552 return Err(anyhow!(
1553 "{}() expects a string as first argument",
1554 func_name
1555 ));
1556 }
1557 };
1558 let len = match &args[1] {
1559 Value::Int(n) => *n as usize,
1560 Value::Float(f) => *f as i64 as usize,
1561 Value::Null => return Ok(Value::Null),
1562 _ => {
1563 return Err(anyhow!(
1564 "{}() expects an integer as second argument",
1565 func_name
1566 ));
1567 }
1568 };
1569 if len > 1_000_000 {
1570 return Err(anyhow!(
1571 "{}() length exceeds maximum limit of 1,000,000",
1572 func_name
1573 ));
1574 }
1575 let pad_str = if args.len() == 3 {
1576 match &args[2] {
1577 Value::String(p) => p.as_str(),
1578 Value::Null => return Ok(Value::Null),
1579 _ => {
1580 return Err(anyhow!(
1581 "{}() expects a string as third argument",
1582 func_name
1583 ));
1584 }
1585 }
1586 } else {
1587 " "
1588 };
1589
1590 let s_chars: Vec<char> = s.chars().collect();
1591 if s_chars.len() >= len {
1592 return Ok(Value::String(s_chars.into_iter().take(len).collect()));
1593 }
1594
1595 let pad_chars: Vec<char> = pad_str.chars().collect();
1596 if pad_chars.is_empty() {
1597 return Ok(Value::String(s.clone()));
1598 }
1599
1600 let needed = len - s_chars.len();
1601 let full_pads = needed / pad_chars.len();
1602 let partial_pad = needed % pad_chars.len();
1603
1604 let mut padding = String::with_capacity(needed);
1605 for _ in 0..full_pads {
1606 padding.push_str(pad_str);
1607 }
1608 padding.extend(pad_chars.into_iter().take(partial_pad));
1609
1610 let result = if pad_left {
1611 format!("{}{}", padding, s)
1612 } else {
1613 format!("{}{}", s, padding)
1614 };
1615 Ok(Value::String(result))
1616}
1617
1618fn eval_lpad(args: &[Value]) -> Result<Value> {
1619 eval_pad("lpad", args, true)
1620}
1621
1622fn eval_rpad(args: &[Value]) -> Result<Value> {
1623 eval_pad("rpad", args, false)
1624}
1625
1626fn eval_string_function(name: &str, args: &[Value]) -> Result<Value> {
1628 match name {
1629 "TOUPPER" | "UPPER" => eval_toupper(args),
1630 "TOLOWER" | "LOWER" => eval_tolower(args),
1631 "TRIM" => eval_trim(args),
1632 "LTRIM" => eval_ltrim(args),
1633 "RTRIM" => eval_rtrim(args),
1634 "REVERSE" => eval_reverse(args),
1635 "REPLACE" => eval_replace(args),
1636 "SPLIT" => eval_split(args),
1637 "SUBSTRING" => eval_substring(args),
1638 "LEFT" => eval_left(args),
1639 "RIGHT" => eval_right(args),
1640 "LPAD" => eval_lpad(args),
1641 "RPAD" => eval_rpad(args),
1642 _ => Err(anyhow!("Unknown string function: {}", name)),
1643 }
1644}
1645
1646fn eval_range_function(args: &[Value]) -> Result<Value> {
1648 if args.len() < 2 || args.len() > 3 {
1649 return Err(anyhow!("range() requires 2 or 3 arguments"));
1650 }
1651 let start = args[0]
1652 .as_i64()
1653 .ok_or_else(|| anyhow!("range() start must be an integer"))?;
1654 let end = args[1]
1655 .as_i64()
1656 .ok_or_else(|| anyhow!("range() end must be an integer"))?;
1657 let step = if args.len() == 3 {
1658 args[2]
1659 .as_i64()
1660 .ok_or_else(|| anyhow!("range() step must be an integer"))?
1661 } else {
1662 1
1663 };
1664 if step == 0 {
1665 return Err(anyhow!("range() step cannot be zero"));
1666 }
1667 let mut result = Vec::new();
1668 let mut i = start;
1669 if step > 0 {
1670 while i <= end {
1671 result.push(Value::Int(i));
1672 i += step;
1673 }
1674 } else {
1675 while i >= end {
1676 result.push(Value::Int(i));
1677 i += step;
1678 }
1679 }
1680 Ok(Value::List(result))
1681}
1682
1683pub fn eval_scalar_function(
1688 name: &str,
1689 args: &[Value],
1690 custom_fns: Option<&super::executor::custom_functions::CustomFunctionRegistry>,
1691) -> Result<Value> {
1692 let name_upper = name.to_uppercase();
1693
1694 match name_upper.as_str() {
1695 "COALESCE" => {
1697 for arg in args {
1698 if !arg.is_null() {
1699 return Ok(arg.clone());
1700 }
1701 }
1702 Ok(Value::Null)
1703 }
1704 "NULLIF" => {
1705 if args.len() != 2 {
1706 return Err(anyhow!("NULLIF requires 2 arguments"));
1707 }
1708 if args[0] == args[1] {
1709 Ok(Value::Null)
1710 } else {
1711 Ok(args[0].clone())
1712 }
1713 }
1714
1715 "SIZE" | "KEYS" | "HEAD" | "TAIL" | "LAST" | "LENGTH" | "NODES" | "RELATIONSHIPS" => {
1717 eval_list_function(&name_upper, args)
1718 }
1719
1720 "TOINTEGER" | "TOINT" | "TOFLOAT" | "TOSTRING" | "TOBOOLEAN" | "TOBOOL" => {
1722 eval_type_function(&name_upper, args)
1723 }
1724
1725 "ABS" | "CEIL" | "FLOOR" | "ROUND" | "SQRT" | "SIGN" | "LOG" | "LOG10" | "EXP"
1727 | "POWER" | "POW" | "SIN" | "COS" | "TAN" | "ASIN" | "ACOS" | "ATAN" | "ATAN2"
1728 | "DEGREES" | "RADIANS" | "HAVERSIN" | "PI" | "E" | "RAND" => {
1729 eval_math_function(&name_upper, args)
1730 }
1731
1732 "TOUPPER" | "UPPER" | "TOLOWER" | "LOWER" | "TRIM" | "LTRIM" | "RTRIM" | "REVERSE"
1734 | "REPLACE" | "SPLIT" | "SUBSTRING" | "LEFT" | "RIGHT" | "LPAD" | "RPAD" => {
1735 eval_string_function(&name_upper, args)
1736 }
1737
1738 "DATE"
1740 | "TIME"
1741 | "DATETIME"
1742 | "LOCALDATETIME"
1743 | "LOCALTIME"
1744 | "DURATION"
1745 | "YEAR"
1746 | "MONTH"
1747 | "DAY"
1748 | "HOUR"
1749 | "MINUTE"
1750 | "SECOND"
1751 | "DATETIME.FROMEPOCH"
1752 | "DATETIME.FROMEPOCHMILLIS"
1753 | "DATE.TRUNCATE"
1754 | "TIME.TRUNCATE"
1755 | "DATETIME.TRUNCATE"
1756 | "LOCALDATETIME.TRUNCATE"
1757 | "LOCALTIME.TRUNCATE"
1758 | "DATETIME.TRANSACTION"
1759 | "DATETIME.STATEMENT"
1760 | "DATETIME.REALTIME"
1761 | "DATE.TRANSACTION"
1762 | "DATE.STATEMENT"
1763 | "DATE.REALTIME"
1764 | "TIME.TRANSACTION"
1765 | "TIME.STATEMENT"
1766 | "TIME.REALTIME"
1767 | "LOCALTIME.TRANSACTION"
1768 | "LOCALTIME.STATEMENT"
1769 | "LOCALTIME.REALTIME"
1770 | "LOCALDATETIME.TRANSACTION"
1771 | "LOCALDATETIME.STATEMENT"
1772 | "LOCALDATETIME.REALTIME"
1773 | "DURATION.BETWEEN"
1774 | "DURATION.INMONTHS"
1775 | "DURATION.INDAYS"
1776 | "DURATION.INSECONDS" => eval_datetime_function(&name_upper, args),
1777
1778 "POINT" | "DISTANCE" | "POINT.WITHINBBOX" => eval_spatial_function(&name_upper, args),
1780
1781 "RANGE" => eval_range_function(args),
1782
1783 "UNI.TEMPORAL.VALIDAT" => eval_valid_at(args),
1784
1785 "VECTOR_DISTANCE" => {
1786 if args.len() < 2 || args.len() > 3 {
1787 return Err(anyhow!("vector_distance requires 2 or 3 arguments"));
1788 }
1789 let metric = if args.len() == 3 {
1790 args[2].as_str().ok_or(anyhow!("metric must be string"))?
1791 } else {
1792 "cosine"
1793 };
1794 eval_vector_distance(&args[0], &args[1], metric)
1795 }
1796
1797 "UNI_BITWISE_OR"
1799 | "UNI_BITWISE_AND"
1800 | "UNI_BITWISE_XOR"
1801 | "UNI_BITWISE_NOT"
1802 | "UNI_BITWISE_SHIFTLEFT"
1803 | "UNI_BITWISE_SHIFTRIGHT" => eval_bitwise_function(&name_upper, args),
1804
1805 "SIMILAR_TO" => {
1808 if args.len() < 2 {
1809 return Err(anyhow!("similar_to requires at least 2 arguments"));
1810 }
1811 crate::query::similar_to::eval_similar_to_pure(&args[0], &args[1])
1812 }
1813
1814 "VECTOR_SIMILARITY" => {
1815 if args.len() != 2 {
1816 return Err(anyhow!("vector_similarity takes 2 arguments"));
1817 }
1818 eval_vector_similarity(&args[0], &args[1])
1819 }
1820
1821 _ if is_btic_function(&name_upper) => eval_btic_function(&name_upper, args),
1823
1824 _ => {
1825 if let Some(func) = custom_fns.and_then(|r| r.get(name)) {
1827 return func(args).map_err(|e| anyhow!("{}", e));
1828 }
1829 Err(anyhow!("Function {} not implemented or is aggregate", name))
1830 }
1831 }
1832}
1833
1834fn eval_valid_at(args: &[Value]) -> Result<Value> {
1842 if args.len() != 4 {
1843 return Err(anyhow!(
1844 "validAt requires 4 arguments: node, start_prop, end_prop, time"
1845 ));
1846 }
1847
1848 let node_map = match &args[0] {
1849 Value::Map(map) => map,
1850 Value::Null => return Ok(Value::Bool(false)),
1851 _ => {
1852 return Err(anyhow!(
1853 "validAt expects a Node or Edge (Object) as first argument"
1854 ));
1855 }
1856 };
1857
1858 let start_prop = args[1]
1859 .as_str()
1860 .ok_or_else(|| anyhow!("start_prop must be a string"))?;
1861 let end_prop = args[2]
1862 .as_str()
1863 .ok_or_else(|| anyhow!("end_prop must be a string"))?;
1864
1865 let time_str = match &args[3] {
1866 Value::String(s) => s,
1867 _ => return Err(anyhow!("time argument must be a datetime string")),
1868 };
1869
1870 let query_time = parse_datetime_utc(time_str)
1871 .map_err(|_| anyhow!("Invalid query time format: {}", time_str))?;
1872
1873 let valid_from_val = node_map.get(start_prop);
1874 let valid_from = match valid_from_val {
1875 Some(Value::String(s)) => parse_datetime_utc(s)
1876 .map_err(|_| anyhow!("Invalid datetime in property {}: {}", start_prop, s))?,
1877 Some(Value::Null) | None => return Ok(Value::Bool(false)),
1878 _ => return Err(anyhow!("Property {} must be a datetime string", start_prop)),
1879 };
1880
1881 let valid_to_val = node_map.get(end_prop);
1882 let valid_to = match valid_to_val {
1883 Some(Value::String(s)) => Some(
1884 parse_datetime_utc(s)
1885 .map_err(|_| anyhow!("Invalid datetime in property {}: {}", end_prop, s))?,
1886 ),
1887 Some(Value::Null) | None => None,
1888 _ => {
1889 return Err(anyhow!(
1890 "Property {} must be a datetime string or null",
1891 end_prop
1892 ));
1893 }
1894 };
1895
1896 let is_valid = valid_from <= query_time && valid_to.map(|vt| query_time < vt).unwrap_or(true);
1898
1899 Ok(Value::Bool(is_valid))
1900}
1901
1902pub fn eval_vector_similarity(v1: &Value, v2: &Value) -> Result<Value> {
1904 let (arr1, arr2) = match (v1, v2) {
1905 (Value::List(a1), Value::List(a2)) => (a1, a2),
1906 _ => return Err(anyhow!("vector_similarity arguments must be arrays")),
1907 };
1908
1909 if arr1.len() != arr2.len() {
1910 return Err(anyhow!(
1911 "Vector dimensions mismatch: {} vs {}",
1912 arr1.len(),
1913 arr2.len()
1914 ));
1915 }
1916
1917 let mut dot = 0.0;
1918 let mut norm1_sq = 0.0;
1919 let mut norm2_sq = 0.0;
1920
1921 for (v1_elem, v2_elem) in arr1.iter().zip(arr2.iter()) {
1922 let f1 = v1_elem
1923 .as_f64()
1924 .ok_or_else(|| anyhow!("Vector element not a number"))?;
1925 let f2 = v2_elem
1926 .as_f64()
1927 .ok_or_else(|| anyhow!("Vector element not a number"))?;
1928 dot += f1 * f2;
1929 norm1_sq += f1 * f1;
1930 norm2_sq += f2 * f2;
1931 }
1932
1933 let mag1 = norm1_sq.sqrt();
1934 let mag2 = norm2_sq.sqrt();
1935
1936 let sim = if mag1 == 0.0 || mag2 == 0.0 {
1937 0.0
1938 } else {
1939 dot / (mag1 * mag2)
1940 };
1941
1942 Ok(Value::Float(sim))
1943}
1944
1945pub fn eval_vector_distance(v1: &Value, v2: &Value, metric: &str) -> Result<Value> {
1947 let (arr1, arr2) = match (v1, v2) {
1948 (Value::List(a1), Value::List(a2)) => (a1, a2),
1949 _ => return Err(anyhow!("vector_distance arguments must be arrays")),
1950 };
1951
1952 if arr1.len() != arr2.len() {
1953 return Err(anyhow!(
1954 "Vector dimensions mismatch: {} vs {}",
1955 arr1.len(),
1956 arr2.len()
1957 ));
1958 }
1959
1960 let iter1 = arr1
1962 .iter()
1963 .map(|v| v.as_f64().ok_or(anyhow!("Vector element not a number")));
1964 let iter2 = arr2
1965 .iter()
1966 .map(|v| v.as_f64().ok_or(anyhow!("Vector element not a number")));
1967
1968 match metric.to_lowercase().as_str() {
1969 "cosine" => {
1970 let mut dot = 0.0;
1972 let mut norm1_sq = 0.0;
1973 let mut norm2_sq = 0.0;
1974
1975 for (r1, r2) in iter1.zip(iter2) {
1976 let f1 = r1?;
1977 let f2 = r2?;
1978 dot += f1 * f2;
1979 norm1_sq += f1 * f1;
1980 norm2_sq += f2 * f2;
1981 }
1982
1983 let mag1 = norm1_sq.sqrt();
1984 let mag2 = norm2_sq.sqrt();
1985
1986 if mag1 == 0.0 || mag2 == 0.0 {
1987 Ok(Value::Float(1.0))
1988 } else {
1989 let sim = dot / (mag1 * mag2);
1990 let sim = sim.clamp(-1.0, 1.0);
1992 Ok(Value::Float(1.0 - sim))
1993 }
1994 }
1995 "euclidean" | "l2" => {
1996 let mut sum_sq_diff = 0.0;
1997 for (r1, r2) in iter1.zip(iter2) {
1998 let f1 = r1?;
1999 let f2 = r2?;
2000 let diff = f1 - f2;
2001 sum_sq_diff += diff * diff;
2002 }
2003 Ok(Value::Float(sum_sq_diff.sqrt()))
2004 }
2005 "dot" | "inner_product" => {
2006 let mut dot = 0.0;
2007 for (r1, r2) in iter1.zip(iter2) {
2008 let f1 = r1?;
2009 let f2 = r2?;
2010 dot += f1 * f2;
2011 }
2012 Ok(Value::Float(1.0 - dot))
2013 }
2014 _ => Err(anyhow!("Unknown metric: {}", metric)),
2015 }
2016}
2017
2018pub fn is_scalar_function(name: &str) -> bool {
2020 let name_upper = name.to_uppercase();
2021 matches!(
2022 name_upper.as_str(),
2023 "COALESCE"
2024 | "NULLIF"
2025 | "SIZE"
2026 | "KEYS"
2027 | "HEAD"
2028 | "TAIL"
2029 | "LAST"
2030 | "LENGTH"
2031 | "NODES"
2032 | "RELATIONSHIPS"
2033 | "TOINTEGER"
2034 | "TOINT"
2035 | "TOFLOAT"
2036 | "TOSTRING"
2037 | "TOBOOLEAN"
2038 | "TOBOOL"
2039 | "ABS"
2040 | "CEIL"
2041 | "FLOOR"
2042 | "ROUND"
2043 | "SQRT"
2044 | "SIGN"
2045 | "LOG"
2046 | "LOG10"
2047 | "EXP"
2048 | "POWER"
2049 | "POW"
2050 | "SIN"
2051 | "COS"
2052 | "TAN"
2053 | "ASIN"
2054 | "ACOS"
2055 | "ATAN"
2056 | "ATAN2"
2057 | "DEGREES"
2058 | "RADIANS"
2059 | "HAVERSIN"
2060 | "PI"
2061 | "E"
2062 | "RAND"
2063 | "TOUPPER"
2064 | "UPPER"
2065 | "TOLOWER"
2066 | "LOWER"
2067 | "TRIM"
2068 | "LTRIM"
2069 | "RTRIM"
2070 | "REVERSE"
2071 | "REPLACE"
2072 | "SPLIT"
2073 | "SUBSTRING"
2074 | "LEFT"
2075 | "RIGHT"
2076 | "LPAD"
2077 | "RPAD"
2078 | "RANGE"
2079 | "UNI.VALIDAT"
2080 | "VALIDAT"
2081 | "SIMILAR_TO"
2082 | "VECTOR_SIMILARITY"
2083 | "VECTOR_DISTANCE"
2084 | "DATE"
2085 | "TIME"
2086 | "DATETIME"
2087 | "DURATION"
2088 | "YEAR"
2089 | "MONTH"
2090 | "DAY"
2091 | "HOUR"
2092 | "MINUTE"
2093 | "SECOND"
2094 | "ID"
2095 | "ELEMENTID"
2096 | "TYPE"
2097 | "LABELS"
2098 | "PROPERTIES"
2099 | "STARTNODE"
2100 | "ENDNODE"
2101 | "ANY"
2102 | "ALL"
2103 | "NONE"
2104 | "SINGLE"
2105 ) || is_btic_function(&name_upper)
2106}
2107
2108pub fn is_btic_function(name_upper: &str) -> bool {
2110 matches!(
2111 name_upper,
2112 "BTIC_LO"
2113 | "BTIC_HI"
2114 | "BTIC_DURATION"
2115 | "BTIC_CONTAINS_POINT"
2116 | "BTIC_OVERLAPS"
2117 | "BTIC_IS_INSTANT"
2118 | "BTIC_IS_UNBOUNDED"
2119 | "BTIC_IS_FINITE"
2120 | "BTIC_GRANULARITY"
2121 | "BTIC_LO_GRANULARITY"
2122 | "BTIC_HI_GRANULARITY"
2123 | "BTIC_CERTAINTY"
2124 | "BTIC_LO_CERTAINTY"
2125 | "BTIC_HI_CERTAINTY"
2126 | "BTIC_CONTAINS"
2127 | "BTIC_BEFORE"
2128 | "BTIC_AFTER"
2129 | "BTIC_MEETS"
2130 | "BTIC_ADJACENT"
2131 | "BTIC_DISJOINT"
2132 | "BTIC_EQUALS"
2133 | "BTIC_STARTS"
2134 | "BTIC_DURING"
2135 | "BTIC_FINISHES"
2136 | "BTIC_INTERSECTION"
2137 | "BTIC_SPAN"
2138 | "BTIC_GAP"
2139 )
2140}
2141
2142pub fn eval_btic_function(name: &str, args: &[Value]) -> Result<Value> {
2144 match name {
2145 "BTIC_LO" => {
2146 let Some((lo, _, _)) = require_btic_1arg(name, args)? else {
2147 return Ok(Value::Null);
2148 };
2149 if lo == i64::MIN {
2150 return Ok(Value::Null);
2151 }
2152 Ok(ms_to_utc_datetime(lo))
2153 }
2154 "BTIC_HI" => {
2155 let Some((_, hi, _)) = require_btic_1arg(name, args)? else {
2156 return Ok(Value::Null);
2157 };
2158 if hi == i64::MAX {
2159 return Ok(Value::Null);
2160 }
2161 Ok(ms_to_utc_datetime(hi))
2162 }
2163 "BTIC_DURATION" => {
2164 let Some((lo, hi, _)) = require_btic_1arg(name, args)? else {
2165 return Ok(Value::Null);
2166 };
2167 if lo == i64::MIN || hi == i64::MAX {
2168 Ok(Value::Null)
2169 } else {
2170 Ok(Value::Int(hi - lo))
2171 }
2172 }
2173 "BTIC_IS_INSTANT" => {
2174 let Some((lo, hi, _)) = require_btic_1arg(name, args)? else {
2175 return Ok(Value::Null);
2176 };
2177 Ok(Value::Bool(hi == lo + 1))
2178 }
2179 "BTIC_IS_UNBOUNDED" => {
2180 let Some((lo, hi, _)) = require_btic_1arg(name, args)? else {
2181 return Ok(Value::Null);
2182 };
2183 Ok(Value::Bool(lo == i64::MIN || hi == i64::MAX))
2184 }
2185 "BTIC_IS_FINITE" => {
2186 let Some((lo, hi, _)) = require_btic_1arg(name, args)? else {
2187 return Ok(Value::Null);
2188 };
2189 Ok(Value::Bool(lo != i64::MIN && hi != i64::MAX))
2190 }
2191 "BTIC_GRANULARITY" | "BTIC_LO_GRANULARITY" => {
2192 let Some(btic) = require_btic_1arg_parsed(name, args)? else {
2193 return Ok(Value::Null);
2194 };
2195 Ok(Value::String(btic.lo_granularity().name().to_string()))
2196 }
2197 "BTIC_HI_GRANULARITY" => {
2198 let Some(btic) = require_btic_1arg_parsed(name, args)? else {
2199 return Ok(Value::Null);
2200 };
2201 Ok(Value::String(btic.hi_granularity().name().to_string()))
2202 }
2203 "BTIC_CERTAINTY" | "BTIC_LO_CERTAINTY" => {
2204 let Some(btic) = require_btic_1arg_parsed(name, args)? else {
2205 return Ok(Value::Null);
2206 };
2207 if name == "BTIC_CERTAINTY" {
2208 let lc = btic.lo_certainty();
2209 let hc = btic.hi_certainty();
2210 Ok(Value::String(lc.least_certain(hc).name().to_string()))
2211 } else {
2212 Ok(Value::String(btic.lo_certainty().name().to_string()))
2213 }
2214 }
2215 "BTIC_HI_CERTAINTY" => {
2216 let Some(btic) = require_btic_1arg_parsed(name, args)? else {
2217 return Ok(Value::Null);
2218 };
2219 Ok(Value::String(btic.hi_certainty().name().to_string()))
2220 }
2221 "BTIC_CONTAINS_POINT" => {
2222 if args.len() != 2 {
2223 return Err(anyhow!("btic_contains_point requires 2 arguments"));
2224 }
2225 if args[0].is_null() || args[1].is_null() {
2226 return Ok(Value::Null);
2227 }
2228 match &args[0] {
2229 Value::Temporal(TemporalValue::Btic { lo, hi, .. }) => {
2230 let point_ms = extract_point_ms(&args[1])?;
2231 Ok(Value::Bool(*lo <= point_ms && point_ms < *hi))
2232 }
2233 _ => Err(anyhow!("btic_contains_point: first arg must be BTIC")),
2234 }
2235 }
2236 "BTIC_OVERLAPS" | "BTIC_CONTAINS" | "BTIC_BEFORE" | "BTIC_AFTER" | "BTIC_MEETS"
2238 | "BTIC_ADJACENT" | "BTIC_DISJOINT" | "BTIC_EQUALS" | "BTIC_STARTS" | "BTIC_DURING"
2239 | "BTIC_FINISHES" => eval_btic_binary_predicate(name, args),
2240 "BTIC_INTERSECTION" => eval_btic_set_op(name, args, uni_btic::set_ops::intersection),
2242 "BTIC_SPAN" => eval_btic_set_op(name, args, |a, b| Some(uni_btic::set_ops::span(a, b))),
2243 "BTIC_GAP" => eval_btic_set_op(name, args, uni_btic::set_ops::gap),
2244 _ => Err(anyhow!("unknown BTIC function: {}", name)),
2245 }
2246}
2247
2248fn require_btic_1arg(name: &str, args: &[Value]) -> Result<Option<(i64, i64, u64)>> {
2251 if args.len() != 1 {
2252 return Err(anyhow!("{} requires 1 argument", name.to_lowercase()));
2253 }
2254 match &args[0] {
2255 Value::Temporal(TemporalValue::Btic { lo, hi, meta }) => Ok(Some((*lo, *hi, *meta))),
2256 Value::Null => Ok(None),
2257 _ => Err(anyhow!("{}: expected BTIC value", name.to_lowercase())),
2258 }
2259}
2260
2261fn require_btic_1arg_parsed(name: &str, args: &[Value]) -> Result<Option<uni_btic::Btic>> {
2264 let Some((lo, hi, meta)) = require_btic_1arg(name, args)? else {
2265 return Ok(None);
2266 };
2267 uni_btic::Btic::new(lo, hi, meta)
2268 .map(Some)
2269 .map_err(|e| anyhow!("invalid BTIC: {}", e))
2270}
2271
2272fn ms_to_utc_datetime(ms: i64) -> Value {
2274 Value::Temporal(TemporalValue::DateTime {
2275 nanos_since_epoch: ms * 1_000_000,
2276 offset_seconds: 0,
2277 timezone_name: Some("UTC".to_string()),
2278 })
2279}
2280
2281fn eval_btic_binary_predicate(name: &str, args: &[Value]) -> Result<Value> {
2283 if args.len() != 2 {
2284 return Err(anyhow!("{} requires 2 arguments", name.to_lowercase()));
2285 }
2286 if args[0].is_null() || args[1].is_null() {
2287 return Ok(Value::Null);
2288 }
2289 match (&args[0], &args[1]) {
2290 (
2291 Value::Temporal(TemporalValue::Btic {
2292 lo: a_lo, hi: a_hi, ..
2293 }),
2294 Value::Temporal(TemporalValue::Btic {
2295 lo: b_lo, hi: b_hi, ..
2296 }),
2297 ) => {
2298 let result = match name {
2299 "BTIC_OVERLAPS" => *a_lo < *b_hi && *b_lo < *a_hi,
2300 "BTIC_CONTAINS" => *a_lo <= *b_lo && *b_hi <= *a_hi,
2301 "BTIC_BEFORE" => *a_hi <= *b_lo,
2302 "BTIC_AFTER" => *b_hi <= *a_lo,
2303 "BTIC_MEETS" => *a_hi == *b_lo,
2304 "BTIC_ADJACENT" => *a_hi == *b_lo || *b_hi == *a_lo,
2305 "BTIC_DISJOINT" => *a_hi <= *b_lo || *b_hi <= *a_lo,
2306 "BTIC_EQUALS" => *a_lo == *b_lo && *a_hi == *b_hi,
2307 "BTIC_STARTS" => *a_lo == *b_lo && *a_hi < *b_hi,
2308 "BTIC_DURING" => *b_lo < *a_lo && *a_hi < *b_hi,
2309 "BTIC_FINISHES" => *a_hi == *b_hi && *b_lo < *a_lo,
2310 _ => unreachable!(),
2311 };
2312 Ok(Value::Bool(result))
2313 }
2314 _ => Err(anyhow!(
2315 "{}: both args must be BTIC values",
2316 name.to_lowercase()
2317 )),
2318 }
2319}
2320
2321fn eval_btic_set_op<F>(name: &str, args: &[Value], op: F) -> Result<Value>
2326where
2327 F: FnOnce(&uni_btic::Btic, &uni_btic::Btic) -> Option<uni_btic::Btic>,
2328{
2329 if args.len() != 2 {
2330 return Err(anyhow!("{} requires 2 arguments", name.to_lowercase()));
2331 }
2332 if args[0].is_null() || args[1].is_null() {
2333 return Ok(Value::Null);
2334 }
2335 match (&args[0], &args[1]) {
2336 (
2337 Value::Temporal(TemporalValue::Btic {
2338 lo: a_lo,
2339 hi: a_hi,
2340 meta: a_meta,
2341 }),
2342 Value::Temporal(TemporalValue::Btic {
2343 lo: b_lo,
2344 hi: b_hi,
2345 meta: b_meta,
2346 }),
2347 ) => {
2348 let a = uni_btic::Btic::new(*a_lo, *a_hi, *a_meta)
2349 .map_err(|e| anyhow!("invalid BTIC: {}", e))?;
2350 let b = uni_btic::Btic::new(*b_lo, *b_hi, *b_meta)
2351 .map_err(|e| anyhow!("invalid BTIC: {}", e))?;
2352 match op(&a, &b) {
2353 Some(r) => Ok(Value::Temporal(TemporalValue::Btic {
2354 lo: r.lo(),
2355 hi: r.hi(),
2356 meta: r.meta(),
2357 })),
2358 None => Ok(Value::Null),
2359 }
2360 }
2361 _ => Err(anyhow!(
2362 "{}: both args must be BTIC values",
2363 name.to_lowercase()
2364 )),
2365 }
2366}
2367
2368fn extract_point_ms(val: &Value) -> Result<i64> {
2370 match val {
2371 Value::Int(ms) => Ok(*ms),
2372 Value::Temporal(TemporalValue::DateTime {
2373 nanos_since_epoch, ..
2374 }) => Ok(*nanos_since_epoch / 1_000_000),
2375 Value::Temporal(TemporalValue::LocalDateTime {
2376 nanos_since_epoch, ..
2377 }) => Ok(*nanos_since_epoch / 1_000_000),
2378 Value::Temporal(TemporalValue::Date { days_since_epoch }) => {
2379 Ok(*days_since_epoch as i64 * 86_400_000)
2380 }
2381 _ => Err(anyhow!(
2382 "expected a temporal point value (datetime, integer ms), got {:?}",
2383 val
2384 )),
2385 }
2386}
2387
2388fn eval_bitwise_function(name: &str, args: &[Value]) -> Result<Value> {
2390 let require_int = |v: &Value, fname: &str| -> Result<i64> {
2391 v.as_i64()
2392 .ok_or_else(|| anyhow!("{} requires integer arguments", fname))
2393 };
2394
2395 let bitwise_binary = |fname: &str, op: fn(i64, i64) -> i64| -> Result<Value> {
2396 if args.len() != 2 {
2397 return Err(anyhow!("{} requires exactly 2 arguments", fname));
2398 }
2399 let l = require_int(&args[0], fname)?;
2400 let r = require_int(&args[1], fname)?;
2401 Ok(Value::Int(op(l, r)))
2402 };
2403
2404 match name {
2405 "UNI_BITWISE_OR" => bitwise_binary("uni_bitwise_or", |l, r| l | r),
2406 "UNI_BITWISE_AND" => bitwise_binary("uni_bitwise_and", |l, r| l & r),
2407 "UNI_BITWISE_XOR" => bitwise_binary("uni_bitwise_xor", |l, r| l ^ r),
2408 "UNI_BITWISE_SHIFTLEFT" => bitwise_binary("uni_bitwise_shiftLeft", |l, r| l << r),
2409 "UNI_BITWISE_SHIFTRIGHT" => bitwise_binary("uni_bitwise_shiftRight", |l, r| l >> r),
2410 "UNI_BITWISE_NOT" => {
2411 if args.len() != 1 {
2412 return Err(anyhow!("uni_bitwise_not requires exactly 1 argument"));
2413 }
2414 Ok(Value::Int(!require_int(&args[0], "uni_bitwise_not")?))
2415 }
2416 _ => Err(anyhow!("Unknown bitwise function: {}", name)),
2417 }
2418}
2419
2420#[cfg(test)]
2421mod tests {
2422 use super::*;
2423 fn s(v: &str) -> Value {
2425 Value::String(v.into())
2426 }
2427 fn i(v: i64) -> Value {
2429 Value::Int(v)
2430 }
2431
2432 #[test]
2433 fn test_binary_op_eq() {
2434 assert_eq!(
2435 eval_binary_op(&i(1), &BinaryOp::Eq, &i(1)).unwrap(),
2436 Value::Bool(true)
2437 );
2438 assert_eq!(
2439 eval_binary_op(&i(1), &BinaryOp::Eq, &i(2)).unwrap(),
2440 Value::Bool(false)
2441 );
2442 }
2443
2444 #[test]
2445 fn test_binary_op_comparison() {
2446 assert_eq!(
2447 eval_binary_op(&i(5), &BinaryOp::Gt, &i(3)).unwrap(),
2448 Value::Bool(true)
2449 );
2450 assert_eq!(
2451 eval_binary_op(&i(5), &BinaryOp::Lt, &i(3)).unwrap(),
2452 Value::Bool(false)
2453 );
2454 }
2455
2456 #[test]
2457 fn test_binary_op_xor() {
2458 assert_eq!(
2460 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2461 Value::Bool(false)
2462 );
2463 assert_eq!(
2465 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2466 Value::Bool(true)
2467 );
2468 assert_eq!(
2470 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2471 Value::Bool(true)
2472 );
2473 assert_eq!(
2475 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2476 Value::Bool(false)
2477 );
2478 }
2479
2480 #[test]
2481 fn test_binary_op_contains() {
2482 assert_eq!(
2483 eval_binary_op(&s("hello world"), &BinaryOp::Contains, &s("world")).unwrap(),
2484 Value::Bool(true)
2485 );
2486 }
2487
2488 #[test]
2489 fn test_scalar_function_size() {
2490 assert_eq!(
2491 eval_scalar_function("SIZE", &[Value::List(vec![i(1), i(2), i(3)])], None).unwrap(),
2492 Value::Int(3)
2493 );
2494 }
2495
2496 #[test]
2497 fn test_scalar_function_head() {
2498 assert_eq!(
2499 eval_scalar_function("HEAD", &[Value::List(vec![i(1), i(2), i(3)])], None).unwrap(),
2500 Value::Int(1)
2501 );
2502 }
2503
2504 #[test]
2505 fn test_scalar_function_coalesce() {
2506 assert_eq!(
2507 eval_scalar_function(
2508 "COALESCE",
2509 &[Value::Null, Value::Int(1), Value::Int(2)],
2510 None
2511 )
2512 .unwrap(),
2513 Value::Int(1)
2514 );
2515 }
2516
2517 #[test]
2518 fn test_vector_similarity() {
2519 let v1 = Value::List(vec![Value::Float(1.0), Value::Float(0.0)]);
2520 let v2 = Value::List(vec![Value::Float(1.0), Value::Float(0.0)]);
2521 let result = eval_vector_similarity(&v1, &v2).unwrap();
2522 assert_eq!(result.as_f64().unwrap(), 1.0);
2523 }
2524
2525 #[test]
2526 fn test_regex_match() {
2527 assert_eq!(
2529 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("hello.*")).unwrap(),
2530 Value::Bool(true)
2531 );
2532
2533 assert_eq!(
2535 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^world")).unwrap(),
2536 Value::Bool(false)
2537 );
2538
2539 assert_eq!(
2541 eval_binary_op(&s("Hello"), &BinaryOp::Regex, &s("hello")).unwrap(),
2542 Value::Bool(false)
2543 );
2544
2545 assert_eq!(
2547 eval_binary_op(&s("Hello"), &BinaryOp::Regex, &s("(?i)hello")).unwrap(),
2548 Value::Bool(true)
2549 );
2550 }
2551
2552 #[test]
2553 fn test_regex_null_handling() {
2554 assert_eq!(
2556 eval_binary_op(&Value::Null, &BinaryOp::Regex, &s(".*")).unwrap(),
2557 Value::Null
2558 );
2559
2560 assert_eq!(
2562 eval_binary_op(&s("hello"), &BinaryOp::Regex, &Value::Null).unwrap(),
2563 Value::Null
2564 );
2565 }
2566
2567 #[test]
2568 fn test_regex_invalid_pattern() {
2569 let result = eval_binary_op(&s("hello"), &BinaryOp::Regex, &s("[invalid"));
2571 assert!(result.is_err());
2572 assert!(result.unwrap_err().to_string().contains("Invalid regex"));
2573 }
2574
2575 #[test]
2576 fn test_regex_special_characters() {
2577 assert_eq!(
2579 eval_binary_op(
2580 &s("test@example.com"),
2581 &BinaryOp::Regex,
2582 &s(r"^[\w.-]+@[\w.-]+\.\w+$")
2583 )
2584 .unwrap(),
2585 Value::Bool(true)
2586 );
2587
2588 assert_eq!(
2590 eval_binary_op(
2591 &s("123-456-7890"),
2592 &BinaryOp::Regex,
2593 &s(r"^\d{3}-\d{3}-\d{4}$")
2594 )
2595 .unwrap(),
2596 Value::Bool(true)
2597 );
2598
2599 assert_eq!(
2601 eval_binary_op(
2602 &s("1234567890"),
2603 &BinaryOp::Regex,
2604 &s(r"^\d{3}-\d{3}-\d{4}$")
2605 )
2606 .unwrap(),
2607 Value::Bool(false)
2608 );
2609 }
2610
2611 #[test]
2612 fn test_regex_anchors() {
2613 assert_eq!(
2615 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^hello")).unwrap(),
2616 Value::Bool(true)
2617 );
2618 assert_eq!(
2619 eval_binary_op(&s("say hello"), &BinaryOp::Regex, &s("^hello")).unwrap(),
2620 Value::Bool(false)
2621 );
2622
2623 assert_eq!(
2625 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("world$")).unwrap(),
2626 Value::Bool(true)
2627 );
2628 assert_eq!(
2629 eval_binary_op(&s("world hello"), &BinaryOp::Regex, &s("world$")).unwrap(),
2630 Value::Bool(false)
2631 );
2632
2633 assert_eq!(
2635 eval_binary_op(&s("hello"), &BinaryOp::Regex, &s("^hello$")).unwrap(),
2636 Value::Bool(true)
2637 );
2638 assert_eq!(
2639 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^hello$")).unwrap(),
2640 Value::Bool(false)
2641 );
2642 }
2643
2644 #[test]
2645 fn test_temporal_arithmetic() {
2646 let dt = s("2024-01-15T10:00:00Z");
2648 let dur = Value::Int(3_600_000_000_i64);
2649 let result = eval_binary_op(&dt, &BinaryOp::Add, &dur).unwrap();
2650 assert!(result.to_string().contains("11:00"));
2651
2652 let d = s("2024-01-01");
2654 let dur_day = Value::Int(86_400_000_000_i64);
2655 let result = eval_binary_op(&d, &BinaryOp::Add, &dur_day).unwrap();
2656 assert_eq!(result.to_string(), "2024-01-02");
2657
2658 let dt1 = s("2024-01-02T00:00:00Z");
2660 let dt2 = s("2024-01-01T00:00:00Z");
2661 let result = eval_binary_op(&dt1, &BinaryOp::Sub, &dt2).unwrap();
2662 let dur_str = result.to_string();
2664 assert!(dur_str.starts_with('P'));
2665 assert!(dur_str.contains("24H")); }
2667
2668 #[test]
2672 fn test_temporal_arithmetic_edge_cases() {
2673 let dt = s("2024-01-15T10:00:00Z");
2675 let neg_dur = Value::Int(-3_600_000_000_i64); let result = eval_binary_op(&dt, &BinaryOp::Add, &neg_dur).unwrap();
2677 assert!(result.to_string().contains("09:00"));
2678
2679 let dur1 = s("PT1H"); let dur2 = s("PT2H"); let result = eval_binary_op(&dur1, &BinaryOp::Sub, &dur2).unwrap();
2683 let dur_str = result.to_string();
2685 assert!(dur_str.starts_with('P') || dur_str.starts_with("-P"));
2686
2687 let dt = s("2024-01-15T10:00:00Z");
2689 let zero_dur = Value::Int(0_i64);
2690 let result = eval_binary_op(&dt, &BinaryOp::Add, &zero_dur).unwrap();
2691 assert!(result.to_string().contains("10:00"));
2692
2693 let d = s("2023-12-31");
2695 let one_day = Value::Int(86_400_000_000_i64);
2696 let result = eval_binary_op(&d, &BinaryOp::Add, &one_day).unwrap();
2697 assert_eq!(result.to_string(), "2024-01-01");
2698
2699 let dt1 = s("2024-01-15T10:00:00Z");
2701 let dt2 = s("2024-01-15T10:00:00Z");
2702 let result = eval_binary_op(&dt1, &BinaryOp::Sub, &dt2).unwrap();
2703 let dur_str = result.to_string();
2705 assert!(dur_str.starts_with('P'));
2706
2707 let leap_day = s("2024-02-28");
2709 let one_day = Value::Int(86_400_000_000_i64);
2710 let result = eval_binary_op(&leap_day, &BinaryOp::Add, &one_day).unwrap();
2711 assert_eq!(result.to_string(), "2024-02-29");
2712 }
2713
2714 #[test]
2715 fn test_regex_empty_string() {
2716 assert_eq!(
2718 eval_binary_op(&s(""), &BinaryOp::Regex, &s("^$")).unwrap(),
2719 Value::Bool(true)
2720 );
2721
2722 assert_eq!(
2724 eval_binary_op(&s(""), &BinaryOp::Regex, &s(".+")).unwrap(),
2725 Value::Bool(false)
2726 );
2727
2728 assert_eq!(
2730 eval_binary_op(&s("hello"), &BinaryOp::Regex, &s(".*")).unwrap(),
2731 Value::Bool(true)
2732 );
2733 }
2734
2735 #[test]
2736 fn test_regex_type_errors() {
2737 let result = eval_binary_op(&Value::Int(123), &BinaryOp::Regex, &s("\\d+"));
2739 assert!(result.is_err());
2740 assert!(result.unwrap_err().to_string().contains("must be a string"));
2741
2742 let result = eval_binary_op(&s("hello"), &BinaryOp::Regex, &Value::Int(123));
2744 assert!(result.is_err());
2745 assert!(result.unwrap_err().to_string().contains("pattern string"));
2746 }
2747
2748 #[test]
2749 fn test_and_null_handling() {
2750 assert_eq!(
2754 eval_binary_op(&Value::Bool(false), &BinaryOp::And, &Value::Null).unwrap(),
2755 Value::Bool(false)
2756 );
2757 assert_eq!(
2758 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Bool(false)).unwrap(),
2759 Value::Bool(false)
2760 );
2761
2762 assert_eq!(
2764 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Null).unwrap(),
2765 Value::Null
2766 );
2767 assert_eq!(
2768 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Bool(true)).unwrap(),
2769 Value::Null
2770 );
2771
2772 assert_eq!(
2774 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Null).unwrap(),
2775 Value::Null
2776 );
2777
2778 assert_eq!(
2780 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Bool(true)).unwrap(),
2781 Value::Bool(true)
2782 );
2783 assert_eq!(
2784 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Bool(false)).unwrap(),
2785 Value::Bool(false)
2786 );
2787 }
2788
2789 #[test]
2790 fn test_or_null_handling() {
2791 assert_eq!(
2795 eval_binary_op(&Value::Bool(true), &BinaryOp::Or, &Value::Null).unwrap(),
2796 Value::Bool(true)
2797 );
2798 assert_eq!(
2799 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Bool(true)).unwrap(),
2800 Value::Bool(true)
2801 );
2802
2803 assert_eq!(
2805 eval_binary_op(&Value::Bool(false), &BinaryOp::Or, &Value::Null).unwrap(),
2806 Value::Null
2807 );
2808 assert_eq!(
2809 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2810 Value::Null
2811 );
2812
2813 assert_eq!(
2815 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Null).unwrap(),
2816 Value::Null
2817 );
2818
2819 assert_eq!(
2821 eval_binary_op(&Value::Bool(false), &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2822 Value::Bool(false)
2823 );
2824 assert_eq!(
2825 eval_binary_op(&Value::Bool(true), &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2826 Value::Bool(true)
2827 );
2828 }
2829
2830 #[test]
2831 fn test_nan_comparison_with_non_numeric() {
2832 let nan = Value::Float(f64::NAN);
2833
2834 assert_eq!(
2836 eval_binary_op(&nan, &BinaryOp::Gt, &i(1)).unwrap(),
2837 Value::Bool(false)
2838 );
2839
2840 assert_eq!(
2842 eval_binary_op(&nan, &BinaryOp::Gt, &nan).unwrap(),
2843 Value::Bool(false)
2844 );
2845
2846 assert_eq!(
2848 eval_binary_op(&nan, &BinaryOp::Gt, &s("a")).unwrap(),
2849 Value::Null
2850 );
2851
2852 assert_eq!(
2854 eval_binary_op(&s("a"), &BinaryOp::Lt, &nan).unwrap(),
2855 Value::Null
2856 );
2857 }
2858
2859 #[test]
2860 fn test_nan_equality_with_non_numeric() {
2861 let nan = Value::Float(f64::NAN);
2862
2863 assert_eq!(
2865 eval_binary_op(&nan, &BinaryOp::Eq, &nan).unwrap(),
2866 Value::Bool(false)
2867 );
2868
2869 assert_eq!(
2871 eval_binary_op(&nan, &BinaryOp::NotEq, &nan).unwrap(),
2872 Value::Bool(true)
2873 );
2874
2875 assert_eq!(
2877 eval_binary_op(&nan, &BinaryOp::Eq, &s("a")).unwrap(),
2878 Value::Bool(false)
2879 );
2880
2881 assert_eq!(
2883 eval_binary_op(&nan, &BinaryOp::NotEq, &s("a")).unwrap(),
2884 Value::Bool(true)
2885 );
2886 }
2887
2888 #[test]
2889 fn test_large_integer_equality() {
2890 let a = Value::Int(4611686018427387905_i64);
2892 let b = Value::Int(4611686018427387900_i64);
2893
2894 assert_eq!(
2895 eval_binary_op(&a, &BinaryOp::Eq, &b).unwrap(),
2896 Value::Bool(false)
2897 );
2898 assert_eq!(
2899 eval_binary_op(&a, &BinaryOp::Eq, &a).unwrap(),
2900 Value::Bool(true)
2901 );
2902 }
2903
2904 #[test]
2905 fn test_large_integer_ordering() {
2906 let a = Value::Int(4611686018427387905_i64);
2907 let b = Value::Int(4611686018427387900_i64);
2908
2909 assert_eq!(
2910 eval_binary_op(&a, &BinaryOp::Gt, &b).unwrap(),
2911 Value::Bool(true)
2912 );
2913 assert_eq!(
2914 eval_binary_op(&b, &BinaryOp::Lt, &a).unwrap(),
2915 Value::Bool(true)
2916 );
2917 }
2918
2919 #[test]
2920 fn test_int_float_equality_still_works() {
2921 assert_eq!(
2923 eval_binary_op(&i(1), &BinaryOp::Eq, &Value::Float(1.0)).unwrap(),
2924 Value::Bool(true)
2925 );
2926 assert_eq!(
2927 eval_binary_op(&i(1), &BinaryOp::NotEq, &Value::Float(1.0)).unwrap(),
2928 Value::Bool(false)
2929 );
2930 }
2931
2932 #[test]
2933 fn test_xor_null_handling() {
2934 assert_eq!(
2937 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Null).unwrap(),
2938 Value::Null
2939 );
2940 assert_eq!(
2941 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Null).unwrap(),
2942 Value::Null
2943 );
2944 assert_eq!(
2945 eval_binary_op(&Value::Null, &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2946 Value::Null
2947 );
2948 assert_eq!(
2949 eval_binary_op(&Value::Null, &BinaryOp::Xor, &Value::Null).unwrap(),
2950 Value::Null
2951 );
2952
2953 assert_eq!(
2955 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2956 Value::Bool(true)
2957 );
2958 assert_eq!(
2959 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2960 Value::Bool(false)
2961 );
2962 }
2963}