Thanks to visit codestin.com
Credit goes to docs.rs

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, IfStatement, Statement,
21};
22use crate::dialect::Dialect;
23use crate::keywords::{self, Keyword};
24use crate::parser::{Parser, ParserError};
25use crate::tokenizer::Token;
26#[cfg(not(feature = "std"))]
27use alloc::{vec, vec::Vec};
28
29const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[Keyword::IF, Keyword::ELSE];
30
31/// A [`Dialect`] for [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server/)
32#[derive(Debug)]
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    /// SQL Server has `CONVERT(type, value)` instead of `CONVERT(value, type)`
55    /// <https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16>
56    fn convert_type_before_value(&self) -> bool {
57        true
58    }
59
60    fn supports_outer_join_operator(&self) -> bool {
61        true
62    }
63
64    fn supports_connect_by(&self) -> bool {
65        true
66    }
67
68    fn supports_eq_alias_assignment(&self) -> bool {
69        true
70    }
71
72    fn supports_try_convert(&self) -> bool {
73        true
74    }
75
76    /// In MSSQL, there is no boolean type, and `true` and `false` are valid column names
77    fn supports_boolean_literals(&self) -> bool {
78        false
79    }
80
81    fn supports_named_fn_args_with_colon_operator(&self) -> bool {
82        true
83    }
84
85    fn supports_named_fn_args_with_expr_name(&self) -> bool {
86        true
87    }
88
89    fn supports_named_fn_args_with_rarrow_operator(&self) -> bool {
90        false
91    }
92
93    fn supports_start_transaction_modifier(&self) -> bool {
94        true
95    }
96
97    fn supports_end_transaction_modifier(&self) -> bool {
98        true
99    }
100
101    /// See: <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
102    fn supports_set_stmt_without_operator(&self) -> bool {
103        true
104    }
105
106    /// See: <https://learn.microsoft.com/en-us/sql/relational-databases/tables/querying-data-in-a-system-versioned-temporal-table>
107    fn supports_timestamp_versioning(&self) -> bool {
108        true
109    }
110
111    /// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/slash-star-comment-transact-sql?view=sql-server-ver16>
112    fn supports_nested_comments(&self) -> bool {
113        true
114    }
115
116    /// See <https://learn.microsoft.com/en-us/sql/t-sql/queries/from-transact-sql>
117    fn supports_object_name_double_dot_notation(&self) -> bool {
118        true
119    }
120
121    fn is_column_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
122        !keywords::RESERVED_FOR_COLUMN_ALIAS.contains(kw) && !RESERVED_FOR_COLUMN_ALIAS.contains(kw)
123    }
124
125    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
126        if parser.peek_keyword(Keyword::IF) {
127            Some(self.parse_if_stmt(parser))
128        } else {
129            None
130        }
131    }
132}
133
134impl MsSqlDialect {
135    /// ```sql
136    /// IF boolean_expression
137    ///     { sql_statement | statement_block }
138    /// [ ELSE
139    ///     { sql_statement | statement_block } ]
140    /// ```
141    fn parse_if_stmt(&self, parser: &mut Parser) -> Result<Statement, ParserError> {
142        let if_token = parser.expect_keyword(Keyword::IF)?;
143
144        let condition = parser.parse_expr()?;
145
146        let if_block = if parser.peek_keyword(Keyword::BEGIN) {
147            let begin_token = parser.expect_keyword(Keyword::BEGIN)?;
148            let statements = self.parse_statement_list(parser, Some(Keyword::END))?;
149            let end_token = parser.expect_keyword(Keyword::END)?;
150            ConditionalStatementBlock {
151                start_token: AttachedToken(if_token),
152                condition: Some(condition),
153                then_token: None,
154                conditional_statements: ConditionalStatements::BeginEnd(BeginEndStatements {
155                    begin_token: AttachedToken(begin_token),
156                    statements,
157                    end_token: AttachedToken(end_token),
158                }),
159            }
160        } else {
161            let stmt = parser.parse_statement()?;
162            ConditionalStatementBlock {
163                start_token: AttachedToken(if_token),
164                condition: Some(condition),
165                then_token: None,
166                conditional_statements: ConditionalStatements::Sequence {
167                    statements: vec![stmt],
168                },
169            }
170        };
171
172        let mut prior_statement_ended_with_semi_colon = false;
173        while let Token::SemiColon = parser.peek_token_ref().token {
174            parser.advance_token();
175            prior_statement_ended_with_semi_colon = true;
176        }
177
178        let mut else_block = None;
179        if parser.peek_keyword(Keyword::ELSE) {
180            let else_token = parser.expect_keyword(Keyword::ELSE)?;
181            if parser.peek_keyword(Keyword::BEGIN) {
182                let begin_token = parser.expect_keyword(Keyword::BEGIN)?;
183                let statements = self.parse_statement_list(parser, Some(Keyword::END))?;
184                let end_token = parser.expect_keyword(Keyword::END)?;
185                else_block = Some(ConditionalStatementBlock {
186                    start_token: AttachedToken(else_token),
187                    condition: None,
188                    then_token: None,
189                    conditional_statements: ConditionalStatements::BeginEnd(BeginEndStatements {
190                        begin_token: AttachedToken(begin_token),
191                        statements,
192                        end_token: AttachedToken(end_token),
193                    }),
194                });
195            } else {
196                let stmt = parser.parse_statement()?;
197                else_block = Some(ConditionalStatementBlock {
198                    start_token: AttachedToken(else_token),
199                    condition: None,
200                    then_token: None,
201                    conditional_statements: ConditionalStatements::Sequence {
202                        statements: vec![stmt],
203                    },
204                });
205            }
206        } else if prior_statement_ended_with_semi_colon {
207            parser.prev_token();
208        }
209
210        Ok(Statement::If(IfStatement {
211            if_block,
212            else_block,
213            elseif_blocks: Vec::new(),
214            end_token: None,
215        }))
216    }
217
218    /// Parse a sequence of statements, optionally separated by semicolon.
219    ///
220    /// Stops parsing when reaching EOF or the given keyword.
221    fn parse_statement_list(
222        &self,
223        parser: &mut Parser,
224        terminal_keyword: Option<Keyword>,
225    ) -> Result<Vec<Statement>, ParserError> {
226        let mut stmts = Vec::new();
227        loop {
228            if let Token::EOF = parser.peek_token_ref().token {
229                break;
230            }
231            if let Some(term) = terminal_keyword {
232                if parser.peek_keyword(term) {
233                    break;
234                }
235            }
236            stmts.push(parser.parse_statement()?);
237            while let Token::SemiColon = parser.peek_token_ref().token {
238                parser.advance_token();
239            }
240        }
241        Ok(stmts)
242    }
243}