Skip to main content

linkrs_query/parser/parsing/dml_parser/
insert.rs

1use super::*;
2
3impl DmlParser {
4    /// Analyzing the INSERT statement
5    pub fn parse_insert_statement(&mut self, ctx: &mut ParseContext) -> Result<Stmt, ParseError> {
6        let start_span = ctx.current_span();
7        ctx.expect_token(TokenKind::Insert)?;
8
9        // Check whether it is VERTEX or EDGE.
10        let target = if ctx.match_token(TokenKind::Vertex) {
11            self.parse_insert_vertex(ctx, start_span)?
12        } else if ctx.match_token(TokenKind::Edge) {
13            self.parse_insert_edge(ctx, start_span)?
14        } else {
15            return Err(ParseError::new(
16                crate::parser::core::error::ParseErrorKind::UnexpectedToken,
17                "Expected VERTEX or EDGE after INSERT".to_string(),
18                ctx.current_position(),
19            ));
20        };
21
22        Ok(target)
23    }
24
25    /// Analysis of INSERT VERTEX
26    fn parse_insert_vertex(
27        &mut self,
28        ctx: &mut ParseContext,
29        start_span: crate::parser::ast::types::Span,
30    ) -> Result<Stmt, ParseError> {
31        use crate::parser::ast::stmt::{InsertStmt, InsertTarget, TagInsertSpec, VertexRow};
32
33        // Analysis of the IF NOT EXISTS clause (optional)
34        let mut if_not_exists = false;
35        if ctx.match_token(TokenKind::If) {
36            ctx.expect_token(TokenKind::Not)?;
37            ctx.expect_token(TokenKind::Exists)?;
38            if_not_exists = true;
39        }
40
41        // Analyzing the TAG list
42        // Two grammatical styles are supported:
43        // 1. ON tag1, tag2 (optional)
44        // 2. tag_name(prop1, prop2), tag2_name(prop3, prop4) (NebulaGraph standard syntax)
45        let mut tags = vec![];
46        if ctx.match_token(TokenKind::On) {
47            // Syntax: ON tag1, tag2
48            loop {
49                let tag_name = ctx.expect_identifier()?;
50                tags.push(TagInsertSpec {
51                    tag_name,
52                    prop_names: vec![],
53                    is_default_props: true,
54                });
55                if !ctx.match_token(TokenKind::Comma) {
56                    break;
57                }
58            }
59        } else {
60            // Check whether it is the NebulaGraph standard syntax: tag_name(prop1, prop2), tag2_name(prop3, prop4)
61            if ctx.is_identifier_token() {
62                loop {
63                    let tag_name = ctx.expect_identifier()?;
64                    let mut prop_names = vec![];
65                    let mut is_default_props = true;
66
67                    // Check whether there is a list of attribute names.
68                    if ctx.match_token(TokenKind::LParen) {
69                        // Support empty property list: tag() is equivalent
70                        // to the ON shorthand (default properties).
71                        if !ctx.check_token(TokenKind::RParen) {
72                            loop {
73                                let prop_name = ctx.expect_identifier()?;
74                                prop_names.push(prop_name);
75                                if !ctx.match_token(TokenKind::Comma) {
76                                    break;
77                                }
78                            }
79                            is_default_props = false;
80                        }
81                        ctx.expect_token(TokenKind::RParen)?;
82                    }
83
84                    tags.push(TagInsertSpec {
85                        tag_name,
86                        prop_names,
87                        is_default_props,
88                    });
89
90                    // Check to see if there are any additional tags.
91                    if !ctx.match_token(TokenKind::Comma) {
92                        break;
93                    }
94                }
95            }
96        }
97
98        // A vertex carries exactly one tag: multi-tag INSERT is rejected here.
99        if tags.len() != 1 {
100            return Err(ParseError::new(
101                crate::parser::core::error::ParseErrorKind::UnexpectedToken,
102                "INSERT VERTEX accepts exactly one tag".to_string(),
103                ctx.current_position(),
104            ));
105        }
106
107        // Analysis of the VALUES keyword
108        if ctx.check_token(TokenKind::Values) {
109            ctx.next_token(); // Consumption values
110        }
111
112        // Analysis of the list of inserted values
113        let mut values = vec![];
114        loop {
115            // Analyzing the video…
116            let vid = self.parse_expression(ctx)?;
117
118            // Parse the single attribute list `: (...)`. A second value
119            // group would address another tag and is rejected.
120            let mut props = vec![];
121            if ctx.match_token(TokenKind::Colon) {
122                ctx.expect_token(TokenKind::LParen)?;
123                loop {
124                    let value = self.parse_expression(ctx)?;
125                    props.push(value);
126                    if !ctx.match_token(TokenKind::Comma) {
127                        break;
128                    }
129                }
130                ctx.expect_token(TokenKind::RParen)?;
131                if ctx.check_token(TokenKind::Colon) {
132                    return Err(ParseError::new(
133                        crate::parser::core::error::ParseErrorKind::UnexpectedToken,
134                        "INSERT VERTEX accepts exactly one tag value group".to_string(),
135                        ctx.current_position(),
136                    ));
137                }
138            }
139
140            values.push(VertexRow { vid, values: props });
141
142            if !ctx.match_token(TokenKind::Comma) {
143                break;
144            }
145        }
146
147        let span = ctx.span_since(start_span.start);
148
149        let tag = tags.pop().ok_or_else(|| {
150            ParseError::new(
151                crate::parser::core::error::ParseErrorKind::UnexpectedToken,
152                "INSERT VERTEX requires exactly one tag".to_string(),
153                ctx.current_position(),
154            )
155        })?;
156        Ok(Stmt::Insert(InsertStmt {
157            span,
158            target: InsertTarget::Vertices { tag, values },
159            if_not_exists,
160        }))
161    }
162
163    /// Analysis of INSERT EDGE
164    fn parse_insert_edge(
165        &mut self,
166        ctx: &mut ParseContext,
167        start_span: crate::parser::ast::types::Span,
168    ) -> Result<Stmt, ParseError> {
169        use crate::parser::ast::stmt::{InsertStmt, InsertTarget};
170
171        let mut if_not_exists = false;
172        if ctx.match_token(TokenKind::If) {
173            ctx.expect_token(TokenKind::Not)?;
174            ctx.expect_token(TokenKind::Exists)?;
175            if_not_exists = true;
176        }
177
178        // Analyzing the list of edge types and attribute names
179        let edge_name = ctx.expect_identifier()?;
180        let mut prop_names = vec![];
181
182        if ctx.match_token(TokenKind::LParen) {
183            // Support empty property list: EDGE_NAME()
184            if !ctx.check_token(TokenKind::RParen) {
185                loop {
186                    let prop_name = ctx.expect_identifier()?;
187                    prop_names.push(prop_name);
188                    if !ctx.match_token(TokenKind::Comma) {
189                        break;
190                    }
191                }
192            }
193            ctx.expect_token(TokenKind::RParen)?;
194        }
195
196        if ctx.check_token(TokenKind::Values) {
197            ctx.next_token();
198        }
199
200        // `->` is an edge arrow inside this statement, not the JSON access
201        // operator, so expression parsing must not treat `-> 'key'` as a
202        // postfix JSON get.
203        ctx.with_edge_syntax_mode(|ctx| {
204            let mut edges = vec![];
205            loop {
206                let src = self.parse_expression(ctx)?;
207                ctx.expect_token(TokenKind::Arrow)?;
208                let dst = self.parse_expression(ctx)?;
209
210                let rank = if ctx.match_token(TokenKind::At) {
211                    Some(self.parse_expression(ctx)?)
212                } else {
213                    None
214                };
215
216                let mut values = vec![];
217                if ctx.match_token(TokenKind::Colon) {
218                    ctx.expect_token(TokenKind::LParen)?;
219                    // Support empty value list: :()
220                    if !ctx.check_token(TokenKind::RParen) {
221                        loop {
222                            let value = self.parse_expression(ctx)?;
223                            values.push(value);
224                            if !ctx.match_token(TokenKind::Comma) {
225                                break;
226                            }
227                        }
228                    }
229                    ctx.expect_token(TokenKind::RParen)?;
230                }
231
232                edges.push((src, dst, rank, values));
233
234                if !ctx.match_token(TokenKind::Comma) {
235                    break;
236                }
237            }
238
239            let span = ctx.span_since(start_span.start);
240
241            Ok(Stmt::Insert(InsertStmt {
242                span,
243                target: InsertTarget::Edge {
244                    edge_name,
245                    prop_names,
246                    edges,
247                },
248                if_not_exists,
249            }))
250        })
251    }
252}