mssql.rs - source

sqlparser/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, CreateTrigger,
21    GranteesType, IfStatement, Statement,
22};
23use crate::dialect::Dialect;
24use crate::keywords::Keyword;
25use crate::parser::{Parser, ParserError};
26use crate::tokenizer::Token;
27#[cfg(not(feature = "std"))]
28use alloc::{vec, vec::Vec};
29
30/// A [`Dialect`] for [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server/)
31#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct MsSqlDialect {}
34
35impl Dialect for MsSqlDialect {
36    fn is_delimited_identifier_start(&self, ch: char) -> bool {
37        ch == '"' || ch == '['
38    }
39
40    fn is_identifier_start(&self, ch: char) -> bool {
41        // See https://docs.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-2017#rules-for-regular-identifiers
42        ch.is_alphabetic() || ch == '_' || ch == '#' || ch == '@'
43    }
44
45    fn is_identifier_part(&self, ch: char) -> bool {
46        ch.is_alphabetic()
47            || ch.is_ascii_digit()
48            || ch == '@'
49            || ch == '$'
50            || ch == '#'
51            || ch == '_'
52    }
53
54    fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
55        Some('[')
56    }
57
58    /// SQL Server has `CONVERT(type, value)` instead of `CONVERT(value, type)`
59    /// <https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16>
60    fn convert_type_before_value(&self) -> bool {
61        true
62    }
63
64    fn supports_outer_join_operator(&self) -> bool {
65        true
66    }
67
68    fn supports_connect_by(&self) -> bool {
69        true
70    }
71
72    fn supports_eq_alias_assignment(&self) -> bool {
73        true
74    }
75
76    fn supports_try_convert(&self) -> bool {
77        true
78    }
79
80    /// In MSSQL, there is no boolean type, and `true` and `false` are valid column names
81    fn supports_boolean_literals(&self) -> bool {
82        false
83    }
84
85    fn supports_named_fn_args_with_colon_operator(&self) -> bool {
86        true
87    }
88
89    fn supports_named_fn_args_with_expr_name(&self) -> bool {
90        true
91    }
92
93    fn supports_named_fn_args_with_rarrow_operator(&self) -> bool {
94        false
95    }
96
97    fn supports_start_transaction_modifier(&self) -> bool {
98        true
99    }
100
101    fn supports_end_transaction_modifier(&self) -> bool {
102        true
103    }
104
105    /// See: <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
106    fn supports_set_stmt_without_operator(&self) -> bool {
107        true
108    }
109
110    /// See: <https://learn.microsoft.com/en-us/sql/relational-databases/tables/querying-data-in-a-system-versioned-temporal-table>
111    fn supports_table_versioning(&self) -> bool {
112        true
113    }
114
115    /// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/slash-star-comment-transact-sql?view=sql-server-ver16>
116    fn supports_nested_comments(&self) -> bool {
117        true
118    }
119
120    /// See <https://learn.microsoft.com/en-us/sql/t-sql/queries/from-transact-sql>
121    fn supports_object_name_double_dot_notation(&self) -> bool {
122        true
123    }
124
125    /// See <https://learn.microsoft.com/en-us/sql/relational-databases/security/authentication-access/server-level-roles>
126    fn get_reserved_grantees_types(&self) -> &[GranteesType] {
127        &[GranteesType::Public]
128    }
129
130    fn is_select_item_alias(&self, explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
131        match kw {
132            // List of keywords that cannot be used as select item aliases in MSSQL
133            // regardless of whether the alias is explicit or implicit
134            Keyword::IF | Keyword::ELSE => false,
135            _ => explicit || self.is_column_alias(kw, parser),
136        }
137    }
138
139    fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
140        match kw {
141            // List of keywords that cannot be used as table aliases in MSSQL
142            // regardless of whether the alias is explicit or implicit
143            Keyword::IF | Keyword::ELSE => false,
144            _ => explicit || self.is_table_alias(kw, parser),
145        }
146    }
147
148    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
149        if parser.parse_keyword(Keyword::BEGIN) {
150            // Check if this is a BEGIN...END block rather than BEGIN TRANSACTION
151            let is_block = parser
152                .maybe_parse(|p| {
153                    if p.parse_transaction_modifier().is_some()
154                        || p.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK])
155                            .is_some()
156                        || matches!(p.peek_token_ref().token, Token::SemiColon | Token::EOF)
157                    {
158                        p.expected("statement", p.peek_token())
159                    } else {
160                        Ok(())
161                    }
162                })
163                .unwrap_or(None)
164                .is_some();
165            if is_block {
166                Some(parser.parse_begin_exception_end())
167            } else {
168                parser.prev_token();
169                None
170            }
171        } else if parser.peek_keyword(Keyword::IF) {
172            Some(self.parse_if_stmt(parser))
173        } else if parser.parse_keywords(&[Keyword::CREATE, Keyword::TRIGGER]) {
174            Some(self.parse_create_trigger(parser, false))
175        } else if parser.parse_keywords(&[
176            Keyword::CREATE,
177            Keyword::OR,
178            Keyword::ALTER,
179            Keyword::TRIGGER,
180        ]) {
181            Some(self.parse_create_trigger(parser, true))
182        } else {
183            None
184        }
185    }
186
187    fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
188        let token = parser.peek_token();
189        match token.token {
190            // lowest prec to prevent it from turning into a binary op
191            Token::Colon => Some(Ok(self.prec_unknown())),
192            _ => None,
193        }
194    }
195}
196
197impl MsSqlDialect {
198    /// ```sql
199    /// IF boolean_expression
200    ///     { sql_statement | statement_block }
201    /// [ ELSE
202    ///     { sql_statement | statement_block } ]
203    /// ```
204    fn parse_if_stmt(&self, parser: &mut Parser) -> Result<Statement, ParserError> {
205        let if_token = parser.expect_keyword(Keyword::IF)?;
206
207        let condition = parser.parse_expr()?;
208
209        let if_block = if parser.peek_keyword(Keyword::BEGIN) {
210            let begin_token = parser.expect_keyword(Keyword::BEGIN)?;
211            let statements = self.parse_statement_list(parser, Some(Keyword::END))?;
212            let end_token = parser.expect_keyword(Keyword::END)?;
213            ConditionalStatementBlock {
214                start_token: AttachedToken(if_token),
215                condition: Some(condition),
216                then_token: None,
217                conditional_statements: ConditionalStatements::BeginEnd(BeginEndStatements {
218                    begin_token: AttachedToken(begin_token),
219                    statements,
220                    end_token: AttachedToken(end_token),
221                }),
222            }
223        } else {
224            let stmt = parser.parse_statement()?;
225            ConditionalStatementBlock {
226                start_token: AttachedToken(if_token),
227                condition: Some(condition),
228                then_token: None,
229                conditional_statements: ConditionalStatements::Sequence {
230                    statements: vec![stmt],
231                },
232            }
233        };
234
235        let mut prior_statement_ended_with_semi_colon = false;
236        while let Token::SemiColon = parser.peek_token_ref().token {
237            parser.advance_token();
238            prior_statement_ended_with_semi_colon = true;
239        }
240
241        let mut else_block = None;
242        if parser.peek_keyword(Keyword::ELSE) {
243            let else_token = parser.expect_keyword(Keyword::ELSE)?;
244            if parser.peek_keyword(Keyword::BEGIN) {
245                let begin_token = parser.expect_keyword(Keyword::BEGIN)?;
246                let statements = self.parse_statement_list(parser, Some(Keyword::END))?;
247                let end_token = parser.expect_keyword(Keyword::END)?;
248                else_block = Some(ConditionalStatementBlock {
249                    start_token: AttachedToken(else_token),
250                    condition: None,
251                    then_token: None,
252                    conditional_statements: ConditionalStatements::BeginEnd(BeginEndStatements {
253                        begin_token: AttachedToken(begin_token),
254                        statements,
255                        end_token: AttachedToken(end_token),
256                    }),
257                });
258            } else {
259                let stmt = parser.parse_statement()?;
260                else_block = Some(ConditionalStatementBlock {
261                    start_token: AttachedToken(else_token),
262                    condition: None,
263                    then_token: None,
264                    conditional_statements: ConditionalStatements::Sequence {
265                        statements: vec![stmt],
266                    },
267                });
268            }
269        } else if prior_statement_ended_with_semi_colon {
270            parser.prev_token();
271        }
272
273        Ok(IfStatement {
274            if_block,
275            else_block,
276            elseif_blocks: Vec::new(),
277            end_token: None,
278        }
279        .into())
280    }
281
282    /// Parse `CREATE TRIGGER` for [MsSql]
283    ///
284    /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
285    fn parse_create_trigger(
286        &self,
287        parser: &mut Parser,
288        or_alter: bool,
289    ) -> Result<Statement, ParserError> {
290        let name = parser.parse_object_name(false)?;
291        parser.expect_keyword_is(Keyword::ON)?;
292        let table_name = parser.parse_object_name(false)?;
293        let period = parser.parse_trigger_period()?;
294        let events = parser.parse_comma_separated(Parser::parse_trigger_event)?;
295
296        parser.expect_keyword_is(Keyword::AS)?;
297        let statements = Some(parser.parse_conditional_statements(&[Keyword::END])?);
298
299        Ok(CreateTrigger {
300            or_alter,
301            temporary: false,
302            or_replace: false,
303            is_constraint: false,
304            name,
305            period: Some(period),
306            period_before_table: false,
307            events,
308            table_name,
309            referenced_table_name: None,
310            referencing: Vec::new(),
311            trigger_object: None,
312            condition: None,
313            exec_body: None,
314            statements_as: true,
315            statements,
316            characteristics: None,
317        }
318        .into())
319    }
320
321    /// Parse a sequence of statements, optionally separated by semicolon.
322    ///
323    /// Stops parsing when reaching EOF or the given keyword.
324    fn parse_statement_list(
325        &self,
326        parser: &mut Parser,
327        terminal_keyword: Option<Keyword>,
328    ) -> Result<Vec<Statement>, ParserError> {
329        let mut stmts = Vec::new();
330        loop {
331            if let Token::EOF = parser.peek_token_ref().token {
332                break;
333            }
334            if let Some(term) = terminal_keyword {
335                if parser.peek_keyword(term) {
336                    break;
337                }
338            }
339            stmts.push(parser.parse_statement()?);
340            while let Token::SemiColon = parser.peek_token_ref().token {
341                parser.advance_token();
342            }
343        }
344        Ok(stmts)
345    }
346}