sqltk_parser/dialect/mod.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
18mod ansi;
19mod bigquery;
20mod clickhouse;
21mod databricks;
22mod duckdb;
23mod generic;
24mod hive;
25mod mssql;
26mod mysql;
27mod postgresql;
28mod redshift;
29mod snowflake;
30mod sqlite;
31
32use core::any::{Any, TypeId};
33use core::fmt::Debug;
34use core::iter::Peekable;
35use core::str::Chars;
36
37use log::debug;
38
39pub use self::ansi::AnsiDialect;
40pub use self::bigquery::BigQueryDialect;
41pub use self::clickhouse::ClickHouseDialect;
42pub use self::databricks::DatabricksDialect;
43pub use self::duckdb::DuckDbDialect;
44pub use self::generic::GenericDialect;
45pub use self::hive::HiveDialect;
46pub use self::mssql::MsSqlDialect;
47pub use self::mysql::MySqlDialect;
48pub use self::postgresql::PostgreSqlDialect;
49pub use self::redshift::RedshiftSqlDialect;
50pub use self::snowflake::SnowflakeDialect;
51pub use self::sqlite::SQLiteDialect;
52use crate::ast::{ColumnOption, Expr, Statement};
53pub use crate::keywords;
54use crate::keywords::Keyword;
55use crate::parser::{Parser, ParserError};
56use crate::tokenizer::Token;
57
58#[cfg(not(feature = "std"))]
59use alloc::boxed::Box;
60
61/// Convenience check if a [`Parser`] uses a certain dialect.
62///
63/// Note: when possible please the new style, adding a method to the [`Dialect`]
64/// trait rather than using this macro.
65///
66/// The benefits of adding a method on `Dialect` over this macro are:
67/// 1. user defined [`Dialect`]s can customize the parsing behavior
68/// 2. The differences between dialects can be clearly documented in the trait
69///
70/// `dialect_of!(parser is SQLiteDialect | GenericDialect)` evaluates
71/// to `true` if `parser.dialect` is one of the [`Dialect`]s specified.
72macro_rules! dialect_of {
73 ( $parsed_dialect: ident is $($dialect_type: ty)|+ ) => {
74 ($($parsed_dialect.dialect.is::<$dialect_type>())||+)
75 };
76}
77
78/// Encapsulates the differences between SQL implementations.
79///
80/// # SQL Dialects
81///
82/// SQL implementations deviate from one another, either due to
83/// custom extensions or various historical reasons. This trait
84/// encapsulates the parsing differences between dialects.
85///
86/// [`GenericDialect`] is the most permissive dialect, and parses the union of
87/// all the other dialects, when there is no ambiguity. However, it does not
88/// currently allow `CREATE TABLE` statements without types specified for all
89/// columns; use [`SQLiteDialect`] if you require that.
90///
91/// # Examples
92/// Most users create a [`Dialect`] directly, as shown on the [module
93/// level documentation]:
94///
95/// ```
96/// # use sqltk_parser::dialect::AnsiDialect;
97/// let dialect = AnsiDialect {};
98/// ```
99///
100/// It is also possible to dynamically create a [`Dialect`] from its
101/// name. For example:
102///
103/// ```
104/// # use sqltk_parser::dialect::{AnsiDialect, dialect_from_str};
105/// let dialect = dialect_from_str("ansi").unwrap();
106///
107/// // Parsed dialect is an instance of `AnsiDialect`:
108/// assert!(dialect.is::<AnsiDialect>());
109/// ```
110///
111/// [module level documentation]: crate
112pub trait Dialect: Debug + Any {
113 /// Determine the [`TypeId`] of this dialect.
114 ///
115 /// By default, return the same [`TypeId`] as [`Any::type_id`]. Can be overridden
116 /// by dialects that behave like other dialects
117 /// (for example when wrapping a dialect).
118 fn dialect(&self) -> TypeId {
119 self.type_id()
120 }
121
122 /// Determine if a character starts a quoted identifier. The default
123 /// implementation, accepting "double quoted" ids is both ANSI-compliant
124 /// and appropriate for most dialects (with the notable exception of
125 /// MySQL, MS SQL, and sqlite). You can accept one of characters listed
126 /// in `Word::matching_end_quote` here
127 fn is_delimited_identifier_start(&self, ch: char) -> bool {
128 ch == '"' || ch == '`'
129 }
130
131 /// Return the character used to quote identifiers.
132 fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
133 None
134 }
135
136 /// Determine if quoted characters are proper for identifier
137 fn is_proper_identifier_inside_quotes(&self, mut _chars: Peekable<Chars<'_>>) -> bool {
138 true
139 }
140
141 /// Determine if a character is a valid start character for an unquoted identifier
142 fn is_identifier_start(&self, ch: char) -> bool;
143
144 /// Determine if a character is a valid unquoted identifier character
145 fn is_identifier_part(&self, ch: char) -> bool;
146
147 /// Most dialects do not have custom operators. Override this method to provide custom operators.
148 fn is_custom_operator_part(&self, _ch: char) -> bool {
149 false
150 }
151
152 /// Determine if the dialect supports escaping characters via '\' in string literals.
153 ///
154 /// Some dialects like BigQuery and Snowflake support this while others like
155 /// Postgres do not. Such that the following is accepted by the former but
156 /// rejected by the latter.
157 /// ```sql
158 /// SELECT 'ab\'cd';
159 /// ```
160 ///
161 /// Conversely, such dialects reject the following statement which
162 /// otherwise would be valid in the other dialects.
163 /// ```sql
164 /// SELECT '\';
165 /// ```
166 fn supports_string_literal_backslash_escape(&self) -> bool {
167 false
168 }
169
170 /// Determine if the dialect supports string literals with `U&` prefix.
171 /// This is used to specify Unicode code points in string literals.
172 /// For example, in PostgreSQL, the following is a valid string literal:
173 /// ```sql
174 /// SELECT U&'\0061\0062\0063';
175 /// ```
176 /// This is equivalent to the string literal `'abc'`.
177 /// See
178 /// - [Postgres docs](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-UESCAPE)
179 /// - [H2 docs](http://www.h2database.com/html/grammar.html#string)
180 fn supports_unicode_string_literal(&self) -> bool {
181 false
182 }
183
184 /// Does the dialect support `FILTER (WHERE expr)` for aggregate queries?
185 fn supports_filter_during_aggregation(&self) -> bool {
186 false
187 }
188
189 /// Returns true if the dialect supports referencing another named window
190 /// within a window clause declaration.
191 ///
192 /// Example
193 /// ```sql
194 /// SELECT * FROM mytable
195 /// WINDOW mynamed_window AS another_named_window
196 /// ```
197 fn supports_window_clause_named_window_reference(&self) -> bool {
198 false
199 }
200
201 /// Returns true if the dialect supports `ARRAY_AGG() [WITHIN GROUP (ORDER BY)]` expressions.
202 /// Otherwise, the dialect should expect an `ORDER BY` without the `WITHIN GROUP` clause, e.g. [`ANSI`]
203 ///
204 /// [`ANSI`]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#array-aggregate-function
205 fn supports_within_after_array_aggregation(&self) -> bool {
206 false
207 }
208
209 /// Returns true if the dialects supports `group sets, roll up, or cube` expressions.
210 fn supports_group_by_expr(&self) -> bool {
211 false
212 }
213
214 /// Returns true if the dialect supports CONNECT BY.
215 fn supports_connect_by(&self) -> bool {
216 false
217 }
218
219 /// Returns true if the dialect supports the MATCH_RECOGNIZE operation.
220 fn supports_match_recognize(&self) -> bool {
221 false
222 }
223
224 /// Returns true if the dialect supports `(NOT) IN ()` expressions
225 fn supports_in_empty_list(&self) -> bool {
226 false
227 }
228
229 /// Returns true if the dialect supports `BEGIN {DEFERRED | IMMEDIATE | EXCLUSIVE} [TRANSACTION]` statements
230 fn supports_start_transaction_modifier(&self) -> bool {
231 false
232 }
233
234 /// Returns true if the dialect supports named arguments of the form FUN(a = '1', b = '2').
235 fn supports_named_fn_args_with_eq_operator(&self) -> bool {
236 false
237 }
238
239 /// Returns true if the dialect supports identifiers starting with a numeric
240 /// prefix such as tables named `59901_user_login`
241 fn supports_numeric_prefix(&self) -> bool {
242 false
243 }
244
245 /// Returns true if the dialects supports specifying null treatment
246 /// as part of a window function's parameter list as opposed
247 /// to after the parameter list.
248 ///
249 /// i.e The following syntax returns true
250 /// ```sql
251 /// FIRST_VALUE(a IGNORE NULLS) OVER ()
252 /// ```
253 /// while the following syntax returns false
254 /// ```sql
255 /// FIRST_VALUE(a) IGNORE NULLS OVER ()
256 /// ```
257 fn supports_window_function_null_treatment_arg(&self) -> bool {
258 false
259 }
260
261 /// Returns true if the dialect supports defining structs or objects using a
262 /// syntax like `{'x': 1, 'y': 2, 'z': 3}`.
263 fn supports_dictionary_syntax(&self) -> bool {
264 false
265 }
266
267 /// Returns true if the dialect supports defining object using the
268 /// syntax like `Map {1: 10, 2: 20}`.
269 fn support_map_literal_syntax(&self) -> bool {
270 false
271 }
272
273 /// Returns true if the dialect supports lambda functions, for example:
274 ///
275 /// ```sql
276 /// SELECT transform(array(1, 2, 3), x -> x + 1); -- returns [2,3,4]
277 /// ```
278 fn supports_lambda_functions(&self) -> bool {
279 false
280 }
281
282 /// Returns true if the dialect supports multiple variable assignment
283 /// using parentheses in a `SET` variable declaration.
284 ///
285 /// ```sql
286 /// SET (variable[, ...]) = (expression[, ...]);
287 /// ```
288 fn supports_parenthesized_set_variables(&self) -> bool {
289 false
290 }
291
292 /// Returns true if the dialect supports an `EXCEPT` clause following a
293 /// wildcard in a select list.
294 ///
295 /// For example
296 /// ```sql
297 /// SELECT * EXCEPT order_id FROM orders;
298 /// ```
299 fn supports_select_wildcard_except(&self) -> bool {
300 false
301 }
302
303 /// Returns true if the dialect has a CONVERT function which accepts a type first
304 /// and an expression second, e.g. `CONVERT(varchar, 1)`
305 fn convert_type_before_value(&self) -> bool {
306 false
307 }
308
309 /// Returns true if the dialect supports triple quoted string
310 /// e.g. `"""abc"""`
311 fn supports_triple_quoted_string(&self) -> bool {
312 false
313 }
314
315 /// Dialect-specific prefix parser override
316 fn parse_prefix(&self, _parser: &mut Parser) -> Option<Result<Expr, ParserError>> {
317 // return None to fall back to the default behavior
318 None
319 }
320
321 /// Does the dialect support trailing commas around the query?
322 fn supports_trailing_commas(&self) -> bool {
323 false
324 }
325
326 /// Does the dialect support parsing `LIMIT 1, 2` as `LIMIT 2 OFFSET 1`?
327 fn supports_limit_comma(&self) -> bool {
328 false
329 }
330
331 /// Does the dialect support trailing commas in the projection list?
332 fn supports_projection_trailing_commas(&self) -> bool {
333 self.supports_trailing_commas()
334 }
335
336 /// Dialect-specific infix parser override
337 ///
338 /// This method is called to parse the next infix expression.
339 ///
340 /// If `None` is returned, falls back to the default behavior.
341 fn parse_infix(
342 &self,
343 _parser: &mut Parser,
344 _expr: &Expr,
345 _precedence: u8,
346 ) -> Option<Result<Expr, ParserError>> {
347 // return None to fall back to the default behavior
348 None
349 }
350
351 /// Dialect-specific precedence override
352 ///
353 /// This method is called to get the precedence of the next token.
354 ///
355 /// If `None` is returned, falls back to the default behavior.
356 fn get_next_precedence(&self, _parser: &Parser) -> Option<Result<u8, ParserError>> {
357 // return None to fall back to the default behavior
358 None
359 }
360
361 /// Get the precedence of the next token, looking at the full token stream.
362 ///
363 /// A higher number => higher precedence
364 ///
365 /// See [`Self::get_next_precedence`] to override the behavior for just the
366 /// next token.
367 ///
368 /// The default implementation is used for many dialects, but can be
369 /// overridden to provide dialect-specific behavior.
370 fn get_next_precedence_default(&self, parser: &Parser) -> Result<u8, ParserError> {
371 if let Some(precedence) = self.get_next_precedence(parser) {
372 return precedence;
373 }
374 macro_rules! p {
375 ($precedence:ident) => {
376 self.prec_value(Precedence::$precedence)
377 };
378 }
379
380 let token = parser.peek_token();
381 debug!("get_next_precedence_full() {:?}", token);
382 match token.token {
383 Token::Word(w) if w.keyword == Keyword::OR => Ok(p!(Or)),
384 Token::Word(w) if w.keyword == Keyword::AND => Ok(p!(And)),
385 Token::Word(w) if w.keyword == Keyword::XOR => Ok(p!(Xor)),
386
387 Token::Word(w) if w.keyword == Keyword::AT => {
388 match (
389 parser.peek_nth_token(1).token,
390 parser.peek_nth_token(2).token,
391 ) {
392 (Token::Word(w), Token::Word(w2))
393 if w.keyword == Keyword::TIME && w2.keyword == Keyword::ZONE =>
394 {
395 Ok(p!(AtTz))
396 }
397 _ => Ok(self.prec_unknown()),
398 }
399 }
400
401 Token::Word(w) if w.keyword == Keyword::NOT => match parser.peek_nth_token(1).token {
402 // The precedence of NOT varies depending on keyword that
403 // follows it. If it is followed by IN, BETWEEN, or LIKE,
404 // it takes on the precedence of those tokens. Otherwise, it
405 // is not an infix operator, and therefore has zero
406 // precedence.
407 Token::Word(w) if w.keyword == Keyword::IN => Ok(p!(Between)),
408 Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(p!(Between)),
409 Token::Word(w) if w.keyword == Keyword::LIKE => Ok(p!(Like)),
410 Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(p!(Like)),
411 Token::Word(w) if w.keyword == Keyword::RLIKE => Ok(p!(Like)),
412 Token::Word(w) if w.keyword == Keyword::REGEXP => Ok(p!(Like)),
413 Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)),
414 _ => Ok(self.prec_unknown()),
415 },
416 Token::Word(w) if w.keyword == Keyword::IS => Ok(p!(Is)),
417 Token::Word(w) if w.keyword == Keyword::IN => Ok(p!(Between)),
418 Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(p!(Between)),
419 Token::Word(w) if w.keyword == Keyword::LIKE => Ok(p!(Like)),
420 Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(p!(Like)),
421 Token::Word(w) if w.keyword == Keyword::RLIKE => Ok(p!(Like)),
422 Token::Word(w) if w.keyword == Keyword::REGEXP => Ok(p!(Like)),
423 Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)),
424 Token::Word(w) if w.keyword == Keyword::OPERATOR => Ok(p!(Between)),
425 Token::Word(w) if w.keyword == Keyword::DIV => Ok(p!(MulDivModOp)),
426 Token::Eq
427 | Token::Lt
428 | Token::LtEq
429 | Token::Neq
430 | Token::Gt
431 | Token::GtEq
432 | Token::DoubleEq
433 | Token::Tilde
434 | Token::TildeAsterisk
435 | Token::ExclamationMarkTilde
436 | Token::ExclamationMarkTildeAsterisk
437 | Token::DoubleTilde
438 | Token::DoubleTildeAsterisk
439 | Token::ExclamationMarkDoubleTilde
440 | Token::ExclamationMarkDoubleTildeAsterisk
441 | Token::Spaceship => Ok(p!(Eq)),
442 Token::Pipe => Ok(p!(Pipe)),
443 Token::Caret | Token::Sharp | Token::ShiftRight | Token::ShiftLeft => Ok(p!(Caret)),
444 Token::Ampersand => Ok(p!(Ampersand)),
445 Token::Plus | Token::Minus => Ok(p!(PlusMinus)),
446 Token::Mul | Token::Div | Token::DuckIntDiv | Token::Mod | Token::StringConcat => {
447 Ok(p!(MulDivModOp))
448 }
449 Token::DoubleColon
450 | Token::ExclamationMark
451 | Token::LBracket
452 | Token::Overlap
453 | Token::CaretAt => Ok(p!(DoubleColon)),
454 Token::Arrow
455 | Token::LongArrow
456 | Token::HashArrow
457 | Token::HashLongArrow
458 | Token::AtArrow
459 | Token::ArrowAt
460 | Token::HashMinus
461 | Token::AtQuestion
462 | Token::AtAt
463 | Token::Question
464 | Token::QuestionAnd
465 | Token::QuestionPipe
466 | Token::CustomBinaryOperator(_) => Ok(p!(PgOther)),
467 _ => Ok(self.prec_unknown()),
468 }
469 }
470
471 /// Dialect-specific statement parser override
472 ///
473 /// This method is called to parse the next statement.
474 ///
475 /// If `None` is returned, falls back to the default behavior.
476 fn parse_statement(&self, _parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
477 // return None to fall back to the default behavior
478 None
479 }
480
481 /// Dialect-specific column option parser override
482 ///
483 /// This method is called to parse the next column option.
484 ///
485 /// If `None` is returned, falls back to the default behavior.
486 fn parse_column_option(
487 &self,
488 _parser: &mut Parser,
489 ) -> Result<Option<Result<Option<ColumnOption>, ParserError>>, ParserError> {
490 // return None to fall back to the default behavior
491 Ok(None)
492 }
493
494 /// Decide the lexical Precedence of operators.
495 ///
496 /// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference
497 fn prec_value(&self, prec: Precedence) -> u8 {
498 match prec {
499 Precedence::DoubleColon => 50,
500 Precedence::AtTz => 41,
501 Precedence::MulDivModOp => 40,
502 Precedence::PlusMinus => 30,
503 Precedence::Xor => 24,
504 Precedence::Ampersand => 23,
505 Precedence::Caret => 22,
506 Precedence::Pipe => 21,
507 Precedence::Between => 20,
508 Precedence::Eq => 20,
509 Precedence::Like => 19,
510 Precedence::Is => 17,
511 Precedence::PgOther => 16,
512 Precedence::UnaryNot => 15,
513 Precedence::And => 10,
514 Precedence::Or => 5,
515 }
516 }
517
518 /// Returns the precedence when the precedence is otherwise unknown
519 fn prec_unknown(&self) -> u8 {
520 0
521 }
522
523 /// Returns true if this dialect requires the `TABLE` keyword after `DESCRIBE`
524 ///
525 /// Defaults to false.
526 ///
527 /// If true, the following statement is valid: `DESCRIBE TABLE my_table`
528 /// If false, the following statements are valid: `DESCRIBE my_table` and `DESCRIBE table`
529 fn describe_requires_table_keyword(&self) -> bool {
530 false
531 }
532
533 /// Returns true if this dialect allows the `EXTRACT` function to words other than [`Keyword`].
534 fn allow_extract_custom(&self) -> bool {
535 false
536 }
537
538 /// Returns true if this dialect allows the `EXTRACT` function to use single quotes in the part being extracted.
539 fn allow_extract_single_quotes(&self) -> bool {
540 false
541 }
542
543 /// Does the dialect support with clause in create index statement?
544 /// e.g. `CREATE INDEX idx ON t WITH (key = value, key2)`
545 fn supports_create_index_with_clause(&self) -> bool {
546 false
547 }
548
549 /// Whether `INTERVAL` expressions require units (called "qualifiers" in the ANSI SQL spec) to be specified,
550 /// e.g. `INTERVAL 1 DAY` vs `INTERVAL 1`.
551 ///
552 /// Expressions within intervals (e.g. `INTERVAL '1' + '1' DAY`) are only allowed when units are required.
553 ///
554 /// See <https://github.com/sqlparser-rs/sqlparser-rs/pull/1398> for more information.
555 ///
556 /// When `true`:
557 /// * `INTERVAL '1' DAY` is VALID
558 /// * `INTERVAL 1 + 1 DAY` is VALID
559 /// * `INTERVAL '1' + '1' DAY` is VALID
560 /// * `INTERVAL '1'` is INVALID
561 ///
562 /// When `false`:
563 /// * `INTERVAL '1'` is VALID
564 /// * `INTERVAL '1' DAY` is VALID — unit is not required, but still allowed
565 /// * `INTERVAL 1 + 1 DAY` is INVALID
566 fn require_interval_qualifier(&self) -> bool {
567 false
568 }
569
570 fn supports_explain_with_utility_options(&self) -> bool {
571 false
572 }
573
574 fn supports_asc_desc_in_column_definition(&self) -> bool {
575 false
576 }
577
578 /// Returns true if this dialect supports treating the equals operator `=` within a `SelectItem`
579 /// as an alias assignment operator, rather than a boolean expression.
580 /// For example: the following statements are equivalent for such a dialect:
581 /// ```sql
582 /// SELECT col_alias = col FROM tbl;
583 /// SELECT col_alias AS col FROM tbl;
584 /// ```
585 fn supports_eq_alias_assignment(&self) -> bool {
586 false
587 }
588
589 /// Returns true if this dialect supports the `TRY_CONVERT` function
590 fn supports_try_convert(&self) -> bool {
591 false
592 }
593
594 /// Returns true if the dialect supports the `LISTEN` statement
595 fn supports_listen(&self) -> bool {
596 false
597 }
598
599 /// Returns true if the dialect supports the `NOTIFY` statement
600 fn supports_notify(&self) -> bool {
601 false
602 }
603
604 /// Returns true if this dialect expects the the `TOP` option
605 /// before the `ALL`/`DISTINCT` options in a `SELECT` statement.
606 fn supports_top_before_distinct(&self) -> bool {
607 false
608 }
609}
610
611/// This represents the operators for which precedence must be defined
612///
613/// higher number -> higher precedence
614#[derive(Debug, Clone, Copy)]
615pub enum Precedence {
616 DoubleColon,
617 AtTz,
618 MulDivModOp,
619 PlusMinus,
620 Xor,
621 Ampersand,
622 Caret,
623 Pipe,
624 Between,
625 Eq,
626 Like,
627 Is,
628 PgOther,
629 UnaryNot,
630 And,
631 Or,
632}
633
634impl dyn Dialect {
635 #[inline]
636 pub fn is<T: Dialect>(&self) -> bool {
637 // borrowed from `Any` implementation
638 TypeId::of::<T>() == self.dialect()
639 }
640}
641
642/// Returns the built in [`Dialect`] corresponding to `dialect_name`.
643///
644/// See [`Dialect`] documentation for an example.
645pub fn dialect_from_str(dialect_name: impl AsRef<str>) -> Option<Box<dyn Dialect>> {
646 let dialect_name = dialect_name.as_ref();
647 match dialect_name.to_lowercase().as_str() {
648 "generic" => Some(Box::new(GenericDialect)),
649 "mysql" => Some(Box::new(MySqlDialect {})),
650 "postgresql" | "postgres" => Some(Box::new(PostgreSqlDialect {})),
651 "hive" => Some(Box::new(HiveDialect {})),
652 "sqlite" => Some(Box::new(SQLiteDialect {})),
653 "snowflake" => Some(Box::new(SnowflakeDialect)),
654 "redshift" => Some(Box::new(RedshiftSqlDialect {})),
655 "mssql" => Some(Box::new(MsSqlDialect {})),
656 "clickhouse" => Some(Box::new(ClickHouseDialect {})),
657 "bigquery" => Some(Box::new(BigQueryDialect)),
658 "ansi" => Some(Box::new(AnsiDialect {})),
659 "duckdb" => Some(Box::new(DuckDbDialect {})),
660 "databricks" => Some(Box::new(DatabricksDialect {})),
661 _ => None,
662 }
663}
664
665#[cfg(test)]
666mod tests {
667 use super::*;
668
669 struct DialectHolder<'a> {
670 dialect: &'a dyn Dialect,
671 }
672
673 #[test]
674 fn test_is_dialect() {
675 let generic_dialect: &dyn Dialect = &GenericDialect {};
676 let ansi_dialect: &dyn Dialect = &AnsiDialect {};
677
678 let generic_holder = DialectHolder {
679 dialect: generic_dialect,
680 };
681 let ansi_holder = DialectHolder {
682 dialect: ansi_dialect,
683 };
684
685 assert!(dialect_of!(generic_holder is GenericDialect | AnsiDialect),);
686 assert!(!dialect_of!(generic_holder is AnsiDialect));
687 assert!(dialect_of!(ansi_holder is AnsiDialect));
688 assert!(dialect_of!(ansi_holder is GenericDialect | AnsiDialect));
689 assert!(!dialect_of!(ansi_holder is GenericDialect | MsSqlDialect));
690 }
691
692 #[test]
693 fn test_dialect_from_str() {
694 assert!(parse_dialect("generic").is::<GenericDialect>());
695 assert!(parse_dialect("mysql").is::<MySqlDialect>());
696 assert!(parse_dialect("MySql").is::<MySqlDialect>());
697 assert!(parse_dialect("postgresql").is::<PostgreSqlDialect>());
698 assert!(parse_dialect("postgres").is::<PostgreSqlDialect>());
699 assert!(parse_dialect("hive").is::<HiveDialect>());
700 assert!(parse_dialect("sqlite").is::<SQLiteDialect>());
701 assert!(parse_dialect("snowflake").is::<SnowflakeDialect>());
702 assert!(parse_dialect("SnowFlake").is::<SnowflakeDialect>());
703 assert!(parse_dialect("MsSql").is::<MsSqlDialect>());
704 assert!(parse_dialect("clickhouse").is::<ClickHouseDialect>());
705 assert!(parse_dialect("ClickHouse").is::<ClickHouseDialect>());
706 assert!(parse_dialect("bigquery").is::<BigQueryDialect>());
707 assert!(parse_dialect("BigQuery").is::<BigQueryDialect>());
708 assert!(parse_dialect("ansi").is::<AnsiDialect>());
709 assert!(parse_dialect("ANSI").is::<AnsiDialect>());
710 assert!(parse_dialect("duckdb").is::<DuckDbDialect>());
711 assert!(parse_dialect("DuckDb").is::<DuckDbDialect>());
712 assert!(parse_dialect("DataBricks").is::<DatabricksDialect>());
713 assert!(parse_dialect("databricks").is::<DatabricksDialect>());
714
715 // error cases
716 assert!(dialect_from_str("Unknown").is_none());
717 assert!(dialect_from_str("").is_none());
718 }
719
720 fn parse_dialect(v: &str) -> Box<dyn Dialect> {
721 dialect_from_str(v).unwrap()
722 }
723
724 #[test]
725 fn identifier_quote_style() {
726 let tests: Vec<(&dyn Dialect, &str, Option<char>)> = vec![
727 (&GenericDialect {}, "id", None),
728 (&SQLiteDialect {}, "id", Some('`')),
729 (&PostgreSqlDialect {}, "id", Some('"')),
730 ];
731
732 for (dialect, ident, expected) in tests {
733 let actual = dialect.identifier_quote_style(ident);
734
735 assert_eq!(actual, expected);
736 }
737 }
738
739 #[test]
740 fn parse_with_wrapped_dialect() {
741 /// Wrapper for a dialect. In a real-world example, this wrapper
742 /// would tweak the behavior of the dialect. For the test case,
743 /// it wraps all methods unaltered.
744 #[derive(Debug)]
745 struct WrappedDialect(MySqlDialect);
746
747 impl Dialect for WrappedDialect {
748 fn dialect(&self) -> std::any::TypeId {
749 self.0.dialect()
750 }
751
752 fn is_identifier_start(&self, ch: char) -> bool {
753 self.0.is_identifier_start(ch)
754 }
755
756 fn is_delimited_identifier_start(&self, ch: char) -> bool {
757 self.0.is_delimited_identifier_start(ch)
758 }
759
760 fn identifier_quote_style(&self, identifier: &str) -> Option<char> {
761 self.0.identifier_quote_style(identifier)
762 }
763
764 fn supports_string_literal_backslash_escape(&self) -> bool {
765 self.0.supports_string_literal_backslash_escape()
766 }
767
768 fn is_proper_identifier_inside_quotes(
769 &self,
770 chars: std::iter::Peekable<std::str::Chars<'_>>,
771 ) -> bool {
772 self.0.is_proper_identifier_inside_quotes(chars)
773 }
774
775 fn supports_filter_during_aggregation(&self) -> bool {
776 self.0.supports_filter_during_aggregation()
777 }
778
779 fn supports_within_after_array_aggregation(&self) -> bool {
780 self.0.supports_within_after_array_aggregation()
781 }
782
783 fn supports_group_by_expr(&self) -> bool {
784 self.0.supports_group_by_expr()
785 }
786
787 fn supports_in_empty_list(&self) -> bool {
788 self.0.supports_in_empty_list()
789 }
790
791 fn convert_type_before_value(&self) -> bool {
792 self.0.convert_type_before_value()
793 }
794
795 fn parse_prefix(
796 &self,
797 parser: &mut sqltk_parser::parser::Parser,
798 ) -> Option<Result<Expr, sqltk_parser::parser::ParserError>> {
799 self.0.parse_prefix(parser)
800 }
801
802 fn parse_infix(
803 &self,
804 parser: &mut sqltk_parser::parser::Parser,
805 expr: &Expr,
806 precedence: u8,
807 ) -> Option<Result<Expr, sqltk_parser::parser::ParserError>> {
808 self.0.parse_infix(parser, expr, precedence)
809 }
810
811 fn get_next_precedence(
812 &self,
813 parser: &sqltk_parser::parser::Parser,
814 ) -> Option<Result<u8, sqltk_parser::parser::ParserError>> {
815 self.0.get_next_precedence(parser)
816 }
817
818 fn parse_statement(
819 &self,
820 parser: &mut sqltk_parser::parser::Parser,
821 ) -> Option<Result<Statement, sqltk_parser::parser::ParserError>> {
822 self.0.parse_statement(parser)
823 }
824
825 fn is_identifier_part(&self, ch: char) -> bool {
826 self.0.is_identifier_part(ch)
827 }
828 }
829
830 #[allow(clippy::needless_raw_string_hashes)]
831 let statement = r#"SELECT 'Wayne\'s World'"#;
832 let res1 = Parser::parse_sql(&MySqlDialect {}, statement);
833 let res2 = Parser::parse_sql(&WrappedDialect(MySqlDialect {}), statement);
834 assert!(res1.is_ok());
835 assert_eq!(res1, res2);
836 }
837}