1#![allow(dead_code)]
8
9use crate::ast::{
30 is_aggregate_name, ArithOp, CallClause, CallYield, CompareOp, Expr, Literal, MergeClause,
31 NodePattern, Pattern, PropAccess, QuantifierKind, QueryClause, QueryPart, RelDirection,
32 RelPattern, RemoveItem, ReturnExpr, ReturnItem, ReturnTail, SetItem, SortDir, Statement, Tail,
33 UnwindClause, UnwindSource, WithClause, WithExpr,
34};
35use crate::error::QueryError;
36use crate::generated::cypherparser::{
37 AddSubExpressionContext, AndExpressionContext, AndExpressionContextAttrs, AtomContext,
38 AtomContextAttrs, AtomicExpressionContext, AtomicExpressionContextAll,
39 AtomicExpressionContextAttrs, BoolLitContext, BoolLitContextAttrs, CaseExpressionContext,
40 CharLitContext, CharLitContextAttrs, ComparisonExpressionContext,
41 ComparisonExpressionContextAttrs, ComparisonSignsContextAll, ComparisonSignsContextAttrs,
42 CountAllContext, CreateIndexStContext, CreateIndexStContextAttrs, CreateStContext,
43 CreateStContextAttrs, DeleteStContext, DeleteStContextAttrs, ExplainStContext,
44 ExplainStContextAttrs, ExpressionChainContextAttrs, ExpressionContext, ExpressionContextAttrs,
45 FilterExpressionContext, FilterExpressionContextAttrs, FilterWithContext,
46 FilterWithContextAttrs, FunctionInvocationContext, FunctionInvocationContextAttrs,
47 InExpressionContextAttrs, InvocationNameContextAll, InvocationNameContextAttrs,
48 LhsContextAttrs, LimitStContextAttrs, ListComprehensionContext, ListComprehensionContextAttrs,
49 ListExpressionContextAll, ListExpressionContextAttrs, ListLitContext, ListLitContextAttrs,
50 LiteralContext, LiteralContextAttrs, MapLitContext, MapLitContextAttrs, MapPairContextAttrs,
51 MatchStContext, MatchStContextAttrs, MergeActionContextAll, MergeActionContextAttrs,
52 MergeStContext, MergeStContextAttrs, MultDivExpressionContext, MultiPartQContext,
53 MultiPartQContextAttrs, NameContextAll, NameContextAttrs, NodeLabelsContextAttrs,
54 NodePatternContext, NodePatternContextAttrs, NotExpressionContext, NotExpressionContextAttrs,
55 NullExpressionContextAttrs, NumLitContext, NumLitContextAll, NumLitContextAttrs,
56 OrderItemContextAttrs, OrderStContext, OrderStContextAttrs, ParameterContext,
57 ParameterContextAttrs, ParenExpressionChainContextAll, ParenExpressionChainContextAttrs,
58 ParenthesizedExpressionContext, ParenthesizedExpressionContextAttrs,
59 PatternComprehensionContext, PatternComprehensionContextAttrs, PatternContextAttrs,
60 PatternElemChainContextAttrs, PatternElemContext, PatternElemContextAttrs,
61 PatternPartContextAttrs, PatternWhereContextAttrs, PowerExpressionContext,
62 PowerExpressionContextAttrs, ProjectionBodyContext, ProjectionBodyContextAttrs,
63 ProjectionItemContextAttrs, ProjectionItemsContextAttrs, PropertiesContextAll,
64 PropertiesContextAttrs, PropertyExpressionContext, PropertyExpressionContextAttrs,
65 PropertyOrLabelExpressionContext, PropertyOrLabelExpressionContextAttrs, QueryCallStContextAll,
66 QueryCallStContextAttrs, ReadingStatementContextAll, ReadingStatementContextAttrs,
67 RegularQueryContext, RegularQueryContextAttrs, RelationDetailContext,
68 RelationDetailContextAttrs, RelationshipPatternContext, RelationshipPatternContextAttrs,
69 RelationshipTypesContextAttrs, RelationshipsChainPatternContext,
70 RelationshipsChainPatternContextAttrs, RemoveItemContextAll, RemoveItemContextAttrs,
71 RemoveStContext, RemoveStContextAttrs, ReturnStContext, ReturnStContextAttrs,
72 SetItemContextAll, SetItemContextAttrs, SetStContext, SetStContextAttrs,
73 ShortestPathWrapperContextAttrs, SinglePartQContext, SinglePartQContextAttrs,
74 SkipStContextAttrs, StandaloneCallContext, StandaloneCallContextAttrs,
75 StringExpPrefixContextAll, StringExpPrefixContextAttrs, StringExpressionContextAll,
76 StringExpressionContextAttrs, StringListNullExpressionContext,
77 StringListNullExpressionContextAttrs, StringLitContext, StringLitContextAttrs,
78 SubqueryExistContext, SubqueryExistContextAttrs, SymbolContextAll, SymbolContextAttrs,
79 UnaryAddSubExpressionContext, UnaryAddSubExpressionContextAttrs, UnionStContextAttrs,
80 UnwindStContext, UnwindStContextAttrs, UpdatingStatementContextAll,
81 UpdatingStatementContextAttrs, WhereContextAttrs, WithStContext, WithStContextAttrs,
82 XorExpressionContext, XorExpressionContextAttrs, YieldItemContextAttrs, YieldItemsContextAll,
83 YieldItemsContextAttrs,
84};
85use crate::generated::cypherparservisitor::CypherParserVisitorCompat;
86use crate::parse_helpers::{
87 group_into_linear_patterns, parse_int_literal, parse_rel_range, unescape_string,
88 validate_named_path_pattern, validate_shortest_path_pattern,
89};
90use antlr4rust::parser_rule_context::ParserRuleContext;
91use antlr4rust::token::Token;
92use antlr4rust::tree::{ParseTree, ParseTreeVisitorCompat, Tree};
93use std::rc::Rc;
94
95#[derive(Debug, Default)]
96pub(crate) enum AstNode {
97 #[default]
98 None,
99 Literal(Literal),
100 NodePattern(NodePattern),
101 RelPattern(RelPattern),
102 Pattern(Pattern),
103 QueryParts(Vec<QueryPart>),
104 ReturnExpr(ReturnExpr),
105 ReturnClause(ParsedReturnClause),
106 WithClause(WithClause),
107 UnwindClause(UnwindClause),
108 SetItems(Vec<SetItem>),
109 DeleteItems(ParsedDelete),
110 RemoveItems(Vec<RemoveItem>),
111 CreatePatterns(Vec<Pattern>),
112 MergeClause(MergeClause),
113 Statement(Statement),
114 Err(QueryError),
115}
116
117#[derive(Debug)]
118pub(crate) struct ParsedDelete {
119 pub items: Vec<ReturnExpr>,
120 pub detach: bool,
121}
122
123#[derive(Debug)]
130pub(crate) struct ParsedReturnClause {
131 pub tail: Tail,
132 pub order_by: Option<Vec<(ReturnExpr, SortDir)>>,
133 pub skip: Option<ReturnExpr>,
134 pub limit: Option<ReturnExpr>,
135}
136
137macro_rules! ast_node_into {
138 ($name:ident, $variant:ident, $ty:ty) => {
139 fn $name(self) -> Result<$ty, QueryError> {
140 match self {
141 AstNode::$variant(v) => Ok(v),
142 AstNode::Err(e) => Err(e),
143 other => unreachable!("expected AstNode::{}, got {other:?}", stringify!($variant)),
144 }
145 }
146 };
147}
148
149impl AstNode {
150 ast_node_into!(into_literal, Literal, Literal);
151 ast_node_into!(into_node_pattern, NodePattern, NodePattern);
152 ast_node_into!(into_rel_pattern, RelPattern, RelPattern);
153 ast_node_into!(into_pattern, Pattern, Pattern);
154 ast_node_into!(into_query_parts, QueryParts, Vec<QueryPart>);
155 ast_node_into!(into_return_expr, ReturnExpr, ReturnExpr);
156
157 fn into_return_expr_lenient(self) -> Result<ReturnExpr, QueryError> {
165 match self {
166 AstNode::Literal(l) => Ok(ReturnExpr::Lit(l)),
167 AstNode::ReturnExpr(e) => Ok(e),
168 AstNode::Err(e) => Err(e),
169 other => {
170 unreachable!("expected AstNode::Literal or AstNode::ReturnExpr, got {other:?}")
171 }
172 }
173 }
174 ast_node_into!(into_return_clause, ReturnClause, ParsedReturnClause);
175 ast_node_into!(into_with_clause, WithClause, WithClause);
176 ast_node_into!(into_unwind_clause, UnwindClause, UnwindClause);
177 ast_node_into!(into_set_items, SetItems, Vec<SetItem>);
178 ast_node_into!(into_delete_items, DeleteItems, ParsedDelete);
179 ast_node_into!(into_remove_items, RemoveItems, Vec<RemoveItem>);
180 ast_node_into!(into_create_patterns, CreatePatterns, Vec<Pattern>);
181 ast_node_into!(into_merge_clause, MergeClause, MergeClause);
182 ast_node_into!(into_statement, Statement, Statement);
183}
184
185pub(crate) struct AstBuilder {
186 result: AstNode,
187}
188
189impl AstBuilder {
190 pub(crate) fn new() -> Self {
191 AstBuilder {
192 result: AstNode::default(),
193 }
194 }
195}
196
197impl<'input> ParseTreeVisitorCompat<'input> for AstBuilder {
198 type Node = crate::generated::cypherparser::CypherParserContextType;
199 type Return = AstNode;
200
201 fn temp_result(&mut self) -> &mut Self::Return {
202 &mut self.result
203 }
204}
205
206impl<'input> CypherParserVisitorCompat<'input> for AstBuilder {
207 fn visit_literal(&mut self, ctx: &LiteralContext<'input>) -> Self::Return {
208 if ctx.NULL_W().is_some() {
213 return AstNode::Literal(Literal::Null);
214 }
215 self.visit_children(ctx)
216 }
217
218 fn visit_boolLit(&mut self, ctx: &BoolLitContext<'input>) -> Self::Return {
219 AstNode::Literal(Literal::Bool(ctx.TRUE().is_some()))
220 }
221
222 fn visit_numLit(&mut self, ctx: &NumLitContext<'input>) -> Self::Return {
232 let text = ctx
233 .DIGIT()
234 .expect("numLit context always has a DIGIT token")
235 .get_text();
236 match parse_num_lit_text(&text) {
237 Ok(lit) => AstNode::Literal(lit),
238 Err(e) => AstNode::Err(e),
239 }
240 }
241
242 fn visit_stringLit(&mut self, ctx: &StringLitContext<'input>) -> Self::Return {
243 let text = ctx
244 .STRING_LITERAL()
245 .expect("stringLit context always has a STRING_LITERAL token")
246 .get_text();
247 match unescape_string(&text[1..text.len() - 1]) {
248 Ok(s) => AstNode::Literal(Literal::String(s)),
249 Err(e) => AstNode::Err(e),
250 }
251 }
252
253 fn visit_charLit(&mut self, ctx: &CharLitContext<'input>) -> Self::Return {
254 let text = ctx
255 .CHAR_LITERAL()
256 .expect("charLit context always has a CHAR_LITERAL token")
257 .get_text();
258 match unescape_string(&text[1..text.len() - 1]) {
259 Ok(s) => AstNode::Literal(Literal::String(s)),
260 Err(e) => AstNode::Err(e),
261 }
262 }
263
264 fn visit_nodePattern(&mut self, ctx: &NodePatternContext<'input>) -> Self::Return {
265 match self.build_node_pattern(ctx) {
266 Ok(n) => AstNode::NodePattern(n),
267 Err(e) => AstNode::Err(e),
268 }
269 }
270
271 fn visit_relationDetail(&mut self, ctx: &RelationDetailContext<'input>) -> Self::Return {
272 match self.build_rel_detail(ctx) {
273 Ok(r) => AstNode::RelPattern(r),
274 Err(e) => AstNode::Err(e),
275 }
276 }
277
278 fn visit_relationshipPattern(
279 &mut self,
280 ctx: &RelationshipPatternContext<'input>,
281 ) -> Self::Return {
282 match self.build_relationship_pattern(ctx) {
283 Ok(r) => AstNode::RelPattern(r),
284 Err(e) => AstNode::Err(e),
285 }
286 }
287
288 fn visit_patternElem(&mut self, ctx: &PatternElemContext<'input>) -> Self::Return {
289 match self.build_pattern_elem(ctx) {
290 Ok(p) => AstNode::Pattern(p),
291 Err(e) => AstNode::Err(e),
292 }
293 }
294
295 fn visit_matchSt(&mut self, ctx: &MatchStContext<'input>) -> Self::Return {
296 match self.build_match_st(ctx) {
297 Ok(parts) => AstNode::QueryParts(parts),
298 Err(e) => AstNode::Err(e),
299 }
300 }
301
302 fn visit_expression(&mut self, ctx: &ExpressionContext<'input>) -> Self::Return {
303 let mut operands = ctx.xorExpression_all().into_iter();
304 let mut lhs = match self
305 .visit(
306 &*operands
307 .next()
308 .expect("expression has at least one xorExpression"),
309 )
310 .into_return_expr()
311 {
312 Ok(e) => e,
313 Err(e) => return AstNode::Err(e),
314 };
315 for rhs_ctx in operands {
316 let rhs = match self.visit(&*rhs_ctx).into_return_expr() {
317 Ok(e) => e,
318 Err(e) => return AstNode::Err(e),
319 };
320 lhs = ReturnExpr::Or(Box::new(lhs), Box::new(rhs));
321 }
322 AstNode::ReturnExpr(lhs)
323 }
324
325 fn visit_xorExpression(&mut self, ctx: &XorExpressionContext<'input>) -> Self::Return {
326 let mut operands = ctx.andExpression_all().into_iter();
327 let mut lhs = match self
328 .visit(
329 &*operands
330 .next()
331 .expect("xorExpression has at least one andExpression"),
332 )
333 .into_return_expr()
334 {
335 Ok(e) => e,
336 Err(e) => return AstNode::Err(e),
337 };
338 for rhs_ctx in operands {
339 let rhs = match self.visit(&*rhs_ctx).into_return_expr() {
340 Ok(e) => e,
341 Err(e) => return AstNode::Err(e),
342 };
343 lhs = ReturnExpr::Xor(Box::new(lhs), Box::new(rhs));
344 }
345 AstNode::ReturnExpr(lhs)
346 }
347
348 fn visit_andExpression(&mut self, ctx: &AndExpressionContext<'input>) -> Self::Return {
349 let mut operands = ctx.notExpression_all().into_iter();
350 let mut lhs = match self
351 .visit(
352 &*operands
353 .next()
354 .expect("andExpression has at least one notExpression"),
355 )
356 .into_return_expr()
357 {
358 Ok(e) => e,
359 Err(e) => return AstNode::Err(e),
360 };
361 for rhs_ctx in operands {
362 let rhs = match self.visit(&*rhs_ctx).into_return_expr() {
363 Ok(e) => e,
364 Err(e) => return AstNode::Err(e),
365 };
366 lhs = ReturnExpr::And(Box::new(lhs), Box::new(rhs));
367 }
368 AstNode::ReturnExpr(lhs)
369 }
370
371 fn visit_notExpression(&mut self, ctx: &NotExpressionContext<'input>) -> Self::Return {
372 let inner = ctx
373 .comparisonExpression()
374 .expect("notExpression always has a comparisonExpression");
375 match self.visit(&*inner).into_return_expr() {
376 Ok(mut expr) => {
377 for _ in ctx.NOT_all() {
378 expr = ReturnExpr::Not(Box::new(expr));
379 }
380 AstNode::ReturnExpr(expr)
381 }
382 Err(e) => AstNode::Err(e),
383 }
384 }
385
386 fn visit_comparisonExpression(
387 &mut self,
388 ctx: &ComparisonExpressionContext<'input>,
389 ) -> Self::Return {
390 match self.build_comparison_expression(ctx) {
391 Ok(expr) => AstNode::ReturnExpr(expr),
392 Err(e) => AstNode::Err(e),
393 }
394 }
395
396 fn visit_stringListNullExpression(
397 &mut self,
398 ctx: &StringListNullExpressionContext<'input>,
399 ) -> Self::Return {
400 match self.build_string_list_null_expression(ctx) {
401 Ok(expr) => AstNode::ReturnExpr(expr),
402 Err(e) => AstNode::Err(e),
403 }
404 }
405
406 fn visit_addSubExpression(&mut self, ctx: &AddSubExpressionContext<'input>) -> Self::Return {
407 match self.build_add_sub_expression(ctx) {
408 Ok(expr) => AstNode::ReturnExpr(expr),
409 Err(e) => AstNode::Err(e),
410 }
411 }
412
413 fn visit_multDivExpression(&mut self, ctx: &MultDivExpressionContext<'input>) -> Self::Return {
414 match self.build_mult_div_expression(ctx) {
415 Ok(expr) => AstNode::ReturnExpr(expr),
416 Err(e) => AstNode::Err(e),
417 }
418 }
419
420 fn visit_powerExpression(&mut self, ctx: &PowerExpressionContext<'input>) -> Self::Return {
424 let mut operands = ctx.unaryAddSubExpression_all().into_iter();
425 let mut lhs = match self
426 .visit(
427 &*operands
428 .next()
429 .expect("powerExpression has at least one unaryAddSubExpression"),
430 )
431 .into_return_expr()
432 {
433 Ok(e) => e,
434 Err(e) => return AstNode::Err(e),
435 };
436 for rhs_ctx in operands {
437 let rhs = match self.visit(&*rhs_ctx).into_return_expr() {
438 Ok(e) => e,
439 Err(e) => return AstNode::Err(e),
440 };
441 lhs = ReturnExpr::Arith(Box::new(lhs), ArithOp::Pow, Box::new(rhs));
442 }
443 AstNode::ReturnExpr(lhs)
444 }
445
446 fn visit_unaryAddSubExpression(
447 &mut self,
448 ctx: &UnaryAddSubExpressionContext<'input>,
449 ) -> Self::Return {
450 match self.build_unary_add_sub_expression(ctx) {
451 Ok(expr) => AstNode::ReturnExpr(expr),
452 Err(e) => AstNode::Err(e),
453 }
454 }
455
456 fn visit_atomicExpression(&mut self, ctx: &AtomicExpressionContext<'input>) -> Self::Return {
457 match self.build_atomic_expression(ctx) {
458 Ok(expr) => AstNode::ReturnExpr(expr),
459 Err(e) => AstNode::Err(e),
460 }
461 }
462
463 fn visit_propertyOrLabelExpression(
464 &mut self,
465 ctx: &PropertyOrLabelExpressionContext<'input>,
466 ) -> Self::Return {
467 match self.build_property_or_label_expression(ctx) {
468 Ok(expr) => AstNode::ReturnExpr(expr),
469 Err(e) => AstNode::Err(e),
470 }
471 }
472
473 fn visit_propertyExpression(
474 &mut self,
475 ctx: &PropertyExpressionContext<'input>,
476 ) -> Self::Return {
477 match self.build_property_expression(ctx) {
478 Ok(expr) => AstNode::ReturnExpr(expr),
479 Err(e) => AstNode::Err(e),
480 }
481 }
482
483 fn visit_atom(&mut self, ctx: &AtomContext<'input>) -> Self::Return {
484 match self.build_atom(ctx) {
485 Ok(expr) => AstNode::ReturnExpr(expr),
486 Err(e) => AstNode::Err(e),
487 }
488 }
489
490 fn visit_parenthesizedExpression(
491 &mut self,
492 ctx: &ParenthesizedExpressionContext<'input>,
493 ) -> Self::Return {
494 let inner = ctx
495 .expression()
496 .expect("parenthesizedExpression always has an expression");
497 self.visit(&*inner)
498 }
499
500 fn visit_functionInvocation(
501 &mut self,
502 ctx: &FunctionInvocationContext<'input>,
503 ) -> Self::Return {
504 match self.build_function_invocation(ctx) {
505 Ok(expr) => AstNode::ReturnExpr(expr),
506 Err(e) => AstNode::Err(e),
507 }
508 }
509
510 fn visit_parameter(&mut self, ctx: &ParameterContext<'input>) -> Self::Return {
511 match self.build_parameter(ctx) {
512 Ok(expr) => AstNode::ReturnExpr(expr),
513 Err(e) => AstNode::Err(e),
514 }
515 }
516
517 fn visit_countAll(&mut self, _ctx: &CountAllContext<'input>) -> Self::Return {
518 AstNode::ReturnExpr(ReturnExpr::CountStar)
519 }
520
521 fn visit_returnSt(&mut self, ctx: &ReturnStContext<'input>) -> Self::Return {
522 let body_ctx = ctx
523 .projectionBody()
524 .expect("returnSt always has a projectionBody");
525 match self.build_projection_body(&body_ctx) {
526 Ok(c) => AstNode::ReturnClause(c),
527 Err(e) => AstNode::Err(e),
528 }
529 }
530
531 fn visit_withSt(&mut self, ctx: &WithStContext<'input>) -> Self::Return {
532 match self.build_with_clause(ctx) {
533 Ok(c) => AstNode::WithClause(c),
534 Err(e) => AstNode::Err(e),
535 }
536 }
537
538 fn visit_unwindSt(&mut self, ctx: &UnwindStContext<'input>) -> Self::Return {
539 match self.build_unwind_st(ctx) {
540 Ok(c) => AstNode::UnwindClause(c),
541 Err(e) => AstNode::Err(e),
542 }
543 }
544
545 fn visit_setSt(&mut self, ctx: &SetStContext<'input>) -> Self::Return {
546 match self.build_set_st(ctx) {
547 Ok(items) => AstNode::SetItems(items),
548 Err(e) => AstNode::Err(e),
549 }
550 }
551
552 fn visit_deleteSt(&mut self, ctx: &DeleteStContext<'input>) -> Self::Return {
553 match self.build_delete_st(ctx) {
554 Ok(d) => AstNode::DeleteItems(d),
555 Err(e) => AstNode::Err(e),
556 }
557 }
558
559 fn visit_removeSt(&mut self, ctx: &RemoveStContext<'input>) -> Self::Return {
560 match self.build_remove_st(ctx) {
561 Ok(items) => AstNode::RemoveItems(items),
562 Err(e) => AstNode::Err(e),
563 }
564 }
565
566 fn visit_createSt(&mut self, ctx: &CreateStContext<'input>) -> Self::Return {
567 match self.build_create_st(ctx) {
568 Ok(patterns) => AstNode::CreatePatterns(patterns),
569 Err(e) => AstNode::Err(e),
570 }
571 }
572
573 fn visit_mergeSt(&mut self, ctx: &MergeStContext<'input>) -> Self::Return {
574 match self.build_merge_st(ctx) {
575 Ok(c) => AstNode::MergeClause(c),
576 Err(e) => AstNode::Err(e),
577 }
578 }
579
580 fn visit_singlePartQ(&mut self, ctx: &SinglePartQContext<'input>) -> Self::Return {
581 match self.build_single_part_q(ctx) {
582 Ok(s) => AstNode::Statement(s),
583 Err(e) => AstNode::Err(e),
584 }
585 }
586
587 fn visit_multiPartQ(&mut self, ctx: &MultiPartQContext<'input>) -> Self::Return {
588 match self.build_multi_part_q(ctx) {
589 Ok(s) => AstNode::Statement(s),
590 Err(e) => AstNode::Err(e),
591 }
592 }
593
594 fn visit_regularQuery(&mut self, ctx: &RegularQueryContext<'input>) -> Self::Return {
595 match self.build_regular_query(ctx) {
596 Ok(s) => AstNode::Statement(s),
597 Err(e) => AstNode::Err(e),
598 }
599 }
600
601 fn visit_standaloneCall(&mut self, ctx: &StandaloneCallContext<'input>) -> Self::Return {
609 match self.build_standalone_call(ctx) {
610 Ok(s) => AstNode::Statement(s),
611 Err(e) => AstNode::Err(e),
612 }
613 }
614
615 fn visit_explainSt(&mut self, ctx: &ExplainStContext<'input>) -> Self::Return {
616 match self.build_explain_st(ctx) {
617 Ok(s) => AstNode::Statement(s),
618 Err(e) => AstNode::Err(e),
619 }
620 }
621
622 fn visit_createIndexSt(&mut self, ctx: &CreateIndexStContext<'input>) -> Self::Return {
623 match self.build_create_index_st(ctx) {
624 Ok(s) => AstNode::Statement(s),
625 Err(e) => AstNode::Err(e),
626 }
627 }
628
629 fn visit_listLit(&mut self, ctx: &ListLitContext<'input>) -> Self::Return {
630 let mut items = Vec::new();
631 if let Some(chain_ctx) = ctx.expressionChain() {
632 for expr_ctx in chain_ctx.expression_all() {
633 match self.visit(&*expr_ctx).into_return_expr() {
634 Ok(e) => items.push(e),
635 Err(e) => return AstNode::Err(e),
636 }
637 }
638 }
639 AstNode::ReturnExpr(ReturnExpr::ListLit(items))
640 }
641
642 fn visit_mapLit(&mut self, ctx: &MapLitContext<'input>) -> Self::Return {
643 let mut items = Vec::new();
644 for pair_ctx in ctx.mapPair_all() {
645 let name_ctx = pair_ctx.name().expect("mapPair always has a name");
646 let expr_ctx = pair_ctx
647 .expression()
648 .expect("mapPair always has an expression");
649 let value = match self.visit(&*expr_ctx).into_return_expr() {
650 Ok(v) => v,
651 Err(e) => return AstNode::Err(e),
652 };
653 items.push((name_text(&name_ctx), value));
654 }
655 AstNode::ReturnExpr(ReturnExpr::MapLit(items))
656 }
657}
658
659fn symbol_text(ctx: &SymbolContextAll) -> String {
660 match ctx.ESC_LITERAL() {
661 Some(t) => {
663 let text = t.get_text();
664 text[1..text.len() - 1].to_string()
665 }
666 None => ctx.get_text(),
667 }
668}
669
670fn name_text(ctx: &NameContextAll) -> String {
679 match ctx.symbol() {
680 Some(s) => symbol_text(&s),
681 None => ctx.get_text(),
682 }
683}
684
685fn parse_num_lit_text(text: &str) -> Result<Literal, QueryError> {
690 let unsigned = text.strip_prefix('-').unwrap_or(text);
699 let is_hex_or_octal = unsigned
700 .as_bytes()
701 .get(1)
702 .is_some_and(|b| matches!(b, b'x' | b'X' | b'o' | b'O'))
703 && unsigned.starts_with('0');
704 let is_float = !is_hex_or_octal
705 && (text.contains('.')
706 || text.ends_with(['f', 'F', 'd', 'D'])
707 || text
708 .rfind(['e', 'E'])
709 .is_some_and(|i| text[..i].chars().all(|c| c.is_ascii_digit() || c == '-')));
710 if is_float {
711 let f: f64 = text
712 .parse()
713 .map_err(|_| QueryError::Syntax(format!("invalid float literal '{text}'")))?;
714 if f.is_infinite() {
720 Err(QueryError::Syntax(format!(
721 "float literal '{text}' is too large to represent"
722 )))
723 } else {
724 Ok(Literal::Float(f))
725 }
726 } else {
727 parse_int_literal(text).map(Literal::Int)
728 }
729}
730
731fn compare_sign(ctx: &ComparisonSignsContextAll) -> CompareOp {
732 if ctx.LE().is_some() {
733 CompareOp::Le
734 } else if ctx.GE().is_some() {
735 CompareOp::Ge
736 } else if ctx.GT().is_some() {
737 CompareOp::Gt
738 } else if ctx.LT().is_some() {
739 CompareOp::Lt
740 } else if ctx.NOT_EQUAL().is_some() {
741 CompareOp::Ne
742 } else {
743 CompareOp::Eq
746 }
747}
748
749fn string_exp_op(ctx: &StringExpPrefixContextAll) -> CompareOp {
750 if ctx.STARTS().is_some() {
751 CompareOp::StartsWith
752 } else if ctx.ENDS().is_some() {
753 CompareOp::EndsWith
754 } else {
755 CompareOp::Contains
756 }
757}
758
759fn invocation_name_text(ctx: &InvocationNameContextAll) -> String {
760 ctx.symbol_all()
761 .iter()
762 .map(|s| symbol_text(s))
763 .collect::<Vec<_>>()
764 .join(".")
765}
766
767fn bare_num_lit<'i>(
776 ctx: &AtomicExpressionContextAll<'i>,
777) -> Option<std::rc::Rc<NumLitContextAll<'i>>> {
778 if !ctx.listExpression_all().is_empty() {
779 return None;
780 }
781 let prop_or_label = ctx.propertyOrLabelExpression()?;
782 if prop_or_label.nodeLabels().is_some() {
783 return None;
784 }
785 let prop_expr = prop_or_label.propertyExpression()?;
786 if !prop_expr.name_all().is_empty() {
787 return None;
788 }
789 prop_expr.atom()?.literal()?.numLit()
790}
791
792fn list_expr_bound_is_before_range(ctx: &ListExpressionContextAll) -> bool {
799 let mut seen_range = false;
800 for child in ctx.get_children() {
801 match child.get_text().as_str() {
802 "[" | "]" => continue,
803 ".." => seen_range = true,
804 _ => return !seen_range,
805 }
806 }
807 unreachable!("listExpression slice form always has exactly one expression child")
808}
809
810impl AstBuilder {
811 fn build_properties(
818 &mut self,
819 ctx: Option<Rc<PropertiesContextAll>>,
820 ) -> Result<Vec<(String, ReturnExpr)>, QueryError> {
821 let Some(ctx) = ctx else {
822 return Ok(Vec::new());
823 };
824 let Some(map_ctx) = ctx.mapLit() else {
825 return Err(QueryError::Syntax(
826 "a parameter can't be used as a pattern's whole properties map".into(),
827 ));
828 };
829 let expr = self.visit(&*map_ctx).into_return_expr()?;
830 let ReturnExpr::MapLit(items) = expr else {
831 unreachable!("mapLit always builds a ReturnExpr::MapLit");
832 };
833 Ok(items)
834 }
835
836 fn build_node_pattern(&mut self, ctx: &NodePatternContext) -> Result<NodePattern, QueryError> {
837 let var = ctx.symbol().map(|s| symbol_text(&s));
838 let labels = ctx
839 .nodeLabels()
840 .map(|nl| nl.name_all().iter().map(|n| name_text(n)).collect())
841 .unwrap_or_default();
842 let has_explicit_props = ctx.properties().is_some();
843 let props = self.build_properties(ctx.properties())?;
844 Ok(NodePattern {
845 var,
846 labels,
847 props,
848 has_explicit_props,
849 })
850 }
851
852 fn build_rel_detail(&mut self, ctx: &RelationDetailContext) -> Result<RelPattern, QueryError> {
853 let var = ctx.symbol().map(|s| symbol_text(&s));
854 let rel_types = ctx
855 .relationshipTypes()
856 .map(|rt| rt.name_all().iter().map(|n| name_text(n)).collect())
857 .unwrap_or_default();
858 let props = self.build_properties(ctx.properties())?;
859 let hop_range = ctx
860 .rangeLit()
861 .map(|r| parse_rel_range(&r.get_text()))
862 .transpose()?;
863 Ok(RelPattern {
864 var,
865 rel_types,
866 props,
867 direction: RelDirection::Either,
872 hop_range,
873 capture_path_segment: false,
874 rel_list_var: None,
875 })
876 }
877
878 fn build_relationship_pattern(
879 &mut self,
880 ctx: &RelationshipPatternContext,
881 ) -> Result<RelPattern, QueryError> {
882 let mut rel = match ctx.relationDetail() {
883 Some(rd) => self.visit(&*rd).into_rel_pattern()?,
884 None => RelPattern {
885 var: None,
886 rel_types: Vec::new(),
887 props: Vec::new(),
888 direction: RelDirection::Either,
889 hop_range: None,
890 capture_path_segment: false,
891 rel_list_var: None,
892 },
893 };
894 rel.direction = match (ctx.LT().is_some(), ctx.GT().is_some()) {
904 (true, false) => RelDirection::Left,
905 (false, true) => RelDirection::Right,
906 (true, true) | (false, false) => RelDirection::Either,
907 };
908 Ok(rel)
909 }
910
911 fn build_pattern_elem(&mut self, ctx: &PatternElemContext) -> Result<Pattern, QueryError> {
912 if ctx.LPAREN().is_some() || !ctx.qppElemChain_all().is_empty() {
913 return Err(QueryError::Syntax(
914 "quantified path patterns aren't supported yet".into(),
915 ));
916 }
917 let node_ctx = ctx
918 .nodePattern()
919 .expect("patternElem always starts with a nodePattern in the non-QPP alternative");
920 let start = self.visit(&*node_ctx).into_node_pattern()?;
921 let mut hops = Vec::new();
922 for chain in ctx.patternElemChain_all() {
923 let rel_ctx = chain
924 .relationshipPattern()
925 .expect("patternElemChain always has a relationshipPattern");
926 let node_ctx = chain
927 .nodePattern()
928 .expect("patternElemChain always has a nodePattern");
929 let rel = self.visit(&*rel_ctx).into_rel_pattern()?;
930 let node = self.visit(&*node_ctx).into_node_pattern()?;
931 hops.push((rel, node));
932 }
933 Ok(Pattern { start, hops })
934 }
935
936 fn build_relationships_chain_pattern(
943 &mut self,
944 ctx: &RelationshipsChainPatternContext,
945 ) -> Result<Pattern, QueryError> {
946 let node_ctx = ctx
947 .nodePattern()
948 .expect("relationshipsChainPattern always has a nodePattern");
949 let start = self.visit(&*node_ctx).into_node_pattern()?;
950 let mut hops = Vec::new();
951 for chain in ctx.patternElemChain_all() {
952 let rel_ctx = chain
953 .relationshipPattern()
954 .expect("patternElemChain always has a relationshipPattern");
955 let node_ctx = chain
956 .nodePattern()
957 .expect("patternElemChain always has a nodePattern");
958 let rel = self.visit(&*rel_ctx).into_rel_pattern()?;
959 let node = self.visit(&*node_ctx).into_node_pattern()?;
960 hops.push((rel, node));
961 }
962 Ok(Pattern { start, hops })
963 }
964
965 fn build_match_st(&mut self, ctx: &MatchStContext) -> Result<Vec<QueryPart>, QueryError> {
970 let optional = ctx.OPTIONAL().is_some();
971 let pw = ctx
972 .patternWhere()
973 .expect("matchSt always has a patternWhere");
974 let where_clause = match pw.where_() {
975 Some(where_ctx) => {
976 let expr_ctx = where_ctx
977 .expression()
978 .expect("where always has an expression");
979 let expr = self.visit(&*expr_ctx).into_return_expr()?;
980 Some(return_expr_to_expr(expr)?)
981 }
982 None => None,
983 };
984 let pattern_ctx = pw.pattern().expect("patternWhere always has a pattern");
985
986 let mut path_var = None;
987 let mut shortest_path = false;
988 let mut patterns = Vec::new();
989 for (i, part) in pattern_ctx.patternPart_all().into_iter().enumerate() {
990 let pattern = match part.shortestPathWrapper() {
996 Some(sp_ctx) => {
997 if i != 0 {
998 return Err(QueryError::Syntax(
999 "shortestPath() must be the first (and only) comma-separated pattern"
1000 .into(),
1001 ));
1002 }
1003 shortest_path = true;
1004 let elem_ctx = sp_ctx
1005 .patternElem()
1006 .expect("shortestPathWrapper always has a patternElem");
1007 self.visit(&*elem_ctx).into_pattern()?
1008 }
1009 None => {
1010 let elem_ctx = part.patternElem().expect(
1011 "patternPart always has a patternElem when shortestPathWrapper is absent",
1012 );
1013 self.visit(&*elem_ctx).into_pattern()?
1014 }
1015 };
1016 patterns.push(pattern);
1017 if part.ASSIGN().is_some() {
1018 if path_var.is_some() {
1019 return Err(QueryError::Syntax(
1020 "at most one comma-separated pattern part can have a named-path variable"
1021 .into(),
1022 ));
1023 }
1024 let symbol_ctx = part
1025 .symbol()
1026 .expect("patternPart with ASSIGN always has a symbol");
1027 path_var = Some(symbol_text(&symbol_ctx));
1028 }
1029 }
1030
1031 let groups = group_into_linear_patterns(patterns)?;
1032 if groups.len() > 1 && (shortest_path || path_var.is_some()) {
1033 return Err(QueryError::Syntax(
1034 "a named path/shortestPath() can't span a comma-separated cross join".into(),
1035 ));
1036 }
1037 if shortest_path {
1038 validate_shortest_path_pattern(&groups[0])?;
1039 } else if path_var.is_some() {
1040 validate_named_path_pattern(&groups[0])?;
1041 }
1042
1043 let last = groups.len() - 1;
1051 Ok(groups
1052 .into_iter()
1053 .enumerate()
1054 .map(|(i, pattern)| QueryPart {
1055 optional,
1056 path_var: if i == 0 { path_var.clone() } else { None },
1057 shortest_path: i == 0 && shortest_path,
1058 pattern,
1059 where_clause: if i == last {
1060 where_clause.clone()
1061 } else {
1062 None
1063 },
1064 with: None,
1065 continues_clause: i > 0,
1068 })
1069 .collect())
1070 }
1071
1072 fn build_comparison_expression(
1080 &mut self,
1081 ctx: &ComparisonExpressionContext,
1082 ) -> Result<ReturnExpr, QueryError> {
1083 let mut operands = Vec::new();
1084 for operand_ctx in ctx.stringListNullExpression_all() {
1085 operands.push(self.visit(&*operand_ctx).into_return_expr()?);
1086 }
1087 let mut ops = Vec::new();
1088 for sign_ctx in ctx.comparisonSigns_all() {
1089 ops.push(compare_sign(&sign_ctx));
1090 }
1091 if ops.is_empty() {
1092 return Ok(operands
1093 .into_iter()
1094 .next()
1095 .expect("comparisonExpression has at least one stringListNullExpression"));
1096 }
1097 let mut pairs = operands.windows(2).zip(&ops).map(|(pair, op)| {
1098 ReturnExpr::Compare(Box::new(pair[0].clone()), *op, Box::new(pair[1].clone()))
1099 });
1100 let mut acc = pairs
1101 .next()
1102 .expect("a comparison chain has at least one pair");
1103 for next in pairs {
1104 acc = ReturnExpr::And(Box::new(acc), Box::new(next));
1105 }
1106 Ok(acc)
1107 }
1108
1109 fn build_add_sub_expression(
1119 &mut self,
1120 ctx: &AddSubExpressionContext,
1121 ) -> Result<ReturnExpr, QueryError> {
1122 let mut children = ctx.get_children();
1123 let mut lhs = self
1124 .visit(
1125 &*children
1126 .next()
1127 .expect("addSubExpression has at least one multDivExpression"),
1128 )
1129 .into_return_expr()?;
1130 while let Some(op_node) = children.next() {
1131 let op = match op_node.get_text().as_str() {
1132 "+" => ArithOp::Add,
1133 "-" => ArithOp::Sub,
1134 other => unreachable!("unexpected addSubExpression operator {other:?}"),
1135 };
1136 let rhs_node = children
1137 .next()
1138 .expect("addSubExpression operator has a following multDivExpression");
1139 let rhs = self.visit(&*rhs_node).into_return_expr()?;
1140 lhs = ReturnExpr::Arith(Box::new(lhs), op, Box::new(rhs));
1141 }
1142 Ok(lhs)
1143 }
1144
1145 fn build_mult_div_expression(
1146 &mut self,
1147 ctx: &MultDivExpressionContext,
1148 ) -> Result<ReturnExpr, QueryError> {
1149 let mut children = ctx.get_children();
1150 let mut lhs = self
1151 .visit(
1152 &*children
1153 .next()
1154 .expect("multDivExpression has at least one powerExpression"),
1155 )
1156 .into_return_expr()?;
1157 while let Some(op_node) = children.next() {
1158 let op = match op_node.get_text().as_str() {
1159 "*" => ArithOp::Mul,
1160 "/" => ArithOp::Div,
1161 "%" => ArithOp::Mod,
1162 other => unreachable!("unexpected multDivExpression operator {other:?}"),
1163 };
1164 let rhs_node = children
1165 .next()
1166 .expect("multDivExpression operator has a following powerExpression");
1167 let rhs = self.visit(&*rhs_node).into_return_expr()?;
1168 lhs = ReturnExpr::Arith(Box::new(lhs), op, Box::new(rhs));
1169 }
1170 Ok(lhs)
1171 }
1172
1173 fn build_unary_add_sub_expression(
1187 &mut self,
1188 ctx: &UnaryAddSubExpressionContext,
1189 ) -> Result<ReturnExpr, QueryError> {
1190 let atomic_ctx = ctx
1191 .atomicExpression()
1192 .expect("unaryAddSubExpression always has an atomicExpression");
1193 if ctx.SUB().is_some() {
1194 if let Some(numlit_ctx) = bare_num_lit(&atomic_ctx) {
1195 let text = numlit_ctx
1196 .DIGIT()
1197 .expect("numLit context always has a DIGIT token")
1198 .get_text();
1199 return parse_num_lit_text(&format!("-{text}")).map(ReturnExpr::Lit);
1200 }
1201 let operand = self.visit(&*atomic_ctx).into_return_expr()?;
1202 return Ok(ReturnExpr::Neg(Box::new(operand)));
1203 }
1204 self.visit(&*atomic_ctx).into_return_expr()
1206 }
1207
1208 fn build_atomic_expression(
1217 &mut self,
1218 ctx: &AtomicExpressionContext,
1219 ) -> Result<ReturnExpr, QueryError> {
1220 let base_ctx = ctx
1221 .propertyOrLabelExpression()
1222 .expect("atomicExpression always has a propertyOrLabelExpression");
1223 let mut base = self.visit(&*base_ctx).into_return_expr()?;
1224 for l in ctx.listExpression_all() {
1225 base = self.build_list_expression(&l, base)?;
1226 }
1227 Ok(base)
1228 }
1229
1230 fn build_string_list_null_expression(
1242 &mut self,
1243 ctx: &StringListNullExpressionContext,
1244 ) -> Result<ReturnExpr, QueryError> {
1245 let base_ctx = ctx
1246 .addSubExpression()
1247 .expect("stringListNullExpression always has an addSubExpression");
1248 let base = self.visit(&*base_ctx).into_return_expr()?;
1249 if let Some(s) = ctx.stringExpression() {
1250 return self.build_string_expression(&s, base);
1251 }
1252 if let Some(i) = ctx.inExpression() {
1253 let rhs_ctx = i
1254 .addSubExpression()
1255 .expect("inExpression always has an addSubExpression");
1256 let rhs = self.visit(&*rhs_ctx).into_return_expr()?;
1257 return Ok(ReturnExpr::In(Box::new(base), Box::new(rhs)));
1258 }
1259 let Some(n) = ctx.nullExpression() else {
1260 return Ok(base);
1261 };
1262 Ok(if n.NOT().is_some() {
1263 ReturnExpr::Not(Box::new(ReturnExpr::IsNull(Box::new(base))))
1264 } else {
1265 ReturnExpr::IsNull(Box::new(base))
1266 })
1267 }
1268
1269 fn build_string_expression(
1275 &mut self,
1276 ctx: &StringExpressionContextAll,
1277 base: ReturnExpr,
1278 ) -> Result<ReturnExpr, QueryError> {
1279 let prefix_ctx = ctx
1280 .stringExpPrefix()
1281 .expect("stringExpression always has a stringExpPrefix");
1282 let op = string_exp_op(&prefix_ctx);
1283 let rhs_ctx = ctx
1284 .addSubExpression()
1285 .expect("stringExpression always has an addSubExpression");
1286 let rhs = self.visit(&*rhs_ctx).into_return_expr()?;
1287 Ok(ReturnExpr::Compare(Box::new(base), op, Box::new(rhs)))
1288 }
1289
1290 fn build_list_expression(
1295 &mut self,
1296 ctx: &ListExpressionContextAll,
1297 base: ReturnExpr,
1298 ) -> Result<ReturnExpr, QueryError> {
1299 let exprs = ctx.expression_all();
1300 if ctx.RANGE().is_some() {
1301 let (start, end) = match exprs.len() {
1304 0 => (None, None),
1305 1 => {
1306 let before_range = list_expr_bound_is_before_range(ctx);
1312 let e = self.visit(&*exprs[0].clone()).into_return_expr()?;
1313 if before_range {
1314 (Some(Box::new(e)), None)
1315 } else {
1316 (None, Some(Box::new(e)))
1317 }
1318 }
1319 2 => {
1320 let start = self.visit(&*exprs[0].clone()).into_return_expr()?;
1321 let end = self.visit(&*exprs[1].clone()).into_return_expr()?;
1322 (Some(Box::new(start)), Some(Box::new(end)))
1323 }
1324 n => unreachable!("listExpression slice form has {n} expressions, expected 0-2"),
1325 };
1326 return Ok(ReturnExpr::Slice(Box::new(base), start, end));
1327 }
1328 let index_ctx = exprs
1329 .into_iter()
1330 .next()
1331 .expect("non-slice listExpression always has exactly one expression");
1332 let index = self.visit(&*index_ctx).into_return_expr()?;
1333 Ok(ReturnExpr::Index(Box::new(base), Box::new(index)))
1334 }
1335
1336 fn build_property_or_label_expression(
1337 &mut self,
1338 ctx: &PropertyOrLabelExpressionContext,
1339 ) -> Result<ReturnExpr, QueryError> {
1340 let prop_ctx = ctx
1341 .propertyExpression()
1342 .expect("propertyOrLabelExpression always has a propertyExpression");
1343 let base = self.visit(&*prop_ctx).into_return_expr()?;
1344 let Some(labels_ctx) = ctx.nodeLabels() else {
1345 return Ok(base);
1346 };
1347 let ReturnExpr::Var(var) = base else {
1348 return Err(QueryError::Syntax(
1349 "a label check (`x:Label`) only applies to a bare variable".into(),
1350 ));
1351 };
1352 let labels = labels_ctx.name_all().iter().map(|n| name_text(n)).collect();
1353 Ok(ReturnExpr::HasLabel(var, labels))
1354 }
1355
1356 fn build_property_expression(
1366 &mut self,
1367 ctx: &PropertyExpressionContext,
1368 ) -> Result<ReturnExpr, QueryError> {
1369 let atom_ctx = ctx.atom().expect("propertyExpression always has an atom");
1370 let base = self.visit(&*atom_ctx).into_return_expr()?;
1371 let mut names = ctx.name_all().into_iter();
1372 let Some(first) = names.next() else {
1373 return Ok(base);
1374 };
1375 let mut expr = match base {
1381 ReturnExpr::Var(var) => ReturnExpr::Prop(PropAccess {
1382 var,
1383 prop: name_text(&first),
1384 }),
1385 other => ReturnExpr::PropOf(Box::new(other), name_text(&first)),
1386 };
1387 for name in names {
1388 expr = ReturnExpr::PropOf(Box::new(expr), name_text(&name));
1389 }
1390 Ok(expr)
1391 }
1392
1393 fn build_atom(&mut self, ctx: &AtomContext) -> Result<ReturnExpr, QueryError> {
1394 if let Some(lit_ctx) = ctx.literal() {
1395 return self.visit(&*lit_ctx).into_return_expr_lenient();
1396 }
1397 if let Some(param_ctx) = ctx.parameter() {
1398 return self.build_parameter(¶m_ctx);
1399 }
1400 if let Some(paren_ctx) = ctx.parenthesizedExpression() {
1401 return self.visit(&*paren_ctx).into_return_expr();
1402 }
1403 if let Some(func_ctx) = ctx.functionInvocation() {
1404 return self.build_function_invocation(&func_ctx);
1405 }
1406 if let Some(count_ctx) = ctx.countAll() {
1407 let _ = self.visit(&*count_ctx);
1408 return Ok(ReturnExpr::CountStar);
1409 }
1410 if let Some(sym_ctx) = ctx.symbol() {
1411 return Ok(ReturnExpr::Var(symbol_text(&sym_ctx)));
1412 }
1413 if let Some(filter_ctx) = ctx.filterWith() {
1414 return self.build_filter_with(&filter_ctx);
1415 }
1416 if let Some(lc_ctx) = ctx.listComprehension() {
1417 return self.build_list_comprehension(&lc_ctx);
1418 }
1419 if let Some(case_ctx) = ctx.caseExpression() {
1420 return self.build_case_expression(&case_ctx);
1421 }
1422 if let Some(pc_ctx) = ctx.patternComprehension() {
1423 return self.build_pattern_comprehension(&pc_ctx);
1424 }
1425 if let Some(rcp_ctx) = ctx.relationshipsChainPattern() {
1426 return Ok(ReturnExpr::PatternPredicate(
1427 self.build_relationships_chain_pattern(&rcp_ctx)?,
1428 ));
1429 }
1430 if let Some(se_ctx) = ctx.subqueryExist() {
1431 return self.build_subquery_exist(&se_ctx);
1432 }
1433 Err(QueryError::Syntax(
1434 "this expression form (path-as-expression) isn't supported by the ANTLR parser yet"
1435 .into(),
1436 ))
1437 }
1438
1439 fn build_pattern_comprehension(
1450 &mut self,
1451 ctx: &PatternComprehensionContext,
1452 ) -> Result<ReturnExpr, QueryError> {
1453 let path_var = ctx
1454 .lhs()
1455 .and_then(|lhs| lhs.symbol())
1456 .map(|s| symbol_text(&s));
1457 let rcp_ctx = ctx
1458 .relationshipsChainPattern()
1459 .expect("patternComprehension always has a relationshipsChainPattern");
1460 let pattern = self.build_relationships_chain_pattern(&rcp_ctx)?;
1461 let where_clause = match ctx.where_() {
1462 Some(where_ctx) => {
1463 let expr_ctx = where_ctx
1464 .expression()
1465 .expect("where always has an expression");
1466 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1467 Some(Box::new(return_expr_to_expr(expr)?))
1468 }
1469 None => None,
1470 };
1471 let proj_ctx = ctx
1472 .expression()
1473 .expect("patternComprehension always has a projection expression");
1474 let projection = self.visit(&*proj_ctx).into_return_expr()?;
1475 Ok(ReturnExpr::PatternComprehension {
1476 path_var,
1477 pattern: Box::new(pattern),
1478 where_clause,
1479 projection: Box::new(projection),
1480 })
1481 }
1482
1483 fn build_subquery_exist(
1499 &mut self,
1500 ctx: &SubqueryExistContext,
1501 ) -> Result<ReturnExpr, QueryError> {
1502 if let Some(rq_ctx) = ctx.regularQuery() {
1503 let stmt = self.build_regular_query(&rq_ctx)?;
1504 return Ok(ReturnExpr::ExistsSubquery(Box::new(stmt)));
1505 }
1506 let pw_ctx = ctx
1507 .patternWhere()
1508 .expect("subqueryExist always has a regularQuery or patternWhere");
1509 let pattern_ctx = pw_ctx.pattern().expect("patternWhere always has a pattern");
1510 let mut parts = pattern_ctx.patternPart_all().into_iter();
1511 let part = parts
1512 .next()
1513 .expect("pattern always has at least one patternPart");
1514 if parts.next().is_some() {
1515 return Err(QueryError::Syntax(
1516 "exists {} with more than one comma-separated pattern isn't supported yet".into(),
1517 ));
1518 }
1519 if part.ASSIGN().is_some() || part.shortestPathWrapper().is_some() {
1520 return Err(QueryError::Syntax(
1521 "exists {} doesn't support a named path or shortestPath()".into(),
1522 ));
1523 }
1524 let elem_ctx = part
1525 .patternElem()
1526 .expect("a patternPart without ASSIGN/shortestPathWrapper always has a patternElem");
1527 let pattern = self.visit(&*elem_ctx).into_pattern()?;
1528 let where_clause = match pw_ctx.where_() {
1529 Some(where_ctx) => {
1530 let expr_ctx = where_ctx
1531 .expression()
1532 .expect("where always has an expression");
1533 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1534 Some(Box::new(return_expr_to_expr(expr)?))
1535 }
1536 None => None,
1537 };
1538 Ok(ReturnExpr::ExistsPattern {
1539 pattern: Box::new(pattern),
1540 where_clause,
1541 })
1542 }
1543
1544 fn build_case_expression(
1557 &mut self,
1558 ctx: &CaseExpressionContext,
1559 ) -> Result<ReturnExpr, QueryError> {
1560 #[derive(PartialEq)]
1561 enum Pos {
1562 BeforeFirstWhen,
1563 AfterWhen,
1564 AfterThen,
1565 AfterElse,
1566 }
1567 let mut pos = Pos::BeforeFirstWhen;
1568 let mut test = None;
1569 let mut whens: Vec<(ReturnExpr, ReturnExpr)> = Vec::new();
1570 let mut pending_when: Option<ReturnExpr> = None;
1571 let mut else_ = None;
1572 for child in ctx.get_children() {
1573 match child.get_text().to_ascii_uppercase().as_str() {
1574 "CASE" | "END" => continue,
1575 "WHEN" => pos = Pos::AfterWhen,
1576 "THEN" => pos = Pos::AfterThen,
1577 "ELSE" => pos = Pos::AfterElse,
1578 _ => {
1579 let expr = self.visit(&*child).into_return_expr()?;
1580 match pos {
1581 Pos::BeforeFirstWhen => test = Some(Box::new(expr)),
1582 Pos::AfterWhen => pending_when = Some(expr),
1583 Pos::AfterThen => {
1584 let w = pending_when
1585 .take()
1586 .expect("a THEN expression always follows a WHEN expression");
1587 whens.push((w, expr));
1588 }
1589 Pos::AfterElse => else_ = Some(Box::new(expr)),
1590 }
1591 }
1592 }
1593 }
1594 Ok(ReturnExpr::Case { test, whens, else_ })
1595 }
1596
1597 fn build_filter_expression(
1602 &mut self,
1603 ctx: &FilterExpressionContext,
1604 ) -> Result<(String, ReturnExpr, Option<Box<ReturnExpr>>), QueryError> {
1605 let var_ctx = ctx.symbol().expect("filterExpression always has a symbol");
1606 let var = symbol_text(&var_ctx);
1607 let source_ctx = ctx
1608 .expression()
1609 .expect("filterExpression always has an expression");
1610 let source = self.visit(&*source_ctx).into_return_expr()?;
1611 let where_clause = match ctx.where_() {
1612 Some(where_ctx) => {
1613 let expr_ctx = where_ctx
1614 .expression()
1615 .expect("where always has an expression");
1616 Some(Box::new(self.visit(&*expr_ctx).into_return_expr()?))
1617 }
1618 None => None,
1619 };
1620 Ok((var, source, where_clause))
1621 }
1622
1623 fn build_filter_with(&mut self, ctx: &FilterWithContext) -> Result<ReturnExpr, QueryError> {
1628 let kind = if ctx.ALL().is_some() {
1629 QuantifierKind::All
1630 } else if ctx.ANY().is_some() {
1631 QuantifierKind::Any
1632 } else if ctx.NONE().is_some() {
1633 QuantifierKind::None
1634 } else {
1635 ctx.SINGLE()
1636 .expect("filterWith always has one of ALL/ANY/NONE/SINGLE");
1637 QuantifierKind::Single
1638 };
1639 let fe_ctx = ctx
1640 .filterExpression()
1641 .expect("filterWith always has a filterExpression");
1642 let (var, source, where_clause) = self.build_filter_expression(&fe_ctx)?;
1643 Ok(ReturnExpr::Quantifier {
1644 kind,
1645 var,
1646 source: Box::new(source),
1647 where_clause,
1648 })
1649 }
1650
1651 fn build_list_comprehension(
1657 &mut self,
1658 ctx: &ListComprehensionContext,
1659 ) -> Result<ReturnExpr, QueryError> {
1660 let fe_ctx = ctx
1661 .filterExpression()
1662 .expect("listComprehension always has a filterExpression");
1663 let (var, source, where_clause) = self.build_filter_expression(&fe_ctx)?;
1664 let project = match ctx.expression() {
1665 Some(expr_ctx) => Some(Box::new(self.visit(&*expr_ctx).into_return_expr()?)),
1666 None => None,
1667 };
1668 Ok(ReturnExpr::ListComp {
1669 var,
1670 source: Box::new(source),
1671 where_clause,
1672 project,
1673 })
1674 }
1675
1676 fn build_function_invocation(
1677 &mut self,
1678 ctx: &FunctionInvocationContext,
1679 ) -> Result<ReturnExpr, QueryError> {
1680 let name_ctx = ctx
1681 .invocationName()
1682 .expect("functionInvocation always has an invocationName");
1683 let name = invocation_name_text(&name_ctx);
1684 let distinct = ctx.DISTINCT().is_some();
1685 let mut args = Vec::new();
1686 if let Some(chain_ctx) = ctx.expressionChain() {
1687 for arg_ctx in chain_ctx.expression_all() {
1688 args.push(self.visit(&*arg_ctx).into_return_expr()?);
1689 }
1690 }
1691 if distinct && !is_aggregate_name(&name) {
1692 return Err(QueryError::Syntax(format!(
1693 "'{name}(DISTINCT ...)' isn't valid — DISTINCT is only meaningful inside an aggregate function"
1694 )));
1695 }
1696 Ok(ReturnExpr::Call {
1697 name,
1698 args,
1699 distinct,
1700 })
1701 }
1702
1703 fn build_standalone_call(
1710 &mut self,
1711 ctx: &StandaloneCallContext,
1712 ) -> Result<Statement, QueryError> {
1713 let name_ctx = ctx
1714 .invocationName()
1715 .expect("standaloneCall always has an invocationName");
1716 let name = invocation_name_text(&name_ctx);
1717 let args = match ctx.parenExpressionChain() {
1718 Some(paren_ctx) => Some(self.build_call_args(&paren_ctx)?),
1719 None => None,
1720 };
1721 let yield_items = if ctx.MULT().is_some() {
1722 Some(CallYield::Star)
1723 } else if let Some(yi_ctx) = ctx.yieldItems() {
1724 Some(self.build_yield_items(&yi_ctx)?)
1725 } else {
1726 None
1727 };
1728 Ok(Statement::StandaloneCall(Box::new(CallClause {
1729 name,
1730 args,
1731 with: None,
1732 yield_items,
1733 })))
1734 }
1735
1736 fn build_query_call_st(
1744 &mut self,
1745 ctx: &QueryCallStContextAll,
1746 ) -> Result<CallClause, QueryError> {
1747 let name_ctx = ctx
1748 .invocationName()
1749 .expect("queryCallSt always has an invocationName");
1750 let name = invocation_name_text(&name_ctx);
1751 let paren_ctx = ctx
1752 .parenExpressionChain()
1753 .expect("queryCallSt always has a parenExpressionChain");
1754 let args = Some(self.build_call_args(&paren_ctx)?);
1755 let yield_items = match ctx.yieldItems() {
1756 Some(yi_ctx) => Some(self.build_yield_items(&yi_ctx)?),
1757 None => None,
1758 };
1759 Ok(CallClause {
1760 name,
1761 args,
1762 with: None,
1763 yield_items,
1764 })
1765 }
1766
1767 fn build_call_args(
1768 &mut self,
1769 ctx: &ParenExpressionChainContextAll,
1770 ) -> Result<Vec<ReturnExpr>, QueryError> {
1771 let mut args = Vec::new();
1772 if let Some(chain_ctx) = ctx.expressionChain() {
1773 for arg_ctx in chain_ctx.expression_all() {
1774 args.push(self.visit(&*arg_ctx).into_return_expr()?);
1775 }
1776 }
1777 Ok(args)
1778 }
1779
1780 fn build_yield_items(&mut self, ctx: &YieldItemsContextAll) -> Result<CallYield, QueryError> {
1786 let mut items = Vec::new();
1787 for item_ctx in ctx.yieldItem_all() {
1788 let symbols = item_ctx.symbol_all();
1789 let (name, alias) = match symbols.len() {
1790 1 => (symbol_text(&symbols[0]), None),
1791 2 => (symbol_text(&symbols[0]), Some(symbol_text(&symbols[1]))),
1792 other => unreachable!("yieldItem always has 1 or 2 symbols, got {other}"),
1793 };
1794 items.push((name, alias));
1795 }
1796 let where_clause = match ctx.where_() {
1797 Some(where_ctx) => {
1798 let expr_ctx = where_ctx
1799 .expression()
1800 .expect("where always has an expression");
1801 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1802 Some(Box::new(return_expr_to_expr(expr)?))
1803 }
1804 None => None,
1805 };
1806 Ok(CallYield::Items(items, where_clause))
1807 }
1808
1809 fn build_parameter(&mut self, ctx: &ParameterContext) -> Result<ReturnExpr, QueryError> {
1810 let name = if let Some(sym_ctx) = ctx.symbol() {
1811 symbol_text(&sym_ctx)
1812 } else if let Some(num_ctx) = ctx.numLit() {
1813 num_ctx
1814 .DIGIT()
1815 .expect("numLit context always has a DIGIT token")
1816 .get_text()
1817 } else {
1818 unreachable!("parameter always has a symbol or numLit")
1819 };
1820 Ok(ReturnExpr::Lit(Literal::Param(name)))
1821 }
1822
1823 fn build_projection_body(
1824 &mut self,
1825 ctx: &ProjectionBodyContext,
1826 ) -> Result<ParsedReturnClause, QueryError> {
1827 let distinct = ctx.DISTINCT().is_some();
1828 let items_ctx = ctx
1829 .projectionItems()
1830 .expect("projectionBody always has projectionItems");
1831 let tail = if items_ctx.MULT().is_some() {
1832 if !items_ctx.projectionItem_all().is_empty() {
1840 return Err(QueryError::Syntax(
1841 "RETURN * can't be combined with additional items".into(),
1842 ));
1843 }
1844 Tail::ReturnStar(distinct)
1845 } else {
1846 let mut items = Vec::new();
1847 for item_ctx in items_ctx.projectionItem_all() {
1848 let expr_ctx = item_ctx
1849 .expression()
1850 .expect("projectionItem always has an expression");
1851 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1852 let alias = item_ctx.symbol().map(|s| symbol_text(&s));
1853 items.push(ReturnItem { expr, alias });
1854 }
1855 Tail::Return(items, distinct)
1856 };
1857
1858 let (order_by, skip, limit) = self.build_order_skip_limit(ctx)?;
1859
1860 Ok(ParsedReturnClause {
1861 tail,
1862 order_by,
1863 skip,
1864 limit,
1865 })
1866 }
1867
1868 #[allow(clippy::type_complexity)]
1872 fn build_order_skip_limit(
1873 &mut self,
1874 ctx: &ProjectionBodyContext,
1875 ) -> Result<
1876 (
1877 Option<Vec<(ReturnExpr, SortDir)>>,
1878 Option<ReturnExpr>,
1879 Option<ReturnExpr>,
1880 ),
1881 QueryError,
1882 > {
1883 let order_by = match ctx.orderSt() {
1884 Some(order_ctx) => Some(self.build_order_by(&order_ctx)?),
1885 None => None,
1886 };
1887 let skip = match ctx.skipSt() {
1888 Some(skip_ctx) => {
1889 let expr_ctx = skip_ctx
1890 .expression()
1891 .expect("skipSt always has an expression");
1892 Some(self.visit(&*expr_ctx).into_return_expr()?)
1893 }
1894 None => None,
1895 };
1896 let limit = match ctx.limitSt() {
1897 Some(limit_ctx) => {
1898 let expr_ctx = limit_ctx
1899 .expression()
1900 .expect("limitSt always has an expression");
1901 Some(self.visit(&*expr_ctx).into_return_expr()?)
1902 }
1903 None => None,
1904 };
1905 Ok((order_by, skip, limit))
1906 }
1907
1908 fn build_order_by(
1909 &mut self,
1910 ctx: &OrderStContext,
1911 ) -> Result<Vec<(ReturnExpr, SortDir)>, QueryError> {
1912 let mut items = Vec::new();
1913 for item_ctx in ctx.orderItem_all() {
1914 let expr_ctx = item_ctx
1915 .expression()
1916 .expect("orderItem always has an expression");
1917 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1918 let dir = if item_ctx.DESC().is_some() || item_ctx.DESCENDING().is_some() {
1919 SortDir::Desc
1920 } else {
1921 SortDir::Asc
1922 };
1923 items.push((expr, dir));
1924 }
1925 Ok(items)
1926 }
1927
1928 fn build_with_clause(&mut self, ctx: &WithStContext) -> Result<WithClause, QueryError> {
1929 let body_ctx = ctx
1930 .projectionBody()
1931 .expect("withSt always has a projectionBody");
1932 let distinct = body_ctx.DISTINCT().is_some();
1933 let items_ctx = body_ctx
1934 .projectionItems()
1935 .expect("projectionBody always has projectionItems");
1936 let star = items_ctx.MULT().is_some();
1937 let mut items = Vec::new();
1938 for item_ctx in items_ctx.projectionItem_all() {
1939 let expr_ctx = item_ctx
1940 .expression()
1941 .expect("projectionItem always has an expression");
1942 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1943 let alias = item_ctx.symbol().map(|s| symbol_text(&s));
1944 items.push(ReturnItem { expr, alias });
1945 }
1946 let (order_by, skip, limit) = self.build_order_skip_limit(&body_ctx)?;
1947 let where_clause = match ctx.where_() {
1948 Some(where_ctx) => {
1949 let expr_ctx = where_ctx
1950 .expression()
1951 .expect("where always has an expression");
1952 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1953 Some(return_expr_to_with_expr(expr))
1954 }
1955 None => None,
1956 };
1957 Ok(WithClause {
1958 items,
1959 star,
1960 distinct,
1961 where_clause,
1962 order_by,
1963 skip,
1964 limit,
1965 })
1966 }
1967
1968 fn build_unwind_st(&mut self, ctx: &UnwindStContext) -> Result<UnwindClause, QueryError> {
1977 let expr_ctx = ctx.expression().expect("unwindSt always has an expression");
1978 let source = UnwindSource(self.visit(&*expr_ctx).into_return_expr()?);
1979 let var_ctx = ctx.symbol().expect("unwindSt always has a symbol");
1980 Ok(UnwindClause {
1981 source,
1982 var: symbol_text(&var_ctx),
1983 where_clause: None,
1984 with: None,
1985 })
1986 }
1987
1988 fn build_set_st(&mut self, ctx: &SetStContext) -> Result<Vec<SetItem>, QueryError> {
1989 ctx.setItem_all()
1990 .into_iter()
1991 .map(|item_ctx| self.build_set_item(&item_ctx))
1992 .collect()
1993 }
1994
1995 fn build_set_item(&mut self, ctx: &SetItemContextAll) -> Result<SetItem, QueryError> {
1996 if let Some(prop_ctx) = ctx.propertyExpression() {
2007 let expr_ctx = ctx
2008 .expression()
2009 .expect("setItem's propertyExpression form always has an expression");
2010 return match self.build_property_expression(&prop_ctx)? {
2011 ReturnExpr::Prop(prop) => {
2012 let value = self.visit(&*expr_ctx).into_return_expr()?;
2013 Ok(SetItem::Prop(prop, value))
2014 }
2015 ReturnExpr::Var(var) => {
2016 let value = self.visit(&*expr_ctx).into_return_expr()?;
2017 Ok(SetItem::MapAssign {
2018 var,
2019 value,
2020 merge: false,
2021 })
2022 }
2023 _ => Err(QueryError::Syntax(
2024 "expected a property access (x.prop) or variable on the left of SET's `=`"
2025 .into(),
2026 )),
2027 };
2028 }
2029 let sym_ctx = ctx
2030 .symbol()
2031 .expect("setItem always has a propertyExpression or symbol");
2032 let var = symbol_text(&sym_ctx);
2033 if let Some(labels_ctx) = ctx.nodeLabels() {
2034 let labels = labels_ctx.name_all().iter().map(|n| name_text(n)).collect();
2035 return Ok(SetItem::Labels(var, labels));
2036 }
2037 let expr_ctx = ctx
2038 .expression()
2039 .expect("setItem's symbol-assign form always has an expression");
2040 let value = self.visit(&*expr_ctx).into_return_expr()?;
2041 Ok(SetItem::MapAssign {
2042 var,
2043 value,
2044 merge: ctx.ADD_ASSIGN().is_some(),
2045 })
2046 }
2047
2048 fn build_delete_st(&mut self, ctx: &DeleteStContext) -> Result<ParsedDelete, QueryError> {
2049 let chain_ctx = ctx
2050 .expressionChain()
2051 .expect("deleteSt always has an expressionChain");
2052 let mut items = Vec::new();
2053 for expr_ctx in chain_ctx.expression_all() {
2054 items.push(self.visit(&*expr_ctx).into_return_expr()?);
2055 }
2056 Ok(ParsedDelete {
2057 items,
2058 detach: ctx.DETACH().is_some(),
2059 })
2060 }
2061
2062 fn build_remove_st(&mut self, ctx: &RemoveStContext) -> Result<Vec<RemoveItem>, QueryError> {
2063 ctx.removeItem_all()
2064 .into_iter()
2065 .map(|item_ctx| self.build_remove_item(&item_ctx))
2066 .collect()
2067 }
2068
2069 fn build_remove_item(&mut self, ctx: &RemoveItemContextAll) -> Result<RemoveItem, QueryError> {
2070 if let Some(prop_ctx) = ctx.propertyExpression() {
2071 return Ok(RemoveItem::Prop(self.build_prop_access(&prop_ctx)?));
2072 }
2073 let sym_ctx = ctx
2074 .symbol()
2075 .expect("removeItem always has a symbol+nodeLabels or a propertyExpression");
2076 let labels_ctx = ctx
2077 .nodeLabels()
2078 .expect("removeItem's symbol form always has nodeLabels");
2079 let labels = labels_ctx.name_all().iter().map(|n| name_text(n)).collect();
2080 Ok(RemoveItem::Labels(symbol_text(&sym_ctx), labels))
2081 }
2082
2083 fn build_prop_access(
2092 &mut self,
2093 ctx: &PropertyExpressionContext,
2094 ) -> Result<PropAccess, QueryError> {
2095 match self.build_property_expression(ctx)? {
2096 ReturnExpr::Prop(p) => Ok(p),
2097 _ => Err(QueryError::Syntax(
2098 "expected a property access (x.prop)".into(),
2099 )),
2100 }
2101 }
2102
2103 fn build_create_st(&mut self, ctx: &CreateStContext) -> Result<Vec<Pattern>, QueryError> {
2110 let pattern_ctx = ctx.pattern().expect("createSt always has a pattern");
2111 pattern_ctx
2112 .patternPart_all()
2113 .into_iter()
2114 .map(|part_ctx| {
2115 if part_ctx.ASSIGN().is_some() {
2116 return Err(QueryError::Syntax(
2117 "named-path capture (`p = ...`) isn't supported on CREATE".into(),
2118 ));
2119 }
2120 if part_ctx.shortestPathWrapper().is_some() {
2121 return Err(QueryError::Syntax(
2122 "shortestPath() isn't valid in CREATE".into(),
2123 ));
2124 }
2125 let elem_ctx = part_ctx.patternElem().expect(
2126 "patternPart always has a patternElem when shortestPathWrapper is absent",
2127 );
2128 self.visit(&*elem_ctx).into_pattern()
2129 })
2130 .collect()
2131 }
2132
2133 fn build_merge_st(&mut self, ctx: &MergeStContext) -> Result<MergeClause, QueryError> {
2147 let part_ctx = ctx.patternPart().expect("mergeSt always has a patternPart");
2148 let path_var = if part_ctx.ASSIGN().is_some() {
2149 let symbol_ctx = part_ctx
2150 .symbol()
2151 .expect("patternPart with ASSIGN always has a symbol");
2152 Some(symbol_text(&symbol_ctx))
2153 } else {
2154 None
2155 };
2156 if part_ctx.shortestPathWrapper().is_some() {
2157 return Err(QueryError::Syntax(
2158 "shortestPath() isn't valid in MERGE".into(),
2159 ));
2160 }
2161 let elem_ctx = part_ctx
2162 .patternElem()
2163 .expect("patternPart always has a patternElem when shortestPathWrapper is absent");
2164 let pattern = self.visit(&*elem_ctx).into_pattern()?;
2165 if pattern.hops.len() > 1 {
2166 return Err(QueryError::Syntax(
2167 "MERGE with more than one relationship hop isn't supported yet — split it into a MATCH \
2168 for the already-known part and a MERGE for one new hop"
2169 .into(),
2170 ));
2171 }
2172
2173 let mut on_create = Vec::new();
2174 let mut on_match = Vec::new();
2175 for action_ctx in ctx.mergeAction_all() {
2176 let set_items = self.build_merge_action(&action_ctx)?;
2177 if action_ctx.MATCH().is_some() {
2178 if !on_match.is_empty() {
2179 return Err(QueryError::Syntax(
2180 "MERGE can have at most one ON MATCH SET clause".into(),
2181 ));
2182 }
2183 on_match = set_items;
2184 } else {
2185 if !on_create.is_empty() {
2186 return Err(QueryError::Syntax(
2187 "MERGE can have at most one ON CREATE SET clause".into(),
2188 ));
2189 }
2190 on_create = set_items;
2191 }
2192 }
2193
2194 Ok(MergeClause {
2195 pattern,
2196 path_var,
2197 on_create,
2198 on_match,
2199 with: None,
2200 })
2201 }
2202
2203 fn build_merge_action(
2204 &mut self,
2205 ctx: &MergeActionContextAll,
2206 ) -> Result<Vec<SetItem>, QueryError> {
2207 let set_ctx = ctx.setSt().expect("mergeAction always has a setSt");
2208 self.build_set_st(&set_ctx)
2209 }
2210
2211 fn append_reading_statement(
2222 &mut self,
2223 ctx: &ReadingStatementContextAll,
2224 clauses: &mut Vec<QueryClause>,
2225 ) -> Result<(), QueryError> {
2226 if let Some(match_ctx) = ctx.matchSt() {
2227 let parts = self.visit(&*match_ctx).into_query_parts()?;
2228 clauses.extend(parts.into_iter().map(QueryClause::Match));
2229 return Ok(());
2230 }
2231 if let Some(unwind_ctx) = ctx.unwindSt() {
2232 let clause = self.visit(&*unwind_ctx).into_unwind_clause()?;
2233 clauses.push(QueryClause::Unwind(clause));
2234 return Ok(());
2235 }
2236 let call_ctx = ctx
2237 .queryCallSt()
2238 .expect("readingStatement is matchSt | unwindSt | queryCallSt");
2239 let call = self.build_query_call_st(&call_ctx)?;
2240 clauses.push(QueryClause::Call(call));
2241 Ok(())
2242 }
2243
2244 fn build_updating_statement_as_clause(
2249 &mut self,
2250 ctx: &UpdatingStatementContextAll,
2251 ) -> Result<QueryClause, QueryError> {
2252 if let Some(create_ctx) = ctx.createSt() {
2253 return Ok(QueryClause::Create(
2254 self.visit(&*create_ctx).into_create_patterns()?,
2255 ));
2256 }
2257 if let Some(merge_ctx) = ctx.mergeSt() {
2258 return Ok(QueryClause::Merge(
2259 self.visit(&*merge_ctx).into_merge_clause()?,
2260 ));
2261 }
2262 if let Some(delete_ctx) = ctx.deleteSt() {
2263 let d = self.visit(&*delete_ctx).into_delete_items()?;
2264 return Ok(QueryClause::Delete {
2265 items: d.items,
2266 detach: d.detach,
2267 });
2268 }
2269 if let Some(set_ctx) = ctx.setSt() {
2270 return Ok(QueryClause::Set(self.visit(&*set_ctx).into_set_items()?));
2271 }
2272 let remove_ctx = ctx
2273 .removeSt()
2274 .expect("updatingStatement always has one of its 5 alternatives");
2275 Ok(QueryClause::Remove(
2276 self.visit(&*remove_ctx).into_remove_items()?,
2277 ))
2278 }
2279
2280 #[allow(clippy::type_complexity)]
2304 fn build_mutating_tail(
2305 &mut self,
2306 ctx: &UpdatingStatementContextAll,
2307 return_ctx: Option<&ReturnStContext>,
2308 ) -> Result<
2309 (
2310 Tail,
2311 Option<Vec<(ReturnExpr, SortDir)>>,
2312 Option<ReturnExpr>,
2313 Option<ReturnExpr>,
2314 ),
2315 QueryError,
2316 > {
2317 let mut order_by = None;
2318 let mut skip = None;
2319 let mut limit = None;
2320 let ret = match return_ctx {
2321 Some(return_ctx) => {
2322 let c = self.visit(return_ctx).into_return_clause()?;
2323 order_by = c.order_by;
2324 skip = c.skip;
2325 limit = c.limit;
2326 let Tail::Return(items, distinct) = c.tail else {
2327 return Err(QueryError::Syntax(
2328 "RETURN * isn't supported as a mutating clause's own trailing RETURN"
2329 .into(),
2330 ));
2331 };
2332 Some(ReturnTail { items, distinct })
2333 }
2334 None => None,
2335 };
2336 let tail = if let Some(create_ctx) = ctx.createSt() {
2337 Tail::Create(self.visit(&*create_ctx).into_create_patterns()?, ret)
2338 } else if let Some(delete_ctx) = ctx.deleteSt() {
2339 let d = self.visit(&*delete_ctx).into_delete_items()?;
2340 if d.detach {
2341 Tail::DetachDelete(d.items, ret)
2342 } else {
2343 Tail::Delete(d.items, ret)
2344 }
2345 } else if let Some(set_ctx) = ctx.setSt() {
2346 Tail::Set(self.visit(&*set_ctx).into_set_items()?, ret)
2347 } else {
2348 let remove_ctx = ctx
2349 .removeSt()
2350 .expect("build_mutating_tail's caller already excluded mergeSt");
2351 Tail::Remove(self.visit(&*remove_ctx).into_remove_items()?, ret)
2352 };
2353 Ok((tail, order_by, skip, limit))
2354 }
2355
2356 fn build_single_part_q(&mut self, ctx: &SinglePartQContext) -> Result<Statement, QueryError> {
2370 let mut clauses = Vec::new();
2371 for rs_ctx in ctx.readingStatement_all() {
2372 self.append_reading_statement(&rs_ctx, &mut clauses)?;
2373 }
2374
2375 let updating = ctx.updatingStatement_all();
2376 let return_ctx = ctx.returnSt();
2377
2378 if clauses.is_empty() && return_ctx.is_none() && updating.len() == 1 {
2388 if let Some(create_ctx) = updating[0].createSt() {
2389 let patterns = self.visit(&*create_ctx).into_create_patterns()?;
2390 return Ok(Statement::Create(patterns));
2391 }
2392 }
2393
2394 let mut tail = None;
2395 let mut order_by = None;
2396 let mut skip = None;
2397 let mut limit = None;
2398 let mut consumed_return = false;
2399
2400 if let Some((last, earlier)) = updating.split_last() {
2401 for us_ctx in earlier {
2402 clauses.push(self.build_updating_statement_as_clause(us_ctx)?);
2403 }
2404 if last.mergeSt().is_some() {
2405 clauses.push(self.build_updating_statement_as_clause(last)?);
2406 } else {
2407 let (t, ob, sk, lim) = self.build_mutating_tail(last, return_ctx.as_deref())?;
2408 tail = Some(t);
2409 order_by = ob;
2410 skip = sk;
2411 limit = lim;
2412 consumed_return = return_ctx.is_some();
2413 }
2414 }
2415
2416 if !consumed_return {
2417 if let Some(return_ctx) = return_ctx {
2418 let c = self.visit(&*return_ctx).into_return_clause()?;
2419 tail = Some(c.tail);
2420 order_by = c.order_by;
2421 skip = c.skip;
2422 limit = c.limit;
2423 }
2424 }
2425
2426 if tail.is_none() && !clauses.iter().any(|c| matches!(c, QueryClause::Merge(_))) {
2427 return Err(QueryError::Syntax(
2428 "a query needs a RETURN/DELETE/SET tail, unless it has a MERGE clause with nothing after it".into(),
2429 ));
2430 }
2431
2432 Ok(Statement::Match {
2433 clauses,
2434 tail,
2435 order_by,
2436 skip: skip.map(Box::new),
2437 limit: limit.map(Box::new),
2438 })
2439 }
2440
2441 fn build_multi_part_q(&mut self, ctx: &MultiPartQContext) -> Result<Statement, QueryError> {
2463 enum Item<'i> {
2464 Reading(Rc<ReadingStatementContextAll<'i>>),
2465 Updating(Rc<UpdatingStatementContextAll<'i>>),
2466 With(Rc<WithStContext<'i>>),
2467 }
2468 let mut items: Vec<(isize, Item)> = Vec::new();
2469 for rs in ctx.readingStatement_all() {
2470 let idx = rs.start().get_token_index();
2471 items.push((idx, Item::Reading(rs)));
2472 }
2473 for us in ctx.updatingStatement_all() {
2474 let idx = us.start().get_token_index();
2475 items.push((idx, Item::Updating(us)));
2476 }
2477 for w in ctx.withSt_all() {
2478 let idx = w.start().get_token_index();
2479 items.push((idx, Item::With(w)));
2480 }
2481 items.sort_by_key(|(idx, _)| *idx);
2482
2483 let mut clauses: Vec<QueryClause> = Vec::new();
2484 let mut attach_target: Option<usize> = None;
2485 for (_, item) in items {
2486 match item {
2487 Item::Reading(rs) => {
2488 self.append_reading_statement(&rs, &mut clauses)?;
2489 attach_target = Some(clauses.len() - 1);
2490 }
2491 Item::Updating(us) => {
2492 let clause = self.build_updating_statement_as_clause(&us)?;
2493 let can_attach = matches!(clause, QueryClause::Merge(_));
2494 clauses.push(clause);
2495 attach_target = can_attach.then_some(clauses.len() - 1);
2496 }
2497 Item::With(w) => {
2498 let with = self.visit(&*w).into_with_clause()?;
2499 match attach_target.take() {
2500 Some(i) => match &mut clauses[i] {
2501 QueryClause::Match(part) => part.with = Some(with),
2502 QueryClause::Unwind(u) => u.with = Some(with),
2503 QueryClause::Merge(m) => m.with = Some(with),
2504 QueryClause::Call(call) => call.with = Some(with),
2505 _ => unreachable!(
2506 "attach_target is only ever set right after pushing a Match/Unwind/Merge/Call clause"
2507 ),
2508 },
2509 None => clauses.push(QueryClause::With(with)),
2510 }
2511 }
2512 }
2513 }
2514
2515 let sp_ctx = ctx
2516 .singlePartQ()
2517 .expect("multiPartQ always ends in a singlePartQ");
2518 let (tail_clauses, tail, order_by, skip, limit) =
2527 match self.build_single_part_q(&sp_ctx)? {
2528 Statement::Match {
2529 clauses,
2530 tail,
2531 order_by,
2532 skip,
2533 limit,
2534 } => (clauses, tail, order_by, skip, limit),
2535 Statement::Create(patterns) => {
2536 (Vec::new(), Some(Tail::Create(patterns, None)), None, None, None)
2537 }
2538 other => unreachable!(
2539 "build_single_part_q only ever returns Statement::Match or Statement::Create, got {other:?}"
2540 ),
2541 };
2542 clauses.extend(tail_clauses);
2543 Ok(Statement::Match {
2544 clauses,
2545 tail,
2546 order_by,
2547 skip,
2548 limit,
2549 })
2550 }
2551
2552 fn build_explain_st(&mut self, ctx: &ExplainStContext) -> Result<Statement, QueryError> {
2557 let inner = match ctx.createIndexSt() {
2558 Some(ci_ctx) => self.build_create_index_st(&ci_ctx)?,
2559 None => {
2560 let rq_ctx = ctx
2561 .regularQuery()
2562 .expect("explainSt always has a createIndexSt or regularQuery");
2563 self.visit(&*rq_ctx).into_statement()?
2564 }
2565 };
2566 Ok(Statement::Explain(Box::new(inner)))
2567 }
2568
2569 fn build_create_index_st(
2575 &mut self,
2576 ctx: &CreateIndexStContext,
2577 ) -> Result<Statement, QueryError> {
2578 let names = ctx.name_all();
2579 let label = name_text(
2580 names
2581 .first()
2582 .expect("createIndexSt always has a label name"),
2583 );
2584 let prop = name_text(
2585 names
2586 .get(1)
2587 .expect("createIndexSt always has a property name"),
2588 );
2589 Ok(Statement::CreateIndex {
2590 label,
2591 prop,
2592 unique: ctx.UNIQUE().is_some(),
2593 })
2594 }
2595
2596 fn build_regular_query(&mut self, ctx: &RegularQueryContext) -> Result<Statement, QueryError> {
2606 let sq_ctx = ctx
2607 .singleQuery()
2608 .expect("regularQuery always has a singleQuery");
2609 let first = self.visit(&*sq_ctx).into_statement()?;
2610 let unions = ctx.unionSt_all();
2611 if unions.is_empty() {
2612 return Ok(first);
2613 }
2614 let mut parts = vec![first];
2615 let mut all: Option<bool> = None;
2616 for u_ctx in unions {
2617 let this_all = u_ctx.ALL().is_some();
2618 match all {
2619 None => all = Some(this_all),
2620 Some(prev) if prev != this_all => {
2621 return Err(QueryError::Syntax(
2622 "can't mix UNION and UNION ALL in the same statement".into(),
2623 ));
2624 }
2625 Some(_) => {}
2626 }
2627 let part_sq = u_ctx
2628 .singleQuery()
2629 .expect("unionSt always has a singleQuery");
2630 parts.push(self.visit(&*part_sq).into_statement()?);
2631 }
2632 Ok(Statement::Union {
2633 parts,
2634 all: all.unwrap_or(false),
2635 })
2636 }
2637}
2638
2639pub fn parse_antlr(input: &str) -> Result<Statement, QueryError> {
2643 let trimmed = input.trim();
2656 let trimmed = trimmed
2657 .strip_suffix(';')
2658 .map(str::trim_end)
2659 .unwrap_or(trimmed);
2660 let mut words = trimmed.split_whitespace();
2661 match (words.next(), words.next(), words.next()) {
2662 (Some(begin), rest, None)
2663 if begin.eq_ignore_ascii_case("BEGIN")
2664 && rest.is_none_or(|w| w.eq_ignore_ascii_case("TRANSACTION")) =>
2665 {
2666 return Ok(Statement::Begin);
2667 }
2668 (Some(commit), None, None) if commit.eq_ignore_ascii_case("COMMIT") => {
2669 return Ok(Statement::Commit);
2670 }
2671 (Some(rollback), None, None) if rollback.eq_ignore_ascii_case("ROLLBACK") => {
2672 return Ok(Statement::Rollback);
2673 }
2674 _ => {}
2675 }
2676 use crate::generated::cypherlexer::CypherLexer;
2677 use crate::generated::cypherparser::{CypherParser, ScriptContextAttrs};
2678 use antlr4rust::common_token_stream::CommonTokenStream;
2679 use antlr4rust::error_listener::ErrorListener;
2680 use antlr4rust::recognizer::Recognizer;
2681 use antlr4rust::token_factory::TokenFactory;
2682 use antlr4rust::InputStream;
2683 use antlr4rust::Parser as _;
2684 use std::cell::RefCell;
2685
2686 struct CollectErrors(Rc<RefCell<Vec<String>>>);
2687 impl<'a, T: Recognizer<'a>> ErrorListener<'a, T> for CollectErrors {
2688 fn syntax_error(
2689 &self,
2690 _recognizer: &T,
2691 _offending_symbol: Option<&<T::TF as TokenFactory<'a>>::Inner>,
2692 line: isize,
2693 column: isize,
2694 msg: &str,
2695 _e: Option<&antlr4rust::errors::ANTLRError>,
2696 ) {
2697 self.0
2698 .borrow_mut()
2699 .push(format!("line {line}:{column} {msg}"));
2700 }
2701 }
2702
2703 let errors = Rc::new(RefCell::new(Vec::new()));
2704 let stream = InputStream::new(input);
2705 let mut lexer = CypherLexer::new(stream);
2706 lexer.remove_error_listeners();
2707 lexer.add_error_listener(Box::new(CollectErrors(errors.clone())));
2708 let tokens = CommonTokenStream::new(lexer);
2709 let mut parser = CypherParser::new(tokens);
2710 parser.remove_error_listeners();
2711 parser.add_error_listener(Box::new(CollectErrors(errors.clone())));
2712 let ctx = parser
2713 .script()
2714 .map_err(|e| QueryError::Syntax(e.to_string()))?;
2715 if let Some(msg) = errors.borrow().first() {
2716 return Err(QueryError::Syntax(format!("syntax error: {msg}")));
2717 }
2718 let query_ctx = ctx.query().expect("script always has a query");
2729 AstBuilder::new().visit(&*query_ctx).into_statement()
2730}
2731
2732pub fn parse_antlr_many(input: &str) -> Result<Vec<Statement>, QueryError> {
2753 let trimmed = input.trim_end();
2754 let trimmed = trimmed.strip_suffix(';').unwrap_or(trimmed);
2755 split_statements(trimmed)
2756 .into_iter()
2757 .map(parse_antlr)
2758 .collect()
2759}
2760
2761pub fn split_statements(input: &str) -> Vec<&str> {
2774 let bytes = input.as_bytes();
2775 let mut starts = vec![0usize];
2776 let mut semicolons = Vec::new();
2777 let mut quote: Option<u8> = None;
2778 let mut i = 0;
2779 while i < bytes.len() {
2780 let b = bytes[i];
2781 match quote {
2782 Some(q) => {
2783 if b == b'\\' && q != b'`' {
2784 i += 1; } else if b == q {
2786 quote = None;
2787 }
2788 }
2789 None => match b {
2790 b'\'' | b'"' | b'`' => quote = Some(b),
2791 b';' => {
2792 semicolons.push(i);
2793 starts.push(i + 1);
2794 }
2795 _ => {}
2796 },
2797 }
2798 i += 1;
2799 }
2800 starts
2801 .iter()
2802 .enumerate()
2803 .map(|(idx, &start)| {
2804 let end = semicolons.get(idx).copied().unwrap_or(bytes.len());
2805 &input[start..end]
2806 })
2807 .collect()
2808}
2809
2810fn return_expr_to_with_expr(expr: ReturnExpr) -> WithExpr {
2820 match expr {
2821 ReturnExpr::And(l, r) => WithExpr::And(
2822 Box::new(return_expr_to_with_expr(*l)),
2823 Box::new(return_expr_to_with_expr(*r)),
2824 ),
2825 ReturnExpr::Or(l, r) => WithExpr::Or(
2826 Box::new(return_expr_to_with_expr(*l)),
2827 Box::new(return_expr_to_with_expr(*r)),
2828 ),
2829 ReturnExpr::Not(inner) => WithExpr::Not(Box::new(return_expr_to_with_expr(*inner))),
2830 ReturnExpr::Compare(l, op, r) => WithExpr::Compare(*l, op, *r),
2831 ReturnExpr::IsNull(inner) => WithExpr::IsNull(*inner),
2832 other => WithExpr::Bare(other),
2833 }
2834}
2835
2836fn return_expr_to_expr(expr: ReturnExpr) -> Result<Expr, QueryError> {
2854 Ok(match expr {
2855 ReturnExpr::And(l, r) => Expr::And(
2856 Box::new(return_expr_to_expr(*l)?),
2857 Box::new(return_expr_to_expr(*r)?),
2858 ),
2859 ReturnExpr::Or(l, r) => Expr::Or(
2860 Box::new(return_expr_to_expr(*l)?),
2861 Box::new(return_expr_to_expr(*r)?),
2862 ),
2863 ReturnExpr::Not(inner) => Expr::Not(Box::new(return_expr_to_expr(*inner)?)),
2864 ReturnExpr::Compare(l, op, r) => match (*l, *r) {
2865 (ReturnExpr::Prop(pa), ReturnExpr::Lit(lit)) => Expr::Compare(pa, op, lit),
2866 (ReturnExpr::Prop(pa1), ReturnExpr::Prop(pa2)) => Expr::PropCompare(pa1, op, pa2),
2867 (ReturnExpr::Var(a), ReturnExpr::Var(b)) => match op {
2868 CompareOp::Eq => Expr::VarEq(a, b),
2869 CompareOp::Ne => Expr::Not(Box::new(Expr::VarEq(a, b))),
2870 _ => {
2871 return Err(QueryError::Syntax(format!(
2872 "{a} {op:?} {b}: only = and <> are meaningful for comparing two \
2873 nodes/relationships by identity (no ordering exists between them)"
2874 )))
2875 }
2876 },
2877 (l, r) => Expr::GeneralCompare(l, op, r),
2878 },
2879 ReturnExpr::IsNull(inner) => match *inner {
2880 ReturnExpr::Prop(pa) => Expr::IsNull(pa),
2881 other => Expr::GeneralIsNull(other),
2882 },
2883 ReturnExpr::HasLabel(var, labels) => {
2884 let mut labels = labels.into_iter();
2885 let first = labels
2886 .next()
2887 .expect("HasLabel always carries at least one label");
2888 labels.fold(Expr::HasLabel(var.clone(), first), |acc, label| {
2889 Expr::And(Box::new(acc), Box::new(Expr::HasLabel(var.clone(), label)))
2890 })
2891 }
2892 ReturnExpr::PatternPredicate(pattern) => Expr::Pattern(pattern),
2893 ReturnExpr::ExistsPattern {
2894 pattern,
2895 where_clause,
2896 } => Expr::Exists {
2897 pattern,
2898 where_clause,
2899 },
2900 ReturnExpr::ExistsSubquery(stmt) => Expr::ExistsSubquery(stmt),
2901 other => Expr::GeneralBare(other),
2902 })
2903}
2904
2905#[cfg(test)]
2906mod tests {
2907 use super::*;
2908 use crate::generated::cypherlexer::CypherLexer;
2909 use crate::generated::cypherparser::CypherParser;
2910 use antlr4rust::common_token_stream::CommonTokenStream;
2911 use antlr4rust::InputStream;
2912
2913 fn parse_literal_expr(input: &str) -> Result<Literal, QueryError> {
2914 let stream = InputStream::new(input);
2915 let lexer = CypherLexer::new(stream);
2916 let tokens = CommonTokenStream::new(lexer);
2917 let mut parser = CypherParser::new(tokens);
2918 let ctx = parser
2919 .literal()
2920 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `literal`: {e:?}"));
2921 AstBuilder::new().visit(&*ctx).into_literal()
2922 }
2923
2924 fn parse_pattern(input: &str) -> Result<Pattern, QueryError> {
2925 let stream = InputStream::new(input);
2926 let lexer = CypherLexer::new(stream);
2927 let tokens = CommonTokenStream::new(lexer);
2928 let mut parser = CypherParser::new(tokens);
2929 let ctx = parser
2930 .patternElem()
2931 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `patternElem`: {e:?}"));
2932 AstBuilder::new().visit(&*ctx).into_pattern()
2933 }
2934
2935 fn parse_match(input: &str) -> Result<Vec<QueryPart>, QueryError> {
2936 let stream = InputStream::new(input);
2937 let lexer = CypherLexer::new(stream);
2938 let tokens = CommonTokenStream::new(lexer);
2939 let mut parser = CypherParser::new(tokens);
2940 let ctx = parser
2941 .matchSt()
2942 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `matchSt`: {e:?}"));
2943 AstBuilder::new().visit(&*ctx).into_query_parts()
2944 }
2945
2946 fn parse_expr(input: &str) -> Result<ReturnExpr, QueryError> {
2947 let stream = InputStream::new(input);
2948 let lexer = CypherLexer::new(stream);
2949 let tokens = CommonTokenStream::new(lexer);
2950 let mut parser = CypherParser::new(tokens);
2951 let ctx = parser
2952 .expression()
2953 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `expression`: {e:?}"));
2954 AstBuilder::new().visit(&*ctx).into_return_expr()
2955 }
2956
2957 fn parse_return(input: &str) -> Result<ParsedReturnClause, QueryError> {
2958 let stream = InputStream::new(input);
2959 let lexer = CypherLexer::new(stream);
2960 let tokens = CommonTokenStream::new(lexer);
2961 let mut parser = CypherParser::new(tokens);
2962 let ctx = parser
2963 .returnSt()
2964 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `returnSt`: {e:?}"));
2965 AstBuilder::new().visit(&*ctx).into_return_clause()
2966 }
2967
2968 fn parse_with(input: &str) -> Result<WithClause, QueryError> {
2969 let stream = InputStream::new(input);
2970 let lexer = CypherLexer::new(stream);
2971 let tokens = CommonTokenStream::new(lexer);
2972 let mut parser = CypherParser::new(tokens);
2973 let ctx = parser
2974 .withSt()
2975 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `withSt`: {e:?}"));
2976 AstBuilder::new().visit(&*ctx).into_with_clause()
2977 }
2978
2979 fn parse_unwind(input: &str) -> Result<UnwindClause, QueryError> {
2980 let stream = InputStream::new(input);
2981 let lexer = CypherLexer::new(stream);
2982 let tokens = CommonTokenStream::new(lexer);
2983 let mut parser = CypherParser::new(tokens);
2984 let ctx = parser
2985 .unwindSt()
2986 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `unwindSt`: {e:?}"));
2987 AstBuilder::new().visit(&*ctx).into_unwind_clause()
2988 }
2989
2990 fn parse_set(input: &str) -> Result<Vec<SetItem>, QueryError> {
2991 let stream = InputStream::new(input);
2992 let lexer = CypherLexer::new(stream);
2993 let tokens = CommonTokenStream::new(lexer);
2994 let mut parser = CypherParser::new(tokens);
2995 let ctx = parser
2996 .setSt()
2997 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `setSt`: {e:?}"));
2998 AstBuilder::new().visit(&*ctx).into_set_items()
2999 }
3000
3001 fn parse_delete(input: &str) -> Result<ParsedDelete, QueryError> {
3002 let stream = InputStream::new(input);
3003 let lexer = CypherLexer::new(stream);
3004 let tokens = CommonTokenStream::new(lexer);
3005 let mut parser = CypherParser::new(tokens);
3006 let ctx = parser
3007 .deleteSt()
3008 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `deleteSt`: {e:?}"));
3009 AstBuilder::new().visit(&*ctx).into_delete_items()
3010 }
3011
3012 fn parse_remove(input: &str) -> Result<Vec<RemoveItem>, QueryError> {
3013 let stream = InputStream::new(input);
3014 let lexer = CypherLexer::new(stream);
3015 let tokens = CommonTokenStream::new(lexer);
3016 let mut parser = CypherParser::new(tokens);
3017 let ctx = parser
3018 .removeSt()
3019 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `removeSt`: {e:?}"));
3020 AstBuilder::new().visit(&*ctx).into_remove_items()
3021 }
3022
3023 fn parse_create(input: &str) -> Result<Vec<Pattern>, QueryError> {
3024 let stream = InputStream::new(input);
3025 let lexer = CypherLexer::new(stream);
3026 let tokens = CommonTokenStream::new(lexer);
3027 let mut parser = CypherParser::new(tokens);
3028 let ctx = parser
3029 .createSt()
3030 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `createSt`: {e:?}"));
3031 AstBuilder::new().visit(&*ctx).into_create_patterns()
3032 }
3033
3034 fn parse_merge(input: &str) -> Result<MergeClause, QueryError> {
3035 let stream = InputStream::new(input);
3036 let lexer = CypherLexer::new(stream);
3037 let tokens = CommonTokenStream::new(lexer);
3038 let mut parser = CypherParser::new(tokens);
3039 let ctx = parser
3040 .mergeSt()
3041 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `mergeSt`: {e:?}"));
3042 AstBuilder::new().visit(&*ctx).into_merge_clause()
3043 }
3044
3045 fn parse_statement(input: &str) -> Result<Statement, QueryError> {
3046 let stream = InputStream::new(input);
3047 let lexer = CypherLexer::new(stream);
3048 let tokens = CommonTokenStream::new(lexer);
3049 let mut parser = CypherParser::new(tokens);
3050 let ctx = parser
3051 .singlePartQ()
3052 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `singlePartQ`: {e:?}"));
3053 AstBuilder::new().visit(&*ctx).into_statement()
3054 }
3055
3056 fn parse_multi_part_statement(input: &str) -> Result<Statement, QueryError> {
3057 let stream = InputStream::new(input);
3058 let lexer = CypherLexer::new(stream);
3059 let tokens = CommonTokenStream::new(lexer);
3060 let mut parser = CypherParser::new(tokens);
3061 let ctx = parser
3062 .multiPartQ()
3063 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `multiPartQ`: {e:?}"));
3064 AstBuilder::new().visit(&*ctx).into_statement()
3065 }
3066
3067 #[test]
3068 fn bool_literals() {
3069 assert_eq!(parse_literal_expr("true").unwrap(), Literal::Bool(true));
3070 assert_eq!(parse_literal_expr("FALSE").unwrap(), Literal::Bool(false));
3071 }
3072
3073 #[test]
3074 fn null_literal() {
3075 assert_eq!(parse_literal_expr("null").unwrap(), Literal::Null);
3076 }
3077
3078 #[test]
3079 fn decimal_int() {
3080 assert_eq!(parse_literal_expr("42").unwrap(), Literal::Int(42));
3081 assert_eq!(parse_literal_expr("007").unwrap(), Literal::Int(7));
3082 }
3083
3084 #[test]
3085 fn hex_and_octal_int() {
3086 assert_eq!(parse_literal_expr("0x1A").unwrap(), Literal::Int(26));
3087 assert_eq!(parse_literal_expr("0o17").unwrap(), Literal::Int(15));
3088 }
3089
3090 #[test]
3102 fn float_literals() {
3103 assert_eq!(parse_literal_expr("2.5").unwrap(), Literal::Float(2.5));
3104 assert_eq!(parse_literal_expr("1e10").unwrap(), Literal::Float(1e10));
3105 assert_eq!(parse_literal_expr(".5").unwrap(), Literal::Float(0.5));
3106 }
3107
3108 #[test]
3109 fn float_overflow_errors() {
3110 assert!(parse_literal_expr("1e999").is_err());
3111 }
3112
3113 #[test]
3114 fn string_and_char_literals() {
3115 assert_eq!(
3116 parse_literal_expr("\"hello\"").unwrap(),
3117 Literal::String("hello".to_string())
3118 );
3119 assert_eq!(
3120 parse_literal_expr("'a string with spaces and a hyphen-in-it'").unwrap(),
3121 Literal::String("a string with spaces and a hyphen-in-it".to_string())
3122 );
3123 }
3124
3125 #[test]
3126 fn string_escapes() {
3127 assert_eq!(
3128 parse_literal_expr(r#"'line1\nline2'"#).unwrap(),
3129 Literal::String("line1\nline2".to_string())
3130 );
3131 assert_eq!(
3132 parse_literal_expr(r#"'é'"#).unwrap(),
3133 Literal::String("é".to_string())
3134 );
3135 }
3136
3137 #[test]
3138 fn single_node() {
3139 let p = parse_pattern("(a:Person)").unwrap();
3140 assert_eq!(p.start.var.as_deref(), Some("a"));
3141 assert_eq!(p.start.labels, vec!["Person".to_string()]);
3142 assert!(p.hops.is_empty());
3143 }
3144
3145 #[test]
3146 fn anonymous_node() {
3147 let p = parse_pattern("()").unwrap();
3148 assert_eq!(p.start.var, None);
3149 assert!(p.start.labels.is_empty());
3150 }
3151
3152 #[test]
3153 fn multiple_labels() {
3154 let p = parse_pattern("(a:Person:Employee)").unwrap();
3155 assert_eq!(
3156 p.start.labels,
3157 vec!["Person".to_string(), "Employee".to_string()]
3158 );
3159 }
3160
3161 #[test]
3162 fn escaped_identifier() {
3163 let p = parse_pattern("(`weird name`)").unwrap();
3164 assert_eq!(p.start.var.as_deref(), Some("weird name"));
3165 }
3166
3167 #[test]
3168 fn directions() {
3169 assert_eq!(
3170 parse_pattern("(a)-->(b)").unwrap().hops[0].0.direction,
3171 RelDirection::Right
3172 );
3173 assert_eq!(
3174 parse_pattern("(a)<--(b)").unwrap().hops[0].0.direction,
3175 RelDirection::Left
3176 );
3177 assert_eq!(
3178 parse_pattern("(a)--(b)").unwrap().hops[0].0.direction,
3179 RelDirection::Either
3180 );
3181 assert_eq!(
3186 parse_pattern("(a)<-->(b)").unwrap().hops[0].0.direction,
3187 RelDirection::Either
3188 );
3189 }
3190
3191 #[test]
3192 fn rel_type_and_var() {
3193 let p = parse_pattern("(a)-[r:KNOWS]->(b)").unwrap();
3194 let (rel, node) = &p.hops[0];
3195 assert_eq!(rel.var.as_deref(), Some("r"));
3196 assert_eq!(rel.rel_types, vec!["KNOWS".to_string()]);
3197 assert_eq!(node.var.as_deref(), Some("b"));
3198 assert_eq!(rel.hop_range, None);
3199 }
3200
3201 #[test]
3202 fn multiple_rel_types() {
3203 let p = parse_pattern("(a)-[:KNOWS|LIKES]->(b)").unwrap();
3204 assert_eq!(
3205 p.hops[0].0.rel_types,
3206 vec!["KNOWS".to_string(), "LIKES".to_string()]
3207 );
3208 }
3209
3210 #[test]
3211 fn var_length_bounds() {
3212 assert_eq!(
3215 parse_pattern("(a)-[*0]->(b)").unwrap().hops[0].0.hop_range,
3216 Some((0, Some(0)))
3217 );
3218 assert_eq!(
3219 parse_pattern("(a)-[*2]->(b)").unwrap().hops[0].0.hop_range,
3220 Some((2, Some(2)))
3221 );
3222 assert_eq!(
3223 parse_pattern("(a)-[*1..3]->(b)").unwrap().hops[0]
3224 .0
3225 .hop_range,
3226 Some((1, Some(3)))
3227 );
3228 assert_eq!(
3229 parse_pattern("(a)-[*]->(b)").unwrap().hops[0].0.hop_range,
3230 Some((1, None))
3231 );
3232 }
3233
3234 #[test]
3235 fn multi_hop_chain() {
3236 let p = parse_pattern("(a)-[:KNOWS]->(b)<-[:LIKES]-(c)").unwrap();
3237 assert_eq!(p.hops.len(), 2);
3238 assert_eq!(p.hops[0].0.direction, RelDirection::Right);
3239 assert_eq!(p.hops[1].0.direction, RelDirection::Left);
3240 }
3241
3242 #[test]
3243 fn node_pattern_properties() {
3244 let pattern = parse_pattern("(a {name: 'x', age: 1 + 1})").unwrap();
3245 assert_eq!(
3246 pattern.start.props,
3247 vec![
3248 (
3249 "name".to_string(),
3250 ReturnExpr::Lit(Literal::String("x".to_string()))
3251 ),
3252 (
3253 "age".to_string(),
3254 ReturnExpr::Arith(
3255 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3256 ArithOp::Add,
3257 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3258 )
3259 ),
3260 ]
3261 );
3262 }
3263
3264 #[test]
3265 fn rel_pattern_properties() {
3266 let pattern = parse_pattern("(a)-[:T {weight: 5}]->(b)").unwrap();
3267 assert_eq!(
3268 pattern.hops[0].0.props,
3269 vec![("weight".to_string(), ReturnExpr::Lit(Literal::Int(5)))]
3270 );
3271 }
3272
3273 #[test]
3274 fn pattern_properties_parameter_not_supported() {
3275 assert!(parse_pattern("(a $props)").is_err());
3276 }
3277
3278 #[test]
3279 fn simple_match() {
3280 let parts = parse_match("MATCH (a:Person)-[:KNOWS]->(b)").unwrap();
3281 assert_eq!(parts.len(), 1);
3282 assert!(!parts[0].optional);
3283 assert_eq!(parts[0].path_var, None);
3284 assert_eq!(parts[0].pattern.start.var.as_deref(), Some("a"));
3285 assert_eq!(parts[0].pattern.hops.len(), 1);
3286 }
3287
3288 #[test]
3289 fn optional_match() {
3290 let parts = parse_match("OPTIONAL MATCH (a)").unwrap();
3291 assert!(parts[0].optional);
3292 }
3293
3294 #[test]
3295 fn named_path() {
3296 let parts = parse_match("MATCH p = (a)-->(b)").unwrap();
3297 assert_eq!(parts.len(), 1);
3298 assert_eq!(parts[0].path_var.as_deref(), Some("p"));
3299 }
3300
3301 #[test]
3302 fn comma_pattern_shared_node_merges_into_one_linear_chain() {
3303 let parts = parse_match("MATCH (a)-->(b), (b)-->(c)").unwrap();
3306 assert_eq!(parts.len(), 1);
3307 assert_eq!(parts[0].pattern.hops.len(), 2);
3308 }
3309
3310 #[test]
3311 fn comma_pattern_disjoint_becomes_multiple_query_parts() {
3312 let parts = parse_match("MATCH (a), (b)").unwrap();
3313 assert_eq!(parts.len(), 2);
3314 }
3315
3316 #[test]
3317 fn named_path_over_disjoint_cross_join_errors() {
3318 assert!(parse_match("MATCH p = (a), (b)").is_err());
3319 }
3320
3321 #[test]
3322 fn shortest_path() {
3323 let parts = parse_match("MATCH shortestPath((a)-[*1..3]->(b))").unwrap();
3324 assert_eq!(parts.len(), 1);
3325 assert!(parts[0].shortest_path);
3326 assert_eq!(parts[0].pattern.hops.len(), 1);
3327 }
3328
3329 #[test]
3330 fn shortest_path_with_named_path_capture() {
3331 let parts = parse_match("MATCH p = shortestPath((a)-[*1..3]->(b))").unwrap();
3332 assert_eq!(parts[0].path_var.as_deref(), Some("p"));
3333 assert!(parts[0].shortest_path);
3334 }
3335
3336 #[test]
3337 fn shortest_path_requires_variable_length_hop() {
3338 assert!(parse_match("MATCH shortestPath((a)-->(b))").is_err());
3339 }
3340
3341 #[test]
3342 fn shortest_path_not_first_in_cross_join_errors() {
3343 assert!(parse_match("MATCH (c), shortestPath((a)-[*1..3]->(b))").is_err());
3344 }
3345
3346 #[test]
3347 fn shortest_path_over_disjoint_cross_join_errors() {
3348 assert!(parse_match("MATCH shortestPath((a)-[*1..3]->(b)), (c)").is_err());
3349 }
3350
3351 #[test]
3352 fn shortest_path_not_valid_in_create() {
3353 assert!(parse_statement("CREATE shortestPath((a)-[*1..3]->(b))").is_err());
3354 }
3355
3356 #[test]
3357 fn shortest_path_not_valid_in_merge() {
3358 assert!(parse_merge("MERGE shortestPath((a)-[*1..3]->(b))").is_err());
3359 }
3360
3361 #[test]
3362 fn named_path_over_a_single_variable_length_hop_is_supported() {
3363 let parts = parse_match("MATCH p = (a)-[*1..3]->(b)").unwrap();
3367 assert_eq!(parts[0].path_var.as_deref(), Some("p"));
3368 }
3369
3370 #[test]
3371 fn named_path_over_variable_length_mixed_with_another_hop_is_supported() {
3372 let parts = parse_match("MATCH p = (a)-[*1..3]->(b)-->(c)").unwrap();
3375 assert_eq!(parts[0].path_var.as_deref(), Some("p"));
3376 }
3377
3378 #[test]
3379 fn match_where() {
3380 let parts = parse_match("MATCH (a) WHERE a.x = 1").unwrap();
3381 assert_eq!(parts.len(), 1);
3382 assert!(matches!(
3383 parts[0].where_clause,
3384 Some(Expr::Compare(
3385 PropAccess { .. },
3386 CompareOp::Eq,
3387 Literal::Int(1),
3388 ))
3389 ));
3390 }
3391
3392 #[test]
3393 fn match_where_var_eq() {
3394 let parts = parse_match("MATCH (a), (b) WHERE a = b").unwrap();
3395 assert!(matches!(parts[1].where_clause, Some(Expr::VarEq(_, _))));
3396 }
3397
3398 #[test]
3399 fn match_where_label_predicate() {
3400 let parts = parse_match("MATCH (a) WHERE a:A:B").unwrap();
3401 assert!(matches!(parts[0].where_clause, Some(Expr::And(_, _))));
3402 }
3403
3404 #[test]
3405 fn match_where_pattern_predicate() {
3406 let parts = parse_match("MATCH (n) WHERE (n)-[]->() RETURN n")
3407 .unwrap_or_else(|e| panic!("expected pattern predicate to parse, got {e:?}"));
3408 let Some(Expr::Pattern(pattern)) = &parts[0].where_clause else {
3409 panic!("expected Expr::Pattern");
3410 };
3411 assert_eq!(pattern.hops.len(), 1);
3412 }
3413
3414 #[test]
3415 fn match_where_pattern_predicate_combined_with_and() {
3416 let parts = parse_match("MATCH (n) WHERE (n)-->() AND n.x = 1").unwrap();
3417 let Some(Expr::And(l, r)) = &parts[0].where_clause else {
3418 panic!("expected Expr::And");
3419 };
3420 assert!(matches!(**l, Expr::Pattern(_)));
3421 assert!(matches!(**r, Expr::Compare(..)));
3422 }
3423
3424 #[test]
3425 fn pattern_predicate_outside_where_still_parses() {
3426 let expr = parse_expr("(n)-->()").unwrap();
3431 assert!(matches!(expr, ReturnExpr::PatternPredicate(_)));
3432 }
3433
3434 #[test]
3435 fn match_where_on_last_group_of_cross_join() {
3436 let parts = parse_match("MATCH (a), (b) WHERE b.x = 1").unwrap();
3437 assert_eq!(parts.len(), 2);
3438 assert!(parts[0].where_clause.is_none());
3439 assert!(parts[1].where_clause.is_some());
3440 }
3441
3442 #[test]
3443 fn arithmetic_precedence() {
3444 assert_eq!(
3446 parse_expr("1 + 2 * 3").unwrap(),
3447 ReturnExpr::Arith(
3448 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3449 ArithOp::Add,
3450 Box::new(ReturnExpr::Arith(
3451 Box::new(ReturnExpr::Lit(Literal::Int(2))),
3452 ArithOp::Mul,
3453 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3454 )),
3455 )
3456 );
3457 }
3458
3459 #[test]
3460 fn arithmetic_left_associative() {
3461 assert_eq!(
3463 parse_expr("10 - 2 - 3").unwrap(),
3464 ReturnExpr::Arith(
3465 Box::new(ReturnExpr::Arith(
3466 Box::new(ReturnExpr::Lit(Literal::Int(10))),
3467 ArithOp::Sub,
3468 Box::new(ReturnExpr::Lit(Literal::Int(2))),
3469 )),
3470 ArithOp::Sub,
3471 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3472 )
3473 );
3474 }
3475
3476 #[test]
3477 fn power_left_associative() {
3478 assert_eq!(
3479 parse_expr("4 ^ 3 ^ 2").unwrap(),
3480 ReturnExpr::Arith(
3481 Box::new(ReturnExpr::Arith(
3482 Box::new(ReturnExpr::Lit(Literal::Int(4))),
3483 ArithOp::Pow,
3484 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3485 )),
3486 ArithOp::Pow,
3487 Box::new(ReturnExpr::Lit(Literal::Int(2))),
3488 )
3489 );
3490 }
3491
3492 #[test]
3493 fn binary_minus_no_whitespace() {
3494 assert_eq!(
3497 parse_expr("5-1").unwrap(),
3498 ReturnExpr::Arith(
3499 Box::new(ReturnExpr::Lit(Literal::Int(5))),
3500 ArithOp::Sub,
3501 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3502 )
3503 );
3504 }
3505
3506 #[test]
3507 fn unary_minus_on_variable() {
3508 assert_eq!(
3509 parse_expr("-x").unwrap(),
3510 ReturnExpr::Neg(Box::new(ReturnExpr::Var("x".to_string())))
3511 );
3512 }
3513
3514 #[test]
3515 fn unary_minus_folds_into_literal() {
3516 assert_eq!(parse_expr("-5").unwrap(), ReturnExpr::Lit(Literal::Int(-5)));
3517 assert_eq!(
3518 parse_expr("-5.5").unwrap(),
3519 ReturnExpr::Lit(Literal::Float(-5.5))
3520 );
3521 }
3522
3523 #[test]
3524 fn unary_minus_int_min_two_complement_edge_case() {
3525 assert_eq!(
3530 parse_expr("-9223372036854775808").unwrap(),
3531 ReturnExpr::Lit(Literal::Int(i64::MIN))
3532 );
3533 }
3534
3535 #[test]
3536 fn comparison_chain_folds_into_nested_and() {
3537 assert_eq!(
3540 parse_expr("1 < x < 3").unwrap(),
3541 ReturnExpr::And(
3542 Box::new(ReturnExpr::Compare(
3543 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3544 CompareOp::Lt,
3545 Box::new(ReturnExpr::Var("x".to_string())),
3546 )),
3547 Box::new(ReturnExpr::Compare(
3548 Box::new(ReturnExpr::Var("x".to_string())),
3549 CompareOp::Lt,
3550 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3551 )),
3552 )
3553 );
3554 }
3555
3556 #[test]
3557 fn boolean_operators() {
3558 assert_eq!(
3559 parse_expr("true AND false").unwrap(),
3560 ReturnExpr::And(
3561 Box::new(ReturnExpr::Lit(Literal::Bool(true))),
3562 Box::new(ReturnExpr::Lit(Literal::Bool(false))),
3563 )
3564 );
3565 assert_eq!(
3566 parse_expr("true OR false").unwrap(),
3567 ReturnExpr::Or(
3568 Box::new(ReturnExpr::Lit(Literal::Bool(true))),
3569 Box::new(ReturnExpr::Lit(Literal::Bool(false))),
3570 )
3571 );
3572 assert_eq!(
3573 parse_expr("true XOR false").unwrap(),
3574 ReturnExpr::Xor(
3575 Box::new(ReturnExpr::Lit(Literal::Bool(true))),
3576 Box::new(ReturnExpr::Lit(Literal::Bool(false))),
3577 )
3578 );
3579 }
3580
3581 #[test]
3582 fn double_negation() {
3583 assert_eq!(
3585 parse_expr("NOT NOT true").unwrap(),
3586 ReturnExpr::Not(Box::new(ReturnExpr::Not(Box::new(ReturnExpr::Lit(
3587 Literal::Bool(true)
3588 )))))
3589 );
3590 }
3591
3592 #[test]
3593 fn is_null() {
3594 assert_eq!(
3595 parse_expr("x IS NULL").unwrap(),
3596 ReturnExpr::IsNull(Box::new(ReturnExpr::Var("x".to_string())))
3597 );
3598 assert_eq!(
3599 parse_expr("x IS NOT NULL").unwrap(),
3600 ReturnExpr::Not(Box::new(ReturnExpr::IsNull(Box::new(ReturnExpr::Var(
3601 "x".to_string()
3602 )))))
3603 );
3604 }
3605
3606 #[test]
3607 fn in_operator() {
3608 assert_eq!(
3609 parse_expr("x IN y").unwrap(),
3610 ReturnExpr::In(
3611 Box::new(ReturnExpr::Var("x".to_string())),
3612 Box::new(ReturnExpr::Var("y".to_string())),
3613 )
3614 );
3615 }
3616
3617 #[test]
3618 fn is_null_binds_looser_than_arithmetic() {
3619 assert_eq!(
3624 parse_expr("x + 0 IS NULL").unwrap(),
3625 ReturnExpr::IsNull(Box::new(ReturnExpr::Arith(
3626 Box::new(ReturnExpr::Var("x".to_string())),
3627 ArithOp::Add,
3628 Box::new(ReturnExpr::Lit(Literal::Int(0))),
3629 )))
3630 );
3631 }
3632
3633 #[test]
3634 fn in_binds_looser_than_arithmetic_and_operand_can_be_sliced() {
3635 assert_eq!(
3636 parse_expr("3 IN [1, 2, 3][0..2]").unwrap(),
3637 ReturnExpr::In(
3638 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3639 Box::new(ReturnExpr::Slice(
3640 Box::new(ReturnExpr::ListLit(vec![
3641 ReturnExpr::Lit(Literal::Int(1)),
3642 ReturnExpr::Lit(Literal::Int(2)),
3643 ReturnExpr::Lit(Literal::Int(3)),
3644 ])),
3645 Some(Box::new(ReturnExpr::Lit(Literal::Int(0)))),
3646 Some(Box::new(ReturnExpr::Lit(Literal::Int(2)))),
3647 ))
3648 )
3649 );
3650 }
3651
3652 #[test]
3653 fn starts_with_operand_can_be_an_arithmetic_expression() {
3654 assert_eq!(
3655 parse_expr("x STARTS WITH y + z").unwrap(),
3656 ReturnExpr::Compare(
3657 Box::new(ReturnExpr::Var("x".to_string())),
3658 CompareOp::StartsWith,
3659 Box::new(ReturnExpr::Arith(
3660 Box::new(ReturnExpr::Var("y".to_string())),
3661 ArithOp::Add,
3662 Box::new(ReturnExpr::Var("z".to_string())),
3663 )),
3664 )
3665 );
3666 }
3667
3668 #[test]
3669 fn chained_index_postfix_still_works() {
3670 assert_eq!(
3671 parse_expr("[[1, 2], [3, 4]][0][1]").unwrap(),
3672 ReturnExpr::Index(
3673 Box::new(ReturnExpr::Index(
3674 Box::new(ReturnExpr::ListLit(vec![
3675 ReturnExpr::ListLit(vec![
3676 ReturnExpr::Lit(Literal::Int(1)),
3677 ReturnExpr::Lit(Literal::Int(2)),
3678 ]),
3679 ReturnExpr::ListLit(vec![
3680 ReturnExpr::Lit(Literal::Int(3)),
3681 ReturnExpr::Lit(Literal::Int(4)),
3682 ]),
3683 ])),
3684 Box::new(ReturnExpr::Lit(Literal::Int(0))),
3685 )),
3686 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3687 )
3688 );
3689 }
3690
3691 #[test]
3692 fn case_searched_form() {
3693 assert_eq!(
3694 parse_expr("CASE WHEN x > 1 THEN 'big' WHEN x > 0 THEN 'small' ELSE 'none' END")
3695 .unwrap(),
3696 ReturnExpr::Case {
3697 test: None,
3698 whens: vec![
3699 (
3700 ReturnExpr::Compare(
3701 Box::new(ReturnExpr::Var("x".to_string())),
3702 CompareOp::Gt,
3703 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3704 ),
3705 ReturnExpr::Lit(Literal::String("big".to_string())),
3706 ),
3707 (
3708 ReturnExpr::Compare(
3709 Box::new(ReturnExpr::Var("x".to_string())),
3710 CompareOp::Gt,
3711 Box::new(ReturnExpr::Lit(Literal::Int(0))),
3712 ),
3713 ReturnExpr::Lit(Literal::String("small".to_string())),
3714 ),
3715 ],
3716 else_: Some(Box::new(ReturnExpr::Lit(Literal::String(
3717 "none".to_string()
3718 )))),
3719 }
3720 );
3721 }
3722
3723 #[test]
3724 fn case_simple_form_with_test_no_else() {
3725 assert_eq!(
3726 parse_expr("CASE x WHEN 1 THEN 'one' WHEN 2 THEN 'two' END").unwrap(),
3727 ReturnExpr::Case {
3728 test: Some(Box::new(ReturnExpr::Var("x".to_string()))),
3729 whens: vec![
3730 (
3731 ReturnExpr::Lit(Literal::Int(1)),
3732 ReturnExpr::Lit(Literal::String("one".to_string())),
3733 ),
3734 (
3735 ReturnExpr::Lit(Literal::Int(2)),
3736 ReturnExpr::Lit(Literal::String("two".to_string())),
3737 ),
3738 ],
3739 else_: None,
3740 }
3741 );
3742 }
3743
3744 #[test]
3745 fn quantifier_none() {
3746 assert_eq!(
3747 parse_expr("none(x IN [1,2] WHERE x > 1)").unwrap(),
3748 ReturnExpr::Quantifier {
3749 kind: QuantifierKind::None,
3750 var: "x".to_string(),
3751 source: Box::new(ReturnExpr::ListLit(vec![
3752 ReturnExpr::Lit(Literal::Int(1)),
3753 ReturnExpr::Lit(Literal::Int(2)),
3754 ])),
3755 where_clause: Some(Box::new(ReturnExpr::Compare(
3756 Box::new(ReturnExpr::Var("x".to_string())),
3757 CompareOp::Gt,
3758 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3759 ))),
3760 }
3761 );
3762 }
3763
3764 #[test]
3765 fn quantifier_all_any_single_no_where() {
3766 assert!(matches!(
3767 parse_expr("all(x IN [1]) ").unwrap(),
3768 ReturnExpr::Quantifier {
3769 kind: QuantifierKind::All,
3770 where_clause: None,
3771 ..
3772 }
3773 ));
3774 assert!(matches!(
3775 parse_expr("any(x IN [1])").unwrap(),
3776 ReturnExpr::Quantifier {
3777 kind: QuantifierKind::Any,
3778 ..
3779 }
3780 ));
3781 assert!(matches!(
3782 parse_expr("single(x IN [1])").unwrap(),
3783 ReturnExpr::Quantifier {
3784 kind: QuantifierKind::Single,
3785 ..
3786 }
3787 ));
3788 }
3789
3790 #[test]
3791 fn list_comprehension_with_projection() {
3792 assert_eq!(
3793 parse_expr("[x IN [1,2] WHERE x > 1 | x * 2]").unwrap(),
3794 ReturnExpr::ListComp {
3795 var: "x".to_string(),
3796 source: Box::new(ReturnExpr::ListLit(vec![
3797 ReturnExpr::Lit(Literal::Int(1)),
3798 ReturnExpr::Lit(Literal::Int(2)),
3799 ])),
3800 where_clause: Some(Box::new(ReturnExpr::Compare(
3801 Box::new(ReturnExpr::Var("x".to_string())),
3802 CompareOp::Gt,
3803 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3804 ))),
3805 project: Some(Box::new(ReturnExpr::Arith(
3806 Box::new(ReturnExpr::Var("x".to_string())),
3807 ArithOp::Mul,
3808 Box::new(ReturnExpr::Lit(Literal::Int(2))),
3809 ))),
3810 }
3811 );
3812 }
3813
3814 #[test]
3815 fn list_comprehension_with_where_no_project() {
3816 assert_eq!(
3817 parse_expr("[x IN [1,2] WHERE x > 1]").unwrap(),
3818 ReturnExpr::ListComp {
3819 var: "x".to_string(),
3820 source: Box::new(ReturnExpr::ListLit(vec![
3821 ReturnExpr::Lit(Literal::Int(1)),
3822 ReturnExpr::Lit(Literal::Int(2)),
3823 ])),
3824 where_clause: Some(Box::new(ReturnExpr::Compare(
3825 Box::new(ReturnExpr::Var("x".to_string())),
3826 CompareOp::Gt,
3827 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3828 ))),
3829 project: None,
3830 }
3831 );
3832 }
3833
3834 #[test]
3835 fn list_comprehension_bare_identity_no_where_no_project() {
3836 assert_eq!(
3844 parse_expr("[x IN [1, 2, 3]]").unwrap(),
3845 ReturnExpr::ListComp {
3846 var: "x".to_string(),
3847 source: Box::new(ReturnExpr::ListLit(vec![
3848 ReturnExpr::Lit(Literal::Int(1)),
3849 ReturnExpr::Lit(Literal::Int(2)),
3850 ReturnExpr::Lit(Literal::Int(3)),
3851 ])),
3852 where_clause: None,
3853 project: None,
3854 }
3855 );
3856 }
3857
3858 #[test]
3859 fn string_predicates() {
3860 assert_eq!(
3861 parse_expr("x STARTS WITH y").unwrap(),
3862 ReturnExpr::Compare(
3863 Box::new(ReturnExpr::Var("x".to_string())),
3864 CompareOp::StartsWith,
3865 Box::new(ReturnExpr::Var("y".to_string())),
3866 )
3867 );
3868 assert_eq!(
3869 parse_expr("x ENDS WITH y").unwrap(),
3870 ReturnExpr::Compare(
3871 Box::new(ReturnExpr::Var("x".to_string())),
3872 CompareOp::EndsWith,
3873 Box::new(ReturnExpr::Var("y".to_string())),
3874 )
3875 );
3876 assert_eq!(
3877 parse_expr("x CONTAINS y").unwrap(),
3878 ReturnExpr::Compare(
3879 Box::new(ReturnExpr::Var("x".to_string())),
3880 CompareOp::Contains,
3881 Box::new(ReturnExpr::Var("y".to_string())),
3882 )
3883 );
3884 }
3885
3886 #[test]
3887 fn index_and_slice() {
3888 assert_eq!(
3889 parse_expr("list[0]").unwrap(),
3890 ReturnExpr::Index(
3891 Box::new(ReturnExpr::Var("list".to_string())),
3892 Box::new(ReturnExpr::Lit(Literal::Int(0))),
3893 )
3894 );
3895 assert_eq!(
3896 parse_expr("list[1..3]").unwrap(),
3897 ReturnExpr::Slice(
3898 Box::new(ReturnExpr::Var("list".to_string())),
3899 Some(Box::new(ReturnExpr::Lit(Literal::Int(1)))),
3900 Some(Box::new(ReturnExpr::Lit(Literal::Int(3)))),
3901 )
3902 );
3903 assert_eq!(
3904 parse_expr("list[..3]").unwrap(),
3905 ReturnExpr::Slice(
3906 Box::new(ReturnExpr::Var("list".to_string())),
3907 None,
3908 Some(Box::new(ReturnExpr::Lit(Literal::Int(3)))),
3909 )
3910 );
3911 assert_eq!(
3912 parse_expr("list[1..]").unwrap(),
3913 ReturnExpr::Slice(
3914 Box::new(ReturnExpr::Var("list".to_string())),
3915 Some(Box::new(ReturnExpr::Lit(Literal::Int(1)))),
3916 None,
3917 )
3918 );
3919 }
3920
3921 #[test]
3922 fn property_access() {
3923 assert_eq!(
3924 parse_expr("n.name").unwrap(),
3925 ReturnExpr::Prop(PropAccess {
3926 var: "n".to_string(),
3927 prop: "name".to_string(),
3928 })
3929 );
3930 }
3931
3932 #[test]
3933 fn property_access_with_backtick_escaped_name() {
3934 assert_eq!(
3940 parse_expr("n.`weird name`").unwrap(),
3941 ReturnExpr::Prop(PropAccess {
3942 var: "n".to_string(),
3943 prop: "weird name".to_string(),
3944 })
3945 );
3946 }
3947
3948 #[test]
3949 fn property_access_on_computed_expr_becomes_prop_of() {
3950 let expr = parse_expr("duration.between(a, b).days").unwrap();
3955 let ReturnExpr::PropOf(base, prop) = expr else {
3956 panic!("expected PropOf, got {expr:?}");
3957 };
3958 assert_eq!(prop, "days");
3959 assert!(matches!(*base, ReturnExpr::Call { .. }));
3960 }
3961
3962 #[test]
3963 fn chained_property_access_folds_left_to_right() {
3964 let expr = parse_expr("a.b.c").unwrap();
3966 let ReturnExpr::PropOf(base, prop) = expr else {
3967 panic!("expected PropOf, got {expr:?}");
3968 };
3969 assert_eq!(prop, "c");
3970 assert_eq!(
3971 *base,
3972 ReturnExpr::Prop(PropAccess {
3973 var: "a".to_string(),
3974 prop: "b".to_string(),
3975 })
3976 );
3977 }
3978
3979 #[test]
3980 fn has_label() {
3981 assert_eq!(
3982 parse_expr("n:Person").unwrap(),
3983 ReturnExpr::HasLabel("n".to_string(), vec!["Person".to_string()])
3984 );
3985 }
3986
3987 #[test]
3988 fn function_call() {
3989 assert_eq!(
3990 parse_expr("size(list)").unwrap(),
3991 ReturnExpr::Call {
3992 name: "size".to_string(),
3993 args: vec![ReturnExpr::Var("list".to_string())],
3994 distinct: false,
3995 }
3996 );
3997 }
3998
3999 #[test]
4000 fn namespaced_function_call() {
4001 assert_eq!(
4002 parse_expr("duration.between(a, b)").unwrap(),
4003 ReturnExpr::Call {
4004 name: "duration.between".to_string(),
4005 args: vec![
4006 ReturnExpr::Var("a".to_string()),
4007 ReturnExpr::Var("b".to_string())
4008 ],
4009 distinct: false,
4010 }
4011 );
4012 }
4013
4014 #[test]
4015 fn count_star() {
4016 assert_eq!(parse_expr("count(*)").unwrap(), ReturnExpr::CountStar);
4017 }
4018
4019 #[test]
4020 fn aggregate_distinct() {
4021 assert_eq!(
4022 parse_expr("count(DISTINCT x)").unwrap(),
4023 ReturnExpr::Call {
4024 name: "count".to_string(),
4025 args: vec![ReturnExpr::Var("x".to_string())],
4026 distinct: true,
4027 }
4028 );
4029 }
4030
4031 #[test]
4032 fn distinct_on_non_aggregate_errors() {
4033 assert!(parse_expr("size(DISTINCT x)").is_err());
4034 }
4035
4036 #[test]
4037 fn distinct_on_namespaced_call_errors() {
4038 assert!(parse_expr("duration.between(DISTINCT a, b)").is_err());
4039 }
4040
4041 #[test]
4042 fn parameter_by_name() {
4043 assert_eq!(
4044 parse_expr("$name").unwrap(),
4045 ReturnExpr::Lit(Literal::Param("name".to_string()))
4046 );
4047 }
4048
4049 #[test]
4050 fn parameter_by_position() {
4051 assert_eq!(
4052 parse_expr("$0").unwrap(),
4053 ReturnExpr::Lit(Literal::Param("0".to_string()))
4054 );
4055 }
4056
4057 #[test]
4058 fn parenthesized_expression() {
4059 assert_eq!(
4060 parse_expr("(1 + 2) * 3").unwrap(),
4061 ReturnExpr::Arith(
4062 Box::new(ReturnExpr::Arith(
4063 Box::new(ReturnExpr::Lit(Literal::Int(1))),
4064 ArithOp::Add,
4065 Box::new(ReturnExpr::Lit(Literal::Int(2))),
4066 )),
4067 ArithOp::Mul,
4068 Box::new(ReturnExpr::Lit(Literal::Int(3))),
4069 )
4070 );
4071 }
4072
4073 #[test]
4074 fn return_simple_items() {
4075 let c = parse_return("RETURN a, b.name AS name").unwrap();
4076 let Tail::Return(items, distinct) = c.tail else {
4077 panic!("expected Tail::Return");
4078 };
4079 assert!(!distinct);
4080 assert_eq!(items.len(), 2);
4081 assert_eq!(items[0].expr, ReturnExpr::Var("a".to_string()));
4082 assert_eq!(items[0].alias, None);
4083 assert_eq!(
4084 items[1].expr,
4085 ReturnExpr::Prop(PropAccess {
4086 var: "b".to_string(),
4087 prop: "name".to_string(),
4088 })
4089 );
4090 assert_eq!(items[1].alias.as_deref(), Some("name"));
4091 }
4092
4093 #[test]
4094 fn return_distinct() {
4095 let c = parse_return("RETURN DISTINCT a").unwrap();
4096 let Tail::Return(_, distinct) = c.tail else {
4097 panic!("expected Tail::Return");
4098 };
4099 assert!(distinct);
4100 }
4101
4102 #[test]
4103 fn return_star() {
4104 let c = parse_return("RETURN *").unwrap();
4105 assert!(matches!(c.tail, Tail::ReturnStar(false)));
4106 }
4107
4108 #[test]
4109 fn return_order_by_skip_limit() {
4110 let c = parse_return("RETURN a ORDER BY a DESC SKIP 5 LIMIT 10").unwrap();
4111 let order_by = c.order_by.unwrap();
4112 assert_eq!(order_by.len(), 1);
4113 assert_eq!(order_by[0].0, ReturnExpr::Var("a".to_string()));
4114 assert_eq!(order_by[0].1, SortDir::Desc);
4115 assert_eq!(c.skip, Some(ReturnExpr::Lit(Literal::Int(5))));
4116 assert_eq!(c.limit, Some(ReturnExpr::Lit(Literal::Int(10))));
4117 }
4118
4119 #[test]
4120 fn order_by_default_ascending() {
4121 let c = parse_return("RETURN a ORDER BY a").unwrap();
4122 assert_eq!(c.order_by.unwrap()[0].1, SortDir::Asc);
4123 }
4124
4125 #[test]
4126 fn limit_accepts_arbitrary_expression() {
4127 let c = parse_return("RETURN a LIMIT 1 + 1").unwrap();
4134 assert!(c.limit.is_some());
4135 }
4136
4137 #[test]
4138 fn return_star_with_extra_items_errors() {
4139 assert!(parse_return("RETURN *, x AS y").is_err());
4143 }
4144
4145 #[test]
4146 fn with_items() {
4147 let c = parse_with("WITH a, b.name AS name").unwrap();
4148 assert!(!c.star);
4149 assert!(!c.distinct);
4150 assert_eq!(c.items.len(), 2);
4151 assert_eq!(c.items[0].expr, ReturnExpr::Var("a".to_string()));
4152 assert_eq!(c.items[1].alias.as_deref(), Some("name"));
4153 }
4154
4155 #[test]
4156 fn with_star() {
4157 let c = parse_with("WITH *").unwrap();
4158 assert!(c.star);
4159 assert!(c.items.is_empty());
4160 }
4161
4162 #[test]
4163 fn with_star_and_items() {
4164 let c = parse_with("WITH *, x AS y").unwrap();
4167 assert!(c.star);
4168 assert_eq!(c.items.len(), 1);
4169 assert_eq!(c.items[0].alias.as_deref(), Some("y"));
4170 }
4171
4172 #[test]
4173 fn with_distinct_order_skip_limit() {
4174 let c = parse_with("WITH DISTINCT a ORDER BY a SKIP 1 LIMIT 2").unwrap();
4175 assert!(c.distinct);
4176 assert!(c.order_by.is_some());
4177 assert_eq!(c.skip, Some(ReturnExpr::Lit(Literal::Int(1))));
4178 assert_eq!(c.limit, Some(ReturnExpr::Lit(Literal::Int(2))));
4179 }
4180
4181 #[test]
4182 fn with_where_compare() {
4183 let c = parse_with("WITH a WHERE a.x = 1").unwrap();
4184 let WithExpr::Compare(lhs, op, rhs) = c.where_clause.unwrap() else {
4185 panic!("expected WithExpr::Compare");
4186 };
4187 assert_eq!(
4188 lhs,
4189 ReturnExpr::Prop(PropAccess {
4190 var: "a".to_string(),
4191 prop: "x".to_string()
4192 })
4193 );
4194 assert_eq!(op, CompareOp::Eq);
4195 assert_eq!(rhs, ReturnExpr::Lit(Literal::Int(1)));
4196 }
4197
4198 #[test]
4199 fn with_where_and_or_not() {
4200 let c = parse_with("WITH a WHERE NOT (a.x = 1 AND a.y = 2)").unwrap();
4201 assert!(matches!(c.where_clause.unwrap(), WithExpr::Not(_)));
4202
4203 let c = parse_with("WITH a WHERE a.x = 1 OR a.y = 2").unwrap();
4204 assert!(matches!(c.where_clause.unwrap(), WithExpr::Or(_, _)));
4205 }
4206
4207 #[test]
4208 fn with_where_is_null() {
4209 let c = parse_with("WITH a WHERE a IS NULL").unwrap();
4210 assert!(matches!(c.where_clause.unwrap(), WithExpr::IsNull(_)));
4211 }
4212
4213 #[test]
4214 fn with_where_bare_expression() {
4215 let c = parse_with("WITH n WHERE n:Person").unwrap();
4219 assert!(matches!(c.where_clause.unwrap(), WithExpr::Bare(_)));
4220 }
4221
4222 #[test]
4223 fn with_where_xor_becomes_bare() {
4224 let c = parse_with("WITH a WHERE a.x XOR a.y").unwrap();
4227 assert!(matches!(c.where_clause.unwrap(), WithExpr::Bare(_)));
4228 }
4229
4230 #[test]
4231 fn unwind_basic() {
4232 let c = parse_unwind("UNWIND [1, 2, 3] AS x").unwrap();
4233 assert_eq!(c.var, "x");
4234 assert_eq!(
4235 c.source.0,
4236 ReturnExpr::ListLit(vec![
4237 ReturnExpr::Lit(Literal::Int(1)),
4238 ReturnExpr::Lit(Literal::Int(2)),
4239 ReturnExpr::Lit(Literal::Int(3)),
4240 ])
4241 );
4242 assert!(c.where_clause.is_none());
4243 assert!(c.with.is_none());
4244 }
4245
4246 #[test]
4247 fn set_prop() {
4248 let items = parse_set("SET n.name = 'x'").unwrap();
4249 assert_eq!(items.len(), 1);
4250 let SetItem::Prop(prop, value) = &items[0] else {
4251 panic!("expected SetItem::Prop");
4252 };
4253 assert_eq!(prop.var, "n");
4254 assert_eq!(prop.prop, "name");
4255 assert_eq!(*value, ReturnExpr::Lit(Literal::String("x".to_string())));
4256 }
4257
4258 #[test]
4259 fn set_labels() {
4260 let items = parse_set("SET n:A:B").unwrap();
4261 let SetItem::Labels(var, labels) = &items[0] else {
4262 panic!("expected SetItem::Labels");
4263 };
4264 assert_eq!(var, "n");
4265 assert_eq!(labels, &vec!["A".to_string(), "B".to_string()]);
4266 }
4267
4268 #[test]
4269 fn set_map_assign() {
4270 let items = parse_set("SET n = {a: 1}").unwrap();
4271 let SetItem::MapAssign { var, merge, .. } = &items[0] else {
4272 panic!("expected SetItem::MapAssign");
4273 };
4274 assert_eq!(var, "n");
4275 assert!(!merge);
4276
4277 let items = parse_set("SET n += {a: 1}").unwrap();
4278 let SetItem::MapAssign { merge, .. } = &items[0] else {
4279 panic!("expected SetItem::MapAssign");
4280 };
4281 assert!(merge);
4282 }
4283
4284 #[test]
4285 fn set_multiple_items() {
4286 assert_eq!(parse_set("SET n.a = 1, n.b = 2").unwrap().len(), 2);
4287 }
4288
4289 #[test]
4290 fn delete_items() {
4291 let d = parse_delete("DELETE n, r").unwrap();
4292 assert!(!d.detach);
4293 assert_eq!(d.items.len(), 2);
4294 }
4295
4296 #[test]
4297 fn detach_delete() {
4298 let d = parse_delete("DETACH DELETE n").unwrap();
4299 assert!(d.detach);
4300 }
4301
4302 #[test]
4303 fn remove_prop() {
4304 let items = parse_remove("REMOVE n.name").unwrap();
4305 let RemoveItem::Prop(prop) = &items[0] else {
4306 panic!("expected RemoveItem::Prop");
4307 };
4308 assert_eq!(prop.var, "n");
4309 assert_eq!(prop.prop, "name");
4310 }
4311
4312 #[test]
4313 fn remove_labels() {
4314 let items = parse_remove("REMOVE n:A:B").unwrap();
4315 let RemoveItem::Labels(var, labels) = &items[0] else {
4316 panic!("expected RemoveItem::Labels");
4317 };
4318 assert_eq!(var, "n");
4319 assert_eq!(labels, &vec!["A".to_string(), "B".to_string()]);
4320 }
4321
4322 #[test]
4323 fn create_single_pattern() {
4324 let patterns = parse_create("CREATE (a:Person)").unwrap();
4325 assert_eq!(patterns.len(), 1);
4326 assert_eq!(patterns[0].start.var.as_deref(), Some("a"));
4327 }
4328
4329 #[test]
4330 fn create_comma_patterns_stay_separate() {
4331 let patterns = parse_create("CREATE (a), (a)-->(b)").unwrap();
4334 assert_eq!(patterns.len(), 2);
4335 }
4336
4337 #[test]
4338 fn create_named_path_errors() {
4339 assert!(parse_create("CREATE p = (a)-->(b)").is_err());
4340 }
4341
4342 #[test]
4343 fn merge_single_hop() {
4344 let m = parse_merge("MERGE (a)-[:KNOWS]->(b)").unwrap();
4345 assert_eq!(m.pattern.hops.len(), 1);
4346 assert!(m.on_create.is_empty());
4347 assert!(m.on_match.is_empty());
4348 }
4349
4350 #[test]
4351 fn merge_multi_hop_errors() {
4352 assert!(parse_merge("MERGE (a)-->(b)-->(c)").is_err());
4353 }
4354
4355 #[test]
4356 fn merge_named_path_capture() {
4357 let m = parse_merge("MERGE p = (a)-->(b)").unwrap();
4358 assert_eq!(m.path_var.as_deref(), Some("p"));
4359 }
4360
4361 #[test]
4362 fn merge_on_create_on_match() {
4363 let m = parse_merge("MERGE (a) ON CREATE SET a.created = true ON MATCH SET a.seen = true")
4364 .unwrap();
4365 assert_eq!(m.on_create.len(), 1);
4366 assert_eq!(m.on_match.len(), 1);
4367 }
4368
4369 #[test]
4370 fn merge_duplicate_on_create_errors() {
4371 assert!(parse_merge("MERGE (a) ON CREATE SET a.x = 1 ON CREATE SET a.y = 2").is_err());
4372 }
4373
4374 #[test]
4375 fn merge_duplicate_on_match_errors() {
4376 assert!(parse_merge("MERGE (a) ON MATCH SET a.x = 1 ON MATCH SET a.y = 2").is_err());
4377 }
4378
4379 #[test]
4380 fn statement_match_return() {
4381 let s = parse_statement("MATCH (a) RETURN a").unwrap();
4382 let Statement::Match {
4383 clauses,
4384 tail,
4385 order_by,
4386 skip,
4387 limit,
4388 } = s
4389 else {
4390 panic!("expected Statement::Match");
4391 };
4392 assert_eq!(clauses.len(), 1);
4393 assert!(matches!(clauses[0], QueryClause::Match(_)));
4394 assert!(matches!(tail, Some(Tail::Return(_, false))));
4395 assert!(order_by.is_none());
4396 assert!(skip.is_none());
4397 assert!(limit.is_none());
4398 }
4399
4400 #[test]
4401 fn statement_return_star() {
4402 let s = parse_statement("MATCH (a) RETURN *").unwrap();
4403 let Statement::Match { tail, .. } = s else {
4404 panic!("expected Statement::Match");
4405 };
4406 assert!(matches!(tail, Some(Tail::ReturnStar(false))));
4407 }
4408
4409 #[test]
4410 fn statement_order_by_skip_limit_on_bare_return() {
4411 let s = parse_statement("MATCH (a) RETURN a ORDER BY a SKIP 1 LIMIT 2").unwrap();
4412 let Statement::Match {
4413 order_by,
4414 skip,
4415 limit,
4416 ..
4417 } = s
4418 else {
4419 panic!("expected Statement::Match");
4420 };
4421 assert!(order_by.is_some());
4422 assert_eq!(skip, Some(Box::new(ReturnExpr::Lit(Literal::Int(1)))));
4423 assert_eq!(limit, Some(Box::new(ReturnExpr::Lit(Literal::Int(2)))));
4424 }
4425
4426 #[test]
4427 fn statement_multiple_reading_clauses() {
4428 let s = parse_statement("MATCH (a) UNWIND [1,2] AS x RETURN a, x").unwrap();
4429 let Statement::Match { clauses, .. } = s else {
4430 panic!("expected Statement::Match");
4431 };
4432 assert_eq!(clauses.len(), 2);
4433 assert!(matches!(clauses[0], QueryClause::Match(_)));
4434 assert!(matches!(clauses[1], QueryClause::Unwind(_)));
4435 }
4436
4437 #[test]
4438 fn statement_set_becomes_tail_with_return_tail() {
4439 let s = parse_statement("MATCH (n) SET n.x = 1 RETURN n").unwrap();
4440 let Statement::Match { clauses, tail, .. } = s else {
4441 panic!("expected Statement::Match");
4442 };
4443 assert_eq!(clauses.len(), 1);
4444 let Some(Tail::Set(items, Some(ret))) = tail else {
4445 panic!("expected Tail::Set with a ReturnTail");
4446 };
4447 assert_eq!(items.len(), 1);
4448 assert_eq!(ret.items.len(), 1);
4449 }
4450
4451 #[test]
4452 fn statement_set_without_trailing_return() {
4453 let s = parse_statement("MATCH (n) SET n.x = 1").unwrap();
4454 let Statement::Match { tail, .. } = s else {
4455 panic!("expected Statement::Match");
4456 };
4457 assert!(matches!(tail, Some(Tail::Set(_, None))));
4458 }
4459
4460 #[test]
4461 fn statement_detach_delete_tail() {
4462 let s = parse_statement("MATCH (n) DETACH DELETE n").unwrap();
4463 let Statement::Match { tail, .. } = s else {
4464 panic!("expected Statement::Match");
4465 };
4466 assert!(matches!(tail, Some(Tail::DetachDelete(_, None))));
4467 }
4468
4469 #[test]
4470 fn statement_two_updating_clauses_last_becomes_tail() {
4471 let s = parse_statement("MATCH (n) SET n.x = 1 DELETE n RETURN count(n)").unwrap();
4473 let Statement::Match { clauses, tail, .. } = s else {
4474 panic!("expected Statement::Match");
4475 };
4476 assert_eq!(clauses.len(), 2);
4477 assert!(matches!(clauses[1], QueryClause::Set(_)));
4478 assert!(matches!(tail, Some(Tail::Delete(_, Some(_)))));
4479 }
4480
4481 #[test]
4482 fn statement_bare_merge_no_tail() {
4483 let s = parse_statement("MERGE (a)").unwrap();
4486 let Statement::Match { clauses, tail, .. } = s else {
4487 panic!("expected Statement::Match");
4488 };
4489 assert!(matches!(clauses[0], QueryClause::Merge(_)));
4490 assert!(tail.is_none());
4491 }
4492
4493 #[test]
4494 fn statement_merge_with_trailing_return() {
4495 let s = parse_statement("MERGE (a) RETURN a ORDER BY a").unwrap();
4500 let Statement::Match {
4501 clauses,
4502 tail,
4503 order_by,
4504 ..
4505 } = s
4506 else {
4507 panic!("expected Statement::Match");
4508 };
4509 assert!(matches!(clauses[0], QueryClause::Merge(_)));
4510 assert!(matches!(tail, Some(Tail::Return(_, false))));
4511 assert!(order_by.is_some());
4512 }
4513
4514 #[test]
4515 fn statement_bare_match_without_tail_errors() {
4516 assert!(parse_statement("MATCH (n)").is_err());
4519 }
4520
4521 #[test]
4522 fn statement_mutating_tail_order_by_skip_limit_apply_at_statement_level() {
4523 let s =
4530 parse_statement("MATCH (n) SET n.x = 1 RETURN n ORDER BY n.x SKIP 1 LIMIT 2").unwrap();
4531 let Statement::Match {
4532 tail,
4533 order_by,
4534 skip,
4535 limit,
4536 ..
4537 } = s
4538 else {
4539 panic!("expected Statement::Match");
4540 };
4541 assert!(matches!(tail, Some(Tail::Set(_, Some(_)))));
4542 assert!(order_by.is_some());
4543 assert_eq!(skip, Some(Box::new(ReturnExpr::Lit(Literal::Int(1)))));
4544 assert_eq!(limit, Some(Box::new(ReturnExpr::Lit(Literal::Int(2)))));
4545 }
4546
4547 #[test]
4548 fn statement_mutating_tail_return_star_errors() {
4549 assert!(parse_statement("MATCH (n) SET n.x = 1 RETURN *").is_err());
4550 }
4551
4552 #[test]
4553 fn statement_create_tail() {
4554 let s = parse_statement("CREATE (a) RETURN a").unwrap();
4555 let Statement::Match { tail, .. } = s else {
4556 panic!("expected Statement::Match");
4557 };
4558 assert!(matches!(tail, Some(Tail::Create(_, Some(_)))));
4559 }
4560
4561 #[test]
4562 fn statement_bare_create_is_not_wrapped_in_match() {
4563 let s = parse_antlr("CREATE (a);").unwrap();
4569 assert!(matches!(s, Statement::Create(_)));
4570 }
4571
4572 #[test]
4573 fn statement_remove_tail() {
4574 let s = parse_statement("MATCH (n) REMOVE n.x").unwrap();
4575 let Statement::Match { tail, .. } = s else {
4576 panic!("expected Statement::Match");
4577 };
4578 assert!(matches!(tail, Some(Tail::Remove(_, None))));
4579 }
4580
4581 #[test]
4582 fn multi_part_with_attaches_to_preceding_match() {
4583 let s = parse_multi_part_statement("MATCH (a:A) WITH a MATCH (b:B) RETURN a, b").unwrap();
4584 let Statement::Match { clauses, tail, .. } = s else {
4585 panic!("expected Statement::Match");
4586 };
4587 assert_eq!(clauses.len(), 2);
4588 let QueryClause::Match(first) = &clauses[0] else {
4589 panic!("expected first clause to be Match");
4590 };
4591 assert!(first.with.is_some());
4592 assert!(matches!(clauses[1], QueryClause::Match(_)));
4593 assert!(matches!(tail, Some(Tail::Return(_, false))));
4594 }
4595
4596 #[test]
4597 fn multi_part_chained_with_second_one_standalone() {
4598 let s = parse_multi_part_statement("MATCH (a:A) WITH a.num AS x WITH x % 3 AS x RETURN x")
4603 .unwrap();
4604 let Statement::Match { clauses, .. } = s else {
4605 panic!("expected Statement::Match");
4606 };
4607 assert_eq!(clauses.len(), 2);
4608 let QueryClause::Match(first) = &clauses[0] else {
4609 panic!("expected first clause to be Match");
4610 };
4611 assert!(first.with.is_some());
4612 assert!(matches!(clauses[1], QueryClause::With(_)));
4613 }
4614
4615 #[test]
4616 fn multi_part_set_then_with_stays_separate_entries() {
4617 let s = parse_multi_part_statement(
4621 "MATCH (n:N) WITH n, n.num AS num DELETE n WITH num WHERE num % 2 = 0 RETURN num",
4622 )
4623 .unwrap();
4624 let Statement::Match { clauses, tail, .. } = s else {
4625 panic!("expected Statement::Match");
4626 };
4627 assert_eq!(clauses.len(), 3);
4628 assert!(matches!(clauses[0], QueryClause::Match(_)));
4629 assert!(matches!(clauses[1], QueryClause::Delete { .. }));
4630 assert!(matches!(clauses[2], QueryClause::With(_)));
4631 assert!(matches!(tail, Some(Tail::Return(_, false))));
4632 }
4633
4634 #[test]
4635 fn multi_part_create_with_star_create_create_tail() {
4636 let s =
4637 parse_multi_part_statement("CREATE (a) WITH a WITH * CREATE (b) CREATE (a)<-[:T]-(b)")
4638 .unwrap();
4639 let Statement::Match { clauses, tail, .. } = s else {
4640 panic!("expected Statement::Match");
4641 };
4642 assert_eq!(clauses.len(), 4);
4646 assert!(matches!(clauses[0], QueryClause::Create(_)));
4647 assert!(matches!(clauses[1], QueryClause::With(_)));
4648 assert!(matches!(clauses[2], QueryClause::With(_)));
4649 assert!(matches!(clauses[3], QueryClause::Create(_)));
4650 assert!(matches!(tail, Some(Tail::Create(_, None))));
4651 }
4652
4653 #[test]
4654 fn multi_part_merge_with_attaches() {
4655 let s = parse_multi_part_statement("MERGE (a:A) WITH a MATCH (b:B) RETURN a, b").unwrap();
4656 let Statement::Match { clauses, .. } = s else {
4657 panic!("expected Statement::Match");
4658 };
4659 assert_eq!(clauses.len(), 2);
4660 let QueryClause::Merge(m) = &clauses[0] else {
4661 panic!("expected first clause to be Merge");
4662 };
4663 assert!(m.with.is_some());
4664 }
4665
4666 #[test]
4667 fn multi_part_trailing_bare_create_becomes_tail_not_top_level_statement() {
4668 let s = parse_multi_part_statement("MATCH (a) WITH a CREATE (b)").unwrap();
4675 let Statement::Match { clauses, tail, .. } = s else {
4676 panic!("expected Statement::Match");
4677 };
4678 assert_eq!(clauses.len(), 1);
4679 assert!(matches!(clauses[0], QueryClause::Match(_)));
4680 assert!(matches!(tail, Some(Tail::Create(_, None))));
4681 }
4682
4683 #[test]
4684 fn parse_antlr_no_union_passes_through() {
4685 let s = parse_antlr("MATCH (a) RETURN a;").unwrap();
4686 assert!(matches!(s, Statement::Match { .. }));
4687 }
4688
4689 #[test]
4690 fn parse_antlr_union() {
4691 let s = parse_antlr("MATCH (a) RETURN a UNION MATCH (b) RETURN b;").unwrap();
4692 let Statement::Union { parts, all } = s else {
4693 panic!("expected Statement::Union");
4694 };
4695 assert_eq!(parts.len(), 2);
4696 assert!(!all);
4697 }
4698
4699 #[test]
4700 fn parse_antlr_union_all() {
4701 let s = parse_antlr("MATCH (a) RETURN a UNION ALL MATCH (b) RETURN b;").unwrap();
4702 let Statement::Union { parts, all } = s else {
4703 panic!("expected Statement::Union");
4704 };
4705 assert_eq!(parts.len(), 2);
4706 assert!(all);
4707 }
4708
4709 #[test]
4710 fn parse_antlr_union_three_parts() {
4711 let s =
4712 parse_antlr("MATCH (a) RETURN a UNION MATCH (b) RETURN b UNION MATCH (c) RETURN c;")
4713 .unwrap();
4714 let Statement::Union { parts, .. } = s else {
4715 panic!("expected Statement::Union");
4716 };
4717 assert_eq!(parts.len(), 3);
4718 }
4719
4720 #[test]
4721 fn parse_antlr_mixed_union_and_union_all_errors() {
4722 let err = parse_antlr(
4723 "MATCH (a) RETURN a UNION MATCH (b) RETURN b UNION ALL MATCH (c) RETURN c;",
4724 )
4725 .unwrap_err();
4726 assert!(matches!(err, QueryError::Syntax(_)));
4727 }
4728
4729 #[test]
4730 fn parse_antlr_standalone_call() {
4731 let stmt = parse_antlr("CALL db.labels() YIELD label").unwrap();
4732 let Statement::StandaloneCall(call) = stmt else {
4733 panic!("expected a Statement::StandaloneCall, got {stmt:?}");
4734 };
4735 assert_eq!(call.name, "db.labels");
4736 assert_eq!(call.args, Some(vec![]));
4737 assert!(matches!(
4738 call.yield_items,
4739 Some(CallYield::Items(items, None)) if items == vec![("label".to_string(), None)]
4740 ));
4741 }
4742
4743 #[test]
4744 fn parse_antlr_syntax_error() {
4745 assert!(parse_antlr("MATCH (a RETURN a;").is_err());
4746 }
4747
4748 #[test]
4749 fn parse_antlr_many_basic() {
4750 let stmts = parse_antlr_many("CREATE (a); CREATE (b); MATCH (n) RETURN n").unwrap();
4751 assert_eq!(stmts.len(), 3);
4752 assert!(matches!(stmts[0], Statement::Create(_)));
4756 assert!(matches!(stmts[2], Statement::Match { .. }));
4757 }
4758
4759 #[test]
4760 fn parse_antlr_many_single_statement() {
4761 let stmts = parse_antlr_many("RETURN 1").unwrap();
4762 assert_eq!(stmts.len(), 1);
4763 }
4764
4765 #[test]
4766 fn parse_antlr_many_strips_single_trailing_semicolon() {
4767 let stmts = parse_antlr_many("CREATE (a);").unwrap();
4768 assert_eq!(stmts.len(), 1);
4769 }
4770
4771 #[test]
4772 fn parse_antlr_many_semicolon_inside_string_literal_not_a_separator() {
4773 let stmts = parse_antlr_many("RETURN ';'").unwrap();
4774 assert_eq!(stmts.len(), 1);
4775 }
4776
4777 #[test]
4778 fn split_statements_respects_all_three_quote_forms() {
4779 assert_eq!(
4782 split_statements("RETURN ';'; RETURN 1"),
4783 vec!["RETURN ';'", " RETURN 1"]
4784 );
4785 assert_eq!(
4786 split_statements(r#"RETURN ";"; RETURN 1"#),
4787 vec![r#"RETURN ";""#, " RETURN 1"]
4788 );
4789 assert_eq!(
4790 split_statements("MATCH (`a;b`) RETURN 1; RETURN 2"),
4791 vec!["MATCH (`a;b`) RETURN 1", " RETURN 2"]
4792 );
4793 }
4794
4795 #[test]
4796 fn split_statements_handles_escaped_quotes_inside_a_literal() {
4797 assert_eq!(
4801 split_statements(r"RETURN 'it\'s; a test'; RETURN 1"),
4802 vec![r"RETURN 'it\'s; a test'", " RETURN 1"]
4803 );
4804 }
4805
4806 #[test]
4807 fn split_statements_backtick_literal_has_no_escapes() {
4808 assert_eq!(
4813 split_statements(r"MATCH (`a\`) RETURN 1; RETURN 2"),
4814 vec![r"MATCH (`a\`) RETURN 1", " RETURN 2"]
4815 );
4816 }
4817
4818 #[test]
4819 fn parse_antlr_create_index() {
4820 let s = parse_antlr("CREATE INDEX ON :Person(name);").unwrap();
4821 let Statement::CreateIndex {
4822 label,
4823 prop,
4824 unique,
4825 } = s
4826 else {
4827 panic!("expected Statement::CreateIndex");
4828 };
4829 assert_eq!(label, "Person");
4830 assert_eq!(prop, "name");
4831 assert!(!unique);
4832 }
4833
4834 #[test]
4835 fn parse_antlr_create_index_unique() {
4836 let s = parse_antlr("CREATE INDEX ON :Person(name) UNIQUE;").unwrap();
4837 let Statement::CreateIndex { unique, .. } = s else {
4838 panic!("expected Statement::CreateIndex");
4839 };
4840 assert!(unique);
4841 }
4842
4843 #[test]
4844 fn parse_antlr_explain_match() {
4845 let s = parse_antlr("EXPLAIN MATCH (a) RETURN a;").unwrap();
4846 let Statement::Explain(inner) = s else {
4847 panic!("expected Statement::Explain");
4848 };
4849 assert!(matches!(*inner, Statement::Match { .. }));
4850 }
4851
4852 #[test]
4853 fn parse_antlr_explain_create_index() {
4854 let s = parse_antlr("EXPLAIN CREATE INDEX ON :Person(name);").unwrap();
4855 let Statement::Explain(inner) = s else {
4856 panic!("expected Statement::Explain");
4857 };
4858 assert!(matches!(*inner, Statement::CreateIndex { .. }));
4859 }
4860
4861 #[test]
4862 fn parse_antlr_index_still_usable_as_property_name() {
4863 let s = parse_antlr("MATCH (a) RETURN a.index;").unwrap();
4868 assert!(matches!(s, Statement::Match { .. }));
4869 }
4870}