Skip to main content

linkrs_query/parser/parsing/dml_parser/
merge.rs

1use super::*;
2
3impl DmlParser {
4    /// Parse the MERGE statement
5    pub fn parse_merge_statement(&mut self, ctx: &mut ParseContext) -> Result<Stmt, ParseError> {
6        let start_span = ctx.current_span();
7        ctx.expect_token(TokenKind::Merge)?;
8
9        // Support MERGE EDGE/VERTEX ON ... SET ... WHERE ... (UPSERT-style syntax)
10        if ctx.check_token(TokenKind::Edge) || ctx.check_token(TokenKind::Vertex) {
11            ctx.set_upsert_mode(true);
12            let result = self.parse_update_after_token(ctx, start_span);
13            ctx.set_upsert_mode(false);
14            return result;
15        }
16
17        let pattern = TraversalParser::new().parse_pattern(ctx)?;
18
19        // Consume the shared `ON` token once, then branch on CREATE vs MATCH.
20        // Both clauses may appear (each at most once, in any order), mirroring
21        // Cypher: `MERGE ... ON CREATE SET ... ON MATCH SET ...`.
22        let (mut on_create, mut on_match) = if ctx.match_token(TokenKind::On) {
23            if ctx.match_token(TokenKind::Create) {
24                (Some(Self::parse_merge_set_clause(ctx)?), None)
25            } else if ctx.match_token(TokenKind::Match) {
26                (None, Some(Self::parse_merge_set_clause(ctx)?))
27            } else {
28                return Err(ParseError::new(
29                    ParseErrorKind::SyntaxError,
30                    "Expected CREATE or MATCH after ON in MERGE statement".to_string(),
31                    ctx.current_position(),
32                ));
33            }
34        } else {
35            (None, None)
36        };
37        if ctx.match_token(TokenKind::On) {
38            if ctx.match_token(TokenKind::Create) {
39                if on_create.is_some() {
40                    return Err(ParseError::new(
41                        ParseErrorKind::SyntaxError,
42                        "Duplicate ON CREATE clause in MERGE statement".to_string(),
43                        ctx.current_position(),
44                    ));
45                }
46                on_create = Some(Self::parse_merge_set_clause(ctx)?);
47            } else if ctx.match_token(TokenKind::Match) {
48                if on_match.is_some() {
49                    return Err(ParseError::new(
50                        ParseErrorKind::SyntaxError,
51                        "Duplicate ON MATCH clause in MERGE statement".to_string(),
52                        ctx.current_position(),
53                    ));
54                }
55                on_match = Some(Self::parse_merge_set_clause(ctx)?);
56            } else {
57                return Err(ParseError::new(
58                    ParseErrorKind::SyntaxError,
59                    "Expected CREATE or MATCH after ON in MERGE statement".to_string(),
60                    ctx.current_position(),
61                ));
62            }
63        }
64
65        // A bare `SET ...` clause following the pattern (e.g.
66        // `MERGE (v:Tag {..}) SET v.prop = ..`) applies to both the matched
67        // and the created branch, mirroring the post-merge update semantics.
68        // Its assignments are merged into any ON CREATE / ON MATCH clause
69        // already present so no parsed assignment is silently discarded.
70        if ctx.match_token(TokenKind::Set) {
71            let bare = ClauseParser::new().parse_set_clause(ctx)?;
72            if !bare.assignments.is_empty() {
73                if let Some(clause) = on_create.as_mut() {
74                    clause.assignments.extend(bare.assignments.iter().cloned());
75                } else {
76                    on_create = Some(bare.clone());
77                }
78                if let Some(clause) = on_match.as_mut() {
79                    clause.assignments.extend(bare.assignments);
80                } else {
81                    on_match = Some(bare);
82                }
83            }
84        }
85
86        Ok(Stmt::Merge(MergeStmt {
87            span: start_span,
88            pattern,
89            on_create,
90            on_match,
91        }))
92    }
93
94    /// Parse the SET clause following ON CREATE / ON MATCH in a MERGE statement.
95    ///
96    /// Consumes the optional `SET` keyword before delegating to the clause
97    /// parser, matching how `parse_update_after_token` handles SET clauses.
98    fn parse_merge_set_clause(ctx: &mut ParseContext) -> Result<SetClause, ParseError> {
99        if ctx.match_token(TokenKind::Set) {
100            ClauseParser::new().parse_set_clause(ctx)
101        } else {
102            Ok(SetClause {
103                span: ctx.current_span(),
104                assignments: Vec::new(),
105            })
106        }
107    }
108}