sqltk_parser/dialect/
mssql.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::ast::helpers::attached_token::AttachedToken;
19use crate::ast::{
20    BeginEndStatements, ConditionalStatementBlock, ConditionalStatements, IfStatement, Statement,
21    TriggerObject,
22};
23use crate::dialect::Dialect;
24use crate::keywords::{self, Keyword};
25use crate::parser::{Parser, ParserError};
26use crate::tokenizer::Token;
27#[cfg(not(feature = "std"))]
28use alloc::{vec, vec::Vec};
29
30const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[Keyword::IF, Keyword::ELSE];
31
32/// A [`Dialect`] for [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server/)
33#[derive(Debug)]
34pub struct MsSqlDialect {}
35
36impl Dialect for MsSqlDialect {
37    fn is_delimited_identifier_start(&self, ch: char) -> bool {
38        ch == '"' || ch == '['
39    }
40
41    fn is_identifier_start(&self, ch: char) -> bool {
42        // See https://docs.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-2017#rules-for-regular-identifiers
43        ch.is_alphabetic() || ch == '_' || ch == '#' || ch == '@'
44    }
45
46    fn is_identifier_part(&self, ch: char) -> bool {
47        ch.is_alphabetic()
48            || ch.is_ascii_digit()
49            || ch == '@'
50            || ch == '$'
51            || ch == '#'
52            || ch == '_'
53    }
54
55    /// SQL Server has `CONVERT(type, value)` instead of `CONVERT(value, type)`
56    /// <https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16>
57    fn convert_type_before_value(&self) -> bool {
58        true
59    }
60
61    fn supports_outer_join_operator(&self) -> bool {
62        true
63    }
64
65    fn supports_connect_by(&self) -> bool {
66        true
67    }
68
69    fn supports_eq_alias_assignment(&self) -> bool {
70        true
71    }
72
73    fn supports_try_convert(&self) -> bool {
74        true
75    }
76
77    /// In MSSQL, there is no boolean type, and `true` and `false` are valid column names
78    fn supports_boolean_literals(&self) -> bool {
79        false
80    }
81
82    fn supports_named_fn_args_with_colon_operator(&self) -> bool {
83        true
84    }
85
86    fn supports_named_fn_args_with_expr_name(&self) -> bool {
87        true
88    }
89
90    fn supports_named_fn_args_with_rarrow_operator(&self) -> bool {
91        false
92    }
93
94    fn supports_start_transaction_modifier(&self) -> bool {
95        true
96    }
97
98    fn supports_end_transaction_modifier(&self) -> bool {
99        true
100    }
101
102    /// See: <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
103    fn supports_set_stmt_without_operator(&self) -> bool {
104        true
105    }
106
107    /// See: <https://learn.microsoft.com/en-us/sql/relational-databases/tables/querying-data-in-a-system-versioned-temporal-table>
108    fn supports_timestamp_versioning(&self) -> bool {
109        true
110    }
111
112    /// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/slash-star-comment-transact-sql?view=sql-server-ver16>
113    fn supports_nested_comments(&self) -> bool {
114        true
115    }
116
117    /// See <https://learn.microsoft.com/en-us/sql/t-sql/queries/from-transact-sql>
118    fn supports_object_name_double_dot_notation(&self) -> bool {
119        true
120    }
121
122    fn is_column_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
123        !keywords::RESERVED_FOR_COLUMN_ALIAS.contains(kw) && !RESERVED_FOR_COLUMN_ALIAS.contains(kw)
124    }
125
126    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
127        if parser.peek_keyword(Keyword::IF) {
128            Some(self.parse_if_stmt(parser))
129        } else if parser.parse_keywords(&[Keyword::CREATE, Keyword::TRIGGER]) {
130            Some(self.parse_create_trigger(parser, false))
131        } else if parser.parse_keywords(&[
132            Keyword::CREATE,
133            Keyword::OR,
134            Keyword::ALTER,
135            Keyword::TRIGGER,
136        ]) {
137            Some(self.parse_create_trigger(parser, true))
138        } else {
139            None
140        }
141    }
142}
143
144impl MsSqlDialect {
145    /// ```sql
146    /// IF boolean_expression
147    ///     { sql_statement | statement_block }
148    /// [ ELSE
149    ///     { sql_statement | statement_block } ]
150    /// ```
151    fn parse_if_stmt(&self, parser: &mut Parser) -> Result<Statement, ParserError> {
152        let if_token = parser.expect_keyword(Keyword::IF)?;
153
154        let condition = parser.parse_expr()?;
155
156        let if_block = if parser.peek_keyword(Keyword::BEGIN) {
157            let begin_token = parser.expect_keyword(Keyword::BEGIN)?;
158            let statements = self.parse_statement_list(parser, Some(Keyword::END))?;
159            let end_token = parser.expect_keyword(Keyword::END)?;
160            ConditionalStatementBlock {
161                start_token: AttachedToken(if_token),
162                condition: Some(condition),
163                then_token: None,
164                conditional_statements: ConditionalStatements::BeginEnd(BeginEndStatements {
165                    begin_token: AttachedToken(begin_token),
166                    statements,
167                    end_token: AttachedToken(end_token),
168                }),
169            }
170        } else {
171            let stmt = parser.parse_statement()?;
172            ConditionalStatementBlock {
173                start_token: AttachedToken(if_token),
174                condition: Some(condition),
175                then_token: None,
176                conditional_statements: ConditionalStatements::Sequence {
177                    statements: vec![stmt],
178                },
179            }
180        };
181
182        let mut prior_statement_ended_with_semi_colon = false;
183        while let Token::SemiColon = parser.peek_token_ref().token {
184            parser.advance_token();
185            prior_statement_ended_with_semi_colon = true;
186        }
187
188        let mut else_block = None;
189        if parser.peek_keyword(Keyword::ELSE) {
190            let else_token = parser.expect_keyword(Keyword::ELSE)?;
191            if parser.peek_keyword(Keyword::BEGIN) {
192                let begin_token = parser.expect_keyword(Keyword::BEGIN)?;
193                let statements = self.parse_statement_list(parser, Some(Keyword::END))?;
194                let end_token = parser.expect_keyword(Keyword::END)?;
195                else_block = Some(ConditionalStatementBlock {
196                    start_token: AttachedToken(else_token),
197                    condition: None,
198                    then_token: None,
199                    conditional_statements: ConditionalStatements::BeginEnd(BeginEndStatements {
200                        begin_token: AttachedToken(begin_token),
201                        statements,
202                        end_token: AttachedToken(end_token),
203                    }),
204                });
205            } else {
206                let stmt = parser.parse_statement()?;
207                else_block = Some(ConditionalStatementBlock {
208                    start_token: AttachedToken(else_token),
209                    condition: None,
210                    then_token: None,
211                    conditional_statements: ConditionalStatements::Sequence {
212                        statements: vec![stmt],
213                    },
214                });
215            }
216        } else if prior_statement_ended_with_semi_colon {
217            parser.prev_token();
218        }
219
220        Ok(Statement::If(IfStatement {
221            if_block,
222            else_block,
223            elseif_blocks: Vec::new(),
224            end_token: None,
225        }))
226    }
227
228    /// Parse `CREATE TRIGGER` for [MsSql]
229    ///
230    /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
231    fn parse_create_trigger(
232        &self,
233        parser: &mut Parser,
234        or_alter: bool,
235    ) -> Result<Statement, ParserError> {
236        let name = parser.parse_object_name(false)?;
237        parser.expect_keyword_is(Keyword::ON)?;
238        let table_name = parser.parse_object_name(false)?;
239        let period = parser.parse_trigger_period()?;
240        let events = parser.parse_comma_separated(Parser::parse_trigger_event)?;
241
242        parser.expect_keyword_is(Keyword::AS)?;
243        let statements = Some(parser.parse_conditional_statements(&[Keyword::END])?);
244
245        Ok(Statement::CreateTrigger {
246            or_alter,
247            or_replace: false,
248            is_constraint: false,
249            name,
250            period,
251            events,
252            table_name,
253            referenced_table_name: None,
254            referencing: Vec::new(),
255            trigger_object: TriggerObject::Statement,
256            include_each: false,
257            condition: None,
258            exec_body: None,
259            statements,
260            characteristics: None,
261        })
262    }
263
264    /// Parse a sequence of statements, optionally separated by semicolon.
265    ///
266    /// Stops parsing when reaching EOF or the given keyword.
267    fn parse_statement_list(
268        &self,
269        parser: &mut Parser,
270        terminal_keyword: Option<Keyword>,
271    ) -> Result<Vec<Statement>, ParserError> {
272        let mut stmts = Vec::new();
273        loop {
274            if let Token::EOF = parser.peek_token_ref().token {
275                break;
276            }
277            if let Some(term) = terminal_keyword {
278                if parser.peek_keyword(term) {
279                    break;
280                }
281            }
282            stmts.push(parser.parse_statement()?);
283            while let Token::SemiColon = parser.peek_token_ref().token {
284                parser.advance_token();
285            }
286        }
287        Ok(stmts)
288    }
289}