Skip to main content

linkrs_query/parser/parsing/ddl_parser/
alter_parser.rs

1use crate::parser::ast::stmt::*;
2
3use crate::parser::core::error::{ParseError, ParseErrorKind};
4use crate::parser::parsing::parse_context::ParseContext;
5use crate::parser::TokenKind;
6
7use super::DdlParser;
8
9type AlterOpsResult = (
10    Vec<linkrs_core::types::PropertyDef>,
11    Vec<String>,
12    Vec<PropertyChange>,
13    Option<String>,
14);
15
16impl DdlParser {
17    pub fn parse_alter_statement(&mut self, ctx: &mut ParseContext) -> Result<Stmt, ParseError> {
18        let start_span = ctx.current_span();
19        ctx.expect_token(TokenKind::Alter)?;
20
21        if ctx.check_keyword("FULLTEXT") {
22            return crate::parser::parsing::fulltext_parser::parse_alter_fulltext_index_after_alter(
23                ctx,
24            );
25        }
26
27        if ctx.check_token(TokenKind::User) {
28            return crate::parser::parsing::user_parser::UserParser::new()
29                .parse_alter_user_internal(ctx, start_span);
30        }
31
32        if ctx.match_token(TokenKind::Sequence) {
33            let name = ctx.expect_identifier()?;
34            let mut increment = None;
35            let mut min_value = None;
36            let mut max_value = None;
37            let mut cycle = None;
38
39            loop {
40                if ctx.check_keyword("INCREMENT") && increment.is_none() {
41                    ctx.next_token();
42                    ctx.expect_token(TokenKind::Assign)?;
43                    increment = Some(ctx.expect_integer_literal()?);
44                } else if ctx.check_keyword("MINVALUE") && min_value.is_none() {
45                    ctx.next_token();
46                    ctx.expect_token(TokenKind::Assign)?;
47                    min_value = Some(ctx.expect_integer_literal()?);
48                } else if ctx.check_keyword("MAXVALUE") && max_value.is_none() {
49                    ctx.next_token();
50                    ctx.expect_token(TokenKind::Assign)?;
51                    max_value = Some(ctx.expect_integer_literal()?);
52                } else if ctx.match_token(TokenKind::Cycle) {
53                    cycle = Some(true);
54                } else if ctx.check_keyword("NOCYCLE") {
55                    ctx.next_token();
56                    cycle = Some(false);
57                } else {
58                    break;
59                }
60            }
61
62            let span = ctx.span_since(start_span.start);
63            return Ok(Stmt::Alter(AlterStmt {
64                span,
65                target: AlterTarget::Sequence {
66                    name,
67                    increment,
68                    min_value,
69                    max_value,
70                    cycle,
71                },
72            }));
73        }
74
75        let (is_tag, name) = if ctx.match_token(TokenKind::Tag) {
76            (true, ctx.expect_identifier()?)
77        } else if ctx.match_token(TokenKind::Edge) {
78            (false, ctx.expect_identifier()?)
79        } else {
80            return Err(ParseError::new(
81                ParseErrorKind::UnexpectedToken,
82                "Expected TAG, EDGE, SEQUENCE, or USER".to_string(),
83                ctx.current_position(),
84            ));
85        };
86
87        if ctx.match_token(TokenKind::Rename) {
88            ctx.expect_token(TokenKind::To)?;
89            let new_name = ctx.expect_identifier()?;
90            let span = ctx.span_since(start_span.start);
91            return Ok(Stmt::Alter(AlterStmt {
92                span,
93                target: if is_tag {
94                    AlterTarget::RenameTag {
95                        old_name: name,
96                        new_name,
97                    }
98                } else {
99                    AlterTarget::RenameEdge {
100                        old_name: name,
101                        new_name,
102                    }
103                },
104            }));
105        }
106
107        // Edge endpoint-constraint changes:
108        // `ALTER EDGE <name> ADD FROM <src> TO <dst>` /
109        // `ALTER EDGE <name> DROP FROM <src> TO <dst>`.
110        if !is_tag
111            && matches!(ctx.current_token().kind, TokenKind::Add | TokenKind::Drop)
112            && matches!(ctx.peek_token().kind, TokenKind::From)
113        {
114            let is_add = matches!(ctx.current_token().kind, TokenKind::Add);
115            ctx.next_token(); // ADD | DROP
116            ctx.next_token(); // FROM
117            let src_tag = ctx.expect_identifier()?;
118            ctx.expect_token(TokenKind::To)?;
119            let dst_tag = ctx.expect_identifier()?;
120            let span = ctx.span_since(start_span.start);
121            let target = if is_add {
122                AlterTarget::AddFrom {
123                    edge_name: name,
124                    src_tag,
125                    dst_tag,
126                }
127            } else {
128                AlterTarget::DropFrom {
129                    edge_name: name,
130                    src_tag,
131                    dst_tag,
132                }
133            };
134            return Ok(Stmt::Alter(AlterStmt { span, target }));
135        }
136
137        let (additions, deletions, changes, new_comment) = self.parse_alter_operations(ctx)?;
138        if additions.is_empty()
139            && deletions.is_empty()
140            && changes.is_empty()
141            && new_comment.is_none()
142        {
143            return Err(ParseError::new(
144                ParseErrorKind::SyntaxError,
145                "ALTER TAG/EDGE requires ADD, DROP, CHANGE, or COMMENT".to_string(),
146                ctx.current_position(),
147            ));
148        }
149
150        let span = ctx.span_since(start_span.start);
151        if is_tag {
152            Ok(Stmt::Alter(AlterStmt {
153                span,
154                target: AlterTarget::Tag {
155                    tag_name: name,
156                    additions,
157                    deletions,
158                    changes,
159                    new_comment,
160                },
161            }))
162        } else {
163            Ok(Stmt::Alter(AlterStmt {
164                span,
165                target: AlterTarget::Edge {
166                    edge_name: name,
167                    additions,
168                    deletions,
169                    changes,
170                    new_comment,
171                },
172            }))
173        }
174    }
175
176    fn parse_alter_operations(
177        &mut self,
178        ctx: &mut ParseContext,
179    ) -> Result<AlterOpsResult, ParseError> {
180        let mut additions = Vec::new();
181        let mut deletions = Vec::new();
182        let mut changes = Vec::new();
183        let mut new_comment: Option<String> = None;
184
185        loop {
186            if ctx.match_token(TokenKind::Add) {
187                additions.extend(self.parse_property_defs(ctx)?);
188            } else if ctx.match_token(TokenKind::Drop) {
189                ctx.expect_token(TokenKind::LParen)?;
190                loop {
191                    deletions.push(ctx.expect_identifier()?);
192                    if !ctx.match_token(TokenKind::Comma) {
193                        break;
194                    }
195                }
196                ctx.expect_token(TokenKind::RParen)?;
197            } else if ctx.match_token(TokenKind::Change) {
198                ctx.expect_token(TokenKind::LParen)?;
199                loop {
200                    let old_name = ctx.expect_identifier()?;
201                    let new_name = ctx.expect_identifier()?;
202                    ctx.expect_token(TokenKind::Colon)?;
203                    let data_type = self.parse_data_type(ctx)?;
204                    changes.push(PropertyChange {
205                        old_name,
206                        new_name,
207                        data_type,
208                    });
209                    if !ctx.match_token(TokenKind::Comma) {
210                        break;
211                    }
212                }
213                ctx.expect_token(TokenKind::RParen)?;
214            } else if ctx.match_token(TokenKind::Comment) {
215                if new_comment.is_some() {
216                    return Err(ParseError::new(
217                        ParseErrorKind::SyntaxError,
218                        "duplicate COMMENT clause".to_string(),
219                        ctx.current_position(),
220                    ));
221                }
222                ctx.expect_token(TokenKind::Assign)?;
223                new_comment = Some(ctx.expect_string_literal()?);
224            } else {
225                break;
226            }
227        }
228
229        Ok((additions, deletions, changes, new_comment))
230    }
231}