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(name: &str, args: &[Value]) -> Result<Value> {
1684 let name_upper = name.to_uppercase();
1685
1686 match name_upper.as_str() {
1687 "COALESCE" => {
1689 for arg in args {
1690 if !arg.is_null() {
1691 return Ok(arg.clone());
1692 }
1693 }
1694 Ok(Value::Null)
1695 }
1696 "NULLIF" => {
1697 if args.len() != 2 {
1698 return Err(anyhow!("NULLIF requires 2 arguments"));
1699 }
1700 if args[0] == args[1] {
1701 Ok(Value::Null)
1702 } else {
1703 Ok(args[0].clone())
1704 }
1705 }
1706
1707 "SIZE" | "KEYS" | "HEAD" | "TAIL" | "LAST" | "LENGTH" | "NODES" | "RELATIONSHIPS" => {
1709 eval_list_function(&name_upper, args)
1710 }
1711
1712 "TOINTEGER" | "TOINT" | "TOFLOAT" | "TOSTRING" | "TOBOOLEAN" | "TOBOOL" => {
1714 eval_type_function(&name_upper, args)
1715 }
1716
1717 "ABS" | "CEIL" | "FLOOR" | "ROUND" | "SQRT" | "SIGN" | "LOG" | "LOG10" | "EXP"
1719 | "POWER" | "POW" | "SIN" | "COS" | "TAN" | "ASIN" | "ACOS" | "ATAN" | "ATAN2"
1720 | "DEGREES" | "RADIANS" | "HAVERSIN" | "PI" | "E" | "RAND" => {
1721 eval_math_function(&name_upper, args)
1722 }
1723
1724 "TOUPPER" | "UPPER" | "TOLOWER" | "LOWER" | "TRIM" | "LTRIM" | "RTRIM" | "REVERSE"
1726 | "REPLACE" | "SPLIT" | "SUBSTRING" | "LEFT" | "RIGHT" | "LPAD" | "RPAD" => {
1727 eval_string_function(&name_upper, args)
1728 }
1729
1730 "DATE"
1732 | "TIME"
1733 | "DATETIME"
1734 | "LOCALDATETIME"
1735 | "LOCALTIME"
1736 | "DURATION"
1737 | "YEAR"
1738 | "MONTH"
1739 | "DAY"
1740 | "HOUR"
1741 | "MINUTE"
1742 | "SECOND"
1743 | "DATETIME.FROMEPOCH"
1744 | "DATETIME.FROMEPOCHMILLIS"
1745 | "DATE.TRUNCATE"
1746 | "TIME.TRUNCATE"
1747 | "DATETIME.TRUNCATE"
1748 | "LOCALDATETIME.TRUNCATE"
1749 | "LOCALTIME.TRUNCATE"
1750 | "DATETIME.TRANSACTION"
1751 | "DATETIME.STATEMENT"
1752 | "DATETIME.REALTIME"
1753 | "DATE.TRANSACTION"
1754 | "DATE.STATEMENT"
1755 | "DATE.REALTIME"
1756 | "TIME.TRANSACTION"
1757 | "TIME.STATEMENT"
1758 | "TIME.REALTIME"
1759 | "LOCALTIME.TRANSACTION"
1760 | "LOCALTIME.STATEMENT"
1761 | "LOCALTIME.REALTIME"
1762 | "LOCALDATETIME.TRANSACTION"
1763 | "LOCALDATETIME.STATEMENT"
1764 | "LOCALDATETIME.REALTIME"
1765 | "DURATION.BETWEEN"
1766 | "DURATION.INMONTHS"
1767 | "DURATION.INDAYS"
1768 | "DURATION.INSECONDS" => eval_datetime_function(&name_upper, args),
1769
1770 "POINT" | "DISTANCE" | "POINT.WITHINBBOX" => eval_spatial_function(&name_upper, args),
1772
1773 "RANGE" => eval_range_function(args),
1774
1775 "UNI.TEMPORAL.VALIDAT" => eval_valid_at(args),
1776
1777 "VECTOR_DISTANCE" => {
1778 if args.len() < 2 || args.len() > 3 {
1779 return Err(anyhow!("vector_distance requires 2 or 3 arguments"));
1780 }
1781 let metric = if args.len() == 3 {
1782 args[2].as_str().ok_or(anyhow!("metric must be string"))?
1783 } else {
1784 "cosine"
1785 };
1786 eval_vector_distance(&args[0], &args[1], metric)
1787 }
1788
1789 "UNI_BITWISE_OR"
1791 | "UNI_BITWISE_AND"
1792 | "UNI_BITWISE_XOR"
1793 | "UNI_BITWISE_NOT"
1794 | "UNI_BITWISE_SHIFTLEFT"
1795 | "UNI_BITWISE_SHIFTRIGHT" => eval_bitwise_function(&name_upper, args),
1796
1797 "SIMILAR_TO" => {
1800 if args.len() < 2 {
1801 return Err(anyhow!("similar_to requires at least 2 arguments"));
1802 }
1803 crate::query::similar_to::eval_similar_to_pure(&args[0], &args[1])
1804 }
1805
1806 "VECTOR_SIMILARITY" => {
1807 if args.len() != 2 {
1808 return Err(anyhow!("vector_similarity takes 2 arguments"));
1809 }
1810 eval_vector_similarity(&args[0], &args[1])
1811 }
1812
1813 _ => Err(anyhow!("Function {} not implemented or is aggregate", name)),
1814 }
1815}
1816
1817fn eval_valid_at(args: &[Value]) -> Result<Value> {
1825 if args.len() != 4 {
1826 return Err(anyhow!(
1827 "validAt requires 4 arguments: node, start_prop, end_prop, time"
1828 ));
1829 }
1830
1831 let node_map = match &args[0] {
1832 Value::Map(map) => map,
1833 Value::Null => return Ok(Value::Bool(false)),
1834 _ => {
1835 return Err(anyhow!(
1836 "validAt expects a Node or Edge (Object) as first argument"
1837 ));
1838 }
1839 };
1840
1841 let start_prop = args[1]
1842 .as_str()
1843 .ok_or_else(|| anyhow!("start_prop must be a string"))?;
1844 let end_prop = args[2]
1845 .as_str()
1846 .ok_or_else(|| anyhow!("end_prop must be a string"))?;
1847
1848 let time_str = match &args[3] {
1849 Value::String(s) => s,
1850 _ => return Err(anyhow!("time argument must be a datetime string")),
1851 };
1852
1853 let query_time = parse_datetime_utc(time_str)
1854 .map_err(|_| anyhow!("Invalid query time format: {}", time_str))?;
1855
1856 let valid_from_val = node_map.get(start_prop);
1857 let valid_from = match valid_from_val {
1858 Some(Value::String(s)) => parse_datetime_utc(s)
1859 .map_err(|_| anyhow!("Invalid datetime in property {}: {}", start_prop, s))?,
1860 Some(Value::Null) | None => return Ok(Value::Bool(false)),
1861 _ => return Err(anyhow!("Property {} must be a datetime string", start_prop)),
1862 };
1863
1864 let valid_to_val = node_map.get(end_prop);
1865 let valid_to = match valid_to_val {
1866 Some(Value::String(s)) => Some(
1867 parse_datetime_utc(s)
1868 .map_err(|_| anyhow!("Invalid datetime in property {}: {}", end_prop, s))?,
1869 ),
1870 Some(Value::Null) | None => None,
1871 _ => {
1872 return Err(anyhow!(
1873 "Property {} must be a datetime string or null",
1874 end_prop
1875 ));
1876 }
1877 };
1878
1879 let is_valid = valid_from <= query_time && valid_to.map(|vt| query_time < vt).unwrap_or(true);
1881
1882 Ok(Value::Bool(is_valid))
1883}
1884
1885pub fn eval_vector_similarity(v1: &Value, v2: &Value) -> Result<Value> {
1887 let (arr1, arr2) = match (v1, v2) {
1888 (Value::List(a1), Value::List(a2)) => (a1, a2),
1889 _ => return Err(anyhow!("vector_similarity arguments must be arrays")),
1890 };
1891
1892 if arr1.len() != arr2.len() {
1893 return Err(anyhow!(
1894 "Vector dimensions mismatch: {} vs {}",
1895 arr1.len(),
1896 arr2.len()
1897 ));
1898 }
1899
1900 let mut dot = 0.0;
1901 let mut norm1_sq = 0.0;
1902 let mut norm2_sq = 0.0;
1903
1904 for (v1_elem, v2_elem) in arr1.iter().zip(arr2.iter()) {
1905 let f1 = v1_elem
1906 .as_f64()
1907 .ok_or_else(|| anyhow!("Vector element not a number"))?;
1908 let f2 = v2_elem
1909 .as_f64()
1910 .ok_or_else(|| anyhow!("Vector element not a number"))?;
1911 dot += f1 * f2;
1912 norm1_sq += f1 * f1;
1913 norm2_sq += f2 * f2;
1914 }
1915
1916 let mag1 = norm1_sq.sqrt();
1917 let mag2 = norm2_sq.sqrt();
1918
1919 let sim = if mag1 == 0.0 || mag2 == 0.0 {
1920 0.0
1921 } else {
1922 dot / (mag1 * mag2)
1923 };
1924
1925 Ok(Value::Float(sim))
1926}
1927
1928pub fn eval_vector_distance(v1: &Value, v2: &Value, metric: &str) -> Result<Value> {
1930 let (arr1, arr2) = match (v1, v2) {
1931 (Value::List(a1), Value::List(a2)) => (a1, a2),
1932 _ => return Err(anyhow!("vector_distance arguments must be arrays")),
1933 };
1934
1935 if arr1.len() != arr2.len() {
1936 return Err(anyhow!(
1937 "Vector dimensions mismatch: {} vs {}",
1938 arr1.len(),
1939 arr2.len()
1940 ));
1941 }
1942
1943 let iter1 = arr1
1945 .iter()
1946 .map(|v| v.as_f64().ok_or(anyhow!("Vector element not a number")));
1947 let iter2 = arr2
1948 .iter()
1949 .map(|v| v.as_f64().ok_or(anyhow!("Vector element not a number")));
1950
1951 match metric.to_lowercase().as_str() {
1952 "cosine" => {
1953 let mut dot = 0.0;
1955 let mut norm1_sq = 0.0;
1956 let mut norm2_sq = 0.0;
1957
1958 for (r1, r2) in iter1.zip(iter2) {
1959 let f1 = r1?;
1960 let f2 = r2?;
1961 dot += f1 * f2;
1962 norm1_sq += f1 * f1;
1963 norm2_sq += f2 * f2;
1964 }
1965
1966 let mag1 = norm1_sq.sqrt();
1967 let mag2 = norm2_sq.sqrt();
1968
1969 if mag1 == 0.0 || mag2 == 0.0 {
1970 Ok(Value::Float(1.0))
1971 } else {
1972 let sim = dot / (mag1 * mag2);
1973 let sim = sim.clamp(-1.0, 1.0);
1975 Ok(Value::Float(1.0 - sim))
1976 }
1977 }
1978 "euclidean" | "l2" => {
1979 let mut sum_sq_diff = 0.0;
1980 for (r1, r2) in iter1.zip(iter2) {
1981 let f1 = r1?;
1982 let f2 = r2?;
1983 let diff = f1 - f2;
1984 sum_sq_diff += diff * diff;
1985 }
1986 Ok(Value::Float(sum_sq_diff.sqrt()))
1987 }
1988 "dot" | "inner_product" => {
1989 let mut dot = 0.0;
1990 for (r1, r2) in iter1.zip(iter2) {
1991 let f1 = r1?;
1992 let f2 = r2?;
1993 dot += f1 * f2;
1994 }
1995 Ok(Value::Float(1.0 - dot))
1996 }
1997 _ => Err(anyhow!("Unknown metric: {}", metric)),
1998 }
1999}
2000
2001pub fn is_scalar_function(name: &str) -> bool {
2003 let name_upper = name.to_uppercase();
2004 matches!(
2005 name_upper.as_str(),
2006 "COALESCE"
2007 | "NULLIF"
2008 | "SIZE"
2009 | "KEYS"
2010 | "HEAD"
2011 | "TAIL"
2012 | "LAST"
2013 | "LENGTH"
2014 | "NODES"
2015 | "RELATIONSHIPS"
2016 | "TOINTEGER"
2017 | "TOINT"
2018 | "TOFLOAT"
2019 | "TOSTRING"
2020 | "TOBOOLEAN"
2021 | "TOBOOL"
2022 | "ABS"
2023 | "CEIL"
2024 | "FLOOR"
2025 | "ROUND"
2026 | "SQRT"
2027 | "SIGN"
2028 | "LOG"
2029 | "LOG10"
2030 | "EXP"
2031 | "POWER"
2032 | "POW"
2033 | "SIN"
2034 | "COS"
2035 | "TAN"
2036 | "ASIN"
2037 | "ACOS"
2038 | "ATAN"
2039 | "ATAN2"
2040 | "DEGREES"
2041 | "RADIANS"
2042 | "HAVERSIN"
2043 | "PI"
2044 | "E"
2045 | "RAND"
2046 | "TOUPPER"
2047 | "UPPER"
2048 | "TOLOWER"
2049 | "LOWER"
2050 | "TRIM"
2051 | "LTRIM"
2052 | "RTRIM"
2053 | "REVERSE"
2054 | "REPLACE"
2055 | "SPLIT"
2056 | "SUBSTRING"
2057 | "LEFT"
2058 | "RIGHT"
2059 | "LPAD"
2060 | "RPAD"
2061 | "RANGE"
2062 | "UNI.VALIDAT"
2063 | "VALIDAT"
2064 | "SIMILAR_TO"
2065 | "VECTOR_SIMILARITY"
2066 | "VECTOR_DISTANCE"
2067 | "DATE"
2068 | "TIME"
2069 | "DATETIME"
2070 | "DURATION"
2071 | "YEAR"
2072 | "MONTH"
2073 | "DAY"
2074 | "HOUR"
2075 | "MINUTE"
2076 | "SECOND"
2077 | "ID"
2078 | "ELEMENTID"
2079 | "TYPE"
2080 | "LABELS"
2081 | "PROPERTIES"
2082 | "STARTNODE"
2083 | "ENDNODE"
2084 | "ANY"
2085 | "ALL"
2086 | "NONE"
2087 | "SINGLE"
2088 )
2089}
2090
2091fn eval_bitwise_function(name: &str, args: &[Value]) -> Result<Value> {
2093 let require_int = |v: &Value, fname: &str| -> Result<i64> {
2094 v.as_i64()
2095 .ok_or_else(|| anyhow!("{} requires integer arguments", fname))
2096 };
2097
2098 let bitwise_binary = |fname: &str, op: fn(i64, i64) -> i64| -> Result<Value> {
2099 if args.len() != 2 {
2100 return Err(anyhow!("{} requires exactly 2 arguments", fname));
2101 }
2102 let l = require_int(&args[0], fname)?;
2103 let r = require_int(&args[1], fname)?;
2104 Ok(Value::Int(op(l, r)))
2105 };
2106
2107 match name {
2108 "UNI_BITWISE_OR" => bitwise_binary("uni_bitwise_or", |l, r| l | r),
2109 "UNI_BITWISE_AND" => bitwise_binary("uni_bitwise_and", |l, r| l & r),
2110 "UNI_BITWISE_XOR" => bitwise_binary("uni_bitwise_xor", |l, r| l ^ r),
2111 "UNI_BITWISE_SHIFTLEFT" => bitwise_binary("uni_bitwise_shiftLeft", |l, r| l << r),
2112 "UNI_BITWISE_SHIFTRIGHT" => bitwise_binary("uni_bitwise_shiftRight", |l, r| l >> r),
2113 "UNI_BITWISE_NOT" => {
2114 if args.len() != 1 {
2115 return Err(anyhow!("uni_bitwise_not requires exactly 1 argument"));
2116 }
2117 Ok(Value::Int(!require_int(&args[0], "uni_bitwise_not")?))
2118 }
2119 _ => Err(anyhow!("Unknown bitwise function: {}", name)),
2120 }
2121}
2122
2123#[cfg(test)]
2124mod tests {
2125 use super::*;
2126 fn s(v: &str) -> Value {
2128 Value::String(v.into())
2129 }
2130 fn i(v: i64) -> Value {
2132 Value::Int(v)
2133 }
2134
2135 #[test]
2136 fn test_binary_op_eq() {
2137 assert_eq!(
2138 eval_binary_op(&i(1), &BinaryOp::Eq, &i(1)).unwrap(),
2139 Value::Bool(true)
2140 );
2141 assert_eq!(
2142 eval_binary_op(&i(1), &BinaryOp::Eq, &i(2)).unwrap(),
2143 Value::Bool(false)
2144 );
2145 }
2146
2147 #[test]
2148 fn test_binary_op_comparison() {
2149 assert_eq!(
2150 eval_binary_op(&i(5), &BinaryOp::Gt, &i(3)).unwrap(),
2151 Value::Bool(true)
2152 );
2153 assert_eq!(
2154 eval_binary_op(&i(5), &BinaryOp::Lt, &i(3)).unwrap(),
2155 Value::Bool(false)
2156 );
2157 }
2158
2159 #[test]
2160 fn test_binary_op_xor() {
2161 assert_eq!(
2163 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2164 Value::Bool(false)
2165 );
2166 assert_eq!(
2168 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2169 Value::Bool(true)
2170 );
2171 assert_eq!(
2173 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2174 Value::Bool(true)
2175 );
2176 assert_eq!(
2178 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2179 Value::Bool(false)
2180 );
2181 }
2182
2183 #[test]
2184 fn test_binary_op_contains() {
2185 assert_eq!(
2186 eval_binary_op(&s("hello world"), &BinaryOp::Contains, &s("world")).unwrap(),
2187 Value::Bool(true)
2188 );
2189 }
2190
2191 #[test]
2192 fn test_scalar_function_size() {
2193 assert_eq!(
2194 eval_scalar_function("SIZE", &[Value::List(vec![i(1), i(2), i(3)])]).unwrap(),
2195 Value::Int(3)
2196 );
2197 }
2198
2199 #[test]
2200 fn test_scalar_function_head() {
2201 assert_eq!(
2202 eval_scalar_function("HEAD", &[Value::List(vec![i(1), i(2), i(3)])]).unwrap(),
2203 Value::Int(1)
2204 );
2205 }
2206
2207 #[test]
2208 fn test_scalar_function_coalesce() {
2209 assert_eq!(
2210 eval_scalar_function("COALESCE", &[Value::Null, Value::Int(1), Value::Int(2)]).unwrap(),
2211 Value::Int(1)
2212 );
2213 }
2214
2215 #[test]
2216 fn test_vector_similarity() {
2217 let v1 = Value::List(vec![Value::Float(1.0), Value::Float(0.0)]);
2218 let v2 = Value::List(vec![Value::Float(1.0), Value::Float(0.0)]);
2219 let result = eval_vector_similarity(&v1, &v2).unwrap();
2220 assert_eq!(result.as_f64().unwrap(), 1.0);
2221 }
2222
2223 #[test]
2224 fn test_regex_match() {
2225 assert_eq!(
2227 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("hello.*")).unwrap(),
2228 Value::Bool(true)
2229 );
2230
2231 assert_eq!(
2233 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^world")).unwrap(),
2234 Value::Bool(false)
2235 );
2236
2237 assert_eq!(
2239 eval_binary_op(&s("Hello"), &BinaryOp::Regex, &s("hello")).unwrap(),
2240 Value::Bool(false)
2241 );
2242
2243 assert_eq!(
2245 eval_binary_op(&s("Hello"), &BinaryOp::Regex, &s("(?i)hello")).unwrap(),
2246 Value::Bool(true)
2247 );
2248 }
2249
2250 #[test]
2251 fn test_regex_null_handling() {
2252 assert_eq!(
2254 eval_binary_op(&Value::Null, &BinaryOp::Regex, &s(".*")).unwrap(),
2255 Value::Null
2256 );
2257
2258 assert_eq!(
2260 eval_binary_op(&s("hello"), &BinaryOp::Regex, &Value::Null).unwrap(),
2261 Value::Null
2262 );
2263 }
2264
2265 #[test]
2266 fn test_regex_invalid_pattern() {
2267 let result = eval_binary_op(&s("hello"), &BinaryOp::Regex, &s("[invalid"));
2269 assert!(result.is_err());
2270 assert!(result.unwrap_err().to_string().contains("Invalid regex"));
2271 }
2272
2273 #[test]
2274 fn test_regex_special_characters() {
2275 assert_eq!(
2277 eval_binary_op(
2278 &s("test@example.com"),
2279 &BinaryOp::Regex,
2280 &s(r"^[\w.-]+@[\w.-]+\.\w+$")
2281 )
2282 .unwrap(),
2283 Value::Bool(true)
2284 );
2285
2286 assert_eq!(
2288 eval_binary_op(
2289 &s("123-456-7890"),
2290 &BinaryOp::Regex,
2291 &s(r"^\d{3}-\d{3}-\d{4}$")
2292 )
2293 .unwrap(),
2294 Value::Bool(true)
2295 );
2296
2297 assert_eq!(
2299 eval_binary_op(
2300 &s("1234567890"),
2301 &BinaryOp::Regex,
2302 &s(r"^\d{3}-\d{3}-\d{4}$")
2303 )
2304 .unwrap(),
2305 Value::Bool(false)
2306 );
2307 }
2308
2309 #[test]
2310 fn test_regex_anchors() {
2311 assert_eq!(
2313 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^hello")).unwrap(),
2314 Value::Bool(true)
2315 );
2316 assert_eq!(
2317 eval_binary_op(&s("say hello"), &BinaryOp::Regex, &s("^hello")).unwrap(),
2318 Value::Bool(false)
2319 );
2320
2321 assert_eq!(
2323 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("world$")).unwrap(),
2324 Value::Bool(true)
2325 );
2326 assert_eq!(
2327 eval_binary_op(&s("world hello"), &BinaryOp::Regex, &s("world$")).unwrap(),
2328 Value::Bool(false)
2329 );
2330
2331 assert_eq!(
2333 eval_binary_op(&s("hello"), &BinaryOp::Regex, &s("^hello$")).unwrap(),
2334 Value::Bool(true)
2335 );
2336 assert_eq!(
2337 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^hello$")).unwrap(),
2338 Value::Bool(false)
2339 );
2340 }
2341
2342 #[test]
2343 fn test_temporal_arithmetic() {
2344 let dt = s("2024-01-15T10:00:00Z");
2346 let dur = Value::Int(3_600_000_000_i64);
2347 let result = eval_binary_op(&dt, &BinaryOp::Add, &dur).unwrap();
2348 assert!(result.to_string().contains("11:00"));
2349
2350 let d = s("2024-01-01");
2352 let dur_day = Value::Int(86_400_000_000_i64);
2353 let result = eval_binary_op(&d, &BinaryOp::Add, &dur_day).unwrap();
2354 assert_eq!(result.to_string(), "2024-01-02");
2355
2356 let dt1 = s("2024-01-02T00:00:00Z");
2358 let dt2 = s("2024-01-01T00:00:00Z");
2359 let result = eval_binary_op(&dt1, &BinaryOp::Sub, &dt2).unwrap();
2360 let dur_str = result.to_string();
2362 assert!(dur_str.starts_with('P'));
2363 assert!(dur_str.contains("24H")); }
2365
2366 #[test]
2370 fn test_temporal_arithmetic_edge_cases() {
2371 let dt = s("2024-01-15T10:00:00Z");
2373 let neg_dur = Value::Int(-3_600_000_000_i64); let result = eval_binary_op(&dt, &BinaryOp::Add, &neg_dur).unwrap();
2375 assert!(result.to_string().contains("09:00"));
2376
2377 let dur1 = s("PT1H"); let dur2 = s("PT2H"); let result = eval_binary_op(&dur1, &BinaryOp::Sub, &dur2).unwrap();
2381 let dur_str = result.to_string();
2383 assert!(dur_str.starts_with('P') || dur_str.starts_with("-P"));
2384
2385 let dt = s("2024-01-15T10:00:00Z");
2387 let zero_dur = Value::Int(0_i64);
2388 let result = eval_binary_op(&dt, &BinaryOp::Add, &zero_dur).unwrap();
2389 assert!(result.to_string().contains("10:00"));
2390
2391 let d = s("2023-12-31");
2393 let one_day = Value::Int(86_400_000_000_i64);
2394 let result = eval_binary_op(&d, &BinaryOp::Add, &one_day).unwrap();
2395 assert_eq!(result.to_string(), "2024-01-01");
2396
2397 let dt1 = s("2024-01-15T10:00:00Z");
2399 let dt2 = s("2024-01-15T10:00:00Z");
2400 let result = eval_binary_op(&dt1, &BinaryOp::Sub, &dt2).unwrap();
2401 let dur_str = result.to_string();
2403 assert!(dur_str.starts_with('P'));
2404
2405 let leap_day = s("2024-02-28");
2407 let one_day = Value::Int(86_400_000_000_i64);
2408 let result = eval_binary_op(&leap_day, &BinaryOp::Add, &one_day).unwrap();
2409 assert_eq!(result.to_string(), "2024-02-29");
2410 }
2411
2412 #[test]
2413 fn test_regex_empty_string() {
2414 assert_eq!(
2416 eval_binary_op(&s(""), &BinaryOp::Regex, &s("^$")).unwrap(),
2417 Value::Bool(true)
2418 );
2419
2420 assert_eq!(
2422 eval_binary_op(&s(""), &BinaryOp::Regex, &s(".+")).unwrap(),
2423 Value::Bool(false)
2424 );
2425
2426 assert_eq!(
2428 eval_binary_op(&s("hello"), &BinaryOp::Regex, &s(".*")).unwrap(),
2429 Value::Bool(true)
2430 );
2431 }
2432
2433 #[test]
2434 fn test_regex_type_errors() {
2435 let result = eval_binary_op(&Value::Int(123), &BinaryOp::Regex, &s("\\d+"));
2437 assert!(result.is_err());
2438 assert!(result.unwrap_err().to_string().contains("must be a string"));
2439
2440 let result = eval_binary_op(&s("hello"), &BinaryOp::Regex, &Value::Int(123));
2442 assert!(result.is_err());
2443 assert!(result.unwrap_err().to_string().contains("pattern string"));
2444 }
2445
2446 #[test]
2447 fn test_and_null_handling() {
2448 assert_eq!(
2452 eval_binary_op(&Value::Bool(false), &BinaryOp::And, &Value::Null).unwrap(),
2453 Value::Bool(false)
2454 );
2455 assert_eq!(
2456 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Bool(false)).unwrap(),
2457 Value::Bool(false)
2458 );
2459
2460 assert_eq!(
2462 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Null).unwrap(),
2463 Value::Null
2464 );
2465 assert_eq!(
2466 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Bool(true)).unwrap(),
2467 Value::Null
2468 );
2469
2470 assert_eq!(
2472 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Null).unwrap(),
2473 Value::Null
2474 );
2475
2476 assert_eq!(
2478 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Bool(true)).unwrap(),
2479 Value::Bool(true)
2480 );
2481 assert_eq!(
2482 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Bool(false)).unwrap(),
2483 Value::Bool(false)
2484 );
2485 }
2486
2487 #[test]
2488 fn test_or_null_handling() {
2489 assert_eq!(
2493 eval_binary_op(&Value::Bool(true), &BinaryOp::Or, &Value::Null).unwrap(),
2494 Value::Bool(true)
2495 );
2496 assert_eq!(
2497 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Bool(true)).unwrap(),
2498 Value::Bool(true)
2499 );
2500
2501 assert_eq!(
2503 eval_binary_op(&Value::Bool(false), &BinaryOp::Or, &Value::Null).unwrap(),
2504 Value::Null
2505 );
2506 assert_eq!(
2507 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2508 Value::Null
2509 );
2510
2511 assert_eq!(
2513 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Null).unwrap(),
2514 Value::Null
2515 );
2516
2517 assert_eq!(
2519 eval_binary_op(&Value::Bool(false), &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2520 Value::Bool(false)
2521 );
2522 assert_eq!(
2523 eval_binary_op(&Value::Bool(true), &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2524 Value::Bool(true)
2525 );
2526 }
2527
2528 #[test]
2529 fn test_nan_comparison_with_non_numeric() {
2530 let nan = Value::Float(f64::NAN);
2531
2532 assert_eq!(
2534 eval_binary_op(&nan, &BinaryOp::Gt, &i(1)).unwrap(),
2535 Value::Bool(false)
2536 );
2537
2538 assert_eq!(
2540 eval_binary_op(&nan, &BinaryOp::Gt, &nan).unwrap(),
2541 Value::Bool(false)
2542 );
2543
2544 assert_eq!(
2546 eval_binary_op(&nan, &BinaryOp::Gt, &s("a")).unwrap(),
2547 Value::Null
2548 );
2549
2550 assert_eq!(
2552 eval_binary_op(&s("a"), &BinaryOp::Lt, &nan).unwrap(),
2553 Value::Null
2554 );
2555 }
2556
2557 #[test]
2558 fn test_nan_equality_with_non_numeric() {
2559 let nan = Value::Float(f64::NAN);
2560
2561 assert_eq!(
2563 eval_binary_op(&nan, &BinaryOp::Eq, &nan).unwrap(),
2564 Value::Bool(false)
2565 );
2566
2567 assert_eq!(
2569 eval_binary_op(&nan, &BinaryOp::NotEq, &nan).unwrap(),
2570 Value::Bool(true)
2571 );
2572
2573 assert_eq!(
2575 eval_binary_op(&nan, &BinaryOp::Eq, &s("a")).unwrap(),
2576 Value::Bool(false)
2577 );
2578
2579 assert_eq!(
2581 eval_binary_op(&nan, &BinaryOp::NotEq, &s("a")).unwrap(),
2582 Value::Bool(true)
2583 );
2584 }
2585
2586 #[test]
2587 fn test_large_integer_equality() {
2588 let a = Value::Int(4611686018427387905_i64);
2590 let b = Value::Int(4611686018427387900_i64);
2591
2592 assert_eq!(
2593 eval_binary_op(&a, &BinaryOp::Eq, &b).unwrap(),
2594 Value::Bool(false)
2595 );
2596 assert_eq!(
2597 eval_binary_op(&a, &BinaryOp::Eq, &a).unwrap(),
2598 Value::Bool(true)
2599 );
2600 }
2601
2602 #[test]
2603 fn test_large_integer_ordering() {
2604 let a = Value::Int(4611686018427387905_i64);
2605 let b = Value::Int(4611686018427387900_i64);
2606
2607 assert_eq!(
2608 eval_binary_op(&a, &BinaryOp::Gt, &b).unwrap(),
2609 Value::Bool(true)
2610 );
2611 assert_eq!(
2612 eval_binary_op(&b, &BinaryOp::Lt, &a).unwrap(),
2613 Value::Bool(true)
2614 );
2615 }
2616
2617 #[test]
2618 fn test_int_float_equality_still_works() {
2619 assert_eq!(
2621 eval_binary_op(&i(1), &BinaryOp::Eq, &Value::Float(1.0)).unwrap(),
2622 Value::Bool(true)
2623 );
2624 assert_eq!(
2625 eval_binary_op(&i(1), &BinaryOp::NotEq, &Value::Float(1.0)).unwrap(),
2626 Value::Bool(false)
2627 );
2628 }
2629
2630 #[test]
2631 fn test_xor_null_handling() {
2632 assert_eq!(
2635 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Null).unwrap(),
2636 Value::Null
2637 );
2638 assert_eq!(
2639 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Null).unwrap(),
2640 Value::Null
2641 );
2642 assert_eq!(
2643 eval_binary_op(&Value::Null, &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2644 Value::Null
2645 );
2646 assert_eq!(
2647 eval_binary_op(&Value::Null, &BinaryOp::Xor, &Value::Null).unwrap(),
2648 Value::Null
2649 );
2650
2651 assert_eq!(
2653 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2654 Value::Bool(true)
2655 );
2656 assert_eq!(
2657 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2658 Value::Bool(false)
2659 );
2660 }
2661}