1use std::collections::HashMap;
4
5use itertools::Itertools;
6use petgraph::Graph;
7use petgraph::graph::{EdgeIndex, EdgeReference, NodeIndex};
8use petgraph::visit::EdgeRef;
9
10use crate::ast::*;
11use crate::error::CypherError;
12use crate::{CypherEdge, CypherNode, EdgeData, NodeData};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum MatchStrategy {
17 #[default]
19 Backtrack,
20 Fast,
23}
24
25#[derive(Debug, Clone, PartialEq)]
27pub enum ResultValue {
28 Scalar(CypherValue),
30 Node {
32 labels: Vec<String>,
33 properties: HashMap<String, CypherValue>,
34 },
35 Edge {
37 rel_type: Option<String>,
38 properties: HashMap<String, CypherValue>,
39 },
40}
41
42#[derive(Debug, Clone, PartialEq)]
44pub struct Row {
45 pub values: HashMap<String, ResultValue>,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
50enum BoundValue {
51 Node(NodeIndex),
52 Edge(EdgeIndex),
53}
54
55type Bindings = HashMap<String, BoundValue>;
57pub type Parameters = HashMap<String, CypherValue>;
61
62pub struct QueryResult<'a, N = NodeData, E = EdgeData> {
67 columns: Vec<String>,
68 rows: std::vec::IntoIter<Row>,
69 _graph: &'a Graph<N, E>,
70}
71
72impl<'a, N, E> QueryResult<'a, N, E> {
73 fn new(columns: Vec<String>, rows: Vec<Row>, graph: &'a Graph<N, E>) -> Self {
74 Self {
75 columns,
76 rows: rows.into_iter(),
77 _graph: graph,
78 }
79 }
80
81 pub fn columns(&self) -> &[String] {
82 &self.columns
83 }
84}
85
86impl<'a, N, E> Iterator for QueryResult<'a, N, E> {
87 type Item = Row;
88
89 fn next(&mut self) -> Option<Row> {
90 self.rows.next()
91 }
92
93 fn size_hint(&self) -> (usize, Option<usize>) {
94 self.rows.size_hint()
95 }
96}
97
98pub trait PetgraphCypher {
100 fn cypher(&self, query: &str) -> Result<QueryResult<'_>, CypherError>;
107
108 fn cypher_params(
110 &self,
111 query: &str,
112 parameters: &Parameters,
113 ) -> Result<QueryResult<'_>, CypherError> {
114 if parameters.is_empty() {
115 self.cypher(query)
116 } else {
117 Err(CypherError::Unsupported(
118 "parameterized queries are not supported by this PetgraphCypher implementor".into(),
119 ))
120 }
121 }
122
123 fn cypher_mut(&mut self, query: &str) -> Result<(), CypherError>;
128
129 fn cypher_with_strategy(
131 &self,
132 query: &str,
133 strategy: MatchStrategy,
134 ) -> Result<QueryResult<'_>, CypherError>;
135
136 fn cypher_with_strategy_params(
138 &self,
139 query: &str,
140 strategy: MatchStrategy,
141 parameters: &Parameters,
142 ) -> Result<QueryResult<'_>, CypherError> {
143 if parameters.is_empty() {
144 self.cypher_with_strategy(query, strategy)
145 } else {
146 Err(CypherError::Unsupported(
147 "parameterized queries are not supported by this PetgraphCypher implementor".into(),
148 ))
149 }
150 }
151}
152
153pub trait PetgraphCypherRead<N: CypherNode, E: CypherEdge> {
156 fn cypher(&self, query: &str) -> Result<QueryResult<'_, N, E>, CypherError>;
163
164 fn cypher_params(
166 &self,
167 query: &str,
168 parameters: &Parameters,
169 ) -> Result<QueryResult<'_, N, E>, CypherError> {
170 if parameters.is_empty() {
171 self.cypher(query)
172 } else {
173 Err(CypherError::Unsupported(
174 "parameterized queries are not supported by this PetgraphCypherRead implementor"
175 .into(),
176 ))
177 }
178 }
179
180 fn cypher_with_strategy(
182 &self,
183 query: &str,
184 strategy: MatchStrategy,
185 ) -> Result<QueryResult<'_, N, E>, CypherError>;
186
187 fn cypher_with_strategy_params(
189 &self,
190 query: &str,
191 strategy: MatchStrategy,
192 parameters: &Parameters,
193 ) -> Result<QueryResult<'_, N, E>, CypherError> {
194 if parameters.is_empty() {
195 self.cypher_with_strategy(query, strategy)
196 } else {
197 Err(CypherError::Unsupported(
198 "parameterized queries are not supported by this PetgraphCypherRead implementor"
199 .into(),
200 ))
201 }
202 }
203}
204
205impl<N: CypherNode, E: CypherEdge> PetgraphCypherRead<N, E> for Graph<N, E> {
206 fn cypher(&self, query: &str) -> Result<QueryResult<'_, N, E>, CypherError> {
207 let params = Parameters::new();
208 self.cypher_with_strategy_params(query, MatchStrategy::default(), ¶ms)
209 }
210
211 fn cypher_params(
212 &self,
213 query: &str,
214 parameters: &Parameters,
215 ) -> Result<QueryResult<'_, N, E>, CypherError> {
216 self.cypher_with_strategy_params(query, MatchStrategy::default(), parameters)
217 }
218
219 fn cypher_with_strategy(
220 &self,
221 query: &str,
222 strategy: MatchStrategy,
223 ) -> Result<QueryResult<'_, N, E>, CypherError> {
224 let params = Parameters::new();
225 self.cypher_with_strategy_params(query, strategy, ¶ms)
226 }
227
228 fn cypher_with_strategy_params(
229 &self,
230 query: &str,
231 strategy: MatchStrategy,
232 parameters: &Parameters,
233 ) -> Result<QueryResult<'_, N, E>, CypherError> {
234 let ast = crate::parse_cypher(query)?;
235 validate_read_query(&ast)?;
236 validate_read_query_parameters(&ast, parameters)?;
237 ReadQueryExecutor::new(self, strategy, parameters).execute(ast)
238 }
239}
240
241impl PetgraphCypher for Graph<NodeData, EdgeData> {
242 fn cypher(&self, query: &str) -> Result<QueryResult<'_>, CypherError> {
243 let params = Parameters::new();
244 PetgraphCypher::cypher_with_strategy_params(self, query, MatchStrategy::default(), ¶ms)
245 }
246
247 fn cypher_params(
248 &self,
249 query: &str,
250 parameters: &Parameters,
251 ) -> Result<QueryResult<'_>, CypherError> {
252 PetgraphCypher::cypher_with_strategy_params(
253 self,
254 query,
255 MatchStrategy::default(),
256 parameters,
257 )
258 }
259
260 fn cypher_mut(&mut self, query: &str) -> Result<(), CypherError> {
261 let ast = crate::parse_cypher(query)?;
262 validate_mutation_query(&ast)?;
263 validate_mutation_query_parameters(&ast)?;
264 MutQueryExecutor::new(self).execute(ast)
265 }
266
267 fn cypher_with_strategy(
268 &self,
269 query: &str,
270 strategy: MatchStrategy,
271 ) -> Result<QueryResult<'_>, CypherError> {
272 let params = Parameters::new();
273 PetgraphCypher::cypher_with_strategy_params(self, query, strategy, ¶ms)
274 }
275
276 fn cypher_with_strategy_params(
277 &self,
278 query: &str,
279 strategy: MatchStrategy,
280 parameters: &Parameters,
281 ) -> Result<QueryResult<'_>, CypherError> {
282 let ast = crate::parse_cypher(query)?;
283 validate_read_query(&ast)?;
284 validate_read_query_parameters(&ast, parameters)?;
285 ReadQueryExecutor::new(self, strategy, parameters).execute(ast)
286 }
287}
288
289fn validate_read_query(query: &CypherQuery) -> Result<(), CypherError> {
291 for clause in &query.clauses {
292 match clause {
293 Clause::Create { .. } => {
294 return Err(CypherError::Unsupported(
295 "CREATE not allowed in cypher(); use cypher_mut()".into(),
296 ));
297 }
298 Clause::Merge { .. } => {
299 return Err(CypherError::Unsupported(
300 "MERGE not allowed in cypher(); use cypher_mut()".into(),
301 ));
302 }
303 Clause::Delete { .. } => {
304 return Err(CypherError::Unsupported(
305 "DELETE not allowed in cypher(); use cypher_mut()".into(),
306 ));
307 }
308 Clause::Set { .. } | Clause::Remove { .. } => {
309 return Err(CypherError::Unsupported(
310 "SET/REMOVE not allowed in cypher(); use cypher_mut()".into(),
311 ));
312 }
313 Clause::With { .. } => {
314 return Err(CypherError::Unsupported("WITH is not yet supported".into()));
315 }
316 _ => {}
317 }
318 }
319 Ok(())
320}
321
322fn validate_mutation_query(query: &CypherQuery) -> Result<(), CypherError> {
324 for clause in &query.clauses {
325 if matches!(clause, Clause::Return { .. }) {
326 return Err(CypherError::Unsupported(
327 "RETURN not allowed in cypher_mut(); use cypher() for reads".into(),
328 ));
329 }
330 }
331 Ok(())
332}
333
334fn ensure_expression_parameters(
335 expression: &Expression,
336 parameters: &Parameters,
337) -> Result<(), CypherError> {
338 match expression {
339 Expression::Unary(_, inner) => ensure_expression_parameters(inner, parameters),
340 Expression::Binary(_, left, right) => {
341 ensure_expression_parameters(left, parameters)?;
342 ensure_expression_parameters(right, parameters)
343 }
344 Expression::List(items) => {
345 for item in items {
346 ensure_expression_parameters(item, parameters)?;
347 }
348 Ok(())
349 }
350 Expression::FunctionCall { args, .. } => {
351 for arg in args {
352 ensure_expression_parameters(arg, parameters)?;
353 }
354 Ok(())
355 }
356 Expression::Parameter(name) => {
357 if parameters.contains_key(name) {
358 Ok(())
359 } else {
360 Err(CypherError::InvalidQuery(format!(
361 "missing query parameter: ${name}"
362 )))
363 }
364 }
365 Expression::Case(case) => {
366 if let Some(scrutinee) = &case.scrutinee {
367 ensure_expression_parameters(scrutinee, parameters)?;
368 }
369 for (when, then) in &case.alternatives {
370 ensure_expression_parameters(when, parameters)?;
371 ensure_expression_parameters(then, parameters)?;
372 }
373 if let Some(default) = &case.default {
374 ensure_expression_parameters(default, parameters)?;
375 }
376 Ok(())
377 }
378 Expression::Variable(_)
379 | Expression::Property(_, _)
380 | Expression::All
381 | Expression::Literal(_) => Ok(()),
382 }
383}
384
385fn ensure_where_parameters(
386 where_expr: &WhereExpr,
387 parameters: &Parameters,
388) -> Result<(), CypherError> {
389 match where_expr {
390 WhereExpr::Eq(left, right)
391 | WhereExpr::NotEq(left, right)
392 | WhereExpr::Lt(left, right)
393 | WhereExpr::Gt(left, right)
394 | WhereExpr::Le(left, right)
395 | WhereExpr::Ge(left, right) => {
396 ensure_expression_parameters(left, parameters)?;
397 ensure_expression_parameters(right, parameters)
398 }
399 WhereExpr::In(expression, _)
400 | WhereExpr::StartsWith(expression, _)
401 | WhereExpr::EndsWith(expression, _)
402 | WhereExpr::Contains(expression, _)
403 | WhereExpr::IsNull(expression)
404 | WhereExpr::IsNotNull(expression) => ensure_expression_parameters(expression, parameters),
405 WhereExpr::Not(inner) => ensure_where_parameters(inner, parameters),
406 WhereExpr::And(left, right) | WhereExpr::Or(left, right) | WhereExpr::Xor(left, right) => {
407 ensure_where_parameters(left, parameters)?;
408 ensure_where_parameters(right, parameters)
409 }
410 }
411}
412
413fn validate_read_query_parameters(
414 query: &CypherQuery,
415 parameters: &Parameters,
416) -> Result<(), CypherError> {
417 for clause in &query.clauses {
418 match clause {
419 Clause::Match { where_clause, .. } | Clause::OptionalMatch { where_clause, .. } => {
420 if let Some(where_expr) = where_clause {
421 ensure_where_parameters(where_expr, parameters)?;
422 }
423 }
424 Clause::Return { items, .. } => {
425 for item in items {
426 ensure_expression_parameters(&item.expression, parameters)?;
427 }
428 }
429 Clause::Unwind { expression, .. } => {
430 ensure_expression_parameters(expression, parameters)?;
431 }
432 Clause::OrderBy { items } => {
433 for item in items {
434 ensure_expression_parameters(&item.expression, parameters)?;
435 }
436 }
437 Clause::Create { .. }
438 | Clause::With { .. }
439 | Clause::Set { .. }
440 | Clause::Merge { .. }
441 | Clause::Delete { .. }
442 | Clause::Remove { .. }
443 | Clause::Skip { .. }
444 | Clause::Limit { .. } => {}
445 }
446 }
447 Ok(())
448}
449
450fn ensure_expression_has_no_parameters(expression: &Expression) -> Result<(), CypherError> {
451 match expression {
452 Expression::Unary(_, inner) => ensure_expression_has_no_parameters(inner),
453 Expression::Binary(_, left, right) => {
454 ensure_expression_has_no_parameters(left)?;
455 ensure_expression_has_no_parameters(right)
456 }
457 Expression::List(items) => {
458 for item in items {
459 ensure_expression_has_no_parameters(item)?;
460 }
461 Ok(())
462 }
463 Expression::FunctionCall { args, .. } => {
464 for arg in args {
465 ensure_expression_has_no_parameters(arg)?;
466 }
467 Ok(())
468 }
469 Expression::Parameter(name) => Err(CypherError::Unsupported(format!(
470 "query parameters are not supported in cypher_mut(): ${name}"
471 ))),
472 Expression::Case(case) => {
473 if let Some(scrutinee) = &case.scrutinee {
474 ensure_expression_has_no_parameters(scrutinee)?;
475 }
476 for (when, then) in &case.alternatives {
477 ensure_expression_has_no_parameters(when)?;
478 ensure_expression_has_no_parameters(then)?;
479 }
480 if let Some(default) = &case.default {
481 ensure_expression_has_no_parameters(default)?;
482 }
483 Ok(())
484 }
485 Expression::Variable(_)
486 | Expression::Property(_, _)
487 | Expression::All
488 | Expression::Literal(_) => Ok(()),
489 }
490}
491
492fn ensure_where_has_no_parameters(where_expr: &WhereExpr) -> Result<(), CypherError> {
493 match where_expr {
494 WhereExpr::Eq(left, right)
495 | WhereExpr::NotEq(left, right)
496 | WhereExpr::Lt(left, right)
497 | WhereExpr::Gt(left, right)
498 | WhereExpr::Le(left, right)
499 | WhereExpr::Ge(left, right) => {
500 ensure_expression_has_no_parameters(left)?;
501 ensure_expression_has_no_parameters(right)
502 }
503 WhereExpr::In(expression, _)
504 | WhereExpr::StartsWith(expression, _)
505 | WhereExpr::EndsWith(expression, _)
506 | WhereExpr::Contains(expression, _)
507 | WhereExpr::IsNull(expression)
508 | WhereExpr::IsNotNull(expression) => ensure_expression_has_no_parameters(expression),
509 WhereExpr::Not(inner) => ensure_where_has_no_parameters(inner),
510 WhereExpr::And(left, right) | WhereExpr::Or(left, right) | WhereExpr::Xor(left, right) => {
511 ensure_where_has_no_parameters(left)?;
512 ensure_where_has_no_parameters(right)
513 }
514 }
515}
516
517fn ensure_set_item_has_no_parameters(item: &SetItem) -> Result<(), CypherError> {
518 match item {
519 SetItem::SetProperty { value, .. } => ensure_expression_has_no_parameters(value),
520 SetItem::SetVariable { .. }
521 | SetItem::SetLabels { .. }
522 | SetItem::MergeProperties { .. } => Ok(()),
523 }
524}
525
526fn validate_mutation_query_parameters(query: &CypherQuery) -> Result<(), CypherError> {
527 for clause in &query.clauses {
528 match clause {
529 Clause::Match { where_clause, .. } | Clause::OptionalMatch { where_clause, .. } => {
530 if let Some(where_expr) = where_clause {
531 ensure_where_has_no_parameters(where_expr)?;
532 }
533 }
534 Clause::Set { items } => {
535 for item in items {
536 ensure_set_item_has_no_parameters(item)?;
537 }
538 }
539 Clause::Merge {
540 on_create,
541 on_match,
542 ..
543 } => {
544 for item in on_create {
545 ensure_set_item_has_no_parameters(item)?;
546 }
547 for item in on_match {
548 ensure_set_item_has_no_parameters(item)?;
549 }
550 }
551 Clause::Unwind { expression, .. } => ensure_expression_has_no_parameters(expression)?,
552 Clause::Create { .. }
553 | Clause::Delete { .. }
554 | Clause::Remove { .. }
555 | Clause::Return { .. }
556 | Clause::With { .. }
557 | Clause::OrderBy { .. }
558 | Clause::Skip { .. }
559 | Clause::Limit { .. } => {}
560 }
561 }
562 Ok(())
563}
564
565fn is_truthy(value: &CypherValue) -> Option<bool> {
570 match value {
571 CypherValue::Null => None,
572 CypherValue::Boolean(b) => Some(*b),
573 _ => Some(true),
574 }
575}
576
577fn compare_cypher_values(left: &CypherValue, right: &CypherValue) -> Option<std::cmp::Ordering> {
578 match (left, right) {
579 (CypherValue::Null, _) | (_, CypherValue::Null) => None,
580 (CypherValue::Boolean(a), CypherValue::Boolean(b)) => a.partial_cmp(b),
581 (CypherValue::Integer(a), CypherValue::Integer(b)) => a.partial_cmp(b),
582 (CypherValue::Integer(a), CypherValue::Float(b)) => (*a as f64).partial_cmp(b),
583 (CypherValue::Float(a), CypherValue::Integer(b)) => a.partial_cmp(&(*b as f64)),
584 (CypherValue::Float(a), CypherValue::Float(b)) => a.partial_cmp(b),
585 (CypherValue::String(a), CypherValue::String(b)) => a.partial_cmp(b),
586 (CypherValue::List(a), CypherValue::List(b)) => a.partial_cmp(b),
587 _ => None,
588 }
589}
590
591fn cypher_value_eq(left: &CypherValue, right: &CypherValue) -> CypherValue {
592 match (left, right) {
593 (CypherValue::Null, _) | (_, CypherValue::Null) => CypherValue::Null,
594 _ => CypherValue::Boolean(left == right),
595 }
596}
597
598fn evaluate_binary_op(
599 op: BinaryOp,
600 left: CypherValue,
601 right: CypherValue,
602) -> Result<CypherValue, CypherError> {
603 match op {
604 BinaryOp::Add => match (&left, &right) {
605 (CypherValue::Integer(a), CypherValue::Integer(b)) => Ok(CypherValue::Integer(a + b)),
606 (CypherValue::Float(a), CypherValue::Float(b)) => Ok(CypherValue::Float(a + b)),
607 (CypherValue::Integer(a), CypherValue::Float(b)) => {
608 Ok(CypherValue::Float(*a as f64 + b))
609 }
610 (CypherValue::Float(a), CypherValue::Integer(b)) => {
611 Ok(CypherValue::Float(a + *b as f64))
612 }
613 (CypherValue::String(a), CypherValue::String(b)) => {
614 Ok(CypherValue::String(format!("{}{}", a, b)))
615 }
616 (CypherValue::List(a), CypherValue::List(b)) => {
617 let mut result = a.clone();
618 result.extend(b.clone());
619 Ok(CypherValue::List(result))
620 }
621 _ => Err(CypherError::TypeMismatch(format!(
622 "cannot add {:?} and {:?}",
623 left, right
624 ))),
625 },
626 BinaryOp::Subtract => match (&left, &right) {
627 (CypherValue::Integer(a), CypherValue::Integer(b)) => Ok(CypherValue::Integer(a - b)),
628 (CypherValue::Float(a), CypherValue::Float(b)) => Ok(CypherValue::Float(a - b)),
629 (CypherValue::Integer(a), CypherValue::Float(b)) => {
630 Ok(CypherValue::Float(*a as f64 - b))
631 }
632 (CypherValue::Float(a), CypherValue::Integer(b)) => {
633 Ok(CypherValue::Float(a - *b as f64))
634 }
635 _ => Err(CypherError::TypeMismatch(format!(
636 "cannot subtract {:?} and {:?}",
637 left, right
638 ))),
639 },
640 BinaryOp::Multiply => match (&left, &right) {
641 (CypherValue::Integer(a), CypherValue::Integer(b)) => Ok(CypherValue::Integer(a * b)),
642 (CypherValue::Float(a), CypherValue::Float(b)) => Ok(CypherValue::Float(a * b)),
643 (CypherValue::Integer(a), CypherValue::Float(b)) => {
644 Ok(CypherValue::Float(*a as f64 * b))
645 }
646 (CypherValue::Float(a), CypherValue::Integer(b)) => {
647 Ok(CypherValue::Float(a * *b as f64))
648 }
649 _ => Err(CypherError::TypeMismatch(format!(
650 "cannot multiply {:?} and {:?}",
651 left, right
652 ))),
653 },
654 BinaryOp::Divide => match (&left, &right) {
655 (CypherValue::Integer(a), CypherValue::Integer(b)) => {
656 if *b == 0 {
657 return Err(CypherError::DivisionByZero);
658 }
659 Ok(CypherValue::Float(*a as f64 / *b as f64))
660 }
661 (CypherValue::Float(a), CypherValue::Float(b)) => {
662 if *b == 0.0 {
663 return Err(CypherError::DivisionByZero);
664 }
665 Ok(CypherValue::Float(a / b))
666 }
667 (CypherValue::Integer(a), CypherValue::Float(b)) => {
668 if *b == 0.0 {
669 return Err(CypherError::DivisionByZero);
670 }
671 Ok(CypherValue::Float(*a as f64 / b))
672 }
673 (CypherValue::Float(a), CypherValue::Integer(b)) => {
674 if *b == 0 {
675 return Err(CypherError::DivisionByZero);
676 }
677 Ok(CypherValue::Float(a / *b as f64))
678 }
679 _ => Err(CypherError::TypeMismatch(format!(
680 "cannot divide {:?} by {:?}",
681 left, right
682 ))),
683 },
684 BinaryOp::Modulo => match (&left, &right) {
685 (CypherValue::Integer(a), CypherValue::Integer(b)) => {
686 if *b == 0 {
687 return Err(CypherError::DivisionByZero);
688 }
689 Ok(CypherValue::Integer(a % b))
690 }
691 _ => Err(CypherError::TypeMismatch(format!(
692 "cannot modulo {:?} and {:?}",
693 left, right
694 ))),
695 },
696 BinaryOp::Power => match (&left, &right) {
697 (CypherValue::Integer(a), CypherValue::Integer(b)) => {
698 if *b < 0 {
699 return Err(CypherError::TypeMismatch(
700 "negative integer exponents are not supported for integer base; use a float base".into(),
701 ));
702 }
703 if *b > u32::MAX as i64 {
704 return Err(CypherError::TypeMismatch(
705 "integer exponent is too large".into(),
706 ));
707 }
708 Ok(CypherValue::Integer(a.pow(*b as u32)))
709 }
710 (CypherValue::Float(a), CypherValue::Float(b)) => Ok(CypherValue::Float(a.powf(*b))),
711 (CypherValue::Integer(a), CypherValue::Float(b)) => {
712 Ok(CypherValue::Float((*a as f64).powf(*b)))
713 }
714 (CypherValue::Float(a), CypherValue::Integer(b)) => {
715 Ok(CypherValue::Float(a.powf(*b as f64)))
716 }
717 _ => Err(CypherError::TypeMismatch(format!(
718 "cannot power {:?} and {:?}",
719 left, right
720 ))),
721 },
722 BinaryOp::Eq => Ok(cypher_value_eq(&left, &right)),
723 BinaryOp::Ne => {
724 let eq = cypher_value_eq(&left, &right);
725 match eq {
726 CypherValue::Null => Ok(CypherValue::Null),
727 CypherValue::Boolean(b) => Ok(CypherValue::Boolean(!b)),
728 _ => unreachable!(),
729 }
730 }
731 BinaryOp::Lt => Ok(match compare_cypher_values(&left, &right) {
732 None => CypherValue::Null,
733 Some(ord) => CypherValue::Boolean(ord == std::cmp::Ordering::Less),
734 }),
735 BinaryOp::Gt => Ok(match compare_cypher_values(&left, &right) {
736 None => CypherValue::Null,
737 Some(ord) => CypherValue::Boolean(ord == std::cmp::Ordering::Greater),
738 }),
739 BinaryOp::Le => Ok(match compare_cypher_values(&left, &right) {
740 None => CypherValue::Null,
741 Some(ord) => CypherValue::Boolean(ord != std::cmp::Ordering::Greater),
742 }),
743 BinaryOp::Ge => Ok(match compare_cypher_values(&left, &right) {
744 None => CypherValue::Null,
745 Some(ord) => CypherValue::Boolean(ord != std::cmp::Ordering::Less),
746 }),
747 BinaryOp::And => match (is_truthy(&left), is_truthy(&right)) {
748 (Some(false), _) | (_, Some(false)) => Ok(CypherValue::Boolean(false)),
749 (Some(true), Some(true)) => Ok(CypherValue::Boolean(true)),
750 _ => Ok(CypherValue::Null),
751 },
752 BinaryOp::Or => match (is_truthy(&left), is_truthy(&right)) {
753 (Some(true), _) | (_, Some(true)) => Ok(CypherValue::Boolean(true)),
754 (Some(false), Some(false)) => Ok(CypherValue::Boolean(false)),
755 _ => Ok(CypherValue::Null),
756 },
757 BinaryOp::Xor => match (is_truthy(&left), is_truthy(&right)) {
758 (Some(a), Some(b)) => Ok(CypherValue::Boolean(a ^ b)),
759 _ => Ok(CypherValue::Null),
760 },
761 BinaryOp::StartsWith => match (&left, &right) {
762 (CypherValue::String(a), CypherValue::String(b)) => {
763 Ok(CypherValue::Boolean(a.starts_with(b)))
764 }
765 (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
766 _ => Err(CypherError::TypeMismatch(format!(
767 "STARTS WITH requires strings, got {:?} and {:?}",
768 left, right
769 ))),
770 },
771 BinaryOp::EndsWith => match (&left, &right) {
772 (CypherValue::String(a), CypherValue::String(b)) => {
773 Ok(CypherValue::Boolean(a.ends_with(b)))
774 }
775 (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
776 _ => Err(CypherError::TypeMismatch(format!(
777 "ENDS WITH requires strings, got {:?} and {:?}",
778 left, right
779 ))),
780 },
781 BinaryOp::Contains => match (&left, &right) {
782 (CypherValue::String(a), CypherValue::String(b)) => {
783 Ok(CypherValue::Boolean(a.contains(b)))
784 }
785 (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
786 _ => Err(CypherError::TypeMismatch(format!(
787 "CONTAINS requires strings, got {:?} and {:?}",
788 left, right
789 ))),
790 },
791 BinaryOp::In => match (&right, &left) {
792 (CypherValue::List(list), _) => {
793 let found = list.iter().any(|item| item == &left);
794 Ok(CypherValue::Boolean(found))
795 }
796 (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
797 _ => Err(CypherError::TypeMismatch(format!(
798 "IN requires a list on the right side, got {:?}",
799 right
800 ))),
801 },
802 }
803}
804
805fn evaluate_function(
806 name: &str,
807 args: &[Expression],
808 _distinct: bool,
809 bindings: &Bindings,
810 graph: &Graph<impl CypherNode, impl CypherEdge>,
811 parameters: &Parameters,
812) -> Result<CypherValue, CypherError> {
813 let eval_args = || {
815 args.iter()
816 .map(|arg| evaluate_expression(arg, bindings, graph, parameters))
817 .collect::<Result<Vec<_>, _>>()
818 };
819
820 match name.to_lowercase().as_str() {
821 "tostring" => {
822 let vals = eval_args()?;
823 if vals.len() != 1 {
824 return Err(CypherError::InvalidQuery(
825 "toString() takes exactly 1 argument".into(),
826 ));
827 }
828 let s = match &vals[0] {
829 CypherValue::String(s) => s.clone(),
830 CypherValue::Integer(i) => i.to_string(),
831 CypherValue::Float(f) => f.to_string(),
832 CypherValue::Boolean(b) => b.to_string(),
833 CypherValue::Null => "null".to_string(),
834 CypherValue::List(_) | CypherValue::Map(_) => {
835 return Err(CypherError::TypeMismatch(
836 "toString() does not support lists or maps".into(),
837 ));
838 }
839 };
840 Ok(CypherValue::String(s))
841 }
842 "tointeger" | "toint" => {
843 let vals = eval_args()?;
844 if vals.len() != 1 {
845 return Err(CypherError::InvalidQuery(
846 "toInteger() takes exactly 1 argument".into(),
847 ));
848 }
849 match &vals[0] {
850 CypherValue::Integer(i) => Ok(CypherValue::Integer(*i)),
851 CypherValue::Float(f) => Ok(CypherValue::Integer(*f as i64)),
852 CypherValue::String(s) => {
853 s.parse::<i64>().map(CypherValue::Integer).map_err(|_| {
854 CypherError::TypeMismatch(format!("cannot convert '{}' to integer", s))
855 })
856 }
857 CypherValue::Boolean(true) => Ok(CypherValue::Integer(1)),
858 CypherValue::Boolean(false) => Ok(CypherValue::Integer(0)),
859 CypherValue::Null => Ok(CypherValue::Null),
860 _ => Err(CypherError::TypeMismatch(format!(
861 "cannot convert {:?} to integer",
862 vals[0]
863 ))),
864 }
865 }
866 "tofloat" => {
867 let vals = eval_args()?;
868 if vals.len() != 1 {
869 return Err(CypherError::InvalidQuery(
870 "toFloat() takes exactly 1 argument".into(),
871 ));
872 }
873 match &vals[0] {
874 CypherValue::Float(f) => Ok(CypherValue::Float(*f)),
875 CypherValue::Integer(i) => Ok(CypherValue::Float(*i as f64)),
876 CypherValue::String(s) => s.parse::<f64>().map(CypherValue::Float).map_err(|_| {
877 CypherError::TypeMismatch(format!("cannot convert '{}' to float", s))
878 }),
879 CypherValue::Boolean(true) => Ok(CypherValue::Float(1.0)),
880 CypherValue::Boolean(false) => Ok(CypherValue::Float(0.0)),
881 CypherValue::Null => Ok(CypherValue::Null),
882 _ => Err(CypherError::TypeMismatch(format!(
883 "cannot convert {:?} to float",
884 vals[0]
885 ))),
886 }
887 }
888 "coalesce" => {
889 for arg in args {
890 let val = evaluate_expression(arg, bindings, graph, parameters)?;
891 if !matches!(val, CypherValue::Null) {
892 return Ok(val);
893 }
894 }
895 Ok(CypherValue::Null)
896 }
897 "head" => {
898 let vals = eval_args()?;
899 if vals.len() != 1 {
900 return Err(CypherError::InvalidQuery(
901 "head() takes exactly 1 argument".into(),
902 ));
903 }
904 match &vals[0] {
905 CypherValue::List(list) => Ok(list.first().cloned().unwrap_or(CypherValue::Null)),
906 CypherValue::Null => Ok(CypherValue::Null),
907 _ => Err(CypherError::TypeMismatch(format!(
908 "head() requires a list, got {:?}",
909 vals[0]
910 ))),
911 }
912 }
913 "last" => {
914 let vals = eval_args()?;
915 if vals.len() != 1 {
916 return Err(CypherError::InvalidQuery(
917 "last() takes exactly 1 argument".into(),
918 ));
919 }
920 match &vals[0] {
921 CypherValue::List(list) => Ok(list.last().cloned().unwrap_or(CypherValue::Null)),
922 CypherValue::Null => Ok(CypherValue::Null),
923 _ => Err(CypherError::TypeMismatch(format!(
924 "last() requires a list, got {:?}",
925 vals[0]
926 ))),
927 }
928 }
929 "size" | "length" => {
930 let vals = eval_args()?;
931 if vals.len() != 1 {
932 return Err(CypherError::InvalidQuery(format!(
933 "{}() takes exactly 1 argument",
934 name
935 )));
936 }
937 match &vals[0] {
938 CypherValue::List(list) => Ok(CypherValue::Integer(list.len() as i64)),
939 CypherValue::String(s) => Ok(CypherValue::Integer(s.len() as i64)),
940 CypherValue::Null => Ok(CypherValue::Null),
941 _ => Err(CypherError::TypeMismatch(format!(
942 "{}() requires a list or string, got {:?}",
943 name, vals[0]
944 ))),
945 }
946 }
947 "toupper" => {
948 let vals = eval_args()?;
949 if vals.len() != 1 {
950 return Err(CypherError::InvalidQuery(
951 "toUpper() takes exactly 1 argument".into(),
952 ));
953 }
954 match &vals[0] {
955 CypherValue::String(s) => Ok(CypherValue::String(s.to_uppercase())),
956 CypherValue::Null => Ok(CypherValue::Null),
957 _ => Err(CypherError::TypeMismatch(format!(
958 "toUpper() requires a string, got {:?}",
959 vals[0]
960 ))),
961 }
962 }
963 "tolower" => {
964 let vals = eval_args()?;
965 if vals.len() != 1 {
966 return Err(CypherError::InvalidQuery(
967 "toLower() takes exactly 1 argument".into(),
968 ));
969 }
970 match &vals[0] {
971 CypherValue::String(s) => Ok(CypherValue::String(s.to_lowercase())),
972 CypherValue::Null => Ok(CypherValue::Null),
973 _ => Err(CypherError::TypeMismatch(format!(
974 "toLower() requires a string, got {:?}",
975 vals[0]
976 ))),
977 }
978 }
979 "trim" => {
980 let vals = eval_args()?;
981 if vals.len() != 1 {
982 return Err(CypherError::InvalidQuery(
983 "trim() takes exactly 1 argument".into(),
984 ));
985 }
986 match &vals[0] {
987 CypherValue::String(s) => Ok(CypherValue::String(s.trim().to_string())),
988 CypherValue::Null => Ok(CypherValue::Null),
989 _ => Err(CypherError::TypeMismatch(format!(
990 "trim() requires a string, got {:?}",
991 vals[0]
992 ))),
993 }
994 }
995 "ltrim" => {
996 let vals = eval_args()?;
997 if vals.len() != 1 {
998 return Err(CypherError::InvalidQuery(
999 "ltrim() takes exactly 1 argument".into(),
1000 ));
1001 }
1002 match &vals[0] {
1003 CypherValue::String(s) => Ok(CypherValue::String(s.trim_start().to_string())),
1004 CypherValue::Null => Ok(CypherValue::Null),
1005 _ => Err(CypherError::TypeMismatch(format!(
1006 "ltrim() requires a string, got {:?}",
1007 vals[0]
1008 ))),
1009 }
1010 }
1011 "rtrim" => {
1012 let vals = eval_args()?;
1013 if vals.len() != 1 {
1014 return Err(CypherError::InvalidQuery(
1015 "rtrim() takes exactly 1 argument".into(),
1016 ));
1017 }
1018 match &vals[0] {
1019 CypherValue::String(s) => Ok(CypherValue::String(s.trim_end().to_string())),
1020 CypherValue::Null => Ok(CypherValue::Null),
1021 _ => Err(CypherError::TypeMismatch(format!(
1022 "rtrim() requires a string, got {:?}",
1023 vals[0]
1024 ))),
1025 }
1026 }
1027 "substring" => {
1028 let vals = eval_args()?;
1029 if vals.len() < 2 || vals.len() > 3 {
1030 return Err(CypherError::InvalidQuery(
1031 "substring() takes 2 or 3 arguments".into(),
1032 ));
1033 }
1034 match (&vals[0], &vals[1]) {
1035 (CypherValue::String(s), CypherValue::Integer(start)) => {
1036 let start = *start as usize;
1037 let len = vals.get(2).and_then(|v| match v {
1038 CypherValue::Integer(i) => Some(*i as usize),
1039 _ => None,
1040 });
1041 let result = if let Some(len) = len {
1042 s.chars().skip(start).take(len).collect()
1043 } else {
1044 s.chars().skip(start).collect()
1045 };
1046 Ok(CypherValue::String(result))
1047 }
1048 (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
1049 _ => Err(CypherError::TypeMismatch(format!(
1050 "substring() requires (string, integer[, integer]), got {:?}",
1051 vals
1052 ))),
1053 }
1054 }
1055 "replace" => {
1056 let vals = eval_args()?;
1057 if vals.len() != 3 {
1058 return Err(CypherError::InvalidQuery(
1059 "replace() takes exactly 3 arguments".into(),
1060 ));
1061 }
1062 match (&vals[0], &vals[1], &vals[2]) {
1063 (CypherValue::String(s), CypherValue::String(old), CypherValue::String(new)) => {
1064 Ok(CypherValue::String(s.replace(old, new)))
1065 }
1066 (CypherValue::Null, _, _)
1067 | (_, CypherValue::Null, _)
1068 | (_, _, CypherValue::Null) => Ok(CypherValue::Null),
1069 _ => Err(CypherError::TypeMismatch(format!(
1070 "replace() requires (string, string, string), got {:?}",
1071 vals
1072 ))),
1073 }
1074 }
1075 "split" => {
1076 let vals = eval_args()?;
1077 if vals.len() != 2 {
1078 return Err(CypherError::InvalidQuery(
1079 "split() takes exactly 2 arguments".into(),
1080 ));
1081 }
1082 match (&vals[0], &vals[1]) {
1083 (CypherValue::String(s), CypherValue::String(delim)) => {
1084 let parts: Vec<CypherValue> = s
1085 .split(delim)
1086 .map(|p| CypherValue::String(p.to_string()))
1087 .collect();
1088 Ok(CypherValue::List(parts))
1089 }
1090 (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
1091 _ => Err(CypherError::TypeMismatch(format!(
1092 "split() requires (string, string), got {:?}",
1093 vals
1094 ))),
1095 }
1096 }
1097 "reverse" => {
1098 let vals = eval_args()?;
1099 if vals.len() != 1 {
1100 return Err(CypherError::InvalidQuery(
1101 "reverse() takes exactly 1 argument".into(),
1102 ));
1103 }
1104 match &vals[0] {
1105 CypherValue::String(s) => Ok(CypherValue::String(s.chars().rev().collect())),
1106 CypherValue::List(list) => {
1107 let mut reversed = list.clone();
1108 reversed.reverse();
1109 Ok(CypherValue::List(reversed))
1110 }
1111 CypherValue::Null => Ok(CypherValue::Null),
1112 _ => Err(CypherError::TypeMismatch(format!(
1113 "reverse() requires a string or list, got {:?}",
1114 vals[0]
1115 ))),
1116 }
1117 }
1118 "abs" => {
1119 let vals = eval_args()?;
1120 if vals.len() != 1 {
1121 return Err(CypherError::InvalidQuery(
1122 "abs() takes exactly 1 argument".into(),
1123 ));
1124 }
1125 match &vals[0] {
1126 CypherValue::Integer(i) => Ok(CypherValue::Integer(i.abs())),
1127 CypherValue::Float(f) => Ok(CypherValue::Float(f.abs())),
1128 CypherValue::Null => Ok(CypherValue::Null),
1129 _ => Err(CypherError::TypeMismatch(format!(
1130 "abs() requires a number, got {:?}",
1131 vals[0]
1132 ))),
1133 }
1134 }
1135 "ceil" => {
1136 let vals = eval_args()?;
1137 if vals.len() != 1 {
1138 return Err(CypherError::InvalidQuery(
1139 "ceil() takes exactly 1 argument".into(),
1140 ));
1141 }
1142 match &vals[0] {
1143 CypherValue::Float(f) => Ok(CypherValue::Float(f.ceil())),
1144 CypherValue::Integer(i) => Ok(CypherValue::Float(*i as f64)),
1145 CypherValue::Null => Ok(CypherValue::Null),
1146 _ => Err(CypherError::TypeMismatch(format!(
1147 "ceil() requires a number, got {:?}",
1148 vals[0]
1149 ))),
1150 }
1151 }
1152 "floor" => {
1153 let vals = eval_args()?;
1154 if vals.len() != 1 {
1155 return Err(CypherError::InvalidQuery(
1156 "floor() takes exactly 1 argument".into(),
1157 ));
1158 }
1159 match &vals[0] {
1160 CypherValue::Float(f) => Ok(CypherValue::Float(f.floor())),
1161 CypherValue::Integer(i) => Ok(CypherValue::Float(*i as f64)),
1162 CypherValue::Null => Ok(CypherValue::Null),
1163 _ => Err(CypherError::TypeMismatch(format!(
1164 "floor() requires a number, got {:?}",
1165 vals[0]
1166 ))),
1167 }
1168 }
1169 "round" => {
1170 let vals = eval_args()?;
1171 if vals.len() != 1 {
1172 return Err(CypherError::InvalidQuery(
1173 "round() takes exactly 1 argument".into(),
1174 ));
1175 }
1176 match &vals[0] {
1177 CypherValue::Float(f) => Ok(CypherValue::Float(f.round())),
1178 CypherValue::Integer(i) => Ok(CypherValue::Float(*i as f64)),
1179 CypherValue::Null => Ok(CypherValue::Null),
1180 _ => Err(CypherError::TypeMismatch(format!(
1181 "round() requires a number, got {:?}",
1182 vals[0]
1183 ))),
1184 }
1185 }
1186 "sign" => {
1187 let vals = eval_args()?;
1188 if vals.len() != 1 {
1189 return Err(CypherError::InvalidQuery(
1190 "sign() takes exactly 1 argument".into(),
1191 ));
1192 }
1193 match &vals[0] {
1194 CypherValue::Integer(i) => Ok(CypherValue::Integer(i.signum())),
1195 CypherValue::Float(f) => Ok(CypherValue::Integer(f.signum() as i64)),
1196 CypherValue::Null => Ok(CypherValue::Null),
1197 _ => Err(CypherError::TypeMismatch(format!(
1198 "sign() requires a number, got {:?}",
1199 vals[0]
1200 ))),
1201 }
1202 }
1203 "sqrt" => {
1204 let vals = eval_args()?;
1205 if vals.len() != 1 {
1206 return Err(CypherError::InvalidQuery(
1207 "sqrt() takes exactly 1 argument".into(),
1208 ));
1209 }
1210 match &vals[0] {
1211 CypherValue::Float(f) => Ok(CypherValue::Float(f.sqrt())),
1212 CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).sqrt())),
1213 CypherValue::Null => Ok(CypherValue::Null),
1214 _ => Err(CypherError::TypeMismatch(format!(
1215 "sqrt() requires a number, got {:?}",
1216 vals[0]
1217 ))),
1218 }
1219 }
1220 "rand" => {
1221 if !args.is_empty() {
1222 return Err(CypherError::InvalidQuery(
1223 "rand() takes no arguments".into(),
1224 ));
1225 }
1226 #[cfg(feature = "rng-rand")]
1227 {
1228 Ok(CypherValue::Float(rand::random::<f64>()))
1229 }
1230 #[cfg(all(feature = "rng-fastrand", not(feature = "rng-rand")))]
1231 {
1232 Ok(CypherValue::Float(fastrand::f64()))
1233 }
1234 #[cfg(not(any(feature = "rng-rand", feature = "rng-fastrand")))]
1235 {
1236 Err(CypherError::Unsupported(
1237 "rand() requires an RNG feature to be enabled (rng-rand or rng-fastrand)"
1238 .into(),
1239 ))
1240 }
1241 }
1242 "range" => {
1243 let vals = eval_args()?;
1244 if vals.len() < 2 || vals.len() > 3 {
1245 return Err(CypherError::InvalidQuery(
1246 "range() takes 2 or 3 arguments".into(),
1247 ));
1248 }
1249 let start = match &vals[0] {
1250 CypherValue::Integer(i) => *i,
1251 _ => {
1252 return Err(CypherError::TypeMismatch(format!(
1253 "range() requires integers, got {:?}",
1254 vals
1255 )));
1256 }
1257 };
1258 let end = match &vals[1] {
1259 CypherValue::Integer(i) => *i,
1260 _ => {
1261 return Err(CypherError::TypeMismatch(format!(
1262 "range() requires integers, got {:?}",
1263 vals
1264 )));
1265 }
1266 };
1267 let step = match vals.get(2) {
1268 Some(v) => match v {
1269 CypherValue::Integer(i) => *i,
1270 _ => {
1271 return Err(CypherError::TypeMismatch(
1272 "range() step must be an integer".into(),
1273 ));
1274 }
1275 },
1276 None => 1,
1277 };
1278 if step == 0 {
1279 return Err(CypherError::InvalidQuery(
1280 "range() step cannot be zero".into(),
1281 ));
1282 }
1283 let mut result = Vec::new();
1284 let mut current = start;
1285 if step > 0 {
1286 while current <= end {
1287 result.push(CypherValue::Integer(current));
1288 current += step;
1289 }
1290 } else {
1291 while current >= end {
1292 result.push(CypherValue::Integer(current));
1293 current += step;
1294 }
1295 }
1296 Ok(CypherValue::List(result))
1297 }
1298 "tail" => {
1299 let vals = eval_args()?;
1300 if vals.len() != 1 {
1301 return Err(CypherError::InvalidQuery(
1302 "tail() takes exactly 1 argument".into(),
1303 ));
1304 }
1305 match &vals[0] {
1306 CypherValue::List(list) => {
1307 Ok(CypherValue::List(list.iter().skip(1).cloned().collect()))
1308 }
1309 CypherValue::Null => Ok(CypherValue::Null),
1310 _ => Err(CypherError::TypeMismatch(format!(
1311 "tail() requires a list, got {:?}",
1312 vals[0]
1313 ))),
1314 }
1315 }
1316 "type" => {
1317 if args.len() != 1 {
1318 return Err(CypherError::InvalidQuery(
1319 "type() takes exactly 1 argument".into(),
1320 ));
1321 }
1322 match &args[0] {
1323 Expression::Variable(var) => {
1324 if let Some(BoundValue::Edge(idx)) = bindings.get(var) {
1325 let data = &graph[*idx];
1326 Ok(data
1327 .rel_type()
1328 .map(|s| CypherValue::String(s.to_string()))
1329 .unwrap_or(CypherValue::Null))
1330 } else {
1331 Ok(CypherValue::Null)
1332 }
1333 }
1334 _ => Err(CypherError::TypeMismatch(
1335 "type() requires a relationship variable".into(),
1336 )),
1337 }
1338 }
1339 "labels" => {
1340 if args.len() != 1 {
1341 return Err(CypherError::InvalidQuery(
1342 "labels() takes exactly 1 argument".into(),
1343 ));
1344 }
1345 match &args[0] {
1346 Expression::Variable(var) => {
1347 if let Some(BoundValue::Node(idx)) = bindings.get(var) {
1348 let data = &graph[*idx];
1349 let labels: Vec<CypherValue> =
1350 data.labels().into_iter().map(CypherValue::String).collect();
1351 Ok(CypherValue::List(labels))
1352 } else {
1353 Ok(CypherValue::Null)
1354 }
1355 }
1356 _ => Err(CypherError::TypeMismatch(
1357 "labels() requires a node variable".into(),
1358 )),
1359 }
1360 }
1361 "properties" => {
1362 if args.len() != 1 {
1363 return Err(CypherError::InvalidQuery(
1364 "properties() takes exactly 1 argument".into(),
1365 ));
1366 }
1367 match &args[0] {
1368 Expression::Variable(var) => {
1369 if let Some(bound) = bindings.get(var) {
1370 let props = match bound {
1371 BoundValue::Node(idx) => graph[*idx].properties(),
1372 BoundValue::Edge(idx) => graph[*idx].properties(),
1373 };
1374 Ok(CypherValue::Map(props))
1375 } else {
1376 Ok(CypherValue::Null)
1377 }
1378 }
1379 _ => Err(CypherError::TypeMismatch(
1380 "properties() requires a variable".into(),
1381 )),
1382 }
1383 }
1384 "keys" => {
1385 if args.len() != 1 {
1386 return Err(CypherError::InvalidQuery(
1387 "keys() takes exactly 1 argument".into(),
1388 ));
1389 }
1390 match &args[0] {
1391 Expression::Variable(var) => {
1392 if let Some(bound) = bindings.get(var) {
1393 let props = match bound {
1394 BoundValue::Node(idx) => graph[*idx].properties(),
1395 BoundValue::Edge(idx) => graph[*idx].properties(),
1396 };
1397 let keys: Vec<CypherValue> =
1398 props.keys().cloned().map(CypherValue::String).collect();
1399 Ok(CypherValue::List(keys))
1400 } else {
1401 Ok(CypherValue::Null)
1402 }
1403 }
1404 _ => Err(CypherError::TypeMismatch(
1405 "keys() requires a variable".into(),
1406 )),
1407 }
1408 }
1409 "exists" => {
1410 if args.len() != 1 {
1411 return Err(CypherError::InvalidQuery(
1412 "exists() takes exactly 1 argument".into(),
1413 ));
1414 }
1415 match &args[0] {
1416 Expression::Property(var, prop) => {
1417 let exists = if let Some(bound) = bindings.get(var) {
1418 match bound {
1419 BoundValue::Node(idx) => graph[*idx].get(prop).is_some(),
1420 BoundValue::Edge(idx) => graph[*idx].get(prop).is_some(),
1421 }
1422 } else {
1423 false
1424 };
1425 Ok(CypherValue::Boolean(exists))
1426 }
1427 _ => {
1428 let val = evaluate_expression(&args[0], bindings, graph, parameters)?;
1430 Ok(CypherValue::Boolean(!matches!(val, CypherValue::Null)))
1431 }
1432 }
1433 }
1434 _ => Err(CypherError::Unsupported(format!(
1435 "unsupported function: {}",
1436 name
1437 ))),
1438 }
1439}
1440
1441fn evaluate_case(
1442 case: &CaseExpr,
1443 bindings: &Bindings,
1444 graph: &Graph<impl CypherNode, impl CypherEdge>,
1445 parameters: &Parameters,
1446) -> Result<CypherValue, CypherError> {
1447 if let Some(scrutinee) = &case.scrutinee {
1448 let scrutinee_val = evaluate_expression(scrutinee, bindings, graph, parameters)?;
1449 for (when, then) in &case.alternatives {
1450 let when_val = evaluate_expression(when, bindings, graph, parameters)?;
1451 if scrutinee_val == when_val {
1452 return evaluate_expression(then, bindings, graph, parameters);
1453 }
1454 }
1455 } else {
1456 for (when, then) in &case.alternatives {
1457 let when_val = evaluate_expression(when, bindings, graph, parameters)?;
1458 if is_truthy(&when_val) == Some(true) {
1459 return evaluate_expression(then, bindings, graph, parameters);
1460 }
1461 }
1462 }
1463 if let Some(default) = &case.default {
1464 evaluate_expression(default, bindings, graph, parameters)
1465 } else {
1466 Ok(CypherValue::Null)
1467 }
1468}
1469
1470fn evaluate_expression(
1471 expr: &Expression,
1472 bindings: &Bindings,
1473 graph: &Graph<impl CypherNode, impl CypherEdge>,
1474 parameters: &Parameters,
1475) -> Result<CypherValue, CypherError> {
1476 match expr {
1477 Expression::Variable(var) => {
1478 if let Some(bound) = bindings.get(var) {
1479 match bound {
1480 BoundValue::Node(_) | BoundValue::Edge(_) => {
1481 Err(CypherError::TypeMismatch(format!(
1482 "node/edge variable '{}' cannot be used in a scalar expression",
1483 var
1484 )))
1485 }
1486 }
1487 } else {
1488 Ok(CypherValue::Null)
1489 }
1490 }
1491 Expression::Property(var, prop) => {
1492 if let Some(bound) = bindings.get(var) {
1493 match bound {
1494 BoundValue::Node(idx) => {
1495 let data = &graph[*idx];
1496 Ok(data.get(prop).cloned().unwrap_or(CypherValue::Null))
1497 }
1498 BoundValue::Edge(idx) => {
1499 let data = &graph[*idx];
1500 Ok(data.get(prop).cloned().unwrap_or(CypherValue::Null))
1501 }
1502 }
1503 } else {
1504 Ok(CypherValue::Null)
1505 }
1506 }
1507 Expression::Literal(v) => Ok(v.clone()),
1508 Expression::All => Ok(CypherValue::Null),
1509 Expression::Unary(op, expr) => {
1510 let val = evaluate_expression(expr, bindings, graph, parameters)?;
1511 match op {
1512 UnaryOp::Not => match val {
1513 CypherValue::Null => Ok(CypherValue::Null),
1514 _ => Ok(CypherValue::Boolean(is_truthy(&val) == Some(false))),
1515 },
1516 UnaryOp::Negate => match val {
1517 CypherValue::Integer(i) => Ok(CypherValue::Integer(-i)),
1518 CypherValue::Float(f) => Ok(CypherValue::Float(-f)),
1519 CypherValue::Null => Ok(CypherValue::Null),
1520 _ => Err(CypherError::TypeMismatch(format!(
1521 "cannot negate {:?}",
1522 val
1523 ))),
1524 },
1525 UnaryOp::Plus => match val {
1526 CypherValue::Integer(_) | CypherValue::Float(_) | CypherValue::Null => Ok(val),
1527 _ => Err(CypherError::TypeMismatch(format!(
1528 "cannot apply + to {:?}",
1529 val
1530 ))),
1531 },
1532 }
1533 }
1534 Expression::Binary(op, left, right) => {
1535 let left_val = evaluate_expression(left, bindings, graph, parameters)?;
1536 let right_val = evaluate_expression(right, bindings, graph, parameters)?;
1537 evaluate_binary_op(*op, left_val, right_val)
1538 }
1539 Expression::List(items) => {
1540 let values = items
1541 .iter()
1542 .map(|item| evaluate_expression(item, bindings, graph, parameters))
1543 .collect::<Result<Vec<_>, _>>()?;
1544 Ok(CypherValue::List(values))
1545 }
1546 Expression::FunctionCall {
1547 name,
1548 args,
1549 distinct,
1550 } => evaluate_function(name, args, *distinct, bindings, graph, parameters),
1551 Expression::Parameter(name) => parameters
1552 .get(name)
1553 .cloned()
1554 .ok_or_else(|| CypherError::InvalidQuery(format!("missing query parameter: ${name}"))),
1555 Expression::Case(case) => evaluate_case(case, bindings, graph, parameters),
1556 }
1557}
1558
1559fn evaluate_where(
1560 expr: &WhereExpr,
1561 bindings: &Bindings,
1562 graph: &Graph<impl CypherNode, impl CypherEdge>,
1563 parameters: &Parameters,
1564) -> bool {
1565 match expr {
1566 WhereExpr::Eq(left, right) => {
1567 match (
1568 evaluate_expression(left, bindings, graph, parameters),
1569 evaluate_expression(right, bindings, graph, parameters),
1570 ) {
1571 (Ok(l), Ok(r)) => matches!(cypher_value_eq(&l, &r), CypherValue::Boolean(true)),
1572 _ => false,
1573 }
1574 }
1575 WhereExpr::NotEq(left, right) => {
1576 match (
1577 evaluate_expression(left, bindings, graph, parameters),
1578 evaluate_expression(right, bindings, graph, parameters),
1579 ) {
1580 (Ok(l), Ok(r)) => matches!(cypher_value_eq(&l, &r), CypherValue::Boolean(false)),
1581 _ => false,
1582 }
1583 }
1584 WhereExpr::Lt(left, right) => {
1585 match (
1586 evaluate_expression(left, bindings, graph, parameters),
1587 evaluate_expression(right, bindings, graph, parameters),
1588 ) {
1589 (Ok(l), Ok(r)) => compare_cypher_values(&l, &r) == Some(std::cmp::Ordering::Less),
1590 _ => false,
1591 }
1592 }
1593 WhereExpr::Gt(left, right) => {
1594 match (
1595 evaluate_expression(left, bindings, graph, parameters),
1596 evaluate_expression(right, bindings, graph, parameters),
1597 ) {
1598 (Ok(l), Ok(r)) => {
1599 compare_cypher_values(&l, &r) == Some(std::cmp::Ordering::Greater)
1600 }
1601 _ => false,
1602 }
1603 }
1604 WhereExpr::Le(left, right) => {
1605 match (
1606 evaluate_expression(left, bindings, graph, parameters),
1607 evaluate_expression(right, bindings, graph, parameters),
1608 ) {
1609 (Ok(l), Ok(r)) => {
1610 matches!(
1611 compare_cypher_values(&l, &r),
1612 Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
1613 )
1614 }
1615 _ => false,
1616 }
1617 }
1618 WhereExpr::Ge(left, right) => {
1619 match (
1620 evaluate_expression(left, bindings, graph, parameters),
1621 evaluate_expression(right, bindings, graph, parameters),
1622 ) {
1623 (Ok(l), Ok(r)) => {
1624 matches!(
1625 compare_cypher_values(&l, &r),
1626 Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal)
1627 )
1628 }
1629 _ => false,
1630 }
1631 }
1632 WhereExpr::In(expression, values) => {
1633 match evaluate_expression(expression, bindings, graph, parameters) {
1634 Ok(val) => values.iter().any(|v| v == &val),
1635 _ => false,
1636 }
1637 }
1638 WhereExpr::StartsWith(expression, prefix) => {
1639 match evaluate_expression(expression, bindings, graph, parameters) {
1640 Ok(CypherValue::String(s)) => s.starts_with(prefix),
1641 _ => false,
1642 }
1643 }
1644 WhereExpr::EndsWith(expression, suffix) => {
1645 match evaluate_expression(expression, bindings, graph, parameters) {
1646 Ok(CypherValue::String(s)) => s.ends_with(suffix),
1647 _ => false,
1648 }
1649 }
1650 WhereExpr::Contains(expression, substring) => {
1651 match evaluate_expression(expression, bindings, graph, parameters) {
1652 Ok(CypherValue::String(s)) => s.contains(substring),
1653 _ => false,
1654 }
1655 }
1656 WhereExpr::IsNull(expression) => {
1657 matches!(
1658 evaluate_expression(expression, bindings, graph, parameters),
1659 Ok(CypherValue::Null)
1660 )
1661 }
1662 WhereExpr::IsNotNull(expression) => !matches!(
1663 evaluate_expression(expression, bindings, graph, parameters),
1664 Ok(CypherValue::Null)
1665 ),
1666 WhereExpr::Not(inner) => !evaluate_where(inner, bindings, graph, parameters),
1667 WhereExpr::And(left, right) => {
1668 evaluate_where(left, bindings, graph, parameters)
1669 && evaluate_where(right, bindings, graph, parameters)
1670 }
1671 WhereExpr::Or(left, right) => {
1672 evaluate_where(left, bindings, graph, parameters)
1673 || evaluate_where(right, bindings, graph, parameters)
1674 }
1675 WhereExpr::Xor(left, right) => {
1676 evaluate_where(left, bindings, graph, parameters)
1677 ^ evaluate_where(right, bindings, graph, parameters)
1678 }
1679 }
1680}
1681
1682struct PatternMatcher<'a, N: CypherNode, E: CypherEdge> {
1689 graph: &'a Graph<N, E>,
1690 strategy: MatchStrategy,
1691}
1692
1693impl<'a, N: CypherNode, E: CypherEdge> PatternMatcher<'a, N, E> {
1694 fn new(graph: &'a Graph<N, E>, strategy: MatchStrategy) -> Self {
1695 Self { graph, strategy }
1696 }
1697
1698 fn match_patterns(&self, patterns: &[PathPattern], base_bindings: &Bindings) -> Vec<Bindings> {
1699 if patterns.is_empty() {
1700 return vec![base_bindings.clone()];
1701 }
1702
1703 let per_pattern: Vec<Vec<Bindings>> = patterns
1704 .iter()
1705 .map(|p| self.match_single_path(p, base_bindings))
1706 .collect();
1707
1708 per_pattern
1709 .iter()
1710 .multi_cartesian_product()
1711 .filter_map(|combo| {
1712 let mut merged = base_bindings.clone();
1713 for binding_set in combo {
1714 for (k, v) in binding_set {
1715 if let Some(existing) = merged.get(k) {
1716 if existing != v {
1717 return None;
1718 }
1719 } else {
1720 merged.insert(k.clone(), *v);
1721 }
1722 }
1723 }
1724 Some(merged)
1725 })
1726 .collect()
1727 }
1728
1729 fn match_single_path(&self, pattern: &PathPattern, base_bindings: &Bindings) -> Vec<Bindings> {
1730 match self.strategy {
1731 MatchStrategy::Backtrack => self.match_path_backtrack(pattern, base_bindings),
1732 MatchStrategy::Fast => self.match_path_fast(pattern, base_bindings),
1733 }
1734 }
1735
1736 fn match_path_backtrack(
1737 &self,
1738 pattern: &PathPattern,
1739 base_bindings: &Bindings,
1740 ) -> Vec<Bindings> {
1741 let mut results = Vec::new();
1742 let start_candidates = self.find_matching_nodes(&pattern.start, base_bindings);
1743
1744 for &start_node in &start_candidates {
1745 let mut bindings = base_bindings.clone();
1746 if let Some(var) = &pattern.start.variable {
1747 bindings.insert(var.clone(), BoundValue::Node(start_node));
1748 }
1749 self.match_path_hops(pattern, 0, start_node, &mut bindings, &mut results);
1750 }
1751
1752 results
1753 }
1754
1755 fn match_path_hops(
1756 &self,
1757 pattern: &PathPattern,
1758 hop_index: usize,
1759 current_node: NodeIndex,
1760 bindings: &mut Bindings,
1761 results: &mut Vec<Bindings>,
1762 ) {
1763 if hop_index >= pattern.rels.len() {
1764 results.push(bindings.clone());
1765 return;
1766 }
1767
1768 let (rel, target_node) = &pattern.rels[hop_index];
1769
1770 let candidate_edges: Vec<_> = match rel.direction {
1771 RelDirection::Right => self
1772 .graph
1773 .edges_directed(current_node, petgraph::Direction::Outgoing)
1774 .filter(|e| Self::edge_matches_rel(e, rel))
1775 .map(|e| (e, e.target()))
1776 .collect(),
1777 RelDirection::Left => self
1778 .graph
1779 .edges_directed(current_node, petgraph::Direction::Incoming)
1780 .filter(|e| Self::edge_matches_rel(e, rel))
1781 .map(|e| (e, e.source()))
1782 .collect(),
1783 RelDirection::Both => {
1784 let out = self
1785 .graph
1786 .edges_directed(current_node, petgraph::Direction::Outgoing)
1787 .filter(|e| Self::edge_matches_rel(e, rel))
1788 .map(|e| (e, e.target()));
1789 let incoming = self
1790 .graph
1791 .edges_directed(current_node, petgraph::Direction::Incoming)
1792 .filter(|e| Self::edge_matches_rel(e, rel))
1793 .map(|e| (e, e.source()));
1794 out.chain(incoming).collect()
1795 }
1796 };
1797
1798 for (edge_ref, actual_target) in candidate_edges {
1799 if self.node_matches_pattern(actual_target, target_node) {
1800 let mut new_bindings = bindings.clone();
1801
1802 if let Some(var) = &rel.variable {
1803 let edge_val = BoundValue::Edge(edge_ref.id());
1804 if let Some(existing) = new_bindings.get(var) {
1805 if existing != &edge_val {
1806 continue;
1807 }
1808 } else {
1809 new_bindings.insert(var.clone(), edge_val);
1810 }
1811 }
1812 if let Some(var) = &target_node.variable {
1813 let node_val = BoundValue::Node(actual_target);
1814 if let Some(existing) = new_bindings.get(var) {
1815 if existing != &node_val {
1816 continue;
1817 }
1818 } else {
1819 new_bindings.insert(var.clone(), node_val);
1820 }
1821 }
1822
1823 self.match_path_hops(
1824 pattern,
1825 hop_index + 1,
1826 actual_target,
1827 &mut new_bindings,
1828 results,
1829 );
1830 }
1831 }
1832 }
1833
1834 fn match_path_fast(&self, pattern: &PathPattern, base_bindings: &Bindings) -> Vec<Bindings> {
1835 self.match_path_backtrack(pattern, base_bindings)
1836 }
1837
1838 fn find_matching_nodes(
1839 &self,
1840 pattern: &NodePattern,
1841 base_bindings: &Bindings,
1842 ) -> Vec<NodeIndex> {
1843 if let Some(var) = &pattern.variable
1844 && let Some(&BoundValue::Node(idx)) = base_bindings.get(var)
1845 {
1846 if self.node_matches_pattern(idx, pattern) {
1847 return vec![idx];
1848 }
1849 return vec![];
1850 }
1851
1852 self.graph
1853 .node_indices()
1854 .filter(|&idx| self.node_matches_pattern(idx, pattern))
1855 .collect()
1856 }
1857
1858 fn node_matches_pattern(&self, node_idx: NodeIndex, pattern: &NodePattern) -> bool {
1859 let node_data = &self.graph[node_idx];
1860 if !pattern
1861 .labels
1862 .iter()
1863 .all(|label| node_data.has_label(label))
1864 {
1865 return false;
1866 }
1867 for (key, value) in &pattern.properties {
1868 if node_data.get(key) != Some(value) {
1869 return false;
1870 }
1871 }
1872 true
1873 }
1874
1875 fn edge_matches_rel(edge_ref: &EdgeReference<'_, E>, rel: &RelPattern) -> bool {
1876 let edge_data = edge_ref.weight();
1877 if let Some(ref rel_type) = rel.rel_type
1878 && !edge_data.has_rel_type(rel_type)
1879 {
1880 return false;
1881 }
1882 for (key, value) in &rel.properties {
1883 if edge_data.get(key) != Some(value) {
1884 return false;
1885 }
1886 }
1887 true
1888 }
1889}
1890
1891fn project_expression(
1896 expr: &Expression,
1897 bindings: &Bindings,
1898 graph: &Graph<impl CypherNode, impl CypherEdge>,
1899 parameters: &Parameters,
1900) -> Result<ResultValue, CypherError> {
1901 match expr {
1902 Expression::Variable(var) => {
1903 if let Some(&bound) = bindings.get(var) {
1904 match bound {
1905 BoundValue::Node(idx) => {
1906 let data = &graph[idx];
1907 Ok(ResultValue::Node {
1908 labels: data.labels(),
1909 properties: data.properties(),
1910 })
1911 }
1912 BoundValue::Edge(idx) => {
1913 let data = &graph[idx];
1914 Ok(ResultValue::Edge {
1915 rel_type: data.rel_type().map(str::to_string),
1916 properties: data.properties(),
1917 })
1918 }
1919 }
1920 } else {
1921 Ok(ResultValue::Scalar(CypherValue::Null))
1922 }
1923 }
1924 Expression::Property(var, prop) => {
1925 if let Some(&bound) = bindings.get(var) {
1926 match bound {
1927 BoundValue::Node(idx) => {
1928 let data = &graph[idx];
1929 Ok(data
1930 .get(prop)
1931 .cloned()
1932 .map(ResultValue::Scalar)
1933 .unwrap_or(ResultValue::Scalar(CypherValue::Null)))
1934 }
1935 BoundValue::Edge(idx) => {
1936 let data = &graph[idx];
1937 Ok(data
1938 .get(prop)
1939 .cloned()
1940 .map(ResultValue::Scalar)
1941 .unwrap_or(ResultValue::Scalar(CypherValue::Null)))
1942 }
1943 }
1944 } else {
1945 Ok(ResultValue::Scalar(CypherValue::Null))
1946 }
1947 }
1948 Expression::All => Ok(ResultValue::Scalar(CypherValue::Null)),
1949 other => {
1950 let val = evaluate_expression(other, bindings, graph, parameters)?;
1951 Ok(ResultValue::Scalar(val))
1952 }
1953 }
1954}
1955
1956fn column_name(item: &ReturnItem, expr_counter: &mut usize) -> Option<String> {
1957 if let Some(ref alias) = item.alias {
1958 Some(alias.clone())
1959 } else {
1960 match &item.expression {
1961 Expression::Variable(v) => Some(v.clone()),
1962 Expression::Property(v, p) => Some(format!("{}.{}", v, p)),
1963 Expression::All => None,
1964 _ => {
1965 let name = format!("expr{}", *expr_counter);
1966 *expr_counter += 1;
1967 Some(name)
1968 }
1969 }
1970 }
1971}
1972
1973fn result_columns(items: &[ReturnItem]) -> Vec<String> {
1974 let mut expr_counter = 0;
1975 items
1976 .iter()
1977 .filter_map(|item| column_name(item, &mut expr_counter))
1978 .collect()
1979}
1980
1981fn project_row(
1982 items: &[ReturnItem],
1983 bindings: &Bindings,
1984 graph: &Graph<impl CypherNode, impl CypherEdge>,
1985 parameters: &Parameters,
1986) -> Result<Row, CypherError> {
1987 let mut values = HashMap::new();
1988 let mut expr_counter = 0;
1989
1990 for item in items {
1991 let key = column_name(item, &mut expr_counter).unwrap_or_else(|| "*".to_string());
1992
1993 if matches!(item.expression, Expression::All) {
1994 for (var, bound) in bindings {
1995 match bound {
1996 BoundValue::Node(idx) => {
1997 let data = &graph[*idx];
1998 values.insert(
1999 var.clone(),
2000 ResultValue::Node {
2001 labels: data.labels(),
2002 properties: data.properties(),
2003 },
2004 );
2005 }
2006 BoundValue::Edge(idx) => {
2007 let data = &graph[*idx];
2008 values.insert(
2009 var.clone(),
2010 ResultValue::Edge {
2011 rel_type: data.rel_type().map(str::to_string),
2012 properties: data.properties(),
2013 },
2014 );
2015 }
2016 }
2017 }
2018 continue;
2019 }
2020
2021 let result_value = project_expression(&item.expression, bindings, graph, parameters)?;
2022 values.insert(key, result_value);
2023 }
2024
2025 Ok(Row { values })
2026}
2027
2028fn compare_sort_keys(a: &CypherValue, b: &CypherValue) -> std::cmp::Ordering {
2029 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
2030}
2031
2032fn stable_cypher_value_string(v: &CypherValue) -> String {
2033 match v {
2034 CypherValue::Null => "N".to_string(),
2035 CypherValue::Boolean(b) => format!("B{}", b),
2036 CypherValue::Integer(i) => format!("I{}", i),
2037 CypherValue::Float(f) => format!("F{}", f.to_bits()),
2038 CypherValue::String(s) => format!("S{}", s),
2039 CypherValue::List(items) => {
2040 let parts: Vec<_> = items.iter().map(stable_cypher_value_string).collect();
2041 format!("L[{}]", parts.join(","))
2042 }
2043 CypherValue::Map(entries) => {
2044 let mut keys: Vec<_> = entries.keys().collect();
2045 keys.sort();
2046 let parts: Vec<_> = keys
2047 .iter()
2048 .map(|k| format!("{}:{}", k, stable_cypher_value_string(&entries[*k])))
2049 .collect();
2050 format!("M{{{}}}", parts.join(","))
2051 }
2052 }
2053}
2054
2055fn stable_result_value_string(v: &ResultValue) -> String {
2056 match v {
2057 ResultValue::Scalar(cv) => stable_cypher_value_string(cv),
2058 ResultValue::Node { labels, properties } => {
2059 let mut labels: Vec<_> = labels.clone();
2060 labels.sort();
2061 let mut keys: Vec<_> = properties.keys().collect();
2062 keys.sort();
2063 let parts: Vec<_> = keys
2064 .iter()
2065 .map(|k| format!("{}:{}", k, stable_cypher_value_string(&properties[*k])))
2066 .collect();
2067 format!("Node({}:{{{}}})", labels.join(":"), parts.join(","))
2068 }
2069 ResultValue::Edge {
2070 rel_type,
2071 properties,
2072 } => {
2073 let mut keys: Vec<_> = properties.keys().collect();
2074 keys.sort();
2075 let parts: Vec<_> = keys
2076 .iter()
2077 .map(|k| format!("{}:{}", k, stable_cypher_value_string(&properties[*k])))
2078 .collect();
2079 format!(
2080 "Edge({}:{{{}}})",
2081 rel_type.as_deref().unwrap_or(""),
2082 parts.join(",")
2083 )
2084 }
2085 }
2086}
2087
2088fn row_canonical_key(row: &Row) -> String {
2089 let mut pairs: Vec<_> = row
2090 .values
2091 .iter()
2092 .map(|(k, v)| (k.clone(), stable_result_value_string(v)))
2093 .collect();
2094 pairs.sort_by(|a, b| a.0.cmp(&b.0));
2095 let mut result = String::new();
2096 for (k, v) in pairs {
2097 result.push_str(&k);
2098 result.push('=');
2099 result.push_str(&v);
2100 result.push(';');
2101 }
2102 result
2103}
2104
2105fn apply_distinct(rows: Vec<Row>) -> Vec<Row> {
2106 let mut result = Vec::new();
2107 let mut seen = std::collections::HashSet::new();
2108 for row in rows {
2109 let key = row_canonical_key(&row);
2110 if seen.insert(key) {
2111 result.push(row);
2112 }
2113 }
2114 result
2115}
2116
2117fn apply_skip_limit(rows: Vec<Row>, skip: Option<usize>, limit: Option<usize>) -> Vec<Row> {
2118 let skip = skip.unwrap_or(0);
2119 let mut rows: Vec<Row> = rows.into_iter().skip(skip).collect();
2120 if let Some(limit) = limit {
2121 rows.truncate(limit);
2122 }
2123 rows
2124}
2125
2126struct ReadQueryExecutor<'g, 'p, N: CypherNode, E: CypherEdge> {
2131 graph: &'g Graph<N, E>,
2132 strategy: MatchStrategy,
2133 parameters: &'p Parameters,
2134}
2135
2136impl<'g, 'p, N: CypherNode, E: CypherEdge> ReadQueryExecutor<'g, 'p, N, E> {
2137 fn new(graph: &'g Graph<N, E>, strategy: MatchStrategy, parameters: &'p Parameters) -> Self {
2138 Self {
2139 graph,
2140 strategy,
2141 parameters,
2142 }
2143 }
2144
2145 fn execute(self, query: CypherQuery) -> Result<QueryResult<'g, N, E>, CypherError> {
2146 let mut bindings: Vec<Bindings> = vec![HashMap::new()];
2147 let mut return_items: Option<Vec<ReturnItem>> = None;
2148 let mut return_distinct = false;
2149 let mut order_by: Option<Vec<SortItem>> = None;
2150 let mut skip: Option<usize> = None;
2151 let mut limit: Option<usize> = None;
2152
2153 for clause in query.clauses {
2154 match clause {
2155 Clause::Match {
2156 patterns,
2157 where_clause,
2158 } => {
2159 bindings = self.execute_match(patterns, bindings);
2160 if let Some(where_expr) = where_clause {
2161 bindings.retain(|b| {
2162 evaluate_where(&where_expr, b, self.graph, self.parameters)
2163 });
2164 }
2165 }
2166 Clause::OptionalMatch {
2167 patterns,
2168 where_clause,
2169 } => {
2170 bindings = self.execute_optional_match(patterns, bindings);
2171 if let Some(where_expr) = where_clause {
2172 bindings.retain(|b| {
2173 evaluate_where(&where_expr, b, self.graph, self.parameters)
2174 });
2175 }
2176 }
2177 Clause::Unwind {
2178 expression,
2179 variable,
2180 } => {
2181 bindings = self.execute_unwind(expression, variable, bindings)?;
2182 }
2183 Clause::Return { items, distinct } => {
2184 return_items = Some(items);
2185 return_distinct = distinct;
2186 }
2187 Clause::OrderBy { items } => {
2188 order_by = Some(items);
2189 }
2190 Clause::Skip { count } => {
2191 skip = Some(count);
2192 }
2193 Clause::Limit { count } => {
2194 limit = Some(count);
2195 }
2196 Clause::Create { .. }
2197 | Clause::Merge { .. }
2198 | Clause::Delete { .. }
2199 | Clause::Set { .. }
2200 | Clause::Remove { .. }
2201 | Clause::With { .. } => {
2202 unreachable!("validated by validate_read_query")
2203 }
2204 }
2205 }
2206
2207 if let Some(sort_items) = &order_by {
2210 let mut sort_keys: Vec<Vec<CypherValue>> = Vec::with_capacity(bindings.len());
2211 for binding in &bindings {
2212 let mut keys = Vec::with_capacity(sort_items.len());
2213 for item in sort_items {
2214 let val = evaluate_expression(
2215 &item.expression,
2216 binding,
2217 self.graph,
2218 self.parameters,
2219 )?;
2220 keys.push(val);
2221 }
2222 sort_keys.push(keys);
2223 }
2224 let mut indexed: Vec<_> = bindings.into_iter().zip(sort_keys).collect();
2225 indexed.sort_by(|(_, a_keys), (_, b_keys)| {
2226 for ((a_val, b_val), item) in
2227 a_keys.iter().zip(b_keys.iter()).zip(sort_items.iter())
2228 {
2229 let cmp = compare_sort_keys(a_val, b_val);
2230 if cmp != std::cmp::Ordering::Equal {
2231 return match item.direction {
2232 SortDirection::Asc => cmp,
2233 SortDirection::Desc => cmp.reverse(),
2234 };
2235 }
2236 }
2237 std::cmp::Ordering::Equal
2238 });
2239 bindings = indexed.into_iter().map(|(b, _)| b).collect();
2240 }
2241
2242 let mut rows = Vec::new();
2243 if let Some(items) = return_items {
2244 let columns = result_columns(&items);
2245 for binding in bindings {
2246 let row = project_row(&items, &binding, self.graph, self.parameters)?;
2247 rows.push(row);
2248 }
2249
2250 if return_distinct {
2251 rows = apply_distinct(rows);
2252 }
2253
2254 rows = apply_skip_limit(rows, skip, limit);
2255
2256 return Ok(QueryResult::new(columns, rows, self.graph));
2257 }
2258
2259 Ok(QueryResult::new(vec![], rows, self.graph))
2260 }
2261
2262 fn execute_match(
2263 &self,
2264 patterns: Vec<PathPattern>,
2265 input_bindings: Vec<Bindings>,
2266 ) -> Vec<Bindings> {
2267 if patterns.is_empty() {
2268 return input_bindings;
2269 }
2270
2271 let matcher = PatternMatcher::new(self.graph, self.strategy);
2272 let mut results = Vec::new();
2273 for existing_bindings in &input_bindings {
2274 let pattern_results = matcher.match_patterns(&patterns, existing_bindings);
2275 results.extend(pattern_results);
2276 }
2277 results
2278 }
2279
2280 fn execute_optional_match(
2281 &self,
2282 patterns: Vec<PathPattern>,
2283 input_bindings: Vec<Bindings>,
2284 ) -> Vec<Bindings> {
2285 if patterns.is_empty() {
2286 return input_bindings;
2287 }
2288
2289 let matcher = PatternMatcher::new(self.graph, self.strategy);
2290 let mut results = Vec::new();
2291 for existing_bindings in &input_bindings {
2292 let pattern_results = matcher.match_patterns(&patterns, existing_bindings);
2293 if pattern_results.is_empty() {
2294 results.push(existing_bindings.clone());
2297 } else {
2298 results.extend(pattern_results);
2299 }
2300 }
2301 results
2302 }
2303
2304 fn execute_unwind(
2305 &self,
2306 _expression: Expression,
2307 _variable: String,
2308 _input_bindings: Vec<Bindings>,
2309 ) -> Result<Vec<Bindings>, CypherError> {
2310 Err(CypherError::Unsupported(
2311 "UNWIND is not yet fully supported".into(),
2312 ))
2313 }
2314}
2315
2316struct MutQueryExecutor<'a> {
2321 graph: &'a mut Graph<NodeData, EdgeData>,
2322}
2323
2324impl<'a> MutQueryExecutor<'a> {
2325 fn new(graph: &'a mut Graph<NodeData, EdgeData>) -> Self {
2326 Self { graph }
2327 }
2328
2329 fn execute(mut self, query: CypherQuery) -> Result<(), CypherError> {
2330 let mut bindings: Vec<Bindings> = vec![HashMap::new()];
2331 let no_parameters = Parameters::new();
2332
2333 for clause in query.clauses {
2334 match clause {
2335 Clause::Match {
2336 patterns,
2337 where_clause,
2338 } => {
2339 bindings = self.execute_match(patterns, bindings);
2340 if let Some(where_expr) = where_clause {
2341 bindings
2342 .retain(|b| evaluate_where(&where_expr, b, self.graph, &no_parameters));
2343 }
2344 }
2345 Clause::OptionalMatch {
2346 patterns,
2347 where_clause,
2348 } => {
2349 bindings = self.execute_optional_match(patterns, bindings);
2350 if let Some(where_expr) = where_clause {
2351 bindings
2352 .retain(|b| evaluate_where(&where_expr, b, self.graph, &no_parameters));
2353 }
2354 }
2355 Clause::Create { patterns } => {
2356 for bindings_row in &mut bindings {
2357 for pattern in &patterns {
2358 self.apply_path_pattern_mut(bindings_row, pattern)?;
2359 }
2360 }
2361 }
2362 Clause::Merge {
2363 pattern,
2364 on_create,
2365 on_match,
2366 } => {
2367 for bindings_row in &mut bindings {
2368 let matcher = PatternMatcher::new(self.graph, MatchStrategy::Backtrack);
2369 let test_results = matcher.match_single_path(&pattern, bindings_row);
2370 if test_results.is_empty() {
2371 self.apply_path_pattern_mut(bindings_row, &pattern)?;
2372 self.apply_set_items(bindings_row, &on_create, &no_parameters)?;
2373 } else {
2374 let first = &test_results[0];
2376 for (k, v) in first {
2377 if let Some(existing) = bindings_row.get(k) {
2378 if existing != v {
2379 return Err(CypherError::InvalidQuery(format!(
2380 "MERGE binding conflict on '{}': existing value differs from matched value",
2381 k
2382 )));
2383 }
2384 } else {
2385 bindings_row.insert(k.clone(), *v);
2386 }
2387 }
2388 self.apply_set_items(bindings_row, &on_match, &no_parameters)?;
2389 }
2390 }
2391 }
2392 Clause::Delete { variables, detach } => {
2393 self.execute_delete(&variables, detach, &bindings)?;
2394 }
2395 Clause::Set { items } => {
2396 for bindings_row in &bindings {
2397 self.apply_set_items(bindings_row, &items, &no_parameters)?;
2398 }
2399 }
2400 Clause::Remove { items } => {
2401 for bindings_row in &bindings {
2402 self.apply_remove_items(bindings_row, &items)?;
2403 }
2404 }
2405 Clause::Return { .. } => {
2406 unreachable!("validated by validate_mutation_query")
2407 }
2408 Clause::OrderBy { .. }
2409 | Clause::Skip { .. }
2410 | Clause::Limit { .. }
2411 | Clause::Unwind { .. }
2412 | Clause::With { .. } => {
2413 return Err(CypherError::Unsupported(format!(
2414 "clause {:?} is not supported in mutation queries",
2415 std::mem::discriminant(&clause)
2416 )));
2417 }
2418 }
2419 }
2420
2421 Ok(())
2422 }
2423
2424 fn execute_match(
2425 &self,
2426 patterns: Vec<PathPattern>,
2427 input_bindings: Vec<Bindings>,
2428 ) -> Vec<Bindings> {
2429 if patterns.is_empty() {
2430 return input_bindings;
2431 }
2432
2433 let matcher = PatternMatcher::new(self.graph, MatchStrategy::Backtrack);
2434 let mut results = Vec::new();
2435 for existing_bindings in &input_bindings {
2436 let pattern_results = matcher.match_patterns(&patterns, existing_bindings);
2437 results.extend(pattern_results);
2438 }
2439 results
2440 }
2441
2442 fn execute_optional_match(
2443 &self,
2444 patterns: Vec<PathPattern>,
2445 input_bindings: Vec<Bindings>,
2446 ) -> Vec<Bindings> {
2447 if patterns.is_empty() {
2448 return input_bindings;
2449 }
2450
2451 let matcher = PatternMatcher::new(self.graph, MatchStrategy::Backtrack);
2452 let mut results = Vec::new();
2453 for existing_bindings in &input_bindings {
2454 let pattern_results = matcher.match_patterns(&patterns, existing_bindings);
2455 if pattern_results.is_empty() {
2456 results.push(existing_bindings.clone());
2457 } else {
2458 results.extend(pattern_results);
2459 }
2460 }
2461 results
2462 }
2463
2464 fn execute_delete(
2465 &mut self,
2466 variables: &[String],
2467 detach: bool,
2468 bindings: &[Bindings],
2469 ) -> Result<(), CypherError> {
2470 for var in variables {
2471 let bound_values: Vec<BoundValue> = bindings
2472 .iter()
2473 .filter_map(|b| b.get(var).copied())
2474 .unique()
2475 .collect();
2476
2477 if bound_values.is_empty() {
2478 continue;
2479 }
2480
2481 let (nodes, edges): (Vec<_>, Vec<_>) = bound_values
2482 .into_iter()
2483 .partition(|b| matches!(b, BoundValue::Node(_)));
2484
2485 for edge_idx in edges {
2486 if let BoundValue::Edge(idx) = edge_idx {
2487 self.graph.remove_edge(idx);
2488 }
2489 }
2490
2491 for node_idx in nodes {
2492 if let BoundValue::Node(idx) = node_idx {
2493 let out_edges: Vec<_> = self
2494 .graph
2495 .edges_directed(idx, petgraph::Direction::Outgoing)
2496 .map(|e| e.id())
2497 .collect();
2498 let in_edges: Vec<_> = self
2499 .graph
2500 .edges_directed(idx, petgraph::Direction::Incoming)
2501 .map(|e| e.id())
2502 .collect();
2503 let has_incident_edges = !out_edges.is_empty() || !in_edges.is_empty();
2504
2505 if !detach && has_incident_edges {
2506 return Err(CypherError::InvalidQuery(format!(
2507 "Cannot delete node '{}' because it has incident edges. Use DETACH DELETE.",
2508 var
2509 )));
2510 }
2511
2512 if detach {
2513 for edge_id in out_edges.into_iter().chain(in_edges) {
2514 self.graph.remove_edge(edge_id);
2515 }
2516 }
2517 self.graph.remove_node(idx);
2518 }
2519 }
2520 }
2521 Ok(())
2522 }
2523
2524 fn apply_set_items(
2525 &mut self,
2526 bindings: &Bindings,
2527 items: &[SetItem],
2528 parameters: &Parameters,
2529 ) -> Result<(), CypherError> {
2530 for item in items {
2531 match item {
2532 SetItem::SetProperty {
2533 variable,
2534 property,
2535 value,
2536 } => {
2537 if let Some(bound) = bindings.get(variable) {
2538 let val = evaluate_expression(value, bindings, self.graph, parameters)?;
2539 match bound {
2540 BoundValue::Node(idx) => {
2541 if matches!(val, CypherValue::Null) {
2542 self.graph[*idx].properties.remove(property);
2543 } else {
2544 self.graph[*idx].properties.insert(property.clone(), val);
2545 }
2546 }
2547 BoundValue::Edge(idx) => {
2548 if matches!(val, CypherValue::Null) {
2549 self.graph[*idx].properties.remove(property);
2550 } else {
2551 self.graph[*idx].properties.insert(property.clone(), val);
2552 }
2553 }
2554 }
2555 }
2556 }
2557 SetItem::SetVariable {
2558 variable,
2559 properties,
2560 } => {
2561 if let Some(bound) = bindings.get(variable) {
2562 match bound {
2563 BoundValue::Node(idx) => {
2564 self.graph[*idx].properties = properties.clone();
2565 }
2566 BoundValue::Edge(idx) => {
2567 self.graph[*idx].properties = properties.clone();
2568 }
2569 }
2570 }
2571 }
2572 SetItem::SetLabels { variable, labels } => {
2573 if let Some(BoundValue::Node(idx)) = bindings.get(variable) {
2574 let node_data = &mut self.graph[*idx];
2575 for label in labels {
2576 if !node_data.has_label(label) {
2577 node_data.labels.push(label.clone());
2578 }
2579 }
2580 }
2581 }
2582 SetItem::MergeProperties {
2583 variable,
2584 properties,
2585 } => {
2586 if let Some(bound) = bindings.get(variable) {
2587 match bound {
2588 BoundValue::Node(idx) => {
2589 for (k, v) in properties {
2590 self.graph[*idx].properties.insert(k.clone(), v.clone());
2591 }
2592 }
2593 BoundValue::Edge(idx) => {
2594 for (k, v) in properties {
2595 self.graph[*idx].properties.insert(k.clone(), v.clone());
2596 }
2597 }
2598 }
2599 }
2600 }
2601 }
2602 }
2603 Ok(())
2604 }
2605
2606 fn apply_remove_items(
2607 &mut self,
2608 bindings: &Bindings,
2609 items: &[RemoveItem],
2610 ) -> Result<(), CypherError> {
2611 for item in items {
2612 match item {
2613 RemoveItem::RemoveProperty { variable, property } => {
2614 if let Some(bound) = bindings.get(variable) {
2615 match bound {
2616 BoundValue::Node(idx) => {
2617 self.graph[*idx].properties.remove(property);
2618 }
2619 BoundValue::Edge(idx) => {
2620 self.graph[*idx].properties.remove(property);
2621 }
2622 }
2623 }
2624 }
2625 RemoveItem::RemoveLabels { variable, labels } => {
2626 if let Some(BoundValue::Node(idx)) = bindings.get(variable) {
2627 let node_data = &mut self.graph[*idx];
2628 node_data.labels.retain(|l| !labels.contains(l));
2629 }
2630 }
2631 }
2632 }
2633 Ok(())
2634 }
2635
2636 fn apply_path_pattern_mut(
2637 &mut self,
2638 var_map: &mut Bindings,
2639 pattern: &PathPattern,
2640 ) -> Result<(), CypherError> {
2641 let mut prev_idx = self.get_or_add_node_mut(var_map, &pattern.start)?;
2642
2643 for (rel, target_node) in &pattern.rels {
2644 let target_idx = self.get_or_add_node_mut(var_map, target_node)?;
2645
2646 let edge_data = EdgeData::from_cypher(
2647 rel.variable.clone(),
2648 rel.rel_type.clone(),
2649 rel.properties.clone(),
2650 );
2651
2652 let edge_id = match rel.direction {
2653 RelDirection::Right => self.graph.add_edge(prev_idx, target_idx, edge_data),
2654 RelDirection::Left => self.graph.add_edge(target_idx, prev_idx, edge_data),
2655 RelDirection::Both => self.graph.add_edge(prev_idx, target_idx, edge_data),
2656 };
2657
2658 if let Some(var) = &rel.variable {
2659 let edge_val = BoundValue::Edge(edge_id);
2660 if let Some(existing) = var_map.get(var) {
2661 if existing != &edge_val {
2662 return Err(CypherError::InvalidQuery(format!(
2663 "Variable '{}' is already bound to a different value",
2664 var
2665 )));
2666 }
2667 } else {
2668 var_map.insert(var.clone(), edge_val);
2669 }
2670 }
2671
2672 prev_idx = target_idx;
2673 }
2674 Ok(())
2675 }
2676
2677 fn get_or_add_node_mut(
2678 &mut self,
2679 var_map: &mut Bindings,
2680 pattern: &NodePattern,
2681 ) -> Result<NodeIndex, CypherError> {
2682 if let Some(var) = &pattern.variable
2683 && let Some(&existing) = var_map.get(var)
2684 {
2685 match existing {
2686 BoundValue::Node(idx) => return Ok(idx),
2687 BoundValue::Edge(_) => {
2688 return Err(CypherError::InvalidQuery(format!(
2689 "Variable '{}' is bound to an edge, cannot use as node",
2690 var
2691 )));
2692 }
2693 }
2694 }
2695
2696 let data = NodeData::from_cypher(
2697 pattern.variable.clone(),
2698 pattern.labels.clone(),
2699 pattern.properties.clone(),
2700 );
2701
2702 let idx = self.graph.add_node(data);
2703
2704 if let Some(var) = &pattern.variable {
2705 var_map.insert(var.clone(), BoundValue::Node(idx));
2706 }
2707
2708 Ok(idx)
2709 }
2710}