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 })
1066 .collect())
1067 }
1068
1069 fn build_comparison_expression(
1077 &mut self,
1078 ctx: &ComparisonExpressionContext,
1079 ) -> Result<ReturnExpr, QueryError> {
1080 let mut operands = Vec::new();
1081 for operand_ctx in ctx.stringListNullExpression_all() {
1082 operands.push(self.visit(&*operand_ctx).into_return_expr()?);
1083 }
1084 let mut ops = Vec::new();
1085 for sign_ctx in ctx.comparisonSigns_all() {
1086 ops.push(compare_sign(&sign_ctx));
1087 }
1088 if ops.is_empty() {
1089 return Ok(operands
1090 .into_iter()
1091 .next()
1092 .expect("comparisonExpression has at least one stringListNullExpression"));
1093 }
1094 let mut pairs = operands.windows(2).zip(&ops).map(|(pair, op)| {
1095 ReturnExpr::Compare(Box::new(pair[0].clone()), *op, Box::new(pair[1].clone()))
1096 });
1097 let mut acc = pairs
1098 .next()
1099 .expect("a comparison chain has at least one pair");
1100 for next in pairs {
1101 acc = ReturnExpr::And(Box::new(acc), Box::new(next));
1102 }
1103 Ok(acc)
1104 }
1105
1106 fn build_add_sub_expression(
1116 &mut self,
1117 ctx: &AddSubExpressionContext,
1118 ) -> Result<ReturnExpr, QueryError> {
1119 let mut children = ctx.get_children();
1120 let mut lhs = self
1121 .visit(
1122 &*children
1123 .next()
1124 .expect("addSubExpression has at least one multDivExpression"),
1125 )
1126 .into_return_expr()?;
1127 while let Some(op_node) = children.next() {
1128 let op = match op_node.get_text().as_str() {
1129 "+" => ArithOp::Add,
1130 "-" => ArithOp::Sub,
1131 other => unreachable!("unexpected addSubExpression operator {other:?}"),
1132 };
1133 let rhs_node = children
1134 .next()
1135 .expect("addSubExpression operator has a following multDivExpression");
1136 let rhs = self.visit(&*rhs_node).into_return_expr()?;
1137 lhs = ReturnExpr::Arith(Box::new(lhs), op, Box::new(rhs));
1138 }
1139 Ok(lhs)
1140 }
1141
1142 fn build_mult_div_expression(
1143 &mut self,
1144 ctx: &MultDivExpressionContext,
1145 ) -> Result<ReturnExpr, QueryError> {
1146 let mut children = ctx.get_children();
1147 let mut lhs = self
1148 .visit(
1149 &*children
1150 .next()
1151 .expect("multDivExpression has at least one powerExpression"),
1152 )
1153 .into_return_expr()?;
1154 while let Some(op_node) = children.next() {
1155 let op = match op_node.get_text().as_str() {
1156 "*" => ArithOp::Mul,
1157 "/" => ArithOp::Div,
1158 "%" => ArithOp::Mod,
1159 other => unreachable!("unexpected multDivExpression operator {other:?}"),
1160 };
1161 let rhs_node = children
1162 .next()
1163 .expect("multDivExpression operator has a following powerExpression");
1164 let rhs = self.visit(&*rhs_node).into_return_expr()?;
1165 lhs = ReturnExpr::Arith(Box::new(lhs), op, Box::new(rhs));
1166 }
1167 Ok(lhs)
1168 }
1169
1170 fn build_unary_add_sub_expression(
1184 &mut self,
1185 ctx: &UnaryAddSubExpressionContext,
1186 ) -> Result<ReturnExpr, QueryError> {
1187 let atomic_ctx = ctx
1188 .atomicExpression()
1189 .expect("unaryAddSubExpression always has an atomicExpression");
1190 if ctx.SUB().is_some() {
1191 if let Some(numlit_ctx) = bare_num_lit(&atomic_ctx) {
1192 let text = numlit_ctx
1193 .DIGIT()
1194 .expect("numLit context always has a DIGIT token")
1195 .get_text();
1196 return parse_num_lit_text(&format!("-{text}")).map(ReturnExpr::Lit);
1197 }
1198 let operand = self.visit(&*atomic_ctx).into_return_expr()?;
1199 return Ok(ReturnExpr::Neg(Box::new(operand)));
1200 }
1201 self.visit(&*atomic_ctx).into_return_expr()
1203 }
1204
1205 fn build_atomic_expression(
1214 &mut self,
1215 ctx: &AtomicExpressionContext,
1216 ) -> Result<ReturnExpr, QueryError> {
1217 let base_ctx = ctx
1218 .propertyOrLabelExpression()
1219 .expect("atomicExpression always has a propertyOrLabelExpression");
1220 let mut base = self.visit(&*base_ctx).into_return_expr()?;
1221 for l in ctx.listExpression_all() {
1222 base = self.build_list_expression(&l, base)?;
1223 }
1224 Ok(base)
1225 }
1226
1227 fn build_string_list_null_expression(
1239 &mut self,
1240 ctx: &StringListNullExpressionContext,
1241 ) -> Result<ReturnExpr, QueryError> {
1242 let base_ctx = ctx
1243 .addSubExpression()
1244 .expect("stringListNullExpression always has an addSubExpression");
1245 let base = self.visit(&*base_ctx).into_return_expr()?;
1246 if let Some(s) = ctx.stringExpression() {
1247 return self.build_string_expression(&s, base);
1248 }
1249 if let Some(i) = ctx.inExpression() {
1250 let rhs_ctx = i
1251 .addSubExpression()
1252 .expect("inExpression always has an addSubExpression");
1253 let rhs = self.visit(&*rhs_ctx).into_return_expr()?;
1254 return Ok(ReturnExpr::In(Box::new(base), Box::new(rhs)));
1255 }
1256 let Some(n) = ctx.nullExpression() else {
1257 return Ok(base);
1258 };
1259 Ok(if n.NOT().is_some() {
1260 ReturnExpr::Not(Box::new(ReturnExpr::IsNull(Box::new(base))))
1261 } else {
1262 ReturnExpr::IsNull(Box::new(base))
1263 })
1264 }
1265
1266 fn build_string_expression(
1272 &mut self,
1273 ctx: &StringExpressionContextAll,
1274 base: ReturnExpr,
1275 ) -> Result<ReturnExpr, QueryError> {
1276 let prefix_ctx = ctx
1277 .stringExpPrefix()
1278 .expect("stringExpression always has a stringExpPrefix");
1279 let op = string_exp_op(&prefix_ctx);
1280 let rhs_ctx = ctx
1281 .addSubExpression()
1282 .expect("stringExpression always has an addSubExpression");
1283 let rhs = self.visit(&*rhs_ctx).into_return_expr()?;
1284 Ok(ReturnExpr::Compare(Box::new(base), op, Box::new(rhs)))
1285 }
1286
1287 fn build_list_expression(
1292 &mut self,
1293 ctx: &ListExpressionContextAll,
1294 base: ReturnExpr,
1295 ) -> Result<ReturnExpr, QueryError> {
1296 let exprs = ctx.expression_all();
1297 if ctx.RANGE().is_some() {
1298 let (start, end) = match exprs.len() {
1301 0 => (None, None),
1302 1 => {
1303 let before_range = list_expr_bound_is_before_range(ctx);
1309 let e = self.visit(&*exprs[0].clone()).into_return_expr()?;
1310 if before_range {
1311 (Some(Box::new(e)), None)
1312 } else {
1313 (None, Some(Box::new(e)))
1314 }
1315 }
1316 2 => {
1317 let start = self.visit(&*exprs[0].clone()).into_return_expr()?;
1318 let end = self.visit(&*exprs[1].clone()).into_return_expr()?;
1319 (Some(Box::new(start)), Some(Box::new(end)))
1320 }
1321 n => unreachable!("listExpression slice form has {n} expressions, expected 0-2"),
1322 };
1323 return Ok(ReturnExpr::Slice(Box::new(base), start, end));
1324 }
1325 let index_ctx = exprs
1326 .into_iter()
1327 .next()
1328 .expect("non-slice listExpression always has exactly one expression");
1329 let index = self.visit(&*index_ctx).into_return_expr()?;
1330 Ok(ReturnExpr::Index(Box::new(base), Box::new(index)))
1331 }
1332
1333 fn build_property_or_label_expression(
1334 &mut self,
1335 ctx: &PropertyOrLabelExpressionContext,
1336 ) -> Result<ReturnExpr, QueryError> {
1337 let prop_ctx = ctx
1338 .propertyExpression()
1339 .expect("propertyOrLabelExpression always has a propertyExpression");
1340 let base = self.visit(&*prop_ctx).into_return_expr()?;
1341 let Some(labels_ctx) = ctx.nodeLabels() else {
1342 return Ok(base);
1343 };
1344 let ReturnExpr::Var(var) = base else {
1345 return Err(QueryError::Syntax(
1346 "a label check (`x:Label`) only applies to a bare variable".into(),
1347 ));
1348 };
1349 let labels = labels_ctx.name_all().iter().map(|n| name_text(n)).collect();
1350 Ok(ReturnExpr::HasLabel(var, labels))
1351 }
1352
1353 fn build_property_expression(
1363 &mut self,
1364 ctx: &PropertyExpressionContext,
1365 ) -> Result<ReturnExpr, QueryError> {
1366 let atom_ctx = ctx.atom().expect("propertyExpression always has an atom");
1367 let base = self.visit(&*atom_ctx).into_return_expr()?;
1368 let mut names = ctx.name_all().into_iter();
1369 let Some(first) = names.next() else {
1370 return Ok(base);
1371 };
1372 let mut expr = match base {
1378 ReturnExpr::Var(var) => ReturnExpr::Prop(PropAccess {
1379 var,
1380 prop: name_text(&first),
1381 }),
1382 other => ReturnExpr::PropOf(Box::new(other), name_text(&first)),
1383 };
1384 for name in names {
1385 expr = ReturnExpr::PropOf(Box::new(expr), name_text(&name));
1386 }
1387 Ok(expr)
1388 }
1389
1390 fn build_atom(&mut self, ctx: &AtomContext) -> Result<ReturnExpr, QueryError> {
1391 if let Some(lit_ctx) = ctx.literal() {
1392 return self.visit(&*lit_ctx).into_return_expr_lenient();
1393 }
1394 if let Some(param_ctx) = ctx.parameter() {
1395 return self.build_parameter(¶m_ctx);
1396 }
1397 if let Some(paren_ctx) = ctx.parenthesizedExpression() {
1398 return self.visit(&*paren_ctx).into_return_expr();
1399 }
1400 if let Some(func_ctx) = ctx.functionInvocation() {
1401 return self.build_function_invocation(&func_ctx);
1402 }
1403 if let Some(count_ctx) = ctx.countAll() {
1404 let _ = self.visit(&*count_ctx);
1405 return Ok(ReturnExpr::CountStar);
1406 }
1407 if let Some(sym_ctx) = ctx.symbol() {
1408 return Ok(ReturnExpr::Var(symbol_text(&sym_ctx)));
1409 }
1410 if let Some(filter_ctx) = ctx.filterWith() {
1411 return self.build_filter_with(&filter_ctx);
1412 }
1413 if let Some(lc_ctx) = ctx.listComprehension() {
1414 return self.build_list_comprehension(&lc_ctx);
1415 }
1416 if let Some(case_ctx) = ctx.caseExpression() {
1417 return self.build_case_expression(&case_ctx);
1418 }
1419 if let Some(pc_ctx) = ctx.patternComprehension() {
1420 return self.build_pattern_comprehension(&pc_ctx);
1421 }
1422 if let Some(rcp_ctx) = ctx.relationshipsChainPattern() {
1423 return Ok(ReturnExpr::PatternPredicate(
1424 self.build_relationships_chain_pattern(&rcp_ctx)?,
1425 ));
1426 }
1427 if let Some(se_ctx) = ctx.subqueryExist() {
1428 return self.build_subquery_exist(&se_ctx);
1429 }
1430 Err(QueryError::Syntax(
1431 "this expression form (path-as-expression) isn't supported by the ANTLR parser yet"
1432 .into(),
1433 ))
1434 }
1435
1436 fn build_pattern_comprehension(
1447 &mut self,
1448 ctx: &PatternComprehensionContext,
1449 ) -> Result<ReturnExpr, QueryError> {
1450 let path_var = ctx
1451 .lhs()
1452 .and_then(|lhs| lhs.symbol())
1453 .map(|s| symbol_text(&s));
1454 let rcp_ctx = ctx
1455 .relationshipsChainPattern()
1456 .expect("patternComprehension always has a relationshipsChainPattern");
1457 let pattern = self.build_relationships_chain_pattern(&rcp_ctx)?;
1458 let where_clause = match ctx.where_() {
1459 Some(where_ctx) => {
1460 let expr_ctx = where_ctx
1461 .expression()
1462 .expect("where always has an expression");
1463 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1464 Some(Box::new(return_expr_to_expr(expr)?))
1465 }
1466 None => None,
1467 };
1468 let proj_ctx = ctx
1469 .expression()
1470 .expect("patternComprehension always has a projection expression");
1471 let projection = self.visit(&*proj_ctx).into_return_expr()?;
1472 Ok(ReturnExpr::PatternComprehension {
1473 path_var,
1474 pattern: Box::new(pattern),
1475 where_clause,
1476 projection: Box::new(projection),
1477 })
1478 }
1479
1480 fn build_subquery_exist(
1496 &mut self,
1497 ctx: &SubqueryExistContext,
1498 ) -> Result<ReturnExpr, QueryError> {
1499 if let Some(rq_ctx) = ctx.regularQuery() {
1500 let stmt = self.build_regular_query(&rq_ctx)?;
1501 return Ok(ReturnExpr::ExistsSubquery(Box::new(stmt)));
1502 }
1503 let pw_ctx = ctx
1504 .patternWhere()
1505 .expect("subqueryExist always has a regularQuery or patternWhere");
1506 let pattern_ctx = pw_ctx.pattern().expect("patternWhere always has a pattern");
1507 let mut parts = pattern_ctx.patternPart_all().into_iter();
1508 let part = parts
1509 .next()
1510 .expect("pattern always has at least one patternPart");
1511 if parts.next().is_some() {
1512 return Err(QueryError::Syntax(
1513 "exists {} with more than one comma-separated pattern isn't supported yet".into(),
1514 ));
1515 }
1516 if part.ASSIGN().is_some() || part.shortestPathWrapper().is_some() {
1517 return Err(QueryError::Syntax(
1518 "exists {} doesn't support a named path or shortestPath()".into(),
1519 ));
1520 }
1521 let elem_ctx = part
1522 .patternElem()
1523 .expect("a patternPart without ASSIGN/shortestPathWrapper always has a patternElem");
1524 let pattern = self.visit(&*elem_ctx).into_pattern()?;
1525 let where_clause = match pw_ctx.where_() {
1526 Some(where_ctx) => {
1527 let expr_ctx = where_ctx
1528 .expression()
1529 .expect("where always has an expression");
1530 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1531 Some(Box::new(return_expr_to_expr(expr)?))
1532 }
1533 None => None,
1534 };
1535 Ok(ReturnExpr::ExistsPattern {
1536 pattern: Box::new(pattern),
1537 where_clause,
1538 })
1539 }
1540
1541 fn build_case_expression(
1554 &mut self,
1555 ctx: &CaseExpressionContext,
1556 ) -> Result<ReturnExpr, QueryError> {
1557 #[derive(PartialEq)]
1558 enum Pos {
1559 BeforeFirstWhen,
1560 AfterWhen,
1561 AfterThen,
1562 AfterElse,
1563 }
1564 let mut pos = Pos::BeforeFirstWhen;
1565 let mut test = None;
1566 let mut whens: Vec<(ReturnExpr, ReturnExpr)> = Vec::new();
1567 let mut pending_when: Option<ReturnExpr> = None;
1568 let mut else_ = None;
1569 for child in ctx.get_children() {
1570 match child.get_text().to_ascii_uppercase().as_str() {
1571 "CASE" | "END" => continue,
1572 "WHEN" => pos = Pos::AfterWhen,
1573 "THEN" => pos = Pos::AfterThen,
1574 "ELSE" => pos = Pos::AfterElse,
1575 _ => {
1576 let expr = self.visit(&*child).into_return_expr()?;
1577 match pos {
1578 Pos::BeforeFirstWhen => test = Some(Box::new(expr)),
1579 Pos::AfterWhen => pending_when = Some(expr),
1580 Pos::AfterThen => {
1581 let w = pending_when
1582 .take()
1583 .expect("a THEN expression always follows a WHEN expression");
1584 whens.push((w, expr));
1585 }
1586 Pos::AfterElse => else_ = Some(Box::new(expr)),
1587 }
1588 }
1589 }
1590 }
1591 Ok(ReturnExpr::Case { test, whens, else_ })
1592 }
1593
1594 fn build_filter_expression(
1599 &mut self,
1600 ctx: &FilterExpressionContext,
1601 ) -> Result<(String, ReturnExpr, Option<Box<ReturnExpr>>), QueryError> {
1602 let var_ctx = ctx.symbol().expect("filterExpression always has a symbol");
1603 let var = symbol_text(&var_ctx);
1604 let source_ctx = ctx
1605 .expression()
1606 .expect("filterExpression always has an expression");
1607 let source = self.visit(&*source_ctx).into_return_expr()?;
1608 let where_clause = match ctx.where_() {
1609 Some(where_ctx) => {
1610 let expr_ctx = where_ctx
1611 .expression()
1612 .expect("where always has an expression");
1613 Some(Box::new(self.visit(&*expr_ctx).into_return_expr()?))
1614 }
1615 None => None,
1616 };
1617 Ok((var, source, where_clause))
1618 }
1619
1620 fn build_filter_with(&mut self, ctx: &FilterWithContext) -> Result<ReturnExpr, QueryError> {
1625 let kind = if ctx.ALL().is_some() {
1626 QuantifierKind::All
1627 } else if ctx.ANY().is_some() {
1628 QuantifierKind::Any
1629 } else if ctx.NONE().is_some() {
1630 QuantifierKind::None
1631 } else {
1632 ctx.SINGLE()
1633 .expect("filterWith always has one of ALL/ANY/NONE/SINGLE");
1634 QuantifierKind::Single
1635 };
1636 let fe_ctx = ctx
1637 .filterExpression()
1638 .expect("filterWith always has a filterExpression");
1639 let (var, source, where_clause) = self.build_filter_expression(&fe_ctx)?;
1640 Ok(ReturnExpr::Quantifier {
1641 kind,
1642 var,
1643 source: Box::new(source),
1644 where_clause,
1645 })
1646 }
1647
1648 fn build_list_comprehension(
1654 &mut self,
1655 ctx: &ListComprehensionContext,
1656 ) -> Result<ReturnExpr, QueryError> {
1657 let fe_ctx = ctx
1658 .filterExpression()
1659 .expect("listComprehension always has a filterExpression");
1660 let (var, source, where_clause) = self.build_filter_expression(&fe_ctx)?;
1661 let project = match ctx.expression() {
1662 Some(expr_ctx) => Some(Box::new(self.visit(&*expr_ctx).into_return_expr()?)),
1663 None => None,
1664 };
1665 Ok(ReturnExpr::ListComp {
1666 var,
1667 source: Box::new(source),
1668 where_clause,
1669 project,
1670 })
1671 }
1672
1673 fn build_function_invocation(
1674 &mut self,
1675 ctx: &FunctionInvocationContext,
1676 ) -> Result<ReturnExpr, QueryError> {
1677 let name_ctx = ctx
1678 .invocationName()
1679 .expect("functionInvocation always has an invocationName");
1680 let name = invocation_name_text(&name_ctx);
1681 let distinct = ctx.DISTINCT().is_some();
1682 let mut args = Vec::new();
1683 if let Some(chain_ctx) = ctx.expressionChain() {
1684 for arg_ctx in chain_ctx.expression_all() {
1685 args.push(self.visit(&*arg_ctx).into_return_expr()?);
1686 }
1687 }
1688 if distinct && !is_aggregate_name(&name) {
1689 return Err(QueryError::Syntax(format!(
1690 "'{name}(DISTINCT ...)' isn't valid — DISTINCT is only meaningful inside an aggregate function"
1691 )));
1692 }
1693 Ok(ReturnExpr::Call {
1694 name,
1695 args,
1696 distinct,
1697 })
1698 }
1699
1700 fn build_standalone_call(
1707 &mut self,
1708 ctx: &StandaloneCallContext,
1709 ) -> Result<Statement, QueryError> {
1710 let name_ctx = ctx
1711 .invocationName()
1712 .expect("standaloneCall always has an invocationName");
1713 let name = invocation_name_text(&name_ctx);
1714 let args = match ctx.parenExpressionChain() {
1715 Some(paren_ctx) => Some(self.build_call_args(&paren_ctx)?),
1716 None => None,
1717 };
1718 let yield_items = if ctx.MULT().is_some() {
1719 Some(CallYield::Star)
1720 } else if let Some(yi_ctx) = ctx.yieldItems() {
1721 Some(self.build_yield_items(&yi_ctx)?)
1722 } else {
1723 None
1724 };
1725 Ok(Statement::StandaloneCall(Box::new(CallClause {
1726 name,
1727 args,
1728 with: None,
1729 yield_items,
1730 })))
1731 }
1732
1733 fn build_query_call_st(
1741 &mut self,
1742 ctx: &QueryCallStContextAll,
1743 ) -> Result<CallClause, QueryError> {
1744 let name_ctx = ctx
1745 .invocationName()
1746 .expect("queryCallSt always has an invocationName");
1747 let name = invocation_name_text(&name_ctx);
1748 let paren_ctx = ctx
1749 .parenExpressionChain()
1750 .expect("queryCallSt always has a parenExpressionChain");
1751 let args = Some(self.build_call_args(&paren_ctx)?);
1752 let yield_items = match ctx.yieldItems() {
1753 Some(yi_ctx) => Some(self.build_yield_items(&yi_ctx)?),
1754 None => None,
1755 };
1756 Ok(CallClause {
1757 name,
1758 args,
1759 with: None,
1760 yield_items,
1761 })
1762 }
1763
1764 fn build_call_args(
1765 &mut self,
1766 ctx: &ParenExpressionChainContextAll,
1767 ) -> Result<Vec<ReturnExpr>, QueryError> {
1768 let mut args = Vec::new();
1769 if let Some(chain_ctx) = ctx.expressionChain() {
1770 for arg_ctx in chain_ctx.expression_all() {
1771 args.push(self.visit(&*arg_ctx).into_return_expr()?);
1772 }
1773 }
1774 Ok(args)
1775 }
1776
1777 fn build_yield_items(&mut self, ctx: &YieldItemsContextAll) -> Result<CallYield, QueryError> {
1783 let mut items = Vec::new();
1784 for item_ctx in ctx.yieldItem_all() {
1785 let symbols = item_ctx.symbol_all();
1786 let (name, alias) = match symbols.len() {
1787 1 => (symbol_text(&symbols[0]), None),
1788 2 => (symbol_text(&symbols[0]), Some(symbol_text(&symbols[1]))),
1789 other => unreachable!("yieldItem always has 1 or 2 symbols, got {other}"),
1790 };
1791 items.push((name, alias));
1792 }
1793 let where_clause = match ctx.where_() {
1794 Some(where_ctx) => {
1795 let expr_ctx = where_ctx
1796 .expression()
1797 .expect("where always has an expression");
1798 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1799 Some(Box::new(return_expr_to_expr(expr)?))
1800 }
1801 None => None,
1802 };
1803 Ok(CallYield::Items(items, where_clause))
1804 }
1805
1806 fn build_parameter(&mut self, ctx: &ParameterContext) -> Result<ReturnExpr, QueryError> {
1807 let name = if let Some(sym_ctx) = ctx.symbol() {
1808 symbol_text(&sym_ctx)
1809 } else if let Some(num_ctx) = ctx.numLit() {
1810 num_ctx
1811 .DIGIT()
1812 .expect("numLit context always has a DIGIT token")
1813 .get_text()
1814 } else {
1815 unreachable!("parameter always has a symbol or numLit")
1816 };
1817 Ok(ReturnExpr::Lit(Literal::Param(name)))
1818 }
1819
1820 fn build_projection_body(
1821 &mut self,
1822 ctx: &ProjectionBodyContext,
1823 ) -> Result<ParsedReturnClause, QueryError> {
1824 let distinct = ctx.DISTINCT().is_some();
1825 let items_ctx = ctx
1826 .projectionItems()
1827 .expect("projectionBody always has projectionItems");
1828 let tail = if items_ctx.MULT().is_some() {
1829 if !items_ctx.projectionItem_all().is_empty() {
1837 return Err(QueryError::Syntax(
1838 "RETURN * can't be combined with additional items".into(),
1839 ));
1840 }
1841 Tail::ReturnStar(distinct)
1842 } else {
1843 let mut items = Vec::new();
1844 for item_ctx in items_ctx.projectionItem_all() {
1845 let expr_ctx = item_ctx
1846 .expression()
1847 .expect("projectionItem always has an expression");
1848 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1849 let alias = item_ctx.symbol().map(|s| symbol_text(&s));
1850 items.push(ReturnItem { expr, alias });
1851 }
1852 Tail::Return(items, distinct)
1853 };
1854
1855 let (order_by, skip, limit) = self.build_order_skip_limit(ctx)?;
1856
1857 Ok(ParsedReturnClause {
1858 tail,
1859 order_by,
1860 skip,
1861 limit,
1862 })
1863 }
1864
1865 #[allow(clippy::type_complexity)]
1869 fn build_order_skip_limit(
1870 &mut self,
1871 ctx: &ProjectionBodyContext,
1872 ) -> Result<
1873 (
1874 Option<Vec<(ReturnExpr, SortDir)>>,
1875 Option<ReturnExpr>,
1876 Option<ReturnExpr>,
1877 ),
1878 QueryError,
1879 > {
1880 let order_by = match ctx.orderSt() {
1881 Some(order_ctx) => Some(self.build_order_by(&order_ctx)?),
1882 None => None,
1883 };
1884 let skip = match ctx.skipSt() {
1885 Some(skip_ctx) => {
1886 let expr_ctx = skip_ctx
1887 .expression()
1888 .expect("skipSt always has an expression");
1889 Some(self.visit(&*expr_ctx).into_return_expr()?)
1890 }
1891 None => None,
1892 };
1893 let limit = match ctx.limitSt() {
1894 Some(limit_ctx) => {
1895 let expr_ctx = limit_ctx
1896 .expression()
1897 .expect("limitSt always has an expression");
1898 Some(self.visit(&*expr_ctx).into_return_expr()?)
1899 }
1900 None => None,
1901 };
1902 Ok((order_by, skip, limit))
1903 }
1904
1905 fn build_order_by(
1906 &mut self,
1907 ctx: &OrderStContext,
1908 ) -> Result<Vec<(ReturnExpr, SortDir)>, QueryError> {
1909 let mut items = Vec::new();
1910 for item_ctx in ctx.orderItem_all() {
1911 let expr_ctx = item_ctx
1912 .expression()
1913 .expect("orderItem always has an expression");
1914 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1915 let dir = if item_ctx.DESC().is_some() || item_ctx.DESCENDING().is_some() {
1916 SortDir::Desc
1917 } else {
1918 SortDir::Asc
1919 };
1920 items.push((expr, dir));
1921 }
1922 Ok(items)
1923 }
1924
1925 fn build_with_clause(&mut self, ctx: &WithStContext) -> Result<WithClause, QueryError> {
1926 let body_ctx = ctx
1927 .projectionBody()
1928 .expect("withSt always has a projectionBody");
1929 let distinct = body_ctx.DISTINCT().is_some();
1930 let items_ctx = body_ctx
1931 .projectionItems()
1932 .expect("projectionBody always has projectionItems");
1933 let star = items_ctx.MULT().is_some();
1934 let mut items = Vec::new();
1935 for item_ctx in items_ctx.projectionItem_all() {
1936 let expr_ctx = item_ctx
1937 .expression()
1938 .expect("projectionItem always has an expression");
1939 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1940 let alias = item_ctx.symbol().map(|s| symbol_text(&s));
1941 items.push(ReturnItem { expr, alias });
1942 }
1943 let (order_by, skip, limit) = self.build_order_skip_limit(&body_ctx)?;
1944 let where_clause = match ctx.where_() {
1945 Some(where_ctx) => {
1946 let expr_ctx = where_ctx
1947 .expression()
1948 .expect("where always has an expression");
1949 let expr = self.visit(&*expr_ctx).into_return_expr()?;
1950 Some(return_expr_to_with_expr(expr))
1951 }
1952 None => None,
1953 };
1954 Ok(WithClause {
1955 items,
1956 star,
1957 distinct,
1958 where_clause,
1959 order_by,
1960 skip,
1961 limit,
1962 })
1963 }
1964
1965 fn build_unwind_st(&mut self, ctx: &UnwindStContext) -> Result<UnwindClause, QueryError> {
1974 let expr_ctx = ctx.expression().expect("unwindSt always has an expression");
1975 let source = UnwindSource(self.visit(&*expr_ctx).into_return_expr()?);
1976 let var_ctx = ctx.symbol().expect("unwindSt always has a symbol");
1977 Ok(UnwindClause {
1978 source,
1979 var: symbol_text(&var_ctx),
1980 where_clause: None,
1981 with: None,
1982 })
1983 }
1984
1985 fn build_set_st(&mut self, ctx: &SetStContext) -> Result<Vec<SetItem>, QueryError> {
1986 ctx.setItem_all()
1987 .into_iter()
1988 .map(|item_ctx| self.build_set_item(&item_ctx))
1989 .collect()
1990 }
1991
1992 fn build_set_item(&mut self, ctx: &SetItemContextAll) -> Result<SetItem, QueryError> {
1993 if let Some(prop_ctx) = ctx.propertyExpression() {
2004 let expr_ctx = ctx
2005 .expression()
2006 .expect("setItem's propertyExpression form always has an expression");
2007 return match self.build_property_expression(&prop_ctx)? {
2008 ReturnExpr::Prop(prop) => {
2009 let value = self.visit(&*expr_ctx).into_return_expr()?;
2010 Ok(SetItem::Prop(prop, value))
2011 }
2012 ReturnExpr::Var(var) => {
2013 let value = self.visit(&*expr_ctx).into_return_expr()?;
2014 Ok(SetItem::MapAssign {
2015 var,
2016 value,
2017 merge: false,
2018 })
2019 }
2020 _ => Err(QueryError::Syntax(
2021 "expected a property access (x.prop) or variable on the left of SET's `=`"
2022 .into(),
2023 )),
2024 };
2025 }
2026 let sym_ctx = ctx
2027 .symbol()
2028 .expect("setItem always has a propertyExpression or symbol");
2029 let var = symbol_text(&sym_ctx);
2030 if let Some(labels_ctx) = ctx.nodeLabels() {
2031 let labels = labels_ctx.name_all().iter().map(|n| name_text(n)).collect();
2032 return Ok(SetItem::Labels(var, labels));
2033 }
2034 let expr_ctx = ctx
2035 .expression()
2036 .expect("setItem's symbol-assign form always has an expression");
2037 let value = self.visit(&*expr_ctx).into_return_expr()?;
2038 Ok(SetItem::MapAssign {
2039 var,
2040 value,
2041 merge: ctx.ADD_ASSIGN().is_some(),
2042 })
2043 }
2044
2045 fn build_delete_st(&mut self, ctx: &DeleteStContext) -> Result<ParsedDelete, QueryError> {
2046 let chain_ctx = ctx
2047 .expressionChain()
2048 .expect("deleteSt always has an expressionChain");
2049 let mut items = Vec::new();
2050 for expr_ctx in chain_ctx.expression_all() {
2051 items.push(self.visit(&*expr_ctx).into_return_expr()?);
2052 }
2053 Ok(ParsedDelete {
2054 items,
2055 detach: ctx.DETACH().is_some(),
2056 })
2057 }
2058
2059 fn build_remove_st(&mut self, ctx: &RemoveStContext) -> Result<Vec<RemoveItem>, QueryError> {
2060 ctx.removeItem_all()
2061 .into_iter()
2062 .map(|item_ctx| self.build_remove_item(&item_ctx))
2063 .collect()
2064 }
2065
2066 fn build_remove_item(&mut self, ctx: &RemoveItemContextAll) -> Result<RemoveItem, QueryError> {
2067 if let Some(prop_ctx) = ctx.propertyExpression() {
2068 return Ok(RemoveItem::Prop(self.build_prop_access(&prop_ctx)?));
2069 }
2070 let sym_ctx = ctx
2071 .symbol()
2072 .expect("removeItem always has a symbol+nodeLabels or a propertyExpression");
2073 let labels_ctx = ctx
2074 .nodeLabels()
2075 .expect("removeItem's symbol form always has nodeLabels");
2076 let labels = labels_ctx.name_all().iter().map(|n| name_text(n)).collect();
2077 Ok(RemoveItem::Labels(symbol_text(&sym_ctx), labels))
2078 }
2079
2080 fn build_prop_access(
2089 &mut self,
2090 ctx: &PropertyExpressionContext,
2091 ) -> Result<PropAccess, QueryError> {
2092 match self.build_property_expression(ctx)? {
2093 ReturnExpr::Prop(p) => Ok(p),
2094 _ => Err(QueryError::Syntax(
2095 "expected a property access (x.prop)".into(),
2096 )),
2097 }
2098 }
2099
2100 fn build_create_st(&mut self, ctx: &CreateStContext) -> Result<Vec<Pattern>, QueryError> {
2107 let pattern_ctx = ctx.pattern().expect("createSt always has a pattern");
2108 pattern_ctx
2109 .patternPart_all()
2110 .into_iter()
2111 .map(|part_ctx| {
2112 if part_ctx.ASSIGN().is_some() {
2113 return Err(QueryError::Syntax(
2114 "named-path capture (`p = ...`) isn't supported on CREATE".into(),
2115 ));
2116 }
2117 if part_ctx.shortestPathWrapper().is_some() {
2118 return Err(QueryError::Syntax(
2119 "shortestPath() isn't valid in CREATE".into(),
2120 ));
2121 }
2122 let elem_ctx = part_ctx.patternElem().expect(
2123 "patternPart always has a patternElem when shortestPathWrapper is absent",
2124 );
2125 self.visit(&*elem_ctx).into_pattern()
2126 })
2127 .collect()
2128 }
2129
2130 fn build_merge_st(&mut self, ctx: &MergeStContext) -> Result<MergeClause, QueryError> {
2144 let part_ctx = ctx.patternPart().expect("mergeSt always has a patternPart");
2145 let path_var = if part_ctx.ASSIGN().is_some() {
2146 let symbol_ctx = part_ctx
2147 .symbol()
2148 .expect("patternPart with ASSIGN always has a symbol");
2149 Some(symbol_text(&symbol_ctx))
2150 } else {
2151 None
2152 };
2153 if part_ctx.shortestPathWrapper().is_some() {
2154 return Err(QueryError::Syntax(
2155 "shortestPath() isn't valid in MERGE".into(),
2156 ));
2157 }
2158 let elem_ctx = part_ctx
2159 .patternElem()
2160 .expect("patternPart always has a patternElem when shortestPathWrapper is absent");
2161 let pattern = self.visit(&*elem_ctx).into_pattern()?;
2162 if pattern.hops.len() > 1 {
2163 return Err(QueryError::Syntax(
2164 "MERGE with more than one relationship hop isn't supported yet — split it into a MATCH \
2165 for the already-known part and a MERGE for one new hop"
2166 .into(),
2167 ));
2168 }
2169
2170 let mut on_create = Vec::new();
2171 let mut on_match = Vec::new();
2172 for action_ctx in ctx.mergeAction_all() {
2173 let set_items = self.build_merge_action(&action_ctx)?;
2174 if action_ctx.MATCH().is_some() {
2175 if !on_match.is_empty() {
2176 return Err(QueryError::Syntax(
2177 "MERGE can have at most one ON MATCH SET clause".into(),
2178 ));
2179 }
2180 on_match = set_items;
2181 } else {
2182 if !on_create.is_empty() {
2183 return Err(QueryError::Syntax(
2184 "MERGE can have at most one ON CREATE SET clause".into(),
2185 ));
2186 }
2187 on_create = set_items;
2188 }
2189 }
2190
2191 Ok(MergeClause {
2192 pattern,
2193 path_var,
2194 on_create,
2195 on_match,
2196 with: None,
2197 })
2198 }
2199
2200 fn build_merge_action(
2201 &mut self,
2202 ctx: &MergeActionContextAll,
2203 ) -> Result<Vec<SetItem>, QueryError> {
2204 let set_ctx = ctx.setSt().expect("mergeAction always has a setSt");
2205 self.build_set_st(&set_ctx)
2206 }
2207
2208 fn append_reading_statement(
2219 &mut self,
2220 ctx: &ReadingStatementContextAll,
2221 clauses: &mut Vec<QueryClause>,
2222 ) -> Result<(), QueryError> {
2223 if let Some(match_ctx) = ctx.matchSt() {
2224 let parts = self.visit(&*match_ctx).into_query_parts()?;
2225 clauses.extend(parts.into_iter().map(QueryClause::Match));
2226 return Ok(());
2227 }
2228 if let Some(unwind_ctx) = ctx.unwindSt() {
2229 let clause = self.visit(&*unwind_ctx).into_unwind_clause()?;
2230 clauses.push(QueryClause::Unwind(clause));
2231 return Ok(());
2232 }
2233 let call_ctx = ctx
2234 .queryCallSt()
2235 .expect("readingStatement is matchSt | unwindSt | queryCallSt");
2236 let call = self.build_query_call_st(&call_ctx)?;
2237 clauses.push(QueryClause::Call(call));
2238 Ok(())
2239 }
2240
2241 fn build_updating_statement_as_clause(
2246 &mut self,
2247 ctx: &UpdatingStatementContextAll,
2248 ) -> Result<QueryClause, QueryError> {
2249 if let Some(create_ctx) = ctx.createSt() {
2250 return Ok(QueryClause::Create(
2251 self.visit(&*create_ctx).into_create_patterns()?,
2252 ));
2253 }
2254 if let Some(merge_ctx) = ctx.mergeSt() {
2255 return Ok(QueryClause::Merge(
2256 self.visit(&*merge_ctx).into_merge_clause()?,
2257 ));
2258 }
2259 if let Some(delete_ctx) = ctx.deleteSt() {
2260 let d = self.visit(&*delete_ctx).into_delete_items()?;
2261 return Ok(QueryClause::Delete {
2262 items: d.items,
2263 detach: d.detach,
2264 });
2265 }
2266 if let Some(set_ctx) = ctx.setSt() {
2267 return Ok(QueryClause::Set(self.visit(&*set_ctx).into_set_items()?));
2268 }
2269 let remove_ctx = ctx
2270 .removeSt()
2271 .expect("updatingStatement always has one of its 5 alternatives");
2272 Ok(QueryClause::Remove(
2273 self.visit(&*remove_ctx).into_remove_items()?,
2274 ))
2275 }
2276
2277 #[allow(clippy::type_complexity)]
2301 fn build_mutating_tail(
2302 &mut self,
2303 ctx: &UpdatingStatementContextAll,
2304 return_ctx: Option<&ReturnStContext>,
2305 ) -> Result<
2306 (
2307 Tail,
2308 Option<Vec<(ReturnExpr, SortDir)>>,
2309 Option<ReturnExpr>,
2310 Option<ReturnExpr>,
2311 ),
2312 QueryError,
2313 > {
2314 let mut order_by = None;
2315 let mut skip = None;
2316 let mut limit = None;
2317 let ret = match return_ctx {
2318 Some(return_ctx) => {
2319 let c = self.visit(return_ctx).into_return_clause()?;
2320 order_by = c.order_by;
2321 skip = c.skip;
2322 limit = c.limit;
2323 let Tail::Return(items, distinct) = c.tail else {
2324 return Err(QueryError::Syntax(
2325 "RETURN * isn't supported as a mutating clause's own trailing RETURN"
2326 .into(),
2327 ));
2328 };
2329 Some(ReturnTail { items, distinct })
2330 }
2331 None => None,
2332 };
2333 let tail = if let Some(create_ctx) = ctx.createSt() {
2334 Tail::Create(self.visit(&*create_ctx).into_create_patterns()?, ret)
2335 } else if let Some(delete_ctx) = ctx.deleteSt() {
2336 let d = self.visit(&*delete_ctx).into_delete_items()?;
2337 if d.detach {
2338 Tail::DetachDelete(d.items, ret)
2339 } else {
2340 Tail::Delete(d.items, ret)
2341 }
2342 } else if let Some(set_ctx) = ctx.setSt() {
2343 Tail::Set(self.visit(&*set_ctx).into_set_items()?, ret)
2344 } else {
2345 let remove_ctx = ctx
2346 .removeSt()
2347 .expect("build_mutating_tail's caller already excluded mergeSt");
2348 Tail::Remove(self.visit(&*remove_ctx).into_remove_items()?, ret)
2349 };
2350 Ok((tail, order_by, skip, limit))
2351 }
2352
2353 fn build_single_part_q(&mut self, ctx: &SinglePartQContext) -> Result<Statement, QueryError> {
2367 let mut clauses = Vec::new();
2368 for rs_ctx in ctx.readingStatement_all() {
2369 self.append_reading_statement(&rs_ctx, &mut clauses)?;
2370 }
2371
2372 let updating = ctx.updatingStatement_all();
2373 let return_ctx = ctx.returnSt();
2374
2375 if clauses.is_empty() && return_ctx.is_none() && updating.len() == 1 {
2385 if let Some(create_ctx) = updating[0].createSt() {
2386 let patterns = self.visit(&*create_ctx).into_create_patterns()?;
2387 return Ok(Statement::Create(patterns));
2388 }
2389 }
2390
2391 let mut tail = None;
2392 let mut order_by = None;
2393 let mut skip = None;
2394 let mut limit = None;
2395 let mut consumed_return = false;
2396
2397 if let Some((last, earlier)) = updating.split_last() {
2398 for us_ctx in earlier {
2399 clauses.push(self.build_updating_statement_as_clause(us_ctx)?);
2400 }
2401 if last.mergeSt().is_some() {
2402 clauses.push(self.build_updating_statement_as_clause(last)?);
2403 } else {
2404 let (t, ob, sk, lim) = self.build_mutating_tail(last, return_ctx.as_deref())?;
2405 tail = Some(t);
2406 order_by = ob;
2407 skip = sk;
2408 limit = lim;
2409 consumed_return = return_ctx.is_some();
2410 }
2411 }
2412
2413 if !consumed_return {
2414 if let Some(return_ctx) = return_ctx {
2415 let c = self.visit(&*return_ctx).into_return_clause()?;
2416 tail = Some(c.tail);
2417 order_by = c.order_by;
2418 skip = c.skip;
2419 limit = c.limit;
2420 }
2421 }
2422
2423 if tail.is_none() && !clauses.iter().any(|c| matches!(c, QueryClause::Merge(_))) {
2424 return Err(QueryError::Syntax(
2425 "a query needs a RETURN/DELETE/SET tail, unless it has a MERGE clause with nothing after it".into(),
2426 ));
2427 }
2428
2429 Ok(Statement::Match {
2430 clauses,
2431 tail,
2432 order_by,
2433 skip: skip.map(Box::new),
2434 limit: limit.map(Box::new),
2435 })
2436 }
2437
2438 fn build_multi_part_q(&mut self, ctx: &MultiPartQContext) -> Result<Statement, QueryError> {
2460 enum Item<'i> {
2461 Reading(Rc<ReadingStatementContextAll<'i>>),
2462 Updating(Rc<UpdatingStatementContextAll<'i>>),
2463 With(Rc<WithStContext<'i>>),
2464 }
2465 let mut items: Vec<(isize, Item)> = Vec::new();
2466 for rs in ctx.readingStatement_all() {
2467 let idx = rs.start().get_token_index();
2468 items.push((idx, Item::Reading(rs)));
2469 }
2470 for us in ctx.updatingStatement_all() {
2471 let idx = us.start().get_token_index();
2472 items.push((idx, Item::Updating(us)));
2473 }
2474 for w in ctx.withSt_all() {
2475 let idx = w.start().get_token_index();
2476 items.push((idx, Item::With(w)));
2477 }
2478 items.sort_by_key(|(idx, _)| *idx);
2479
2480 let mut clauses: Vec<QueryClause> = Vec::new();
2481 let mut attach_target: Option<usize> = None;
2482 for (_, item) in items {
2483 match item {
2484 Item::Reading(rs) => {
2485 self.append_reading_statement(&rs, &mut clauses)?;
2486 attach_target = Some(clauses.len() - 1);
2487 }
2488 Item::Updating(us) => {
2489 let clause = self.build_updating_statement_as_clause(&us)?;
2490 let can_attach = matches!(clause, QueryClause::Merge(_));
2491 clauses.push(clause);
2492 attach_target = can_attach.then_some(clauses.len() - 1);
2493 }
2494 Item::With(w) => {
2495 let with = self.visit(&*w).into_with_clause()?;
2496 match attach_target.take() {
2497 Some(i) => match &mut clauses[i] {
2498 QueryClause::Match(part) => part.with = Some(with),
2499 QueryClause::Unwind(u) => u.with = Some(with),
2500 QueryClause::Merge(m) => m.with = Some(with),
2501 QueryClause::Call(call) => call.with = Some(with),
2502 _ => unreachable!(
2503 "attach_target is only ever set right after pushing a Match/Unwind/Merge/Call clause"
2504 ),
2505 },
2506 None => clauses.push(QueryClause::With(with)),
2507 }
2508 }
2509 }
2510 }
2511
2512 let sp_ctx = ctx
2513 .singlePartQ()
2514 .expect("multiPartQ always ends in a singlePartQ");
2515 let (tail_clauses, tail, order_by, skip, limit) =
2524 match self.build_single_part_q(&sp_ctx)? {
2525 Statement::Match {
2526 clauses,
2527 tail,
2528 order_by,
2529 skip,
2530 limit,
2531 } => (clauses, tail, order_by, skip, limit),
2532 Statement::Create(patterns) => {
2533 (Vec::new(), Some(Tail::Create(patterns, None)), None, None, None)
2534 }
2535 other => unreachable!(
2536 "build_single_part_q only ever returns Statement::Match or Statement::Create, got {other:?}"
2537 ),
2538 };
2539 clauses.extend(tail_clauses);
2540 Ok(Statement::Match {
2541 clauses,
2542 tail,
2543 order_by,
2544 skip,
2545 limit,
2546 })
2547 }
2548
2549 fn build_explain_st(&mut self, ctx: &ExplainStContext) -> Result<Statement, QueryError> {
2554 let inner = match ctx.createIndexSt() {
2555 Some(ci_ctx) => self.build_create_index_st(&ci_ctx)?,
2556 None => {
2557 let rq_ctx = ctx
2558 .regularQuery()
2559 .expect("explainSt always has a createIndexSt or regularQuery");
2560 self.visit(&*rq_ctx).into_statement()?
2561 }
2562 };
2563 Ok(Statement::Explain(Box::new(inner)))
2564 }
2565
2566 fn build_create_index_st(
2572 &mut self,
2573 ctx: &CreateIndexStContext,
2574 ) -> Result<Statement, QueryError> {
2575 let names = ctx.name_all();
2576 let label = name_text(
2577 names
2578 .first()
2579 .expect("createIndexSt always has a label name"),
2580 );
2581 let prop = name_text(
2582 names
2583 .get(1)
2584 .expect("createIndexSt always has a property name"),
2585 );
2586 Ok(Statement::CreateIndex {
2587 label,
2588 prop,
2589 unique: ctx.UNIQUE().is_some(),
2590 })
2591 }
2592
2593 fn build_regular_query(&mut self, ctx: &RegularQueryContext) -> Result<Statement, QueryError> {
2603 let sq_ctx = ctx
2604 .singleQuery()
2605 .expect("regularQuery always has a singleQuery");
2606 let first = self.visit(&*sq_ctx).into_statement()?;
2607 let unions = ctx.unionSt_all();
2608 if unions.is_empty() {
2609 return Ok(first);
2610 }
2611 let mut parts = vec![first];
2612 let mut all: Option<bool> = None;
2613 for u_ctx in unions {
2614 let this_all = u_ctx.ALL().is_some();
2615 match all {
2616 None => all = Some(this_all),
2617 Some(prev) if prev != this_all => {
2618 return Err(QueryError::Syntax(
2619 "can't mix UNION and UNION ALL in the same statement".into(),
2620 ));
2621 }
2622 Some(_) => {}
2623 }
2624 let part_sq = u_ctx
2625 .singleQuery()
2626 .expect("unionSt always has a singleQuery");
2627 parts.push(self.visit(&*part_sq).into_statement()?);
2628 }
2629 Ok(Statement::Union {
2630 parts,
2631 all: all.unwrap_or(false),
2632 })
2633 }
2634}
2635
2636pub fn parse_antlr(input: &str) -> Result<Statement, QueryError> {
2640 let trimmed = input.trim();
2653 let trimmed = trimmed
2654 .strip_suffix(';')
2655 .map(str::trim_end)
2656 .unwrap_or(trimmed);
2657 let mut words = trimmed.split_whitespace();
2658 match (words.next(), words.next(), words.next()) {
2659 (Some(begin), rest, None)
2660 if begin.eq_ignore_ascii_case("BEGIN")
2661 && rest.is_none_or(|w| w.eq_ignore_ascii_case("TRANSACTION")) =>
2662 {
2663 return Ok(Statement::Begin);
2664 }
2665 (Some(commit), None, None) if commit.eq_ignore_ascii_case("COMMIT") => {
2666 return Ok(Statement::Commit);
2667 }
2668 (Some(rollback), None, None) if rollback.eq_ignore_ascii_case("ROLLBACK") => {
2669 return Ok(Statement::Rollback);
2670 }
2671 _ => {}
2672 }
2673 use crate::generated::cypherlexer::CypherLexer;
2674 use crate::generated::cypherparser::{CypherParser, ScriptContextAttrs};
2675 use antlr4rust::common_token_stream::CommonTokenStream;
2676 use antlr4rust::error_listener::ErrorListener;
2677 use antlr4rust::recognizer::Recognizer;
2678 use antlr4rust::token_factory::TokenFactory;
2679 use antlr4rust::InputStream;
2680 use antlr4rust::Parser as _;
2681 use std::cell::RefCell;
2682
2683 struct CollectErrors(Rc<RefCell<Vec<String>>>);
2684 impl<'a, T: Recognizer<'a>> ErrorListener<'a, T> for CollectErrors {
2685 fn syntax_error(
2686 &self,
2687 _recognizer: &T,
2688 _offending_symbol: Option<&<T::TF as TokenFactory<'a>>::Inner>,
2689 line: isize,
2690 column: isize,
2691 msg: &str,
2692 _e: Option<&antlr4rust::errors::ANTLRError>,
2693 ) {
2694 self.0
2695 .borrow_mut()
2696 .push(format!("line {line}:{column} {msg}"));
2697 }
2698 }
2699
2700 let errors = Rc::new(RefCell::new(Vec::new()));
2701 let stream = InputStream::new(input);
2702 let mut lexer = CypherLexer::new(stream);
2703 lexer.remove_error_listeners();
2704 lexer.add_error_listener(Box::new(CollectErrors(errors.clone())));
2705 let tokens = CommonTokenStream::new(lexer);
2706 let mut parser = CypherParser::new(tokens);
2707 parser.remove_error_listeners();
2708 parser.add_error_listener(Box::new(CollectErrors(errors.clone())));
2709 let ctx = parser
2710 .script()
2711 .map_err(|e| QueryError::Syntax(e.to_string()))?;
2712 if let Some(msg) = errors.borrow().first() {
2713 return Err(QueryError::Syntax(format!("syntax error: {msg}")));
2714 }
2715 let query_ctx = ctx.query().expect("script always has a query");
2726 AstBuilder::new().visit(&*query_ctx).into_statement()
2727}
2728
2729pub fn parse_antlr_many(input: &str) -> Result<Vec<Statement>, QueryError> {
2750 let trimmed = input.trim_end();
2751 let trimmed = trimmed.strip_suffix(';').unwrap_or(trimmed);
2752 split_statements(trimmed)
2753 .into_iter()
2754 .map(parse_antlr)
2755 .collect()
2756}
2757
2758pub fn split_statements(input: &str) -> Vec<&str> {
2771 let bytes = input.as_bytes();
2772 let mut starts = vec![0usize];
2773 let mut semicolons = Vec::new();
2774 let mut quote: Option<u8> = None;
2775 let mut i = 0;
2776 while i < bytes.len() {
2777 let b = bytes[i];
2778 match quote {
2779 Some(q) => {
2780 if b == b'\\' && q != b'`' {
2781 i += 1; } else if b == q {
2783 quote = None;
2784 }
2785 }
2786 None => match b {
2787 b'\'' | b'"' | b'`' => quote = Some(b),
2788 b';' => {
2789 semicolons.push(i);
2790 starts.push(i + 1);
2791 }
2792 _ => {}
2793 },
2794 }
2795 i += 1;
2796 }
2797 starts
2798 .iter()
2799 .enumerate()
2800 .map(|(idx, &start)| {
2801 let end = semicolons.get(idx).copied().unwrap_or(bytes.len());
2802 &input[start..end]
2803 })
2804 .collect()
2805}
2806
2807fn return_expr_to_with_expr(expr: ReturnExpr) -> WithExpr {
2817 match expr {
2818 ReturnExpr::And(l, r) => WithExpr::And(
2819 Box::new(return_expr_to_with_expr(*l)),
2820 Box::new(return_expr_to_with_expr(*r)),
2821 ),
2822 ReturnExpr::Or(l, r) => WithExpr::Or(
2823 Box::new(return_expr_to_with_expr(*l)),
2824 Box::new(return_expr_to_with_expr(*r)),
2825 ),
2826 ReturnExpr::Not(inner) => WithExpr::Not(Box::new(return_expr_to_with_expr(*inner))),
2827 ReturnExpr::Compare(l, op, r) => WithExpr::Compare(*l, op, *r),
2828 ReturnExpr::IsNull(inner) => WithExpr::IsNull(*inner),
2829 other => WithExpr::Bare(other),
2830 }
2831}
2832
2833fn return_expr_to_expr(expr: ReturnExpr) -> Result<Expr, QueryError> {
2851 Ok(match expr {
2852 ReturnExpr::And(l, r) => Expr::And(
2853 Box::new(return_expr_to_expr(*l)?),
2854 Box::new(return_expr_to_expr(*r)?),
2855 ),
2856 ReturnExpr::Or(l, r) => Expr::Or(
2857 Box::new(return_expr_to_expr(*l)?),
2858 Box::new(return_expr_to_expr(*r)?),
2859 ),
2860 ReturnExpr::Not(inner) => Expr::Not(Box::new(return_expr_to_expr(*inner)?)),
2861 ReturnExpr::Compare(l, op, r) => match (*l, *r) {
2862 (ReturnExpr::Prop(pa), ReturnExpr::Lit(lit)) => Expr::Compare(pa, op, lit),
2863 (ReturnExpr::Prop(pa1), ReturnExpr::Prop(pa2)) => Expr::PropCompare(pa1, op, pa2),
2864 (ReturnExpr::Var(a), ReturnExpr::Var(b)) => match op {
2865 CompareOp::Eq => Expr::VarEq(a, b),
2866 CompareOp::Ne => Expr::Not(Box::new(Expr::VarEq(a, b))),
2867 _ => {
2868 return Err(QueryError::Syntax(format!(
2869 "{a} {op:?} {b}: only = and <> are meaningful for comparing two \
2870 nodes/relationships by identity (no ordering exists between them)"
2871 )))
2872 }
2873 },
2874 (l, r) => Expr::GeneralCompare(l, op, r),
2875 },
2876 ReturnExpr::IsNull(inner) => match *inner {
2877 ReturnExpr::Prop(pa) => Expr::IsNull(pa),
2878 other => Expr::GeneralIsNull(other),
2879 },
2880 ReturnExpr::HasLabel(var, labels) => {
2881 let mut labels = labels.into_iter();
2882 let first = labels
2883 .next()
2884 .expect("HasLabel always carries at least one label");
2885 labels.fold(Expr::HasLabel(var.clone(), first), |acc, label| {
2886 Expr::And(Box::new(acc), Box::new(Expr::HasLabel(var.clone(), label)))
2887 })
2888 }
2889 ReturnExpr::PatternPredicate(pattern) => Expr::Pattern(pattern),
2890 ReturnExpr::ExistsPattern {
2891 pattern,
2892 where_clause,
2893 } => Expr::Exists {
2894 pattern,
2895 where_clause,
2896 },
2897 ReturnExpr::ExistsSubquery(stmt) => Expr::ExistsSubquery(stmt),
2898 other => Expr::GeneralBare(other),
2899 })
2900}
2901
2902#[cfg(test)]
2903mod tests {
2904 use super::*;
2905 use crate::generated::cypherlexer::CypherLexer;
2906 use crate::generated::cypherparser::CypherParser;
2907 use antlr4rust::common_token_stream::CommonTokenStream;
2908 use antlr4rust::InputStream;
2909
2910 fn parse_literal_expr(input: &str) -> Result<Literal, QueryError> {
2911 let stream = InputStream::new(input);
2912 let lexer = CypherLexer::new(stream);
2913 let tokens = CommonTokenStream::new(lexer);
2914 let mut parser = CypherParser::new(tokens);
2915 let ctx = parser
2916 .literal()
2917 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `literal`: {e:?}"));
2918 AstBuilder::new().visit(&*ctx).into_literal()
2919 }
2920
2921 fn parse_pattern(input: &str) -> Result<Pattern, QueryError> {
2922 let stream = InputStream::new(input);
2923 let lexer = CypherLexer::new(stream);
2924 let tokens = CommonTokenStream::new(lexer);
2925 let mut parser = CypherParser::new(tokens);
2926 let ctx = parser
2927 .patternElem()
2928 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `patternElem`: {e:?}"));
2929 AstBuilder::new().visit(&*ctx).into_pattern()
2930 }
2931
2932 fn parse_match(input: &str) -> Result<Vec<QueryPart>, QueryError> {
2933 let stream = InputStream::new(input);
2934 let lexer = CypherLexer::new(stream);
2935 let tokens = CommonTokenStream::new(lexer);
2936 let mut parser = CypherParser::new(tokens);
2937 let ctx = parser
2938 .matchSt()
2939 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `matchSt`: {e:?}"));
2940 AstBuilder::new().visit(&*ctx).into_query_parts()
2941 }
2942
2943 fn parse_expr(input: &str) -> Result<ReturnExpr, QueryError> {
2944 let stream = InputStream::new(input);
2945 let lexer = CypherLexer::new(stream);
2946 let tokens = CommonTokenStream::new(lexer);
2947 let mut parser = CypherParser::new(tokens);
2948 let ctx = parser
2949 .expression()
2950 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `expression`: {e:?}"));
2951 AstBuilder::new().visit(&*ctx).into_return_expr()
2952 }
2953
2954 fn parse_return(input: &str) -> Result<ParsedReturnClause, QueryError> {
2955 let stream = InputStream::new(input);
2956 let lexer = CypherLexer::new(stream);
2957 let tokens = CommonTokenStream::new(lexer);
2958 let mut parser = CypherParser::new(tokens);
2959 let ctx = parser
2960 .returnSt()
2961 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `returnSt`: {e:?}"));
2962 AstBuilder::new().visit(&*ctx).into_return_clause()
2963 }
2964
2965 fn parse_with(input: &str) -> Result<WithClause, QueryError> {
2966 let stream = InputStream::new(input);
2967 let lexer = CypherLexer::new(stream);
2968 let tokens = CommonTokenStream::new(lexer);
2969 let mut parser = CypherParser::new(tokens);
2970 let ctx = parser
2971 .withSt()
2972 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `withSt`: {e:?}"));
2973 AstBuilder::new().visit(&*ctx).into_with_clause()
2974 }
2975
2976 fn parse_unwind(input: &str) -> Result<UnwindClause, QueryError> {
2977 let stream = InputStream::new(input);
2978 let lexer = CypherLexer::new(stream);
2979 let tokens = CommonTokenStream::new(lexer);
2980 let mut parser = CypherParser::new(tokens);
2981 let ctx = parser
2982 .unwindSt()
2983 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `unwindSt`: {e:?}"));
2984 AstBuilder::new().visit(&*ctx).into_unwind_clause()
2985 }
2986
2987 fn parse_set(input: &str) -> Result<Vec<SetItem>, QueryError> {
2988 let stream = InputStream::new(input);
2989 let lexer = CypherLexer::new(stream);
2990 let tokens = CommonTokenStream::new(lexer);
2991 let mut parser = CypherParser::new(tokens);
2992 let ctx = parser
2993 .setSt()
2994 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `setSt`: {e:?}"));
2995 AstBuilder::new().visit(&*ctx).into_set_items()
2996 }
2997
2998 fn parse_delete(input: &str) -> Result<ParsedDelete, QueryError> {
2999 let stream = InputStream::new(input);
3000 let lexer = CypherLexer::new(stream);
3001 let tokens = CommonTokenStream::new(lexer);
3002 let mut parser = CypherParser::new(tokens);
3003 let ctx = parser
3004 .deleteSt()
3005 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `deleteSt`: {e:?}"));
3006 AstBuilder::new().visit(&*ctx).into_delete_items()
3007 }
3008
3009 fn parse_remove(input: &str) -> Result<Vec<RemoveItem>, QueryError> {
3010 let stream = InputStream::new(input);
3011 let lexer = CypherLexer::new(stream);
3012 let tokens = CommonTokenStream::new(lexer);
3013 let mut parser = CypherParser::new(tokens);
3014 let ctx = parser
3015 .removeSt()
3016 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `removeSt`: {e:?}"));
3017 AstBuilder::new().visit(&*ctx).into_remove_items()
3018 }
3019
3020 fn parse_create(input: &str) -> Result<Vec<Pattern>, QueryError> {
3021 let stream = InputStream::new(input);
3022 let lexer = CypherLexer::new(stream);
3023 let tokens = CommonTokenStream::new(lexer);
3024 let mut parser = CypherParser::new(tokens);
3025 let ctx = parser
3026 .createSt()
3027 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `createSt`: {e:?}"));
3028 AstBuilder::new().visit(&*ctx).into_create_patterns()
3029 }
3030
3031 fn parse_merge(input: &str) -> Result<MergeClause, QueryError> {
3032 let stream = InputStream::new(input);
3033 let lexer = CypherLexer::new(stream);
3034 let tokens = CommonTokenStream::new(lexer);
3035 let mut parser = CypherParser::new(tokens);
3036 let ctx = parser
3037 .mergeSt()
3038 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `mergeSt`: {e:?}"));
3039 AstBuilder::new().visit(&*ctx).into_merge_clause()
3040 }
3041
3042 fn parse_statement(input: &str) -> Result<Statement, QueryError> {
3043 let stream = InputStream::new(input);
3044 let lexer = CypherLexer::new(stream);
3045 let tokens = CommonTokenStream::new(lexer);
3046 let mut parser = CypherParser::new(tokens);
3047 let ctx = parser
3048 .singlePartQ()
3049 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `singlePartQ`: {e:?}"));
3050 AstBuilder::new().visit(&*ctx).into_statement()
3051 }
3052
3053 fn parse_multi_part_statement(input: &str) -> Result<Statement, QueryError> {
3054 let stream = InputStream::new(input);
3055 let lexer = CypherLexer::new(stream);
3056 let tokens = CommonTokenStream::new(lexer);
3057 let mut parser = CypherParser::new(tokens);
3058 let ctx = parser
3059 .multiPartQ()
3060 .unwrap_or_else(|e| panic!("failed to parse {input:?} as `multiPartQ`: {e:?}"));
3061 AstBuilder::new().visit(&*ctx).into_statement()
3062 }
3063
3064 #[test]
3065 fn bool_literals() {
3066 assert_eq!(parse_literal_expr("true").unwrap(), Literal::Bool(true));
3067 assert_eq!(parse_literal_expr("FALSE").unwrap(), Literal::Bool(false));
3068 }
3069
3070 #[test]
3071 fn null_literal() {
3072 assert_eq!(parse_literal_expr("null").unwrap(), Literal::Null);
3073 }
3074
3075 #[test]
3076 fn decimal_int() {
3077 assert_eq!(parse_literal_expr("42").unwrap(), Literal::Int(42));
3078 assert_eq!(parse_literal_expr("007").unwrap(), Literal::Int(7));
3079 }
3080
3081 #[test]
3082 fn hex_and_octal_int() {
3083 assert_eq!(parse_literal_expr("0x1A").unwrap(), Literal::Int(26));
3084 assert_eq!(parse_literal_expr("0o17").unwrap(), Literal::Int(15));
3085 }
3086
3087 #[test]
3099 fn float_literals() {
3100 assert_eq!(parse_literal_expr("2.5").unwrap(), Literal::Float(2.5));
3101 assert_eq!(parse_literal_expr("1e10").unwrap(), Literal::Float(1e10));
3102 assert_eq!(parse_literal_expr(".5").unwrap(), Literal::Float(0.5));
3103 }
3104
3105 #[test]
3106 fn float_overflow_errors() {
3107 assert!(parse_literal_expr("1e999").is_err());
3108 }
3109
3110 #[test]
3111 fn string_and_char_literals() {
3112 assert_eq!(
3113 parse_literal_expr("\"hello\"").unwrap(),
3114 Literal::String("hello".to_string())
3115 );
3116 assert_eq!(
3117 parse_literal_expr("'a string with spaces and a hyphen-in-it'").unwrap(),
3118 Literal::String("a string with spaces and a hyphen-in-it".to_string())
3119 );
3120 }
3121
3122 #[test]
3123 fn string_escapes() {
3124 assert_eq!(
3125 parse_literal_expr(r#"'line1\nline2'"#).unwrap(),
3126 Literal::String("line1\nline2".to_string())
3127 );
3128 assert_eq!(
3129 parse_literal_expr(r#"'é'"#).unwrap(),
3130 Literal::String("é".to_string())
3131 );
3132 }
3133
3134 #[test]
3135 fn single_node() {
3136 let p = parse_pattern("(a:Person)").unwrap();
3137 assert_eq!(p.start.var.as_deref(), Some("a"));
3138 assert_eq!(p.start.labels, vec!["Person".to_string()]);
3139 assert!(p.hops.is_empty());
3140 }
3141
3142 #[test]
3143 fn anonymous_node() {
3144 let p = parse_pattern("()").unwrap();
3145 assert_eq!(p.start.var, None);
3146 assert!(p.start.labels.is_empty());
3147 }
3148
3149 #[test]
3150 fn multiple_labels() {
3151 let p = parse_pattern("(a:Person:Employee)").unwrap();
3152 assert_eq!(
3153 p.start.labels,
3154 vec!["Person".to_string(), "Employee".to_string()]
3155 );
3156 }
3157
3158 #[test]
3159 fn escaped_identifier() {
3160 let p = parse_pattern("(`weird name`)").unwrap();
3161 assert_eq!(p.start.var.as_deref(), Some("weird name"));
3162 }
3163
3164 #[test]
3165 fn directions() {
3166 assert_eq!(
3167 parse_pattern("(a)-->(b)").unwrap().hops[0].0.direction,
3168 RelDirection::Right
3169 );
3170 assert_eq!(
3171 parse_pattern("(a)<--(b)").unwrap().hops[0].0.direction,
3172 RelDirection::Left
3173 );
3174 assert_eq!(
3175 parse_pattern("(a)--(b)").unwrap().hops[0].0.direction,
3176 RelDirection::Either
3177 );
3178 assert_eq!(
3183 parse_pattern("(a)<-->(b)").unwrap().hops[0].0.direction,
3184 RelDirection::Either
3185 );
3186 }
3187
3188 #[test]
3189 fn rel_type_and_var() {
3190 let p = parse_pattern("(a)-[r:KNOWS]->(b)").unwrap();
3191 let (rel, node) = &p.hops[0];
3192 assert_eq!(rel.var.as_deref(), Some("r"));
3193 assert_eq!(rel.rel_types, vec!["KNOWS".to_string()]);
3194 assert_eq!(node.var.as_deref(), Some("b"));
3195 assert_eq!(rel.hop_range, None);
3196 }
3197
3198 #[test]
3199 fn multiple_rel_types() {
3200 let p = parse_pattern("(a)-[:KNOWS|LIKES]->(b)").unwrap();
3201 assert_eq!(
3202 p.hops[0].0.rel_types,
3203 vec!["KNOWS".to_string(), "LIKES".to_string()]
3204 );
3205 }
3206
3207 #[test]
3208 fn var_length_bounds() {
3209 assert_eq!(
3212 parse_pattern("(a)-[*0]->(b)").unwrap().hops[0].0.hop_range,
3213 Some((0, Some(0)))
3214 );
3215 assert_eq!(
3216 parse_pattern("(a)-[*2]->(b)").unwrap().hops[0].0.hop_range,
3217 Some((2, Some(2)))
3218 );
3219 assert_eq!(
3220 parse_pattern("(a)-[*1..3]->(b)").unwrap().hops[0]
3221 .0
3222 .hop_range,
3223 Some((1, Some(3)))
3224 );
3225 assert_eq!(
3226 parse_pattern("(a)-[*]->(b)").unwrap().hops[0].0.hop_range,
3227 Some((1, None))
3228 );
3229 }
3230
3231 #[test]
3232 fn multi_hop_chain() {
3233 let p = parse_pattern("(a)-[:KNOWS]->(b)<-[:LIKES]-(c)").unwrap();
3234 assert_eq!(p.hops.len(), 2);
3235 assert_eq!(p.hops[0].0.direction, RelDirection::Right);
3236 assert_eq!(p.hops[1].0.direction, RelDirection::Left);
3237 }
3238
3239 #[test]
3240 fn node_pattern_properties() {
3241 let pattern = parse_pattern("(a {name: 'x', age: 1 + 1})").unwrap();
3242 assert_eq!(
3243 pattern.start.props,
3244 vec![
3245 (
3246 "name".to_string(),
3247 ReturnExpr::Lit(Literal::String("x".to_string()))
3248 ),
3249 (
3250 "age".to_string(),
3251 ReturnExpr::Arith(
3252 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3253 ArithOp::Add,
3254 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3255 )
3256 ),
3257 ]
3258 );
3259 }
3260
3261 #[test]
3262 fn rel_pattern_properties() {
3263 let pattern = parse_pattern("(a)-[:T {weight: 5}]->(b)").unwrap();
3264 assert_eq!(
3265 pattern.hops[0].0.props,
3266 vec![("weight".to_string(), ReturnExpr::Lit(Literal::Int(5)))]
3267 );
3268 }
3269
3270 #[test]
3271 fn pattern_properties_parameter_not_supported() {
3272 assert!(parse_pattern("(a $props)").is_err());
3273 }
3274
3275 #[test]
3276 fn simple_match() {
3277 let parts = parse_match("MATCH (a:Person)-[:KNOWS]->(b)").unwrap();
3278 assert_eq!(parts.len(), 1);
3279 assert!(!parts[0].optional);
3280 assert_eq!(parts[0].path_var, None);
3281 assert_eq!(parts[0].pattern.start.var.as_deref(), Some("a"));
3282 assert_eq!(parts[0].pattern.hops.len(), 1);
3283 }
3284
3285 #[test]
3286 fn optional_match() {
3287 let parts = parse_match("OPTIONAL MATCH (a)").unwrap();
3288 assert!(parts[0].optional);
3289 }
3290
3291 #[test]
3292 fn named_path() {
3293 let parts = parse_match("MATCH p = (a)-->(b)").unwrap();
3294 assert_eq!(parts.len(), 1);
3295 assert_eq!(parts[0].path_var.as_deref(), Some("p"));
3296 }
3297
3298 #[test]
3299 fn comma_pattern_shared_node_merges_into_one_linear_chain() {
3300 let parts = parse_match("MATCH (a)-->(b), (b)-->(c)").unwrap();
3303 assert_eq!(parts.len(), 1);
3304 assert_eq!(parts[0].pattern.hops.len(), 2);
3305 }
3306
3307 #[test]
3308 fn comma_pattern_disjoint_becomes_multiple_query_parts() {
3309 let parts = parse_match("MATCH (a), (b)").unwrap();
3310 assert_eq!(parts.len(), 2);
3311 }
3312
3313 #[test]
3314 fn named_path_over_disjoint_cross_join_errors() {
3315 assert!(parse_match("MATCH p = (a), (b)").is_err());
3316 }
3317
3318 #[test]
3319 fn shortest_path() {
3320 let parts = parse_match("MATCH shortestPath((a)-[*1..3]->(b))").unwrap();
3321 assert_eq!(parts.len(), 1);
3322 assert!(parts[0].shortest_path);
3323 assert_eq!(parts[0].pattern.hops.len(), 1);
3324 }
3325
3326 #[test]
3327 fn shortest_path_with_named_path_capture() {
3328 let parts = parse_match("MATCH p = shortestPath((a)-[*1..3]->(b))").unwrap();
3329 assert_eq!(parts[0].path_var.as_deref(), Some("p"));
3330 assert!(parts[0].shortest_path);
3331 }
3332
3333 #[test]
3334 fn shortest_path_requires_variable_length_hop() {
3335 assert!(parse_match("MATCH shortestPath((a)-->(b))").is_err());
3336 }
3337
3338 #[test]
3339 fn shortest_path_not_first_in_cross_join_errors() {
3340 assert!(parse_match("MATCH (c), shortestPath((a)-[*1..3]->(b))").is_err());
3341 }
3342
3343 #[test]
3344 fn shortest_path_over_disjoint_cross_join_errors() {
3345 assert!(parse_match("MATCH shortestPath((a)-[*1..3]->(b)), (c)").is_err());
3346 }
3347
3348 #[test]
3349 fn shortest_path_not_valid_in_create() {
3350 assert!(parse_statement("CREATE shortestPath((a)-[*1..3]->(b))").is_err());
3351 }
3352
3353 #[test]
3354 fn shortest_path_not_valid_in_merge() {
3355 assert!(parse_merge("MERGE shortestPath((a)-[*1..3]->(b))").is_err());
3356 }
3357
3358 #[test]
3359 fn named_path_over_a_single_variable_length_hop_is_supported() {
3360 let parts = parse_match("MATCH p = (a)-[*1..3]->(b)").unwrap();
3364 assert_eq!(parts[0].path_var.as_deref(), Some("p"));
3365 }
3366
3367 #[test]
3368 fn named_path_over_variable_length_mixed_with_another_hop_is_supported() {
3369 let parts = parse_match("MATCH p = (a)-[*1..3]->(b)-->(c)").unwrap();
3372 assert_eq!(parts[0].path_var.as_deref(), Some("p"));
3373 }
3374
3375 #[test]
3376 fn match_where() {
3377 let parts = parse_match("MATCH (a) WHERE a.x = 1").unwrap();
3378 assert_eq!(parts.len(), 1);
3379 assert!(matches!(
3380 parts[0].where_clause,
3381 Some(Expr::Compare(
3382 PropAccess { .. },
3383 CompareOp::Eq,
3384 Literal::Int(1),
3385 ))
3386 ));
3387 }
3388
3389 #[test]
3390 fn match_where_var_eq() {
3391 let parts = parse_match("MATCH (a), (b) WHERE a = b").unwrap();
3392 assert!(matches!(parts[1].where_clause, Some(Expr::VarEq(_, _))));
3393 }
3394
3395 #[test]
3396 fn match_where_label_predicate() {
3397 let parts = parse_match("MATCH (a) WHERE a:A:B").unwrap();
3398 assert!(matches!(parts[0].where_clause, Some(Expr::And(_, _))));
3399 }
3400
3401 #[test]
3402 fn match_where_pattern_predicate() {
3403 let parts = parse_match("MATCH (n) WHERE (n)-[]->() RETURN n")
3404 .unwrap_or_else(|e| panic!("expected pattern predicate to parse, got {e:?}"));
3405 let Some(Expr::Pattern(pattern)) = &parts[0].where_clause else {
3406 panic!("expected Expr::Pattern");
3407 };
3408 assert_eq!(pattern.hops.len(), 1);
3409 }
3410
3411 #[test]
3412 fn match_where_pattern_predicate_combined_with_and() {
3413 let parts = parse_match("MATCH (n) WHERE (n)-->() AND n.x = 1").unwrap();
3414 let Some(Expr::And(l, r)) = &parts[0].where_clause else {
3415 panic!("expected Expr::And");
3416 };
3417 assert!(matches!(**l, Expr::Pattern(_)));
3418 assert!(matches!(**r, Expr::Compare(..)));
3419 }
3420
3421 #[test]
3422 fn pattern_predicate_outside_where_still_parses() {
3423 let expr = parse_expr("(n)-->()").unwrap();
3428 assert!(matches!(expr, ReturnExpr::PatternPredicate(_)));
3429 }
3430
3431 #[test]
3432 fn match_where_on_last_group_of_cross_join() {
3433 let parts = parse_match("MATCH (a), (b) WHERE b.x = 1").unwrap();
3434 assert_eq!(parts.len(), 2);
3435 assert!(parts[0].where_clause.is_none());
3436 assert!(parts[1].where_clause.is_some());
3437 }
3438
3439 #[test]
3440 fn arithmetic_precedence() {
3441 assert_eq!(
3443 parse_expr("1 + 2 * 3").unwrap(),
3444 ReturnExpr::Arith(
3445 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3446 ArithOp::Add,
3447 Box::new(ReturnExpr::Arith(
3448 Box::new(ReturnExpr::Lit(Literal::Int(2))),
3449 ArithOp::Mul,
3450 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3451 )),
3452 )
3453 );
3454 }
3455
3456 #[test]
3457 fn arithmetic_left_associative() {
3458 assert_eq!(
3460 parse_expr("10 - 2 - 3").unwrap(),
3461 ReturnExpr::Arith(
3462 Box::new(ReturnExpr::Arith(
3463 Box::new(ReturnExpr::Lit(Literal::Int(10))),
3464 ArithOp::Sub,
3465 Box::new(ReturnExpr::Lit(Literal::Int(2))),
3466 )),
3467 ArithOp::Sub,
3468 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3469 )
3470 );
3471 }
3472
3473 #[test]
3474 fn power_left_associative() {
3475 assert_eq!(
3476 parse_expr("4 ^ 3 ^ 2").unwrap(),
3477 ReturnExpr::Arith(
3478 Box::new(ReturnExpr::Arith(
3479 Box::new(ReturnExpr::Lit(Literal::Int(4))),
3480 ArithOp::Pow,
3481 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3482 )),
3483 ArithOp::Pow,
3484 Box::new(ReturnExpr::Lit(Literal::Int(2))),
3485 )
3486 );
3487 }
3488
3489 #[test]
3490 fn binary_minus_no_whitespace() {
3491 assert_eq!(
3494 parse_expr("5-1").unwrap(),
3495 ReturnExpr::Arith(
3496 Box::new(ReturnExpr::Lit(Literal::Int(5))),
3497 ArithOp::Sub,
3498 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3499 )
3500 );
3501 }
3502
3503 #[test]
3504 fn unary_minus_on_variable() {
3505 assert_eq!(
3506 parse_expr("-x").unwrap(),
3507 ReturnExpr::Neg(Box::new(ReturnExpr::Var("x".to_string())))
3508 );
3509 }
3510
3511 #[test]
3512 fn unary_minus_folds_into_literal() {
3513 assert_eq!(parse_expr("-5").unwrap(), ReturnExpr::Lit(Literal::Int(-5)));
3514 assert_eq!(
3515 parse_expr("-5.5").unwrap(),
3516 ReturnExpr::Lit(Literal::Float(-5.5))
3517 );
3518 }
3519
3520 #[test]
3521 fn unary_minus_int_min_two_complement_edge_case() {
3522 assert_eq!(
3527 parse_expr("-9223372036854775808").unwrap(),
3528 ReturnExpr::Lit(Literal::Int(i64::MIN))
3529 );
3530 }
3531
3532 #[test]
3533 fn comparison_chain_folds_into_nested_and() {
3534 assert_eq!(
3537 parse_expr("1 < x < 3").unwrap(),
3538 ReturnExpr::And(
3539 Box::new(ReturnExpr::Compare(
3540 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3541 CompareOp::Lt,
3542 Box::new(ReturnExpr::Var("x".to_string())),
3543 )),
3544 Box::new(ReturnExpr::Compare(
3545 Box::new(ReturnExpr::Var("x".to_string())),
3546 CompareOp::Lt,
3547 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3548 )),
3549 )
3550 );
3551 }
3552
3553 #[test]
3554 fn boolean_operators() {
3555 assert_eq!(
3556 parse_expr("true AND false").unwrap(),
3557 ReturnExpr::And(
3558 Box::new(ReturnExpr::Lit(Literal::Bool(true))),
3559 Box::new(ReturnExpr::Lit(Literal::Bool(false))),
3560 )
3561 );
3562 assert_eq!(
3563 parse_expr("true OR false").unwrap(),
3564 ReturnExpr::Or(
3565 Box::new(ReturnExpr::Lit(Literal::Bool(true))),
3566 Box::new(ReturnExpr::Lit(Literal::Bool(false))),
3567 )
3568 );
3569 assert_eq!(
3570 parse_expr("true XOR false").unwrap(),
3571 ReturnExpr::Xor(
3572 Box::new(ReturnExpr::Lit(Literal::Bool(true))),
3573 Box::new(ReturnExpr::Lit(Literal::Bool(false))),
3574 )
3575 );
3576 }
3577
3578 #[test]
3579 fn double_negation() {
3580 assert_eq!(
3582 parse_expr("NOT NOT true").unwrap(),
3583 ReturnExpr::Not(Box::new(ReturnExpr::Not(Box::new(ReturnExpr::Lit(
3584 Literal::Bool(true)
3585 )))))
3586 );
3587 }
3588
3589 #[test]
3590 fn is_null() {
3591 assert_eq!(
3592 parse_expr("x IS NULL").unwrap(),
3593 ReturnExpr::IsNull(Box::new(ReturnExpr::Var("x".to_string())))
3594 );
3595 assert_eq!(
3596 parse_expr("x IS NOT NULL").unwrap(),
3597 ReturnExpr::Not(Box::new(ReturnExpr::IsNull(Box::new(ReturnExpr::Var(
3598 "x".to_string()
3599 )))))
3600 );
3601 }
3602
3603 #[test]
3604 fn in_operator() {
3605 assert_eq!(
3606 parse_expr("x IN y").unwrap(),
3607 ReturnExpr::In(
3608 Box::new(ReturnExpr::Var("x".to_string())),
3609 Box::new(ReturnExpr::Var("y".to_string())),
3610 )
3611 );
3612 }
3613
3614 #[test]
3615 fn is_null_binds_looser_than_arithmetic() {
3616 assert_eq!(
3621 parse_expr("x + 0 IS NULL").unwrap(),
3622 ReturnExpr::IsNull(Box::new(ReturnExpr::Arith(
3623 Box::new(ReturnExpr::Var("x".to_string())),
3624 ArithOp::Add,
3625 Box::new(ReturnExpr::Lit(Literal::Int(0))),
3626 )))
3627 );
3628 }
3629
3630 #[test]
3631 fn in_binds_looser_than_arithmetic_and_operand_can_be_sliced() {
3632 assert_eq!(
3633 parse_expr("3 IN [1, 2, 3][0..2]").unwrap(),
3634 ReturnExpr::In(
3635 Box::new(ReturnExpr::Lit(Literal::Int(3))),
3636 Box::new(ReturnExpr::Slice(
3637 Box::new(ReturnExpr::ListLit(vec![
3638 ReturnExpr::Lit(Literal::Int(1)),
3639 ReturnExpr::Lit(Literal::Int(2)),
3640 ReturnExpr::Lit(Literal::Int(3)),
3641 ])),
3642 Some(Box::new(ReturnExpr::Lit(Literal::Int(0)))),
3643 Some(Box::new(ReturnExpr::Lit(Literal::Int(2)))),
3644 ))
3645 )
3646 );
3647 }
3648
3649 #[test]
3650 fn starts_with_operand_can_be_an_arithmetic_expression() {
3651 assert_eq!(
3652 parse_expr("x STARTS WITH y + z").unwrap(),
3653 ReturnExpr::Compare(
3654 Box::new(ReturnExpr::Var("x".to_string())),
3655 CompareOp::StartsWith,
3656 Box::new(ReturnExpr::Arith(
3657 Box::new(ReturnExpr::Var("y".to_string())),
3658 ArithOp::Add,
3659 Box::new(ReturnExpr::Var("z".to_string())),
3660 )),
3661 )
3662 );
3663 }
3664
3665 #[test]
3666 fn chained_index_postfix_still_works() {
3667 assert_eq!(
3668 parse_expr("[[1, 2], [3, 4]][0][1]").unwrap(),
3669 ReturnExpr::Index(
3670 Box::new(ReturnExpr::Index(
3671 Box::new(ReturnExpr::ListLit(vec![
3672 ReturnExpr::ListLit(vec![
3673 ReturnExpr::Lit(Literal::Int(1)),
3674 ReturnExpr::Lit(Literal::Int(2)),
3675 ]),
3676 ReturnExpr::ListLit(vec![
3677 ReturnExpr::Lit(Literal::Int(3)),
3678 ReturnExpr::Lit(Literal::Int(4)),
3679 ]),
3680 ])),
3681 Box::new(ReturnExpr::Lit(Literal::Int(0))),
3682 )),
3683 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3684 )
3685 );
3686 }
3687
3688 #[test]
3689 fn case_searched_form() {
3690 assert_eq!(
3691 parse_expr("CASE WHEN x > 1 THEN 'big' WHEN x > 0 THEN 'small' ELSE 'none' END")
3692 .unwrap(),
3693 ReturnExpr::Case {
3694 test: None,
3695 whens: vec![
3696 (
3697 ReturnExpr::Compare(
3698 Box::new(ReturnExpr::Var("x".to_string())),
3699 CompareOp::Gt,
3700 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3701 ),
3702 ReturnExpr::Lit(Literal::String("big".to_string())),
3703 ),
3704 (
3705 ReturnExpr::Compare(
3706 Box::new(ReturnExpr::Var("x".to_string())),
3707 CompareOp::Gt,
3708 Box::new(ReturnExpr::Lit(Literal::Int(0))),
3709 ),
3710 ReturnExpr::Lit(Literal::String("small".to_string())),
3711 ),
3712 ],
3713 else_: Some(Box::new(ReturnExpr::Lit(Literal::String(
3714 "none".to_string()
3715 )))),
3716 }
3717 );
3718 }
3719
3720 #[test]
3721 fn case_simple_form_with_test_no_else() {
3722 assert_eq!(
3723 parse_expr("CASE x WHEN 1 THEN 'one' WHEN 2 THEN 'two' END").unwrap(),
3724 ReturnExpr::Case {
3725 test: Some(Box::new(ReturnExpr::Var("x".to_string()))),
3726 whens: vec![
3727 (
3728 ReturnExpr::Lit(Literal::Int(1)),
3729 ReturnExpr::Lit(Literal::String("one".to_string())),
3730 ),
3731 (
3732 ReturnExpr::Lit(Literal::Int(2)),
3733 ReturnExpr::Lit(Literal::String("two".to_string())),
3734 ),
3735 ],
3736 else_: None,
3737 }
3738 );
3739 }
3740
3741 #[test]
3742 fn quantifier_none() {
3743 assert_eq!(
3744 parse_expr("none(x IN [1,2] WHERE x > 1)").unwrap(),
3745 ReturnExpr::Quantifier {
3746 kind: QuantifierKind::None,
3747 var: "x".to_string(),
3748 source: Box::new(ReturnExpr::ListLit(vec![
3749 ReturnExpr::Lit(Literal::Int(1)),
3750 ReturnExpr::Lit(Literal::Int(2)),
3751 ])),
3752 where_clause: Some(Box::new(ReturnExpr::Compare(
3753 Box::new(ReturnExpr::Var("x".to_string())),
3754 CompareOp::Gt,
3755 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3756 ))),
3757 }
3758 );
3759 }
3760
3761 #[test]
3762 fn quantifier_all_any_single_no_where() {
3763 assert!(matches!(
3764 parse_expr("all(x IN [1]) ").unwrap(),
3765 ReturnExpr::Quantifier {
3766 kind: QuantifierKind::All,
3767 where_clause: None,
3768 ..
3769 }
3770 ));
3771 assert!(matches!(
3772 parse_expr("any(x IN [1])").unwrap(),
3773 ReturnExpr::Quantifier {
3774 kind: QuantifierKind::Any,
3775 ..
3776 }
3777 ));
3778 assert!(matches!(
3779 parse_expr("single(x IN [1])").unwrap(),
3780 ReturnExpr::Quantifier {
3781 kind: QuantifierKind::Single,
3782 ..
3783 }
3784 ));
3785 }
3786
3787 #[test]
3788 fn list_comprehension_with_projection() {
3789 assert_eq!(
3790 parse_expr("[x IN [1,2] WHERE x > 1 | x * 2]").unwrap(),
3791 ReturnExpr::ListComp {
3792 var: "x".to_string(),
3793 source: Box::new(ReturnExpr::ListLit(vec![
3794 ReturnExpr::Lit(Literal::Int(1)),
3795 ReturnExpr::Lit(Literal::Int(2)),
3796 ])),
3797 where_clause: Some(Box::new(ReturnExpr::Compare(
3798 Box::new(ReturnExpr::Var("x".to_string())),
3799 CompareOp::Gt,
3800 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3801 ))),
3802 project: Some(Box::new(ReturnExpr::Arith(
3803 Box::new(ReturnExpr::Var("x".to_string())),
3804 ArithOp::Mul,
3805 Box::new(ReturnExpr::Lit(Literal::Int(2))),
3806 ))),
3807 }
3808 );
3809 }
3810
3811 #[test]
3812 fn list_comprehension_with_where_no_project() {
3813 assert_eq!(
3814 parse_expr("[x IN [1,2] WHERE x > 1]").unwrap(),
3815 ReturnExpr::ListComp {
3816 var: "x".to_string(),
3817 source: Box::new(ReturnExpr::ListLit(vec![
3818 ReturnExpr::Lit(Literal::Int(1)),
3819 ReturnExpr::Lit(Literal::Int(2)),
3820 ])),
3821 where_clause: Some(Box::new(ReturnExpr::Compare(
3822 Box::new(ReturnExpr::Var("x".to_string())),
3823 CompareOp::Gt,
3824 Box::new(ReturnExpr::Lit(Literal::Int(1))),
3825 ))),
3826 project: None,
3827 }
3828 );
3829 }
3830
3831 #[test]
3832 fn list_comprehension_bare_identity_no_where_no_project() {
3833 assert_eq!(
3841 parse_expr("[x IN [1, 2, 3]]").unwrap(),
3842 ReturnExpr::ListComp {
3843 var: "x".to_string(),
3844 source: Box::new(ReturnExpr::ListLit(vec![
3845 ReturnExpr::Lit(Literal::Int(1)),
3846 ReturnExpr::Lit(Literal::Int(2)),
3847 ReturnExpr::Lit(Literal::Int(3)),
3848 ])),
3849 where_clause: None,
3850 project: None,
3851 }
3852 );
3853 }
3854
3855 #[test]
3856 fn string_predicates() {
3857 assert_eq!(
3858 parse_expr("x STARTS WITH y").unwrap(),
3859 ReturnExpr::Compare(
3860 Box::new(ReturnExpr::Var("x".to_string())),
3861 CompareOp::StartsWith,
3862 Box::new(ReturnExpr::Var("y".to_string())),
3863 )
3864 );
3865 assert_eq!(
3866 parse_expr("x ENDS WITH y").unwrap(),
3867 ReturnExpr::Compare(
3868 Box::new(ReturnExpr::Var("x".to_string())),
3869 CompareOp::EndsWith,
3870 Box::new(ReturnExpr::Var("y".to_string())),
3871 )
3872 );
3873 assert_eq!(
3874 parse_expr("x CONTAINS y").unwrap(),
3875 ReturnExpr::Compare(
3876 Box::new(ReturnExpr::Var("x".to_string())),
3877 CompareOp::Contains,
3878 Box::new(ReturnExpr::Var("y".to_string())),
3879 )
3880 );
3881 }
3882
3883 #[test]
3884 fn index_and_slice() {
3885 assert_eq!(
3886 parse_expr("list[0]").unwrap(),
3887 ReturnExpr::Index(
3888 Box::new(ReturnExpr::Var("list".to_string())),
3889 Box::new(ReturnExpr::Lit(Literal::Int(0))),
3890 )
3891 );
3892 assert_eq!(
3893 parse_expr("list[1..3]").unwrap(),
3894 ReturnExpr::Slice(
3895 Box::new(ReturnExpr::Var("list".to_string())),
3896 Some(Box::new(ReturnExpr::Lit(Literal::Int(1)))),
3897 Some(Box::new(ReturnExpr::Lit(Literal::Int(3)))),
3898 )
3899 );
3900 assert_eq!(
3901 parse_expr("list[..3]").unwrap(),
3902 ReturnExpr::Slice(
3903 Box::new(ReturnExpr::Var("list".to_string())),
3904 None,
3905 Some(Box::new(ReturnExpr::Lit(Literal::Int(3)))),
3906 )
3907 );
3908 assert_eq!(
3909 parse_expr("list[1..]").unwrap(),
3910 ReturnExpr::Slice(
3911 Box::new(ReturnExpr::Var("list".to_string())),
3912 Some(Box::new(ReturnExpr::Lit(Literal::Int(1)))),
3913 None,
3914 )
3915 );
3916 }
3917
3918 #[test]
3919 fn property_access() {
3920 assert_eq!(
3921 parse_expr("n.name").unwrap(),
3922 ReturnExpr::Prop(PropAccess {
3923 var: "n".to_string(),
3924 prop: "name".to_string(),
3925 })
3926 );
3927 }
3928
3929 #[test]
3930 fn property_access_with_backtick_escaped_name() {
3931 assert_eq!(
3937 parse_expr("n.`weird name`").unwrap(),
3938 ReturnExpr::Prop(PropAccess {
3939 var: "n".to_string(),
3940 prop: "weird name".to_string(),
3941 })
3942 );
3943 }
3944
3945 #[test]
3946 fn property_access_on_computed_expr_becomes_prop_of() {
3947 let expr = parse_expr("duration.between(a, b).days").unwrap();
3952 let ReturnExpr::PropOf(base, prop) = expr else {
3953 panic!("expected PropOf, got {expr:?}");
3954 };
3955 assert_eq!(prop, "days");
3956 assert!(matches!(*base, ReturnExpr::Call { .. }));
3957 }
3958
3959 #[test]
3960 fn chained_property_access_folds_left_to_right() {
3961 let expr = parse_expr("a.b.c").unwrap();
3963 let ReturnExpr::PropOf(base, prop) = expr else {
3964 panic!("expected PropOf, got {expr:?}");
3965 };
3966 assert_eq!(prop, "c");
3967 assert_eq!(
3968 *base,
3969 ReturnExpr::Prop(PropAccess {
3970 var: "a".to_string(),
3971 prop: "b".to_string(),
3972 })
3973 );
3974 }
3975
3976 #[test]
3977 fn has_label() {
3978 assert_eq!(
3979 parse_expr("n:Person").unwrap(),
3980 ReturnExpr::HasLabel("n".to_string(), vec!["Person".to_string()])
3981 );
3982 }
3983
3984 #[test]
3985 fn function_call() {
3986 assert_eq!(
3987 parse_expr("size(list)").unwrap(),
3988 ReturnExpr::Call {
3989 name: "size".to_string(),
3990 args: vec![ReturnExpr::Var("list".to_string())],
3991 distinct: false,
3992 }
3993 );
3994 }
3995
3996 #[test]
3997 fn namespaced_function_call() {
3998 assert_eq!(
3999 parse_expr("duration.between(a, b)").unwrap(),
4000 ReturnExpr::Call {
4001 name: "duration.between".to_string(),
4002 args: vec![
4003 ReturnExpr::Var("a".to_string()),
4004 ReturnExpr::Var("b".to_string())
4005 ],
4006 distinct: false,
4007 }
4008 );
4009 }
4010
4011 #[test]
4012 fn count_star() {
4013 assert_eq!(parse_expr("count(*)").unwrap(), ReturnExpr::CountStar);
4014 }
4015
4016 #[test]
4017 fn aggregate_distinct() {
4018 assert_eq!(
4019 parse_expr("count(DISTINCT x)").unwrap(),
4020 ReturnExpr::Call {
4021 name: "count".to_string(),
4022 args: vec![ReturnExpr::Var("x".to_string())],
4023 distinct: true,
4024 }
4025 );
4026 }
4027
4028 #[test]
4029 fn distinct_on_non_aggregate_errors() {
4030 assert!(parse_expr("size(DISTINCT x)").is_err());
4031 }
4032
4033 #[test]
4034 fn distinct_on_namespaced_call_errors() {
4035 assert!(parse_expr("duration.between(DISTINCT a, b)").is_err());
4036 }
4037
4038 #[test]
4039 fn parameter_by_name() {
4040 assert_eq!(
4041 parse_expr("$name").unwrap(),
4042 ReturnExpr::Lit(Literal::Param("name".to_string()))
4043 );
4044 }
4045
4046 #[test]
4047 fn parameter_by_position() {
4048 assert_eq!(
4049 parse_expr("$0").unwrap(),
4050 ReturnExpr::Lit(Literal::Param("0".to_string()))
4051 );
4052 }
4053
4054 #[test]
4055 fn parenthesized_expression() {
4056 assert_eq!(
4057 parse_expr("(1 + 2) * 3").unwrap(),
4058 ReturnExpr::Arith(
4059 Box::new(ReturnExpr::Arith(
4060 Box::new(ReturnExpr::Lit(Literal::Int(1))),
4061 ArithOp::Add,
4062 Box::new(ReturnExpr::Lit(Literal::Int(2))),
4063 )),
4064 ArithOp::Mul,
4065 Box::new(ReturnExpr::Lit(Literal::Int(3))),
4066 )
4067 );
4068 }
4069
4070 #[test]
4071 fn return_simple_items() {
4072 let c = parse_return("RETURN a, b.name AS name").unwrap();
4073 let Tail::Return(items, distinct) = c.tail else {
4074 panic!("expected Tail::Return");
4075 };
4076 assert!(!distinct);
4077 assert_eq!(items.len(), 2);
4078 assert_eq!(items[0].expr, ReturnExpr::Var("a".to_string()));
4079 assert_eq!(items[0].alias, None);
4080 assert_eq!(
4081 items[1].expr,
4082 ReturnExpr::Prop(PropAccess {
4083 var: "b".to_string(),
4084 prop: "name".to_string(),
4085 })
4086 );
4087 assert_eq!(items[1].alias.as_deref(), Some("name"));
4088 }
4089
4090 #[test]
4091 fn return_distinct() {
4092 let c = parse_return("RETURN DISTINCT a").unwrap();
4093 let Tail::Return(_, distinct) = c.tail else {
4094 panic!("expected Tail::Return");
4095 };
4096 assert!(distinct);
4097 }
4098
4099 #[test]
4100 fn return_star() {
4101 let c = parse_return("RETURN *").unwrap();
4102 assert!(matches!(c.tail, Tail::ReturnStar(false)));
4103 }
4104
4105 #[test]
4106 fn return_order_by_skip_limit() {
4107 let c = parse_return("RETURN a ORDER BY a DESC SKIP 5 LIMIT 10").unwrap();
4108 let order_by = c.order_by.unwrap();
4109 assert_eq!(order_by.len(), 1);
4110 assert_eq!(order_by[0].0, ReturnExpr::Var("a".to_string()));
4111 assert_eq!(order_by[0].1, SortDir::Desc);
4112 assert_eq!(c.skip, Some(ReturnExpr::Lit(Literal::Int(5))));
4113 assert_eq!(c.limit, Some(ReturnExpr::Lit(Literal::Int(10))));
4114 }
4115
4116 #[test]
4117 fn order_by_default_ascending() {
4118 let c = parse_return("RETURN a ORDER BY a").unwrap();
4119 assert_eq!(c.order_by.unwrap()[0].1, SortDir::Asc);
4120 }
4121
4122 #[test]
4123 fn limit_accepts_arbitrary_expression() {
4124 let c = parse_return("RETURN a LIMIT 1 + 1").unwrap();
4131 assert!(c.limit.is_some());
4132 }
4133
4134 #[test]
4135 fn return_star_with_extra_items_errors() {
4136 assert!(parse_return("RETURN *, x AS y").is_err());
4140 }
4141
4142 #[test]
4143 fn with_items() {
4144 let c = parse_with("WITH a, b.name AS name").unwrap();
4145 assert!(!c.star);
4146 assert!(!c.distinct);
4147 assert_eq!(c.items.len(), 2);
4148 assert_eq!(c.items[0].expr, ReturnExpr::Var("a".to_string()));
4149 assert_eq!(c.items[1].alias.as_deref(), Some("name"));
4150 }
4151
4152 #[test]
4153 fn with_star() {
4154 let c = parse_with("WITH *").unwrap();
4155 assert!(c.star);
4156 assert!(c.items.is_empty());
4157 }
4158
4159 #[test]
4160 fn with_star_and_items() {
4161 let c = parse_with("WITH *, x AS y").unwrap();
4164 assert!(c.star);
4165 assert_eq!(c.items.len(), 1);
4166 assert_eq!(c.items[0].alias.as_deref(), Some("y"));
4167 }
4168
4169 #[test]
4170 fn with_distinct_order_skip_limit() {
4171 let c = parse_with("WITH DISTINCT a ORDER BY a SKIP 1 LIMIT 2").unwrap();
4172 assert!(c.distinct);
4173 assert!(c.order_by.is_some());
4174 assert_eq!(c.skip, Some(ReturnExpr::Lit(Literal::Int(1))));
4175 assert_eq!(c.limit, Some(ReturnExpr::Lit(Literal::Int(2))));
4176 }
4177
4178 #[test]
4179 fn with_where_compare() {
4180 let c = parse_with("WITH a WHERE a.x = 1").unwrap();
4181 let WithExpr::Compare(lhs, op, rhs) = c.where_clause.unwrap() else {
4182 panic!("expected WithExpr::Compare");
4183 };
4184 assert_eq!(
4185 lhs,
4186 ReturnExpr::Prop(PropAccess {
4187 var: "a".to_string(),
4188 prop: "x".to_string()
4189 })
4190 );
4191 assert_eq!(op, CompareOp::Eq);
4192 assert_eq!(rhs, ReturnExpr::Lit(Literal::Int(1)));
4193 }
4194
4195 #[test]
4196 fn with_where_and_or_not() {
4197 let c = parse_with("WITH a WHERE NOT (a.x = 1 AND a.y = 2)").unwrap();
4198 assert!(matches!(c.where_clause.unwrap(), WithExpr::Not(_)));
4199
4200 let c = parse_with("WITH a WHERE a.x = 1 OR a.y = 2").unwrap();
4201 assert!(matches!(c.where_clause.unwrap(), WithExpr::Or(_, _)));
4202 }
4203
4204 #[test]
4205 fn with_where_is_null() {
4206 let c = parse_with("WITH a WHERE a IS NULL").unwrap();
4207 assert!(matches!(c.where_clause.unwrap(), WithExpr::IsNull(_)));
4208 }
4209
4210 #[test]
4211 fn with_where_bare_expression() {
4212 let c = parse_with("WITH n WHERE n:Person").unwrap();
4216 assert!(matches!(c.where_clause.unwrap(), WithExpr::Bare(_)));
4217 }
4218
4219 #[test]
4220 fn with_where_xor_becomes_bare() {
4221 let c = parse_with("WITH a WHERE a.x XOR a.y").unwrap();
4224 assert!(matches!(c.where_clause.unwrap(), WithExpr::Bare(_)));
4225 }
4226
4227 #[test]
4228 fn unwind_basic() {
4229 let c = parse_unwind("UNWIND [1, 2, 3] AS x").unwrap();
4230 assert_eq!(c.var, "x");
4231 assert_eq!(
4232 c.source.0,
4233 ReturnExpr::ListLit(vec![
4234 ReturnExpr::Lit(Literal::Int(1)),
4235 ReturnExpr::Lit(Literal::Int(2)),
4236 ReturnExpr::Lit(Literal::Int(3)),
4237 ])
4238 );
4239 assert!(c.where_clause.is_none());
4240 assert!(c.with.is_none());
4241 }
4242
4243 #[test]
4244 fn set_prop() {
4245 let items = parse_set("SET n.name = 'x'").unwrap();
4246 assert_eq!(items.len(), 1);
4247 let SetItem::Prop(prop, value) = &items[0] else {
4248 panic!("expected SetItem::Prop");
4249 };
4250 assert_eq!(prop.var, "n");
4251 assert_eq!(prop.prop, "name");
4252 assert_eq!(*value, ReturnExpr::Lit(Literal::String("x".to_string())));
4253 }
4254
4255 #[test]
4256 fn set_labels() {
4257 let items = parse_set("SET n:A:B").unwrap();
4258 let SetItem::Labels(var, labels) = &items[0] else {
4259 panic!("expected SetItem::Labels");
4260 };
4261 assert_eq!(var, "n");
4262 assert_eq!(labels, &vec!["A".to_string(), "B".to_string()]);
4263 }
4264
4265 #[test]
4266 fn set_map_assign() {
4267 let items = parse_set("SET n = {a: 1}").unwrap();
4268 let SetItem::MapAssign { var, merge, .. } = &items[0] else {
4269 panic!("expected SetItem::MapAssign");
4270 };
4271 assert_eq!(var, "n");
4272 assert!(!merge);
4273
4274 let items = parse_set("SET n += {a: 1}").unwrap();
4275 let SetItem::MapAssign { merge, .. } = &items[0] else {
4276 panic!("expected SetItem::MapAssign");
4277 };
4278 assert!(merge);
4279 }
4280
4281 #[test]
4282 fn set_multiple_items() {
4283 assert_eq!(parse_set("SET n.a = 1, n.b = 2").unwrap().len(), 2);
4284 }
4285
4286 #[test]
4287 fn delete_items() {
4288 let d = parse_delete("DELETE n, r").unwrap();
4289 assert!(!d.detach);
4290 assert_eq!(d.items.len(), 2);
4291 }
4292
4293 #[test]
4294 fn detach_delete() {
4295 let d = parse_delete("DETACH DELETE n").unwrap();
4296 assert!(d.detach);
4297 }
4298
4299 #[test]
4300 fn remove_prop() {
4301 let items = parse_remove("REMOVE n.name").unwrap();
4302 let RemoveItem::Prop(prop) = &items[0] else {
4303 panic!("expected RemoveItem::Prop");
4304 };
4305 assert_eq!(prop.var, "n");
4306 assert_eq!(prop.prop, "name");
4307 }
4308
4309 #[test]
4310 fn remove_labels() {
4311 let items = parse_remove("REMOVE n:A:B").unwrap();
4312 let RemoveItem::Labels(var, labels) = &items[0] else {
4313 panic!("expected RemoveItem::Labels");
4314 };
4315 assert_eq!(var, "n");
4316 assert_eq!(labels, &vec!["A".to_string(), "B".to_string()]);
4317 }
4318
4319 #[test]
4320 fn create_single_pattern() {
4321 let patterns = parse_create("CREATE (a:Person)").unwrap();
4322 assert_eq!(patterns.len(), 1);
4323 assert_eq!(patterns[0].start.var.as_deref(), Some("a"));
4324 }
4325
4326 #[test]
4327 fn create_comma_patterns_stay_separate() {
4328 let patterns = parse_create("CREATE (a), (a)-->(b)").unwrap();
4331 assert_eq!(patterns.len(), 2);
4332 }
4333
4334 #[test]
4335 fn create_named_path_errors() {
4336 assert!(parse_create("CREATE p = (a)-->(b)").is_err());
4337 }
4338
4339 #[test]
4340 fn merge_single_hop() {
4341 let m = parse_merge("MERGE (a)-[:KNOWS]->(b)").unwrap();
4342 assert_eq!(m.pattern.hops.len(), 1);
4343 assert!(m.on_create.is_empty());
4344 assert!(m.on_match.is_empty());
4345 }
4346
4347 #[test]
4348 fn merge_multi_hop_errors() {
4349 assert!(parse_merge("MERGE (a)-->(b)-->(c)").is_err());
4350 }
4351
4352 #[test]
4353 fn merge_named_path_capture() {
4354 let m = parse_merge("MERGE p = (a)-->(b)").unwrap();
4355 assert_eq!(m.path_var.as_deref(), Some("p"));
4356 }
4357
4358 #[test]
4359 fn merge_on_create_on_match() {
4360 let m = parse_merge("MERGE (a) ON CREATE SET a.created = true ON MATCH SET a.seen = true")
4361 .unwrap();
4362 assert_eq!(m.on_create.len(), 1);
4363 assert_eq!(m.on_match.len(), 1);
4364 }
4365
4366 #[test]
4367 fn merge_duplicate_on_create_errors() {
4368 assert!(parse_merge("MERGE (a) ON CREATE SET a.x = 1 ON CREATE SET a.y = 2").is_err());
4369 }
4370
4371 #[test]
4372 fn merge_duplicate_on_match_errors() {
4373 assert!(parse_merge("MERGE (a) ON MATCH SET a.x = 1 ON MATCH SET a.y = 2").is_err());
4374 }
4375
4376 #[test]
4377 fn statement_match_return() {
4378 let s = parse_statement("MATCH (a) RETURN a").unwrap();
4379 let Statement::Match {
4380 clauses,
4381 tail,
4382 order_by,
4383 skip,
4384 limit,
4385 } = s
4386 else {
4387 panic!("expected Statement::Match");
4388 };
4389 assert_eq!(clauses.len(), 1);
4390 assert!(matches!(clauses[0], QueryClause::Match(_)));
4391 assert!(matches!(tail, Some(Tail::Return(_, false))));
4392 assert!(order_by.is_none());
4393 assert!(skip.is_none());
4394 assert!(limit.is_none());
4395 }
4396
4397 #[test]
4398 fn statement_return_star() {
4399 let s = parse_statement("MATCH (a) RETURN *").unwrap();
4400 let Statement::Match { tail, .. } = s else {
4401 panic!("expected Statement::Match");
4402 };
4403 assert!(matches!(tail, Some(Tail::ReturnStar(false))));
4404 }
4405
4406 #[test]
4407 fn statement_order_by_skip_limit_on_bare_return() {
4408 let s = parse_statement("MATCH (a) RETURN a ORDER BY a SKIP 1 LIMIT 2").unwrap();
4409 let Statement::Match {
4410 order_by,
4411 skip,
4412 limit,
4413 ..
4414 } = s
4415 else {
4416 panic!("expected Statement::Match");
4417 };
4418 assert!(order_by.is_some());
4419 assert_eq!(skip, Some(Box::new(ReturnExpr::Lit(Literal::Int(1)))));
4420 assert_eq!(limit, Some(Box::new(ReturnExpr::Lit(Literal::Int(2)))));
4421 }
4422
4423 #[test]
4424 fn statement_multiple_reading_clauses() {
4425 let s = parse_statement("MATCH (a) UNWIND [1,2] AS x RETURN a, x").unwrap();
4426 let Statement::Match { clauses, .. } = s else {
4427 panic!("expected Statement::Match");
4428 };
4429 assert_eq!(clauses.len(), 2);
4430 assert!(matches!(clauses[0], QueryClause::Match(_)));
4431 assert!(matches!(clauses[1], QueryClause::Unwind(_)));
4432 }
4433
4434 #[test]
4435 fn statement_set_becomes_tail_with_return_tail() {
4436 let s = parse_statement("MATCH (n) SET n.x = 1 RETURN n").unwrap();
4437 let Statement::Match { clauses, tail, .. } = s else {
4438 panic!("expected Statement::Match");
4439 };
4440 assert_eq!(clauses.len(), 1);
4441 let Some(Tail::Set(items, Some(ret))) = tail else {
4442 panic!("expected Tail::Set with a ReturnTail");
4443 };
4444 assert_eq!(items.len(), 1);
4445 assert_eq!(ret.items.len(), 1);
4446 }
4447
4448 #[test]
4449 fn statement_set_without_trailing_return() {
4450 let s = parse_statement("MATCH (n) SET n.x = 1").unwrap();
4451 let Statement::Match { tail, .. } = s else {
4452 panic!("expected Statement::Match");
4453 };
4454 assert!(matches!(tail, Some(Tail::Set(_, None))));
4455 }
4456
4457 #[test]
4458 fn statement_detach_delete_tail() {
4459 let s = parse_statement("MATCH (n) DETACH DELETE n").unwrap();
4460 let Statement::Match { tail, .. } = s else {
4461 panic!("expected Statement::Match");
4462 };
4463 assert!(matches!(tail, Some(Tail::DetachDelete(_, None))));
4464 }
4465
4466 #[test]
4467 fn statement_two_updating_clauses_last_becomes_tail() {
4468 let s = parse_statement("MATCH (n) SET n.x = 1 DELETE n RETURN count(n)").unwrap();
4470 let Statement::Match { clauses, tail, .. } = s else {
4471 panic!("expected Statement::Match");
4472 };
4473 assert_eq!(clauses.len(), 2);
4474 assert!(matches!(clauses[1], QueryClause::Set(_)));
4475 assert!(matches!(tail, Some(Tail::Delete(_, Some(_)))));
4476 }
4477
4478 #[test]
4479 fn statement_bare_merge_no_tail() {
4480 let s = parse_statement("MERGE (a)").unwrap();
4483 let Statement::Match { clauses, tail, .. } = s else {
4484 panic!("expected Statement::Match");
4485 };
4486 assert!(matches!(clauses[0], QueryClause::Merge(_)));
4487 assert!(tail.is_none());
4488 }
4489
4490 #[test]
4491 fn statement_merge_with_trailing_return() {
4492 let s = parse_statement("MERGE (a) RETURN a ORDER BY a").unwrap();
4497 let Statement::Match {
4498 clauses,
4499 tail,
4500 order_by,
4501 ..
4502 } = s
4503 else {
4504 panic!("expected Statement::Match");
4505 };
4506 assert!(matches!(clauses[0], QueryClause::Merge(_)));
4507 assert!(matches!(tail, Some(Tail::Return(_, false))));
4508 assert!(order_by.is_some());
4509 }
4510
4511 #[test]
4512 fn statement_bare_match_without_tail_errors() {
4513 assert!(parse_statement("MATCH (n)").is_err());
4516 }
4517
4518 #[test]
4519 fn statement_mutating_tail_order_by_skip_limit_apply_at_statement_level() {
4520 let s =
4527 parse_statement("MATCH (n) SET n.x = 1 RETURN n ORDER BY n.x SKIP 1 LIMIT 2").unwrap();
4528 let Statement::Match {
4529 tail,
4530 order_by,
4531 skip,
4532 limit,
4533 ..
4534 } = s
4535 else {
4536 panic!("expected Statement::Match");
4537 };
4538 assert!(matches!(tail, Some(Tail::Set(_, Some(_)))));
4539 assert!(order_by.is_some());
4540 assert_eq!(skip, Some(Box::new(ReturnExpr::Lit(Literal::Int(1)))));
4541 assert_eq!(limit, Some(Box::new(ReturnExpr::Lit(Literal::Int(2)))));
4542 }
4543
4544 #[test]
4545 fn statement_mutating_tail_return_star_errors() {
4546 assert!(parse_statement("MATCH (n) SET n.x = 1 RETURN *").is_err());
4547 }
4548
4549 #[test]
4550 fn statement_create_tail() {
4551 let s = parse_statement("CREATE (a) RETURN a").unwrap();
4552 let Statement::Match { tail, .. } = s else {
4553 panic!("expected Statement::Match");
4554 };
4555 assert!(matches!(tail, Some(Tail::Create(_, Some(_)))));
4556 }
4557
4558 #[test]
4559 fn statement_bare_create_is_not_wrapped_in_match() {
4560 let s = parse_antlr("CREATE (a);").unwrap();
4566 assert!(matches!(s, Statement::Create(_)));
4567 }
4568
4569 #[test]
4570 fn statement_remove_tail() {
4571 let s = parse_statement("MATCH (n) REMOVE n.x").unwrap();
4572 let Statement::Match { tail, .. } = s else {
4573 panic!("expected Statement::Match");
4574 };
4575 assert!(matches!(tail, Some(Tail::Remove(_, None))));
4576 }
4577
4578 #[test]
4579 fn multi_part_with_attaches_to_preceding_match() {
4580 let s = parse_multi_part_statement("MATCH (a:A) WITH a MATCH (b:B) RETURN a, b").unwrap();
4581 let Statement::Match { clauses, tail, .. } = s else {
4582 panic!("expected Statement::Match");
4583 };
4584 assert_eq!(clauses.len(), 2);
4585 let QueryClause::Match(first) = &clauses[0] else {
4586 panic!("expected first clause to be Match");
4587 };
4588 assert!(first.with.is_some());
4589 assert!(matches!(clauses[1], QueryClause::Match(_)));
4590 assert!(matches!(tail, Some(Tail::Return(_, false))));
4591 }
4592
4593 #[test]
4594 fn multi_part_chained_with_second_one_standalone() {
4595 let s = parse_multi_part_statement("MATCH (a:A) WITH a.num AS x WITH x % 3 AS x RETURN x")
4600 .unwrap();
4601 let Statement::Match { clauses, .. } = s else {
4602 panic!("expected Statement::Match");
4603 };
4604 assert_eq!(clauses.len(), 2);
4605 let QueryClause::Match(first) = &clauses[0] else {
4606 panic!("expected first clause to be Match");
4607 };
4608 assert!(first.with.is_some());
4609 assert!(matches!(clauses[1], QueryClause::With(_)));
4610 }
4611
4612 #[test]
4613 fn multi_part_set_then_with_stays_separate_entries() {
4614 let s = parse_multi_part_statement(
4618 "MATCH (n:N) WITH n, n.num AS num DELETE n WITH num WHERE num % 2 = 0 RETURN num",
4619 )
4620 .unwrap();
4621 let Statement::Match { clauses, tail, .. } = s else {
4622 panic!("expected Statement::Match");
4623 };
4624 assert_eq!(clauses.len(), 3);
4625 assert!(matches!(clauses[0], QueryClause::Match(_)));
4626 assert!(matches!(clauses[1], QueryClause::Delete { .. }));
4627 assert!(matches!(clauses[2], QueryClause::With(_)));
4628 assert!(matches!(tail, Some(Tail::Return(_, false))));
4629 }
4630
4631 #[test]
4632 fn multi_part_create_with_star_create_create_tail() {
4633 let s =
4634 parse_multi_part_statement("CREATE (a) WITH a WITH * CREATE (b) CREATE (a)<-[:T]-(b)")
4635 .unwrap();
4636 let Statement::Match { clauses, tail, .. } = s else {
4637 panic!("expected Statement::Match");
4638 };
4639 assert_eq!(clauses.len(), 4);
4643 assert!(matches!(clauses[0], QueryClause::Create(_)));
4644 assert!(matches!(clauses[1], QueryClause::With(_)));
4645 assert!(matches!(clauses[2], QueryClause::With(_)));
4646 assert!(matches!(clauses[3], QueryClause::Create(_)));
4647 assert!(matches!(tail, Some(Tail::Create(_, None))));
4648 }
4649
4650 #[test]
4651 fn multi_part_merge_with_attaches() {
4652 let s = parse_multi_part_statement("MERGE (a:A) WITH a MATCH (b:B) RETURN a, b").unwrap();
4653 let Statement::Match { clauses, .. } = s else {
4654 panic!("expected Statement::Match");
4655 };
4656 assert_eq!(clauses.len(), 2);
4657 let QueryClause::Merge(m) = &clauses[0] else {
4658 panic!("expected first clause to be Merge");
4659 };
4660 assert!(m.with.is_some());
4661 }
4662
4663 #[test]
4664 fn multi_part_trailing_bare_create_becomes_tail_not_top_level_statement() {
4665 let s = parse_multi_part_statement("MATCH (a) WITH a CREATE (b)").unwrap();
4672 let Statement::Match { clauses, tail, .. } = s else {
4673 panic!("expected Statement::Match");
4674 };
4675 assert_eq!(clauses.len(), 1);
4676 assert!(matches!(clauses[0], QueryClause::Match(_)));
4677 assert!(matches!(tail, Some(Tail::Create(_, None))));
4678 }
4679
4680 #[test]
4681 fn parse_antlr_no_union_passes_through() {
4682 let s = parse_antlr("MATCH (a) RETURN a;").unwrap();
4683 assert!(matches!(s, Statement::Match { .. }));
4684 }
4685
4686 #[test]
4687 fn parse_antlr_union() {
4688 let s = parse_antlr("MATCH (a) RETURN a UNION MATCH (b) RETURN b;").unwrap();
4689 let Statement::Union { parts, all } = s else {
4690 panic!("expected Statement::Union");
4691 };
4692 assert_eq!(parts.len(), 2);
4693 assert!(!all);
4694 }
4695
4696 #[test]
4697 fn parse_antlr_union_all() {
4698 let s = parse_antlr("MATCH (a) RETURN a UNION ALL MATCH (b) RETURN b;").unwrap();
4699 let Statement::Union { parts, all } = s else {
4700 panic!("expected Statement::Union");
4701 };
4702 assert_eq!(parts.len(), 2);
4703 assert!(all);
4704 }
4705
4706 #[test]
4707 fn parse_antlr_union_three_parts() {
4708 let s =
4709 parse_antlr("MATCH (a) RETURN a UNION MATCH (b) RETURN b UNION MATCH (c) RETURN c;")
4710 .unwrap();
4711 let Statement::Union { parts, .. } = s else {
4712 panic!("expected Statement::Union");
4713 };
4714 assert_eq!(parts.len(), 3);
4715 }
4716
4717 #[test]
4718 fn parse_antlr_mixed_union_and_union_all_errors() {
4719 let err = parse_antlr(
4720 "MATCH (a) RETURN a UNION MATCH (b) RETURN b UNION ALL MATCH (c) RETURN c;",
4721 )
4722 .unwrap_err();
4723 assert!(matches!(err, QueryError::Syntax(_)));
4724 }
4725
4726 #[test]
4727 fn parse_antlr_standalone_call() {
4728 let stmt = parse_antlr("CALL db.labels() YIELD label").unwrap();
4729 let Statement::StandaloneCall(call) = stmt else {
4730 panic!("expected a Statement::StandaloneCall, got {stmt:?}");
4731 };
4732 assert_eq!(call.name, "db.labels");
4733 assert_eq!(call.args, Some(vec![]));
4734 assert!(matches!(
4735 call.yield_items,
4736 Some(CallYield::Items(items, None)) if items == vec![("label".to_string(), None)]
4737 ));
4738 }
4739
4740 #[test]
4741 fn parse_antlr_syntax_error() {
4742 assert!(parse_antlr("MATCH (a RETURN a;").is_err());
4743 }
4744
4745 #[test]
4746 fn parse_antlr_many_basic() {
4747 let stmts = parse_antlr_many("CREATE (a); CREATE (b); MATCH (n) RETURN n").unwrap();
4748 assert_eq!(stmts.len(), 3);
4749 assert!(matches!(stmts[0], Statement::Create(_)));
4753 assert!(matches!(stmts[2], Statement::Match { .. }));
4754 }
4755
4756 #[test]
4757 fn parse_antlr_many_single_statement() {
4758 let stmts = parse_antlr_many("RETURN 1").unwrap();
4759 assert_eq!(stmts.len(), 1);
4760 }
4761
4762 #[test]
4763 fn parse_antlr_many_strips_single_trailing_semicolon() {
4764 let stmts = parse_antlr_many("CREATE (a);").unwrap();
4765 assert_eq!(stmts.len(), 1);
4766 }
4767
4768 #[test]
4769 fn parse_antlr_many_semicolon_inside_string_literal_not_a_separator() {
4770 let stmts = parse_antlr_many("RETURN ';'").unwrap();
4771 assert_eq!(stmts.len(), 1);
4772 }
4773
4774 #[test]
4775 fn split_statements_respects_all_three_quote_forms() {
4776 assert_eq!(
4779 split_statements("RETURN ';'; RETURN 1"),
4780 vec!["RETURN ';'", " RETURN 1"]
4781 );
4782 assert_eq!(
4783 split_statements(r#"RETURN ";"; RETURN 1"#),
4784 vec![r#"RETURN ";""#, " RETURN 1"]
4785 );
4786 assert_eq!(
4787 split_statements("MATCH (`a;b`) RETURN 1; RETURN 2"),
4788 vec!["MATCH (`a;b`) RETURN 1", " RETURN 2"]
4789 );
4790 }
4791
4792 #[test]
4793 fn split_statements_handles_escaped_quotes_inside_a_literal() {
4794 assert_eq!(
4798 split_statements(r"RETURN 'it\'s; a test'; RETURN 1"),
4799 vec![r"RETURN 'it\'s; a test'", " RETURN 1"]
4800 );
4801 }
4802
4803 #[test]
4804 fn split_statements_backtick_literal_has_no_escapes() {
4805 assert_eq!(
4810 split_statements(r"MATCH (`a\`) RETURN 1; RETURN 2"),
4811 vec![r"MATCH (`a\`) RETURN 1", " RETURN 2"]
4812 );
4813 }
4814
4815 #[test]
4816 fn parse_antlr_create_index() {
4817 let s = parse_antlr("CREATE INDEX ON :Person(name);").unwrap();
4818 let Statement::CreateIndex {
4819 label,
4820 prop,
4821 unique,
4822 } = s
4823 else {
4824 panic!("expected Statement::CreateIndex");
4825 };
4826 assert_eq!(label, "Person");
4827 assert_eq!(prop, "name");
4828 assert!(!unique);
4829 }
4830
4831 #[test]
4832 fn parse_antlr_create_index_unique() {
4833 let s = parse_antlr("CREATE INDEX ON :Person(name) UNIQUE;").unwrap();
4834 let Statement::CreateIndex { unique, .. } = s else {
4835 panic!("expected Statement::CreateIndex");
4836 };
4837 assert!(unique);
4838 }
4839
4840 #[test]
4841 fn parse_antlr_explain_match() {
4842 let s = parse_antlr("EXPLAIN MATCH (a) RETURN a;").unwrap();
4843 let Statement::Explain(inner) = s else {
4844 panic!("expected Statement::Explain");
4845 };
4846 assert!(matches!(*inner, Statement::Match { .. }));
4847 }
4848
4849 #[test]
4850 fn parse_antlr_explain_create_index() {
4851 let s = parse_antlr("EXPLAIN CREATE INDEX ON :Person(name);").unwrap();
4852 let Statement::Explain(inner) = s else {
4853 panic!("expected Statement::Explain");
4854 };
4855 assert!(matches!(*inner, Statement::CreateIndex { .. }));
4856 }
4857
4858 #[test]
4859 fn parse_antlr_index_still_usable_as_property_name() {
4860 let s = parse_antlr("MATCH (a) RETURN a.index;").unwrap();
4865 assert!(matches!(s, Statement::Match { .. }));
4866 }
4867}