Skip to main content

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