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