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 => {
286 return Err(anyhow!("Cannot add duration to duration this way"));
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 => return Err(anyhow!("Cannot add duration to duration this way")),
308 };
309 let args = [Value::String(result_str)];
311 match ttype {
312 TemporalType::Date => eval_datetime_function("DATE", &args),
313 TemporalType::LocalTime => eval_datetime_function("LOCALTIME", &args),
314 TemporalType::Time => eval_datetime_function("TIME", &args),
315 TemporalType::LocalDateTime => eval_datetime_function("LOCALDATETIME", &args),
316 TemporalType::DateTime => eval_datetime_function("DATETIME", &args),
317 TemporalType::Duration => unreachable!(),
318 }
319}
320
321fn eval_add(left: &Value, right: &Value) -> Result<Value> {
323 if left.is_null() || right.is_null() {
325 return Ok(Value::Null);
326 }
327
328 match (left, right) {
330 (Value::List(l), Value::List(r)) => {
331 let mut result = l.clone();
332 result.extend(r.iter().cloned());
333 return Ok(Value::List(result));
334 }
335 (Value::List(l), _) => {
336 let mut result = l.clone();
337 result.push(right.clone());
338 return Ok(Value::List(result));
339 }
340 (_, Value::List(r)) => {
341 let mut result = vec![left.clone()];
342 result.extend(r.iter().cloned());
343 return Ok(Value::List(result));
344 }
345 _ => {}
346 }
347
348 if let (Some(l), Some(r)) = (left.as_f64(), right.as_f64()) {
350 if left.is_i64() && right.is_i64() {
351 return Ok(Value::Int(left.as_i64().unwrap() + right.as_i64().unwrap()));
352 }
353 return Ok(Value::Float(l + r));
354 }
355
356 if let Value::String(s) = left
358 && classify_temporal(s).is_some_and(|t| t != TemporalType::Duration)
359 && let Ok(dur) = parse_duration_from_value(right)
360 {
361 return add_temporal_duration_to_value(left, &dur);
362 }
363 if let Value::String(s) = right
364 && classify_temporal(s).is_some_and(|t| t != TemporalType::Duration)
365 && let Ok(dur) = parse_duration_from_value(left)
366 {
367 return add_temporal_duration_to_value(right, &dur);
368 }
369
370 if let Some(tv) = temporal_from_value(left)
372 && !matches!(tv, TemporalValue::Duration { .. })
373 && (is_duration_value(right) || right.is_number())
374 {
375 let dur = parse_duration_from_value(right)?;
376 return add_temporal_duration_typed(&tv, &dur);
377 }
378 if let Some(tv) = temporal_from_value(right)
380 && !matches!(tv, TemporalValue::Duration { .. })
381 && (is_duration_value(left) || left.is_number())
382 {
383 let dur = parse_duration_from_value(left)?;
384 return add_temporal_duration_typed(&tv, &dur);
385 }
386 if let (
388 Some(TemporalValue::Duration {
389 months: m1,
390 days: d1,
391 nanos: n1,
392 }),
393 Some(TemporalValue::Duration {
394 months: m2,
395 days: d2,
396 nanos: n2,
397 }),
398 ) = (temporal_from_value(left), temporal_from_value(right))
399 {
400 return Ok(Value::Temporal(TemporalValue::Duration {
401 months: m1 + m2,
402 days: d1 + d2,
403 nanos: n1 + n2,
404 }));
405 }
406
407 if let (Value::String(l), Value::String(r)) = (left, right) {
409 let l_type = classify_temporal(l);
410 let r_type = classify_temporal(r);
411
412 match (l_type, r_type) {
413 (Some(lt), Some(TemporalType::Duration)) if lt != TemporalType::Duration => {
415 let dur = parse_duration_to_cypher(r)?;
416 return add_temporal_duration_to_value(left, &dur);
417 }
418 (Some(TemporalType::Duration), Some(rt)) if rt != TemporalType::Duration => {
420 let dur = parse_duration_to_cypher(l)?;
421 return add_temporal_duration_to_value(right, &dur);
422 }
423 (Some(TemporalType::Duration), Some(TemporalType::Duration)) => {
425 let d1 = parse_duration_to_cypher(l)?;
426 let d2 = parse_duration_to_cypher(r)?;
427 return Ok(Value::String(d1.add(&d2).to_iso8601()));
428 }
429 _ => return Ok(Value::String(format!("{}{}", l, r))),
431 }
432 }
433
434 if let Value::String(_) = left
436 && right.is_number()
437 && classify_value_temporal(left).is_some_and(|t| t != TemporalType::Duration)
438 {
439 let dur = parse_duration_from_value(right)?;
440 return add_temporal_duration_to_value(left, &dur);
441 }
442 if let Value::String(_) = right
444 && left.is_number()
445 && classify_value_temporal(right).is_some_and(|t| t != TemporalType::Duration)
446 {
447 let dur = parse_duration_from_value(left)?;
448 return add_temporal_duration_to_value(right, &dur);
449 }
450
451 Err(anyhow!(
452 "Invalid types for addition: left={:?}, right={:?}",
453 left,
454 right
455 ))
456}
457
458fn classify_value_temporal(val: &Value) -> Option<TemporalType> {
460 match val {
461 Value::Temporal(tv) => Some(tv.temporal_type()),
462 Value::String(s) => classify_temporal(s),
463 _ => None,
464 }
465}
466
467fn eval_sub(left: &Value, right: &Value) -> Result<Value> {
469 if left.is_null() || right.is_null() {
471 return Ok(Value::Null);
472 }
473
474 if let Value::Temporal(tv) = left
476 && !matches!(tv, TemporalValue::Duration { .. })
477 {
478 if let Value::Temporal(TemporalValue::Duration {
479 months,
480 days,
481 nanos,
482 }) = right
483 {
484 let dur = CypherDuration::new(-months, -days, -nanos);
485 return add_temporal_duration_typed(tv, &dur);
486 }
487 if is_duration_value(right) || right.is_number() {
488 let dur = parse_duration_from_value(right)?.negate();
489 return add_temporal_duration_typed(tv, &dur);
490 }
491 }
492 if let (
494 Value::Temporal(TemporalValue::Duration {
495 months: m1,
496 days: d1,
497 nanos: n1,
498 }),
499 Value::Temporal(TemporalValue::Duration {
500 months: m2,
501 days: d2,
502 nanos: n2,
503 }),
504 ) = (left, right)
505 {
506 return Ok(Value::Temporal(TemporalValue::Duration {
507 months: m1 - m2,
508 days: d1 - d2,
509 nanos: n1 - n2,
510 }));
511 }
512 if let (Value::Temporal(l), Value::Temporal(r)) = (left, right)
514 && l.temporal_type() == r.temporal_type()
515 && l.temporal_type() != TemporalType::Duration
516 {
517 let args = [left.clone(), right.clone()];
518 return crate::query::datetime::eval_datetime_function("DURATION.BETWEEN", &args);
519 }
520
521 if let (Value::String(l), Value::String(r)) = (left, right) {
523 let l_type = classify_temporal(l);
524 let r_type = classify_temporal(r);
525
526 match (l_type, r_type) {
527 (Some(lt), Some(TemporalType::Duration)) if lt != TemporalType::Duration => {
528 let dur = parse_duration_to_cypher(r)?.negate();
529 return add_temporal_duration_to_value(left, &dur);
530 }
531 (Some(TemporalType::Duration), Some(TemporalType::Duration)) => {
532 let d1 = parse_duration_to_cypher(l)?;
533 let d2 = parse_duration_to_cypher(r)?;
534 return Ok(Value::String(d1.sub(&d2).to_iso8601()));
535 }
536 (Some(lt), Some(rt))
537 if lt != TemporalType::Duration && rt != TemporalType::Duration && lt == rt =>
538 {
539 let args = [left.clone(), right.clone()];
540 return crate::query::datetime::eval_datetime_function("DURATION.BETWEEN", &args);
541 }
542 _ => {}
543 }
544 }
545
546 if let Value::String(_) = left
548 && right.is_number()
549 && classify_value_temporal(left).is_some_and(|t| t != TemporalType::Duration)
550 {
551 let dur = parse_duration_from_value(right)?.negate();
552 return add_temporal_duration_to_value(left, &dur);
553 }
554
555 eval_numeric_op(left, right, |a, b| a - b)
556}
557
558fn extract_cypher_duration(val: &Value) -> Option<Result<(CypherDuration, bool)>> {
562 match val {
563 Value::Temporal(TemporalValue::Duration {
564 months,
565 days,
566 nanos,
567 }) => Some(Ok((CypherDuration::new(*months, *days, *nanos), true))),
568 Value::String(s) if is_duration_value(val) => {
569 Some(parse_duration_to_cypher(s).map(|d| (d, false)))
570 }
571 _ => None,
572 }
573}
574
575fn duration_to_value(result: CypherDuration, is_temporal: bool) -> Value {
580 if is_temporal {
581 result.to_temporal_value()
582 } else {
583 Value::String(result.to_iso8601())
584 }
585}
586
587fn eval_mul(left: &Value, right: &Value) -> Result<Value> {
589 if left.is_null() || right.is_null() {
590 return Ok(Value::Null);
591 }
592
593 if let Some(dur_result) = extract_cypher_duration(left)
595 && let Some(factor) = right.as_f64()
596 {
597 let (dur, is_temporal) = dur_result?;
598 return Ok(duration_to_value(dur.multiply(factor), is_temporal));
599 }
600 if let Some(dur_result) = extract_cypher_duration(right)
601 && let Some(factor) = left.as_f64()
602 {
603 let (dur, is_temporal) = dur_result?;
604 return Ok(duration_to_value(dur.multiply(factor), is_temporal));
605 }
606
607 eval_numeric_op(left, right, |a, b| a * b)
608}
609
610fn eval_div(left: &Value, right: &Value) -> Result<Value> {
612 if left.is_null() || right.is_null() {
613 return Ok(Value::Null);
614 }
615
616 if let Some(dur_result) = extract_cypher_duration(left)
618 && let Some(divisor) = right.as_f64()
619 {
620 let (dur, is_temporal) = dur_result?;
621 return Ok(duration_to_value(dur.divide(divisor), is_temporal));
622 }
623
624 if let (Value::Int(l), Value::Int(r)) = (left, right) {
626 return if *r == 0 {
627 Err(anyhow!("Division by zero"))
628 } else {
629 Ok(Value::Int(l / r))
630 };
631 }
632
633 eval_numeric_op(left, right, |a, b| a / b)
634}
635
636fn eval_comparison<F>(left: &Value, right: &Value, check: F) -> Result<Value>
642where
643 F: Fn(Ordering) -> bool,
644{
645 if left.is_null() || right.is_null() {
647 return Ok(Value::Null);
648 }
649
650 let left_nan = left.as_f64().is_some_and(|f| f.is_nan());
652 let right_nan = right.as_f64().is_some_and(|f| f.is_nan());
653 if left_nan || right_nan {
654 if left_nan && right_nan {
655 return Ok(Value::Bool(false));
656 }
657 let other = if left_nan { right } else { left };
658 if other.as_f64().is_some() {
659 return Ok(Value::Bool(false)); }
661 return Ok(Value::Null); }
663
664 let ord = cypher_partial_cmp(left, right);
665 match ord {
666 Some(o) => Ok(Value::Bool(check(o))),
667 None => Ok(Value::Null),
668 }
669}
670
671fn cypher_partial_cmp(left: &Value, right: &Value) -> Option<Ordering> {
673 if left.is_null() || right.is_null() {
674 return None;
675 }
676
677 let left_temporal = temporal_from_value(left);
678 let right_temporal = temporal_from_value(right);
679 if let (Some(l), Some(r)) = (&left_temporal, &right_temporal) {
680 return temporal_partial_cmp(l, r);
681 }
682 if let (Some(_), Value::String(rs)) = (&left_temporal, right) {
683 let ls = left.to_string();
684 if let (Some(lt), Some(rt)) = (classify_temporal(&ls), classify_temporal(rs))
685 && lt == rt
686 {
687 return temporal_string_cmp(&ls, rs, lt);
688 }
689 return None;
690 }
691 if let (Value::String(ls), Some(_)) = (left, &right_temporal) {
692 let rs = right.to_string();
693 if let (Some(lt), Some(rt)) = (classify_temporal(ls), classify_temporal(&rs))
694 && lt == rt
695 {
696 return temporal_string_cmp(ls, &rs, lt);
697 }
698 return None;
699 }
700
701 if let (Some(l), Some(r)) = (left.as_i64(), right.as_i64()) {
703 return Some(l.cmp(&r));
704 }
705
706 if let (Some(l), Some(r)) = (left.as_f64(), right.as_f64()) {
708 return l.partial_cmp(&r);
709 }
710
711 if let (Some(l), Some(r)) = (left.as_str(), right.as_str()) {
713 if let (Some(lt), Some(rt)) = (classify_temporal(l), classify_temporal(r))
715 && lt == rt
716 {
717 let res = temporal_string_cmp(l, r, lt);
718 if res.is_some() {
719 return res;
720 }
721 }
722 return l.partial_cmp(r);
723 }
724
725 if let (Some(l), Some(r)) = (left.as_bool(), right.as_bool()) {
727 return l.partial_cmp(&r);
728 }
729
730 if let (Value::List(l), Value::List(r)) = (left, right) {
732 for (lv, rv) in l.iter().zip(r.iter()) {
733 match cypher_partial_cmp(lv, rv) {
734 Some(Ordering::Equal) => continue,
735 other => return other,
736 }
737 }
738 return l.len().partial_cmp(&r.len());
739 }
740
741 None
743}
744
745fn temporal_partial_cmp(left: &TemporalValue, right: &TemporalValue) -> Option<Ordering> {
747 match (left, right) {
748 (
749 TemporalValue::Date {
750 days_since_epoch: l,
751 },
752 TemporalValue::Date {
753 days_since_epoch: r,
754 },
755 ) => Some(l.cmp(r)),
756 (
757 TemporalValue::LocalTime {
758 nanos_since_midnight: l,
759 },
760 TemporalValue::LocalTime {
761 nanos_since_midnight: r,
762 },
763 ) => Some(l.cmp(r)),
764 (
765 TemporalValue::Time {
766 nanos_since_midnight: lm,
767 offset_seconds: lo,
768 },
769 TemporalValue::Time {
770 nanos_since_midnight: rm,
771 offset_seconds: ro,
772 },
773 ) => {
774 let l_utc = *lm as i128 - (*lo as i128) * 1_000_000_000;
776 let r_utc = *rm as i128 - (*ro as i128) * 1_000_000_000;
777 Some(l_utc.cmp(&r_utc))
778 }
779 (
780 TemporalValue::LocalDateTime {
781 nanos_since_epoch: l,
782 },
783 TemporalValue::LocalDateTime {
784 nanos_since_epoch: r,
785 },
786 ) => Some(l.cmp(r)),
787 (
788 TemporalValue::DateTime {
789 nanos_since_epoch: l,
790 ..
791 },
792 TemporalValue::DateTime {
793 nanos_since_epoch: r,
794 ..
795 },
796 ) => {
797 Some(l.cmp(r))
799 }
800 (TemporalValue::Duration { .. }, TemporalValue::Duration { .. }) => None,
802 _ => None,
804 }
805}
806
807pub(crate) fn temporal_from_value(v: &Value) -> Option<TemporalValue> {
814 match v {
815 Value::Temporal(tv) => Some(tv.clone()),
816 Value::Map(map) => temporal_from_map_wrapper(map),
817 Value::String(s) => {
818 temporal_from_json_wrapper_str(s).or_else(|| temporal_from_human_readable_str(s))
819 }
820 _ => None,
821 }
822}
823
824pub(crate) fn temporal_from_human_readable_str(s: &str) -> Option<TemporalValue> {
827 let fn_name = match classify_temporal(s)? {
828 TemporalType::Date => "DATE",
829 TemporalType::LocalTime => "LOCALTIME",
830 TemporalType::Time => "TIME",
831 TemporalType::LocalDateTime => "LOCALDATETIME",
832 TemporalType::DateTime => "DATETIME",
833 TemporalType::Duration => "DURATION",
834 };
835 match eval_datetime_function(fn_name, &[Value::String(s.to_string())]).ok()? {
836 Value::Temporal(tv) => Some(tv),
837 _ => None,
838 }
839}
840
841pub(crate) fn temporal_from_map_wrapper(
847 map: &std::collections::HashMap<String, Value>,
848) -> Option<TemporalValue> {
849 if map.len() != 1 {
850 return None;
851 }
852
853 let as_i32 = |v: &Value| v.as_i64().and_then(|n| i32::try_from(n).ok());
854 let as_i64 = |v: &Value| v.as_i64();
855
856 if let Some(Value::Map(inner)) = map.get("Date") {
857 let days = inner.get("days_since_epoch").and_then(as_i32)?;
858 return Some(TemporalValue::Date {
859 days_since_epoch: days,
860 });
861 }
862 if let Some(Value::Map(inner)) = map.get("LocalTime") {
863 let nanos = inner.get("nanos_since_midnight").and_then(as_i64)?;
864 return Some(TemporalValue::LocalTime {
865 nanos_since_midnight: nanos,
866 });
867 }
868 if let Some(Value::Map(inner)) = map.get("Time") {
869 let nanos = inner.get("nanos_since_midnight").and_then(as_i64)?;
870 let offset = inner.get("offset_seconds").and_then(as_i32)?;
871 return Some(TemporalValue::Time {
872 nanos_since_midnight: nanos,
873 offset_seconds: offset,
874 });
875 }
876 if let Some(Value::Map(inner)) = map.get("LocalDateTime") {
877 let nanos = inner.get("nanos_since_epoch").and_then(as_i64)?;
878 return Some(TemporalValue::LocalDateTime {
879 nanos_since_epoch: nanos,
880 });
881 }
882 if let Some(Value::Map(inner)) = map.get("DateTime") {
883 let nanos = inner.get("nanos_since_epoch").and_then(as_i64)?;
884 let offset = inner.get("offset_seconds").and_then(as_i32)?;
885 let timezone_name = match inner.get("timezone_name") {
886 Some(Value::String(s)) => Some(s.clone()),
887 _ => None,
888 };
889 return Some(TemporalValue::DateTime {
890 nanos_since_epoch: nanos,
891 offset_seconds: offset,
892 timezone_name,
893 });
894 }
895 if let Some(Value::Map(inner)) = map.get("Duration") {
896 let months = inner.get("months").and_then(as_i64)?;
897 let days = inner.get("days").and_then(as_i64)?;
898 let nanos = inner.get("nanos").and_then(as_i64)?;
899 return Some(TemporalValue::Duration {
900 months,
901 days,
902 nanos,
903 });
904 }
905 None
906}
907
908fn temporal_from_json_wrapper_str(s: &str) -> Option<TemporalValue> {
909 let parsed: serde_json::Value = serde_json::from_str(s).ok()?;
910 let obj = parsed.as_object()?;
911 if obj.len() != 1 {
912 return None;
913 }
914
915 let as_i32 = |o: &serde_json::Map<String, serde_json::Value>, key: &str| {
916 o.get(key)
917 .and_then(serde_json::Value::as_i64)
918 .and_then(|n| i32::try_from(n).ok())
919 };
920 let as_i64 = |o: &serde_json::Map<String, serde_json::Value>, key: &str| {
921 o.get(key).and_then(serde_json::Value::as_i64)
922 };
923
924 if let Some(inner) = obj.get("Date").and_then(serde_json::Value::as_object) {
925 return Some(TemporalValue::Date {
926 days_since_epoch: as_i32(inner, "days_since_epoch")?,
927 });
928 }
929 if let Some(inner) = obj.get("LocalTime").and_then(serde_json::Value::as_object) {
930 return Some(TemporalValue::LocalTime {
931 nanos_since_midnight: as_i64(inner, "nanos_since_midnight")?,
932 });
933 }
934 if let Some(inner) = obj.get("Time").and_then(serde_json::Value::as_object) {
935 return Some(TemporalValue::Time {
936 nanos_since_midnight: as_i64(inner, "nanos_since_midnight")?,
937 offset_seconds: as_i32(inner, "offset_seconds")?,
938 });
939 }
940 if let Some(inner) = obj
941 .get("LocalDateTime")
942 .and_then(serde_json::Value::as_object)
943 {
944 return Some(TemporalValue::LocalDateTime {
945 nanos_since_epoch: as_i64(inner, "nanos_since_epoch")?,
946 });
947 }
948 if let Some(inner) = obj.get("DateTime").and_then(serde_json::Value::as_object) {
949 return Some(TemporalValue::DateTime {
950 nanos_since_epoch: as_i64(inner, "nanos_since_epoch")?,
951 offset_seconds: as_i32(inner, "offset_seconds")?,
952 timezone_name: inner
953 .get("timezone_name")
954 .and_then(serde_json::Value::as_str)
955 .map(str::to_string),
956 });
957 }
958 if let Some(inner) = obj.get("Duration").and_then(serde_json::Value::as_object) {
959 return Some(TemporalValue::Duration {
960 months: as_i64(inner, "months")?,
961 days: as_i64(inner, "days")?,
962 nanos: as_i64(inner, "nanos")?,
963 });
964 }
965 None
966}
967
968fn temporal_string_cmp(l: &str, r: &str, ttype: TemporalType) -> Option<Ordering> {
970 match ttype {
971 TemporalType::Date => {
972 let ld = chrono::NaiveDate::parse_from_str(l, "%Y-%m-%d").ok();
973 let rd = chrono::NaiveDate::parse_from_str(r, "%Y-%m-%d").ok();
974 ld.and_then(|l| rd.map(|r| l.cmp(&r)))
975 }
976 TemporalType::LocalTime => {
977 let lt = parse_time_for_cmp(l).ok();
978 let rt = parse_time_for_cmp(r).ok();
979 lt.and_then(|l| rt.map(|r| l.cmp(&r)))
980 }
981 TemporalType::Time => {
982 let ln = time_with_tz_to_utc_nanos(l).ok();
983 let rn = time_with_tz_to_utc_nanos(r).ok();
984 ln.and_then(|l| rn.map(|r| l.cmp(&r)))
985 }
986 TemporalType::LocalDateTime => {
987 let ldt = parse_local_datetime_for_cmp(l).ok();
988 let rdt = parse_local_datetime_for_cmp(r).ok();
989 ldt.and_then(|l| rdt.map(|r| l.cmp(&r)))
990 }
991 TemporalType::DateTime => {
992 let ldt = parse_datetime_utc(l).ok();
993 let rdt = parse_datetime_utc(r).ok();
994 ldt.and_then(|l| rdt.map(|r| l.cmp(&r)))
995 }
996 TemporalType::Duration => None, }
998}
999
1000fn parse_time_for_cmp(s: &str) -> Result<chrono::NaiveTime> {
1002 chrono::NaiveTime::parse_from_str(s, "%H:%M:%S%.f")
1003 .or_else(|_| chrono::NaiveTime::parse_from_str(s, "%H:%M:%S"))
1004 .or_else(|_| chrono::NaiveTime::parse_from_str(s, "%H:%M"))
1005 .map_err(|_| anyhow!("Cannot parse time: {}", s))
1006}
1007
1008fn parse_local_datetime_for_cmp(s: &str) -> Result<chrono::NaiveDateTime> {
1010 chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")
1011 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S"))
1012 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M"))
1013 .map_err(|_| anyhow!("Cannot parse localdatetime: {}", s))
1014}
1015
1016const NANOS_PER_SECOND_CMP: i64 = 1_000_000_000;
1017
1018fn time_with_tz_to_utc_nanos(s: &str) -> Result<i64> {
1020 use chrono::Timelike;
1021 let (_, time, tz_info) = crate::query::datetime::parse_datetime_with_tz(s)?;
1022 let local_nanos = time.hour() as i64 * 3_600 * NANOS_PER_SECOND_CMP
1023 + time.minute() as i64 * 60 * NANOS_PER_SECOND_CMP
1024 + time.second() as i64 * NANOS_PER_SECOND_CMP
1025 + time.nanosecond() as i64;
1026
1027 let offset_secs: i64 = match tz_info {
1029 Some(ref tz) => {
1030 let today = chrono::NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
1031 let ndt = chrono::NaiveDateTime::new(today, time);
1032 tz.offset_for_local(&ndt)?.local_minus_utc() as i64
1033 }
1034 None => 0,
1035 };
1036
1037 Ok(local_nanos - offset_secs * NANOS_PER_SECOND_CMP)
1038}
1039
1040fn eval_size(arg: &Value) -> Result<Value> {
1045 match arg {
1046 Value::List(arr) => Ok(Value::Int(arr.len() as i64)),
1047 Value::Map(map) => Ok(Value::Int(map.len() as i64)),
1048 Value::String(s) => Ok(Value::Int(s.len() as i64)),
1049 Value::Null => Ok(Value::Null),
1050 _ => Err(anyhow!("size() expects a List, Map, or String")),
1051 }
1052}
1053
1054fn eval_keys(arg: &Value) -> Result<Value> {
1055 match arg {
1056 Value::Map(map) => {
1057 let is_entity =
1061 map.contains_key("_vid") || map.contains_key("_eid") || map.contains_key("_labels");
1062 let mut keys: Vec<&String> = map
1063 .iter()
1064 .filter(|(k, v)| !k.starts_with('_') && (!is_entity || !v.is_null()))
1065 .map(|(k, _)| k)
1066 .collect();
1067 keys.sort();
1068 Ok(Value::List(
1069 keys.into_iter().map(|k| Value::String(k.clone())).collect(),
1070 ))
1071 }
1072 Value::Null => Ok(Value::Null),
1073 _ => Err(anyhow!("keys() expects a Map")),
1074 }
1075}
1076
1077fn eval_head(arg: &Value) -> Result<Value> {
1078 match arg {
1079 Value::List(arr) => Ok(arr.first().cloned().unwrap_or(Value::Null)),
1080 Value::Null => Ok(Value::Null),
1081 _ => Err(anyhow!("head() expects a List")),
1082 }
1083}
1084
1085fn eval_tail(arg: &Value) -> Result<Value> {
1086 match arg {
1087 Value::List(arr) => Ok(Value::List(arr.get(1..).unwrap_or_default().to_vec())),
1088 Value::Null => Ok(Value::Null),
1089 _ => Err(anyhow!("tail() expects a List")),
1090 }
1091}
1092
1093fn eval_last(arg: &Value) -> Result<Value> {
1094 match arg {
1095 Value::List(arr) => Ok(arr.last().cloned().unwrap_or(Value::Null)),
1096 Value::Null => Ok(Value::Null),
1097 _ => Err(anyhow!("last() expects a List")),
1098 }
1099}
1100
1101fn eval_length(arg: &Value) -> Result<Value> {
1102 match arg {
1103 Value::List(arr) => Ok(Value::Int(arr.len() as i64)),
1104 Value::String(s) => Ok(Value::Int(s.len() as i64)),
1105 Value::Path(p) => Ok(Value::Int(p.edges.len() as i64)),
1106 Value::Map(map) => {
1107 if map.contains_key("nodes")
1109 && map.contains_key("relationships")
1110 && let Some(Value::List(rels)) = map.get("relationships")
1111 {
1112 return Ok(Value::Int(rels.len() as i64));
1113 }
1114 Ok(Value::Null)
1115 }
1116 Value::Null => Ok(Value::Null),
1117 _ => Err(anyhow!("length() expects a List, String, or Path")),
1118 }
1119}
1120
1121fn eval_nodes(arg: &Value) -> Result<Value> {
1122 match arg {
1123 Value::Path(p) => Ok(Value::List(
1124 p.nodes.iter().map(|n| Value::Node(n.clone())).collect(),
1125 )),
1126 Value::Map(map) => {
1127 if let Some(nodes) = map.get("nodes") {
1128 Ok(nodes.clone())
1129 } else {
1130 Ok(Value::Null)
1131 }
1132 }
1133 Value::Null => Ok(Value::Null),
1134 _ => Err(anyhow!("nodes() expects a Path")),
1135 }
1136}
1137
1138fn eval_relationships(arg: &Value) -> Result<Value> {
1139 match arg {
1140 Value::Path(p) => Ok(Value::List(
1141 p.edges.iter().map(|e| Value::Edge(e.clone())).collect(),
1142 )),
1143 Value::Map(map) => {
1144 if let Some(rels) = map.get("relationships") {
1145 Ok(rels.clone())
1146 } else {
1147 Ok(Value::Null)
1148 }
1149 }
1150 Value::Null => Ok(Value::Null),
1151 _ => Err(anyhow!("relationships() expects a Path")),
1152 }
1153}
1154
1155fn eval_list_function(name: &str, args: &[Value]) -> Result<Value> {
1157 if args.len() != 1 {
1158 return Err(anyhow!("{}() requires 1 argument", name));
1159 }
1160 match name {
1161 "SIZE" => eval_size(&args[0]),
1162 "KEYS" => eval_keys(&args[0]),
1163 "HEAD" => eval_head(&args[0]),
1164 "TAIL" => eval_tail(&args[0]),
1165 "LAST" => eval_last(&args[0]),
1166 "LENGTH" => eval_length(&args[0]),
1167 "NODES" => eval_nodes(&args[0]),
1168 "RELATIONSHIPS" => eval_relationships(&args[0]),
1169 _ => Err(anyhow!("Unknown list function: {}", name)),
1170 }
1171}
1172
1173fn eval_tointeger(arg: &Value) -> Result<Value> {
1178 match arg {
1179 Value::Int(i) => Ok(Value::Int(*i)),
1180 Value::Float(f) => Ok(Value::Int(*f as i64)),
1181 Value::String(s) => Ok(s.parse::<i64>().map(Value::Int).unwrap_or(Value::Null)),
1182 Value::Null => Ok(Value::Null),
1183 _ => Err(anyhow!(
1184 "InvalidArgumentValue: toInteger() cannot convert type"
1185 )),
1186 }
1187}
1188
1189fn eval_tofloat(arg: &Value) -> Result<Value> {
1190 match arg {
1191 Value::Int(i) => Ok(Value::Float(*i as f64)),
1192 Value::Float(f) => Ok(Value::Float(*f)),
1193 Value::String(s) => Ok(s.parse::<f64>().map(Value::Float).unwrap_or(Value::Null)),
1194 Value::Null => Ok(Value::Null),
1195 _ => Err(anyhow!(
1196 "InvalidArgumentValue: toFloat() cannot convert type"
1197 )),
1198 }
1199}
1200
1201fn eval_tostring(arg: &Value) -> Result<Value> {
1202 match arg {
1203 Value::String(s) => Ok(Value::String(s.clone())),
1204 Value::Int(i) => Ok(Value::String(i.to_string())),
1205 Value::Float(f) => {
1206 if f.fract() == 0.0 && f.is_finite() {
1208 Ok(Value::String(format!("{f:.1}")))
1209 } else {
1210 Ok(Value::String(f.to_string()))
1211 }
1212 }
1213 Value::Bool(b) => Ok(Value::String(b.to_string())),
1214 Value::Null => Ok(Value::Null),
1215 other => Ok(Value::String(other.to_string())),
1216 }
1217}
1218
1219fn eval_toboolean(arg: &Value) -> Result<Value> {
1220 match arg {
1221 Value::Bool(b) => Ok(Value::Bool(*b)),
1222 Value::String(s) => {
1223 let lower = s.to_lowercase();
1224 if lower == "true" {
1225 Ok(Value::Bool(true))
1226 } else if lower == "false" {
1227 Ok(Value::Bool(false))
1228 } else {
1229 Ok(Value::Null)
1230 }
1231 }
1232 Value::Null => Ok(Value::Null),
1233 _ => Err(anyhow!(
1234 "InvalidArgumentValue: toBoolean() cannot convert type"
1235 )),
1236 }
1237}
1238
1239fn eval_type_function(name: &str, args: &[Value]) -> Result<Value> {
1241 if args.len() != 1 {
1242 return Err(anyhow!("{}() requires 1 argument", name));
1243 }
1244 match name {
1245 "TOINTEGER" | "TOINT" => eval_tointeger(&args[0]),
1246 "TOFLOAT" => eval_tofloat(&args[0]),
1247 "TOSTRING" => eval_tostring(&args[0]),
1248 "TOBOOLEAN" | "TOBOOL" => eval_toboolean(&args[0]),
1249 _ => Err(anyhow!("Unknown type function: {}", name)),
1250 }
1251}
1252
1253fn eval_abs(arg: &Value) -> Result<Value> {
1258 match arg {
1259 Value::Int(i) => Ok(Value::Int(i.abs())),
1260 Value::Float(f) => Ok(Value::Float(f.abs())),
1261 Value::Null => Ok(Value::Null),
1262 _ => Err(anyhow!("abs() expects a number")),
1263 }
1264}
1265
1266fn eval_sqrt(arg: &Value) -> Result<Value> {
1267 match arg {
1268 v if v.is_number() => {
1269 let f = v.as_f64().unwrap();
1270 if f < 0.0 {
1271 Ok(Value::Null)
1272 } else {
1273 Ok(Value::Float(f.sqrt()))
1274 }
1275 }
1276 Value::Null => Ok(Value::Null),
1277 _ => Err(anyhow!("sqrt() expects a number")),
1278 }
1279}
1280
1281fn eval_sign(arg: &Value) -> Result<Value> {
1282 match arg {
1283 Value::Int(i) => Ok(Value::Int(i.signum())),
1284 Value::Float(f) => Ok(Value::Int(f.signum() as i64)),
1285 Value::Null => Ok(Value::Null),
1286 _ => Err(anyhow!("sign() expects a number")),
1287 }
1288}
1289
1290fn eval_power(args: &[Value]) -> Result<Value> {
1291 if args.len() != 2 {
1292 return Err(anyhow!("power() requires 2 arguments"));
1293 }
1294 match (&args[0], &args[1]) {
1295 (a, b) if a.is_number() && b.is_number() => {
1296 let base = a.as_f64().unwrap();
1297 let exp = b.as_f64().unwrap();
1298 Ok(Value::Float(base.powf(exp)))
1299 }
1300 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
1301 _ => Err(anyhow!("power() expects numeric arguments")),
1302 }
1303}
1304
1305fn eval_unary_numeric_op<F>(arg: &Value, func_name: &str, op: F) -> Result<Value>
1307where
1308 F: Fn(f64) -> f64,
1309{
1310 match arg {
1311 Value::Int(i) => Ok(Value::Float(op(*i as f64))),
1312 Value::Float(f) => Ok(Value::Float(op(*f))),
1313 Value::Null => Ok(Value::Null),
1314 _ => Err(anyhow!("{}() expects a number", func_name)),
1315 }
1316}
1317
1318fn eval_atan2(args: &[Value]) -> Result<Value> {
1319 if args.len() != 2 {
1320 return Err(anyhow!("atan2() requires 2 arguments"));
1321 }
1322 match (&args[0], &args[1]) {
1323 (a, b) if a.is_number() && b.is_number() => {
1324 let y_val = a.as_f64().unwrap();
1325 let x_val = b.as_f64().unwrap();
1326 Ok(Value::Float(y_val.atan2(x_val)))
1327 }
1328 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
1329 _ => Err(anyhow!("atan2() expects numeric arguments")),
1330 }
1331}
1332
1333fn require_one_arg<'a>(name: &str, args: &'a [Value]) -> Result<&'a Value> {
1335 if args.len() != 1 {
1336 return Err(anyhow!("{} requires 1 argument", name));
1337 }
1338 Ok(&args[0])
1339}
1340
1341fn eval_math_function(name: &str, args: &[Value]) -> Result<Value> {
1346 match name {
1347 "ABS" => eval_abs(require_one_arg(name, args)?),
1349 "CEIL" => eval_unary_numeric_op(require_one_arg(name, args)?, "ceil", f64::ceil),
1350 "FLOOR" => eval_unary_numeric_op(require_one_arg(name, args)?, "floor", f64::floor),
1351 "ROUND" => eval_unary_numeric_op(require_one_arg(name, args)?, "round", f64::round),
1352 "SQRT" => eval_sqrt(require_one_arg(name, args)?),
1353 "SIGN" => eval_sign(require_one_arg(name, args)?),
1354 "LOG" => eval_unary_numeric_op(require_one_arg(name, args)?, "log", f64::ln),
1355 "LOG10" => eval_unary_numeric_op(require_one_arg(name, args)?, "log10", f64::log10),
1356 "EXP" => eval_unary_numeric_op(require_one_arg(name, args)?, "exp", f64::exp),
1357 "SIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "sin", f64::sin),
1358 "COS" => eval_unary_numeric_op(require_one_arg(name, args)?, "cos", f64::cos),
1359 "TAN" => eval_unary_numeric_op(require_one_arg(name, args)?, "tan", f64::tan),
1360 "ASIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "asin", f64::asin),
1361 "ACOS" => eval_unary_numeric_op(require_one_arg(name, args)?, "acos", f64::acos),
1362 "ATAN" => eval_unary_numeric_op(require_one_arg(name, args)?, "atan", f64::atan),
1363 "DEGREES" => {
1364 eval_unary_numeric_op(require_one_arg(name, args)?, "degrees", f64::to_degrees)
1365 }
1366 "RADIANS" => {
1367 eval_unary_numeric_op(require_one_arg(name, args)?, "radians", f64::to_radians)
1368 }
1369 "HAVERSIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "haversin", |f| {
1370 (1.0 - f.cos()) / 2.0
1371 }),
1372 "POWER" | "POW" => eval_power(args),
1374 "ATAN2" => eval_atan2(args),
1375 "PI" => {
1377 if !args.is_empty() {
1378 return Err(anyhow!("PI takes no arguments"));
1379 }
1380 Ok(Value::Float(std::f64::consts::PI))
1381 }
1382 "E" => {
1383 if !args.is_empty() {
1384 return Err(anyhow!("E takes no arguments"));
1385 }
1386 Ok(Value::Float(std::f64::consts::E))
1387 }
1388 "RAND" => {
1389 if !args.is_empty() {
1390 return Err(anyhow!("RAND takes no arguments"));
1391 }
1392 use rand::Rng;
1393 let mut rng = rand::thread_rng();
1394 Ok(Value::Float(rng.gen_range(0.0..1.0)))
1395 }
1396 _ => Err(anyhow!("Unknown math function: {}", name)),
1397 }
1398}
1399
1400fn eval_unary_string_op<F>(arg: &Value, func_name: &str, op: F) -> Result<Value>
1406where
1407 F: FnOnce(&str) -> String,
1408{
1409 match arg {
1410 Value::String(s) => Ok(Value::String(op(s))),
1411 Value::Null => Ok(Value::Null),
1412 _ => Err(anyhow!("{}() expects a string", func_name)),
1413 }
1414}
1415
1416fn eval_toupper(args: &[Value]) -> Result<Value> {
1417 let arg = require_one_arg("toUpper", args)?;
1418 eval_unary_string_op(arg, "toUpper", |s| s.to_uppercase())
1419}
1420
1421fn eval_tolower(args: &[Value]) -> Result<Value> {
1422 let arg = require_one_arg("toLower", args)?;
1423 eval_unary_string_op(arg, "toLower", |s| s.to_lowercase())
1424}
1425
1426fn eval_trim(args: &[Value]) -> Result<Value> {
1427 let arg = require_one_arg("trim", args)?;
1428 eval_unary_string_op(arg, "trim", |s| s.trim().to_string())
1429}
1430
1431fn eval_ltrim(args: &[Value]) -> Result<Value> {
1432 let arg = require_one_arg("ltrim", args)?;
1433 eval_unary_string_op(arg, "ltrim", |s| s.trim_start().to_string())
1434}
1435
1436fn eval_rtrim(args: &[Value]) -> Result<Value> {
1437 let arg = require_one_arg("rtrim", args)?;
1438 eval_unary_string_op(arg, "rtrim", |s| s.trim_end().to_string())
1439}
1440
1441fn eval_reverse(args: &[Value]) -> Result<Value> {
1442 let arg = require_one_arg("reverse", args)?;
1443 match arg {
1444 Value::String(s) => Ok(Value::String(s.chars().rev().collect())),
1445 Value::List(arr) => Ok(Value::List(arr.iter().rev().cloned().collect())),
1446 Value::Null => Ok(Value::Null),
1447 _ => Err(anyhow!("reverse() expects a string or list")),
1448 }
1449}
1450
1451fn eval_replace(args: &[Value]) -> Result<Value> {
1452 if args.len() != 3 {
1453 return Err(anyhow!("replace() requires 3 arguments"));
1454 }
1455 match (&args[0], &args[1], &args[2]) {
1456 (Value::String(s), Value::String(search), Value::String(replacement)) => Ok(Value::String(
1457 s.replace(search.as_str(), replacement.as_str()),
1458 )),
1459 (Value::Null, _, _) => Ok(Value::Null),
1460 _ => Err(anyhow!("replace() expects string arguments")),
1461 }
1462}
1463
1464pub(crate) fn eval_split(args: &[Value]) -> Result<Value> {
1465 if args.len() != 2 {
1466 return Err(anyhow!("split() requires 2 arguments"));
1467 }
1468 match (&args[0], &args[1]) {
1469 (Value::String(s), Value::String(delimiter)) => {
1470 let parts: Vec<Value> = s
1471 .split(delimiter.as_str())
1472 .map(|p| Value::String(p.to_string()))
1473 .collect();
1474 Ok(Value::List(parts))
1475 }
1476 (Value::Null, _) => Ok(Value::Null),
1477 _ => Err(anyhow!("split() expects string arguments")),
1478 }
1479}
1480
1481fn eval_substring(args: &[Value]) -> Result<Value> {
1482 if args.len() < 2 || args.len() > 3 {
1483 return Err(anyhow!("substring() requires 2 or 3 arguments"));
1484 }
1485 match &args[0] {
1486 Value::String(s) => {
1487 let start = args[1]
1488 .as_i64()
1489 .ok_or_else(|| anyhow!("substring() start must be an integer"))?
1490 as usize;
1491 let len = if args.len() == 3 {
1492 args[2]
1493 .as_i64()
1494 .ok_or_else(|| anyhow!("substring() length must be an integer"))?
1495 as usize
1496 } else {
1497 s.len().saturating_sub(start)
1498 };
1499 let chars: Vec<char> = s.chars().collect();
1500 let end = (start + len).min(chars.len());
1501 let result: String = chars[start.min(chars.len())..end].iter().collect();
1502 Ok(Value::String(result))
1503 }
1504 Value::Null => Ok(Value::Null),
1505 _ => Err(anyhow!("substring() expects a string")),
1506 }
1507}
1508
1509fn eval_left(args: &[Value]) -> Result<Value> {
1510 if args.len() != 2 {
1511 return Err(anyhow!("left() requires 2 arguments"));
1512 }
1513 match (&args[0], &args[1]) {
1514 (Value::String(s), n) if n.is_number() => {
1515 let len = n.as_i64().unwrap_or(0) as usize;
1516 Ok(Value::String(s.chars().take(len).collect()))
1517 }
1518 (Value::Null, _) => Ok(Value::Null),
1519 _ => Err(anyhow!("left() expects a string and integer")),
1520 }
1521}
1522
1523fn eval_right(args: &[Value]) -> Result<Value> {
1524 if args.len() != 2 {
1525 return Err(anyhow!("right() requires 2 arguments"));
1526 }
1527 match (&args[0], &args[1]) {
1528 (Value::String(s), n) if n.is_number() => {
1529 let len = n.as_i64().unwrap_or(0) as usize;
1530 let chars: Vec<char> = s.chars().collect();
1531 let start = chars.len().saturating_sub(len);
1532 Ok(Value::String(chars[start..].iter().collect()))
1533 }
1534 (Value::Null, _) => Ok(Value::Null),
1535 _ => Err(anyhow!("right() expects a string and integer")),
1536 }
1537}
1538
1539fn eval_pad(func_name: &str, args: &[Value], pad_left: bool) -> Result<Value> {
1541 if args.len() < 2 || args.len() > 3 {
1542 return Err(anyhow!("{}() requires 2 or 3 arguments", func_name));
1543 }
1544 let s = match &args[0] {
1545 Value::String(s) => s,
1546 Value::Null => return Ok(Value::Null),
1547 _ => {
1548 return Err(anyhow!(
1549 "{}() expects a string as first argument",
1550 func_name
1551 ));
1552 }
1553 };
1554 let len = match &args[1] {
1555 Value::Int(n) => *n as usize,
1556 Value::Float(f) => *f as i64 as usize,
1557 Value::Null => return Ok(Value::Null),
1558 _ => {
1559 return Err(anyhow!(
1560 "{}() expects an integer as second argument",
1561 func_name
1562 ));
1563 }
1564 };
1565 if len > 1_000_000 {
1566 return Err(anyhow!(
1567 "{}() length exceeds maximum limit of 1,000,000",
1568 func_name
1569 ));
1570 }
1571 let pad_str = if args.len() == 3 {
1572 match &args[2] {
1573 Value::String(p) => p.as_str(),
1574 Value::Null => return Ok(Value::Null),
1575 _ => {
1576 return Err(anyhow!(
1577 "{}() expects a string as third argument",
1578 func_name
1579 ));
1580 }
1581 }
1582 } else {
1583 " "
1584 };
1585
1586 let s_chars: Vec<char> = s.chars().collect();
1587 if s_chars.len() >= len {
1588 return Ok(Value::String(s_chars.into_iter().take(len).collect()));
1589 }
1590
1591 let pad_chars: Vec<char> = pad_str.chars().collect();
1592 if pad_chars.is_empty() {
1593 return Ok(Value::String(s.clone()));
1594 }
1595
1596 let needed = len - s_chars.len();
1597 let full_pads = needed / pad_chars.len();
1598 let partial_pad = needed % pad_chars.len();
1599
1600 let mut padding = String::with_capacity(needed);
1601 for _ in 0..full_pads {
1602 padding.push_str(pad_str);
1603 }
1604 padding.extend(pad_chars.into_iter().take(partial_pad));
1605
1606 let result = if pad_left {
1607 format!("{}{}", padding, s)
1608 } else {
1609 format!("{}{}", s, padding)
1610 };
1611 Ok(Value::String(result))
1612}
1613
1614fn eval_lpad(args: &[Value]) -> Result<Value> {
1615 eval_pad("lpad", args, true)
1616}
1617
1618fn eval_rpad(args: &[Value]) -> Result<Value> {
1619 eval_pad("rpad", args, false)
1620}
1621
1622fn eval_string_function(name: &str, args: &[Value]) -> Result<Value> {
1624 match name {
1625 "TOUPPER" | "UPPER" => eval_toupper(args),
1626 "TOLOWER" | "LOWER" => eval_tolower(args),
1627 "TRIM" => eval_trim(args),
1628 "LTRIM" => eval_ltrim(args),
1629 "RTRIM" => eval_rtrim(args),
1630 "REVERSE" => eval_reverse(args),
1631 "REPLACE" => eval_replace(args),
1632 "SPLIT" => eval_split(args),
1633 "SUBSTRING" => eval_substring(args),
1634 "LEFT" => eval_left(args),
1635 "RIGHT" => eval_right(args),
1636 "LPAD" => eval_lpad(args),
1637 "RPAD" => eval_rpad(args),
1638 _ => Err(anyhow!("Unknown string function: {}", name)),
1639 }
1640}
1641
1642fn eval_range_function(args: &[Value]) -> Result<Value> {
1644 if args.len() < 2 || args.len() > 3 {
1645 return Err(anyhow!("range() requires 2 or 3 arguments"));
1646 }
1647 let start = args[0]
1648 .as_i64()
1649 .ok_or_else(|| anyhow!("range() start must be an integer"))?;
1650 let end = args[1]
1651 .as_i64()
1652 .ok_or_else(|| anyhow!("range() end must be an integer"))?;
1653 let step = if args.len() == 3 {
1654 args[2]
1655 .as_i64()
1656 .ok_or_else(|| anyhow!("range() step must be an integer"))?
1657 } else {
1658 1
1659 };
1660 if step == 0 {
1661 return Err(anyhow!("range() step cannot be zero"));
1662 }
1663 let mut result = Vec::new();
1664 let mut i = start;
1665 if step > 0 {
1666 while i <= end {
1667 result.push(Value::Int(i));
1668 i += step;
1669 }
1670 } else {
1671 while i >= end {
1672 result.push(Value::Int(i));
1673 i += step;
1674 }
1675 }
1676 Ok(Value::List(result))
1677}
1678
1679pub fn eval_scalar_function(
1684 name: &str,
1685 args: &[Value],
1686 custom_fns: Option<&super::executor::custom_functions::CustomFunctionRegistry>,
1687) -> Result<Value> {
1688 let name_upper = name.to_uppercase();
1689
1690 match name_upper.as_str() {
1691 "COALESCE" => {
1693 for arg in args {
1694 if !arg.is_null() {
1695 return Ok(arg.clone());
1696 }
1697 }
1698 Ok(Value::Null)
1699 }
1700 "NULLIF" => {
1701 if args.len() != 2 {
1702 return Err(anyhow!("NULLIF requires 2 arguments"));
1703 }
1704 if args[0] == args[1] {
1705 Ok(Value::Null)
1706 } else {
1707 Ok(args[0].clone())
1708 }
1709 }
1710
1711 "SIZE" | "KEYS" | "HEAD" | "TAIL" | "LAST" | "LENGTH" | "NODES" | "RELATIONSHIPS" => {
1713 eval_list_function(&name_upper, args)
1714 }
1715
1716 "TOINTEGER" | "TOINT" | "TOFLOAT" | "TOSTRING" | "TOBOOLEAN" | "TOBOOL" => {
1718 eval_type_function(&name_upper, args)
1719 }
1720
1721 "ABS" | "CEIL" | "FLOOR" | "ROUND" | "SQRT" | "SIGN" | "LOG" | "LOG10" | "EXP"
1723 | "POWER" | "POW" | "SIN" | "COS" | "TAN" | "ASIN" | "ACOS" | "ATAN" | "ATAN2"
1724 | "DEGREES" | "RADIANS" | "HAVERSIN" | "PI" | "E" | "RAND" => {
1725 eval_math_function(&name_upper, args)
1726 }
1727
1728 "TOUPPER" | "UPPER" | "TOLOWER" | "LOWER" | "TRIM" | "LTRIM" | "RTRIM" | "REVERSE"
1730 | "REPLACE" | "SPLIT" | "SUBSTRING" | "LEFT" | "RIGHT" | "LPAD" | "RPAD" => {
1731 eval_string_function(&name_upper, args)
1732 }
1733
1734 "DATE"
1736 | "TIME"
1737 | "DATETIME"
1738 | "LOCALDATETIME"
1739 | "LOCALTIME"
1740 | "DURATION"
1741 | "YEAR"
1742 | "MONTH"
1743 | "DAY"
1744 | "HOUR"
1745 | "MINUTE"
1746 | "SECOND"
1747 | "DATETIME.FROMEPOCH"
1748 | "DATETIME.FROMEPOCHMILLIS"
1749 | "DATE.TRUNCATE"
1750 | "TIME.TRUNCATE"
1751 | "DATETIME.TRUNCATE"
1752 | "LOCALDATETIME.TRUNCATE"
1753 | "LOCALTIME.TRUNCATE"
1754 | "DATETIME.TRANSACTION"
1755 | "DATETIME.STATEMENT"
1756 | "DATETIME.REALTIME"
1757 | "DATE.TRANSACTION"
1758 | "DATE.STATEMENT"
1759 | "DATE.REALTIME"
1760 | "TIME.TRANSACTION"
1761 | "TIME.STATEMENT"
1762 | "TIME.REALTIME"
1763 | "LOCALTIME.TRANSACTION"
1764 | "LOCALTIME.STATEMENT"
1765 | "LOCALTIME.REALTIME"
1766 | "LOCALDATETIME.TRANSACTION"
1767 | "LOCALDATETIME.STATEMENT"
1768 | "LOCALDATETIME.REALTIME"
1769 | "DURATION.BETWEEN"
1770 | "DURATION.INMONTHS"
1771 | "DURATION.INDAYS"
1772 | "DURATION.INSECONDS" => eval_datetime_function(&name_upper, args),
1773
1774 "POINT" | "DISTANCE" | "POINT.WITHINBBOX" => eval_spatial_function(&name_upper, args),
1776
1777 "RANGE" => eval_range_function(args),
1778
1779 "UNI.TEMPORAL.VALIDAT" => eval_valid_at(args),
1780
1781 "VECTOR_DISTANCE" => {
1782 if args.len() < 2 || args.len() > 3 {
1783 return Err(anyhow!("vector_distance requires 2 or 3 arguments"));
1784 }
1785 let metric = if args.len() == 3 {
1786 args[2].as_str().ok_or(anyhow!("metric must be string"))?
1787 } else {
1788 "cosine"
1789 };
1790 eval_vector_distance(&args[0], &args[1], metric)
1791 }
1792
1793 "UNI_BITWISE_OR"
1795 | "UNI_BITWISE_AND"
1796 | "UNI_BITWISE_XOR"
1797 | "UNI_BITWISE_NOT"
1798 | "UNI_BITWISE_SHIFTLEFT"
1799 | "UNI_BITWISE_SHIFTRIGHT" => eval_bitwise_function(&name_upper, args),
1800
1801 "SIMILAR_TO" => {
1804 if args.len() < 2 {
1805 return Err(anyhow!("similar_to requires at least 2 arguments"));
1806 }
1807 crate::query::similar_to::eval_similar_to_pure(&args[0], &args[1])
1808 }
1809
1810 "VECTOR_SIMILARITY" => {
1811 if args.len() != 2 {
1812 return Err(anyhow!("vector_similarity takes 2 arguments"));
1813 }
1814 eval_vector_similarity(&args[0], &args[1])
1815 }
1816
1817 _ => {
1818 if let Some(func) = custom_fns.and_then(|r| r.get(name)) {
1820 return func(args).map_err(|e| anyhow!("{}", e));
1821 }
1822 Err(anyhow!("Function {} not implemented or is aggregate", name))
1823 }
1824 }
1825}
1826
1827fn eval_valid_at(args: &[Value]) -> Result<Value> {
1835 if args.len() != 4 {
1836 return Err(anyhow!(
1837 "validAt requires 4 arguments: node, start_prop, end_prop, time"
1838 ));
1839 }
1840
1841 let node_map = match &args[0] {
1842 Value::Map(map) => map,
1843 Value::Null => return Ok(Value::Bool(false)),
1844 _ => {
1845 return Err(anyhow!(
1846 "validAt expects a Node or Edge (Object) as first argument"
1847 ));
1848 }
1849 };
1850
1851 let start_prop = args[1]
1852 .as_str()
1853 .ok_or_else(|| anyhow!("start_prop must be a string"))?;
1854 let end_prop = args[2]
1855 .as_str()
1856 .ok_or_else(|| anyhow!("end_prop must be a string"))?;
1857
1858 let time_str = match &args[3] {
1859 Value::String(s) => s,
1860 _ => return Err(anyhow!("time argument must be a datetime string")),
1861 };
1862
1863 let query_time = parse_datetime_utc(time_str)
1864 .map_err(|_| anyhow!("Invalid query time format: {}", time_str))?;
1865
1866 let valid_from_val = node_map.get(start_prop);
1867 let valid_from = match valid_from_val {
1868 Some(Value::String(s)) => parse_datetime_utc(s)
1869 .map_err(|_| anyhow!("Invalid datetime in property {}: {}", start_prop, s))?,
1870 Some(Value::Null) | None => return Ok(Value::Bool(false)),
1871 _ => return Err(anyhow!("Property {} must be a datetime string", start_prop)),
1872 };
1873
1874 let valid_to_val = node_map.get(end_prop);
1875 let valid_to = match valid_to_val {
1876 Some(Value::String(s)) => Some(
1877 parse_datetime_utc(s)
1878 .map_err(|_| anyhow!("Invalid datetime in property {}: {}", end_prop, s))?,
1879 ),
1880 Some(Value::Null) | None => None,
1881 _ => {
1882 return Err(anyhow!(
1883 "Property {} must be a datetime string or null",
1884 end_prop
1885 ));
1886 }
1887 };
1888
1889 let is_valid = valid_from <= query_time && valid_to.map(|vt| query_time < vt).unwrap_or(true);
1891
1892 Ok(Value::Bool(is_valid))
1893}
1894
1895pub fn eval_vector_similarity(v1: &Value, v2: &Value) -> Result<Value> {
1897 let (arr1, arr2) = match (v1, v2) {
1898 (Value::List(a1), Value::List(a2)) => (a1, a2),
1899 _ => return Err(anyhow!("vector_similarity arguments must be arrays")),
1900 };
1901
1902 if arr1.len() != arr2.len() {
1903 return Err(anyhow!(
1904 "Vector dimensions mismatch: {} vs {}",
1905 arr1.len(),
1906 arr2.len()
1907 ));
1908 }
1909
1910 let mut dot = 0.0;
1911 let mut norm1_sq = 0.0;
1912 let mut norm2_sq = 0.0;
1913
1914 for (v1_elem, v2_elem) in arr1.iter().zip(arr2.iter()) {
1915 let f1 = v1_elem
1916 .as_f64()
1917 .ok_or_else(|| anyhow!("Vector element not a number"))?;
1918 let f2 = v2_elem
1919 .as_f64()
1920 .ok_or_else(|| anyhow!("Vector element not a number"))?;
1921 dot += f1 * f2;
1922 norm1_sq += f1 * f1;
1923 norm2_sq += f2 * f2;
1924 }
1925
1926 let mag1 = norm1_sq.sqrt();
1927 let mag2 = norm2_sq.sqrt();
1928
1929 let sim = if mag1 == 0.0 || mag2 == 0.0 {
1930 0.0
1931 } else {
1932 dot / (mag1 * mag2)
1933 };
1934
1935 Ok(Value::Float(sim))
1936}
1937
1938pub fn eval_vector_distance(v1: &Value, v2: &Value, metric: &str) -> Result<Value> {
1940 let (arr1, arr2) = match (v1, v2) {
1941 (Value::List(a1), Value::List(a2)) => (a1, a2),
1942 _ => return Err(anyhow!("vector_distance arguments must be arrays")),
1943 };
1944
1945 if arr1.len() != arr2.len() {
1946 return Err(anyhow!(
1947 "Vector dimensions mismatch: {} vs {}",
1948 arr1.len(),
1949 arr2.len()
1950 ));
1951 }
1952
1953 let iter1 = arr1
1955 .iter()
1956 .map(|v| v.as_f64().ok_or(anyhow!("Vector element not a number")));
1957 let iter2 = arr2
1958 .iter()
1959 .map(|v| v.as_f64().ok_or(anyhow!("Vector element not a number")));
1960
1961 match metric.to_lowercase().as_str() {
1962 "cosine" => {
1963 let mut dot = 0.0;
1965 let mut norm1_sq = 0.0;
1966 let mut norm2_sq = 0.0;
1967
1968 for (r1, r2) in iter1.zip(iter2) {
1969 let f1 = r1?;
1970 let f2 = r2?;
1971 dot += f1 * f2;
1972 norm1_sq += f1 * f1;
1973 norm2_sq += f2 * f2;
1974 }
1975
1976 let mag1 = norm1_sq.sqrt();
1977 let mag2 = norm2_sq.sqrt();
1978
1979 if mag1 == 0.0 || mag2 == 0.0 {
1980 Ok(Value::Float(1.0))
1981 } else {
1982 let sim = dot / (mag1 * mag2);
1983 let sim = sim.clamp(-1.0, 1.0);
1985 Ok(Value::Float(1.0 - sim))
1986 }
1987 }
1988 "euclidean" | "l2" => {
1989 let mut sum_sq_diff = 0.0;
1990 for (r1, r2) in iter1.zip(iter2) {
1991 let f1 = r1?;
1992 let f2 = r2?;
1993 let diff = f1 - f2;
1994 sum_sq_diff += diff * diff;
1995 }
1996 Ok(Value::Float(sum_sq_diff.sqrt()))
1997 }
1998 "dot" | "inner_product" => {
1999 let mut dot = 0.0;
2000 for (r1, r2) in iter1.zip(iter2) {
2001 let f1 = r1?;
2002 let f2 = r2?;
2003 dot += f1 * f2;
2004 }
2005 Ok(Value::Float(1.0 - dot))
2006 }
2007 _ => Err(anyhow!("Unknown metric: {}", metric)),
2008 }
2009}
2010
2011pub fn is_scalar_function(name: &str) -> bool {
2013 let name_upper = name.to_uppercase();
2014 matches!(
2015 name_upper.as_str(),
2016 "COALESCE"
2017 | "NULLIF"
2018 | "SIZE"
2019 | "KEYS"
2020 | "HEAD"
2021 | "TAIL"
2022 | "LAST"
2023 | "LENGTH"
2024 | "NODES"
2025 | "RELATIONSHIPS"
2026 | "TOINTEGER"
2027 | "TOINT"
2028 | "TOFLOAT"
2029 | "TOSTRING"
2030 | "TOBOOLEAN"
2031 | "TOBOOL"
2032 | "ABS"
2033 | "CEIL"
2034 | "FLOOR"
2035 | "ROUND"
2036 | "SQRT"
2037 | "SIGN"
2038 | "LOG"
2039 | "LOG10"
2040 | "EXP"
2041 | "POWER"
2042 | "POW"
2043 | "SIN"
2044 | "COS"
2045 | "TAN"
2046 | "ASIN"
2047 | "ACOS"
2048 | "ATAN"
2049 | "ATAN2"
2050 | "DEGREES"
2051 | "RADIANS"
2052 | "HAVERSIN"
2053 | "PI"
2054 | "E"
2055 | "RAND"
2056 | "TOUPPER"
2057 | "UPPER"
2058 | "TOLOWER"
2059 | "LOWER"
2060 | "TRIM"
2061 | "LTRIM"
2062 | "RTRIM"
2063 | "REVERSE"
2064 | "REPLACE"
2065 | "SPLIT"
2066 | "SUBSTRING"
2067 | "LEFT"
2068 | "RIGHT"
2069 | "LPAD"
2070 | "RPAD"
2071 | "RANGE"
2072 | "UNI.VALIDAT"
2073 | "VALIDAT"
2074 | "SIMILAR_TO"
2075 | "VECTOR_SIMILARITY"
2076 | "VECTOR_DISTANCE"
2077 | "DATE"
2078 | "TIME"
2079 | "DATETIME"
2080 | "DURATION"
2081 | "YEAR"
2082 | "MONTH"
2083 | "DAY"
2084 | "HOUR"
2085 | "MINUTE"
2086 | "SECOND"
2087 | "ID"
2088 | "ELEMENTID"
2089 | "TYPE"
2090 | "LABELS"
2091 | "PROPERTIES"
2092 | "STARTNODE"
2093 | "ENDNODE"
2094 | "ANY"
2095 | "ALL"
2096 | "NONE"
2097 | "SINGLE"
2098 )
2099}
2100
2101fn eval_bitwise_function(name: &str, args: &[Value]) -> Result<Value> {
2103 let require_int = |v: &Value, fname: &str| -> Result<i64> {
2104 v.as_i64()
2105 .ok_or_else(|| anyhow!("{} requires integer arguments", fname))
2106 };
2107
2108 let bitwise_binary = |fname: &str, op: fn(i64, i64) -> i64| -> Result<Value> {
2109 if args.len() != 2 {
2110 return Err(anyhow!("{} requires exactly 2 arguments", fname));
2111 }
2112 let l = require_int(&args[0], fname)?;
2113 let r = require_int(&args[1], fname)?;
2114 Ok(Value::Int(op(l, r)))
2115 };
2116
2117 match name {
2118 "UNI_BITWISE_OR" => bitwise_binary("uni_bitwise_or", |l, r| l | r),
2119 "UNI_BITWISE_AND" => bitwise_binary("uni_bitwise_and", |l, r| l & r),
2120 "UNI_BITWISE_XOR" => bitwise_binary("uni_bitwise_xor", |l, r| l ^ r),
2121 "UNI_BITWISE_SHIFTLEFT" => bitwise_binary("uni_bitwise_shiftLeft", |l, r| l << r),
2122 "UNI_BITWISE_SHIFTRIGHT" => bitwise_binary("uni_bitwise_shiftRight", |l, r| l >> r),
2123 "UNI_BITWISE_NOT" => {
2124 if args.len() != 1 {
2125 return Err(anyhow!("uni_bitwise_not requires exactly 1 argument"));
2126 }
2127 Ok(Value::Int(!require_int(&args[0], "uni_bitwise_not")?))
2128 }
2129 _ => Err(anyhow!("Unknown bitwise function: {}", name)),
2130 }
2131}
2132
2133#[cfg(test)]
2134mod tests {
2135 use super::*;
2136 fn s(v: &str) -> Value {
2138 Value::String(v.into())
2139 }
2140 fn i(v: i64) -> Value {
2142 Value::Int(v)
2143 }
2144
2145 #[test]
2146 fn test_binary_op_eq() {
2147 assert_eq!(
2148 eval_binary_op(&i(1), &BinaryOp::Eq, &i(1)).unwrap(),
2149 Value::Bool(true)
2150 );
2151 assert_eq!(
2152 eval_binary_op(&i(1), &BinaryOp::Eq, &i(2)).unwrap(),
2153 Value::Bool(false)
2154 );
2155 }
2156
2157 #[test]
2158 fn test_binary_op_comparison() {
2159 assert_eq!(
2160 eval_binary_op(&i(5), &BinaryOp::Gt, &i(3)).unwrap(),
2161 Value::Bool(true)
2162 );
2163 assert_eq!(
2164 eval_binary_op(&i(5), &BinaryOp::Lt, &i(3)).unwrap(),
2165 Value::Bool(false)
2166 );
2167 }
2168
2169 #[test]
2170 fn test_binary_op_xor() {
2171 assert_eq!(
2173 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2174 Value::Bool(false)
2175 );
2176 assert_eq!(
2178 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2179 Value::Bool(true)
2180 );
2181 assert_eq!(
2183 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2184 Value::Bool(true)
2185 );
2186 assert_eq!(
2188 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2189 Value::Bool(false)
2190 );
2191 }
2192
2193 #[test]
2194 fn test_binary_op_contains() {
2195 assert_eq!(
2196 eval_binary_op(&s("hello world"), &BinaryOp::Contains, &s("world")).unwrap(),
2197 Value::Bool(true)
2198 );
2199 }
2200
2201 #[test]
2202 fn test_scalar_function_size() {
2203 assert_eq!(
2204 eval_scalar_function("SIZE", &[Value::List(vec![i(1), i(2), i(3)])], None).unwrap(),
2205 Value::Int(3)
2206 );
2207 }
2208
2209 #[test]
2210 fn test_scalar_function_head() {
2211 assert_eq!(
2212 eval_scalar_function("HEAD", &[Value::List(vec![i(1), i(2), i(3)])], None).unwrap(),
2213 Value::Int(1)
2214 );
2215 }
2216
2217 #[test]
2218 fn test_scalar_function_coalesce() {
2219 assert_eq!(
2220 eval_scalar_function(
2221 "COALESCE",
2222 &[Value::Null, Value::Int(1), Value::Int(2)],
2223 None
2224 )
2225 .unwrap(),
2226 Value::Int(1)
2227 );
2228 }
2229
2230 #[test]
2231 fn test_vector_similarity() {
2232 let v1 = Value::List(vec![Value::Float(1.0), Value::Float(0.0)]);
2233 let v2 = Value::List(vec![Value::Float(1.0), Value::Float(0.0)]);
2234 let result = eval_vector_similarity(&v1, &v2).unwrap();
2235 assert_eq!(result.as_f64().unwrap(), 1.0);
2236 }
2237
2238 #[test]
2239 fn test_regex_match() {
2240 assert_eq!(
2242 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("hello.*")).unwrap(),
2243 Value::Bool(true)
2244 );
2245
2246 assert_eq!(
2248 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^world")).unwrap(),
2249 Value::Bool(false)
2250 );
2251
2252 assert_eq!(
2254 eval_binary_op(&s("Hello"), &BinaryOp::Regex, &s("hello")).unwrap(),
2255 Value::Bool(false)
2256 );
2257
2258 assert_eq!(
2260 eval_binary_op(&s("Hello"), &BinaryOp::Regex, &s("(?i)hello")).unwrap(),
2261 Value::Bool(true)
2262 );
2263 }
2264
2265 #[test]
2266 fn test_regex_null_handling() {
2267 assert_eq!(
2269 eval_binary_op(&Value::Null, &BinaryOp::Regex, &s(".*")).unwrap(),
2270 Value::Null
2271 );
2272
2273 assert_eq!(
2275 eval_binary_op(&s("hello"), &BinaryOp::Regex, &Value::Null).unwrap(),
2276 Value::Null
2277 );
2278 }
2279
2280 #[test]
2281 fn test_regex_invalid_pattern() {
2282 let result = eval_binary_op(&s("hello"), &BinaryOp::Regex, &s("[invalid"));
2284 assert!(result.is_err());
2285 assert!(result.unwrap_err().to_string().contains("Invalid regex"));
2286 }
2287
2288 #[test]
2289 fn test_regex_special_characters() {
2290 assert_eq!(
2292 eval_binary_op(
2293 &s("test@example.com"),
2294 &BinaryOp::Regex,
2295 &s(r"^[\w.-]+@[\w.-]+\.\w+$")
2296 )
2297 .unwrap(),
2298 Value::Bool(true)
2299 );
2300
2301 assert_eq!(
2303 eval_binary_op(
2304 &s("123-456-7890"),
2305 &BinaryOp::Regex,
2306 &s(r"^\d{3}-\d{3}-\d{4}$")
2307 )
2308 .unwrap(),
2309 Value::Bool(true)
2310 );
2311
2312 assert_eq!(
2314 eval_binary_op(
2315 &s("1234567890"),
2316 &BinaryOp::Regex,
2317 &s(r"^\d{3}-\d{3}-\d{4}$")
2318 )
2319 .unwrap(),
2320 Value::Bool(false)
2321 );
2322 }
2323
2324 #[test]
2325 fn test_regex_anchors() {
2326 assert_eq!(
2328 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^hello")).unwrap(),
2329 Value::Bool(true)
2330 );
2331 assert_eq!(
2332 eval_binary_op(&s("say hello"), &BinaryOp::Regex, &s("^hello")).unwrap(),
2333 Value::Bool(false)
2334 );
2335
2336 assert_eq!(
2338 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("world$")).unwrap(),
2339 Value::Bool(true)
2340 );
2341 assert_eq!(
2342 eval_binary_op(&s("world hello"), &BinaryOp::Regex, &s("world$")).unwrap(),
2343 Value::Bool(false)
2344 );
2345
2346 assert_eq!(
2348 eval_binary_op(&s("hello"), &BinaryOp::Regex, &s("^hello$")).unwrap(),
2349 Value::Bool(true)
2350 );
2351 assert_eq!(
2352 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^hello$")).unwrap(),
2353 Value::Bool(false)
2354 );
2355 }
2356
2357 #[test]
2358 fn test_temporal_arithmetic() {
2359 let dt = s("2024-01-15T10:00:00Z");
2361 let dur = Value::Int(3_600_000_000_i64);
2362 let result = eval_binary_op(&dt, &BinaryOp::Add, &dur).unwrap();
2363 assert!(result.to_string().contains("11:00"));
2364
2365 let d = s("2024-01-01");
2367 let dur_day = Value::Int(86_400_000_000_i64);
2368 let result = eval_binary_op(&d, &BinaryOp::Add, &dur_day).unwrap();
2369 assert_eq!(result.to_string(), "2024-01-02");
2370
2371 let dt1 = s("2024-01-02T00:00:00Z");
2373 let dt2 = s("2024-01-01T00:00:00Z");
2374 let result = eval_binary_op(&dt1, &BinaryOp::Sub, &dt2).unwrap();
2375 let dur_str = result.to_string();
2377 assert!(dur_str.starts_with('P'));
2378 assert!(dur_str.contains("24H")); }
2380
2381 #[test]
2385 fn test_temporal_arithmetic_edge_cases() {
2386 let dt = s("2024-01-15T10:00:00Z");
2388 let neg_dur = Value::Int(-3_600_000_000_i64); let result = eval_binary_op(&dt, &BinaryOp::Add, &neg_dur).unwrap();
2390 assert!(result.to_string().contains("09:00"));
2391
2392 let dur1 = s("PT1H"); let dur2 = s("PT2H"); let result = eval_binary_op(&dur1, &BinaryOp::Sub, &dur2).unwrap();
2396 let dur_str = result.to_string();
2398 assert!(dur_str.starts_with('P') || dur_str.starts_with("-P"));
2399
2400 let dt = s("2024-01-15T10:00:00Z");
2402 let zero_dur = Value::Int(0_i64);
2403 let result = eval_binary_op(&dt, &BinaryOp::Add, &zero_dur).unwrap();
2404 assert!(result.to_string().contains("10:00"));
2405
2406 let d = s("2023-12-31");
2408 let one_day = Value::Int(86_400_000_000_i64);
2409 let result = eval_binary_op(&d, &BinaryOp::Add, &one_day).unwrap();
2410 assert_eq!(result.to_string(), "2024-01-01");
2411
2412 let dt1 = s("2024-01-15T10:00:00Z");
2414 let dt2 = s("2024-01-15T10:00:00Z");
2415 let result = eval_binary_op(&dt1, &BinaryOp::Sub, &dt2).unwrap();
2416 let dur_str = result.to_string();
2418 assert!(dur_str.starts_with('P'));
2419
2420 let leap_day = s("2024-02-28");
2422 let one_day = Value::Int(86_400_000_000_i64);
2423 let result = eval_binary_op(&leap_day, &BinaryOp::Add, &one_day).unwrap();
2424 assert_eq!(result.to_string(), "2024-02-29");
2425 }
2426
2427 #[test]
2428 fn test_regex_empty_string() {
2429 assert_eq!(
2431 eval_binary_op(&s(""), &BinaryOp::Regex, &s("^$")).unwrap(),
2432 Value::Bool(true)
2433 );
2434
2435 assert_eq!(
2437 eval_binary_op(&s(""), &BinaryOp::Regex, &s(".+")).unwrap(),
2438 Value::Bool(false)
2439 );
2440
2441 assert_eq!(
2443 eval_binary_op(&s("hello"), &BinaryOp::Regex, &s(".*")).unwrap(),
2444 Value::Bool(true)
2445 );
2446 }
2447
2448 #[test]
2449 fn test_regex_type_errors() {
2450 let result = eval_binary_op(&Value::Int(123), &BinaryOp::Regex, &s("\\d+"));
2452 assert!(result.is_err());
2453 assert!(result.unwrap_err().to_string().contains("must be a string"));
2454
2455 let result = eval_binary_op(&s("hello"), &BinaryOp::Regex, &Value::Int(123));
2457 assert!(result.is_err());
2458 assert!(result.unwrap_err().to_string().contains("pattern string"));
2459 }
2460
2461 #[test]
2462 fn test_and_null_handling() {
2463 assert_eq!(
2467 eval_binary_op(&Value::Bool(false), &BinaryOp::And, &Value::Null).unwrap(),
2468 Value::Bool(false)
2469 );
2470 assert_eq!(
2471 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Bool(false)).unwrap(),
2472 Value::Bool(false)
2473 );
2474
2475 assert_eq!(
2477 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Null).unwrap(),
2478 Value::Null
2479 );
2480 assert_eq!(
2481 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Bool(true)).unwrap(),
2482 Value::Null
2483 );
2484
2485 assert_eq!(
2487 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Null).unwrap(),
2488 Value::Null
2489 );
2490
2491 assert_eq!(
2493 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Bool(true)).unwrap(),
2494 Value::Bool(true)
2495 );
2496 assert_eq!(
2497 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Bool(false)).unwrap(),
2498 Value::Bool(false)
2499 );
2500 }
2501
2502 #[test]
2503 fn test_or_null_handling() {
2504 assert_eq!(
2508 eval_binary_op(&Value::Bool(true), &BinaryOp::Or, &Value::Null).unwrap(),
2509 Value::Bool(true)
2510 );
2511 assert_eq!(
2512 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Bool(true)).unwrap(),
2513 Value::Bool(true)
2514 );
2515
2516 assert_eq!(
2518 eval_binary_op(&Value::Bool(false), &BinaryOp::Or, &Value::Null).unwrap(),
2519 Value::Null
2520 );
2521 assert_eq!(
2522 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2523 Value::Null
2524 );
2525
2526 assert_eq!(
2528 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Null).unwrap(),
2529 Value::Null
2530 );
2531
2532 assert_eq!(
2534 eval_binary_op(&Value::Bool(false), &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2535 Value::Bool(false)
2536 );
2537 assert_eq!(
2538 eval_binary_op(&Value::Bool(true), &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2539 Value::Bool(true)
2540 );
2541 }
2542
2543 #[test]
2544 fn test_nan_comparison_with_non_numeric() {
2545 let nan = Value::Float(f64::NAN);
2546
2547 assert_eq!(
2549 eval_binary_op(&nan, &BinaryOp::Gt, &i(1)).unwrap(),
2550 Value::Bool(false)
2551 );
2552
2553 assert_eq!(
2555 eval_binary_op(&nan, &BinaryOp::Gt, &nan).unwrap(),
2556 Value::Bool(false)
2557 );
2558
2559 assert_eq!(
2561 eval_binary_op(&nan, &BinaryOp::Gt, &s("a")).unwrap(),
2562 Value::Null
2563 );
2564
2565 assert_eq!(
2567 eval_binary_op(&s("a"), &BinaryOp::Lt, &nan).unwrap(),
2568 Value::Null
2569 );
2570 }
2571
2572 #[test]
2573 fn test_nan_equality_with_non_numeric() {
2574 let nan = Value::Float(f64::NAN);
2575
2576 assert_eq!(
2578 eval_binary_op(&nan, &BinaryOp::Eq, &nan).unwrap(),
2579 Value::Bool(false)
2580 );
2581
2582 assert_eq!(
2584 eval_binary_op(&nan, &BinaryOp::NotEq, &nan).unwrap(),
2585 Value::Bool(true)
2586 );
2587
2588 assert_eq!(
2590 eval_binary_op(&nan, &BinaryOp::Eq, &s("a")).unwrap(),
2591 Value::Bool(false)
2592 );
2593
2594 assert_eq!(
2596 eval_binary_op(&nan, &BinaryOp::NotEq, &s("a")).unwrap(),
2597 Value::Bool(true)
2598 );
2599 }
2600
2601 #[test]
2602 fn test_large_integer_equality() {
2603 let a = Value::Int(4611686018427387905_i64);
2605 let b = Value::Int(4611686018427387900_i64);
2606
2607 assert_eq!(
2608 eval_binary_op(&a, &BinaryOp::Eq, &b).unwrap(),
2609 Value::Bool(false)
2610 );
2611 assert_eq!(
2612 eval_binary_op(&a, &BinaryOp::Eq, &a).unwrap(),
2613 Value::Bool(true)
2614 );
2615 }
2616
2617 #[test]
2618 fn test_large_integer_ordering() {
2619 let a = Value::Int(4611686018427387905_i64);
2620 let b = Value::Int(4611686018427387900_i64);
2621
2622 assert_eq!(
2623 eval_binary_op(&a, &BinaryOp::Gt, &b).unwrap(),
2624 Value::Bool(true)
2625 );
2626 assert_eq!(
2627 eval_binary_op(&b, &BinaryOp::Lt, &a).unwrap(),
2628 Value::Bool(true)
2629 );
2630 }
2631
2632 #[test]
2633 fn test_int_float_equality_still_works() {
2634 assert_eq!(
2636 eval_binary_op(&i(1), &BinaryOp::Eq, &Value::Float(1.0)).unwrap(),
2637 Value::Bool(true)
2638 );
2639 assert_eq!(
2640 eval_binary_op(&i(1), &BinaryOp::NotEq, &Value::Float(1.0)).unwrap(),
2641 Value::Bool(false)
2642 );
2643 }
2644
2645 #[test]
2646 fn test_xor_null_handling() {
2647 assert_eq!(
2650 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Null).unwrap(),
2651 Value::Null
2652 );
2653 assert_eq!(
2654 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Null).unwrap(),
2655 Value::Null
2656 );
2657 assert_eq!(
2658 eval_binary_op(&Value::Null, &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2659 Value::Null
2660 );
2661 assert_eq!(
2662 eval_binary_op(&Value::Null, &BinaryOp::Xor, &Value::Null).unwrap(),
2663 Value::Null
2664 );
2665
2666 assert_eq!(
2668 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2669 Value::Bool(true)
2670 );
2671 assert_eq!(
2672 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2673 Value::Bool(false)
2674 );
2675 }
2676}