sqlparser/dialect/
mssql.rs1// 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}