1mod ansi;
14mod bigquery;
15mod clickhouse;
16mod duckdb;
17mod generic;
18mod hive;
19mod mssql;
20mod mysql;
21mod postgresql;
22mod redshift;
23mod snowflake;
24mod sqlite;
25
26use crate::ast::{Expr, Statement};
27use core::any::{Any, TypeId};
28use core::fmt::Debug;
29use core::iter::Peekable;
30use core::str::Chars;
31
32pub use self::ansi::AnsiDialect;
33pub use self::bigquery::BigQueryDialect;
34pub use self::clickhouse::ClickHouseDialect;
35pub use self::duckdb::DuckDbDialect;
36pub use self::generic::GenericDialect;
37pub use self::hive::HiveDialect;
38pub use self::mssql::MsSqlDialect;
39pub use self::mysql::MySqlDialect;
40pub use self::postgresql::PostgreSqlDialect;
41pub use self::redshift::RedshiftSqlDialect;
42pub use self::snowflake::SnowflakeDialect;
43pub use self::sqlite::SQLiteDialect;
44pub use crate::keywords;
45use crate::parser::{Parser, ParserError};
46
47#[cfg(not(feature = "std"))]
48use alloc::boxed::Box;
49
50macro_rules! dialect_of {
55 ( $parsed_dialect: ident is $($dialect_type: ty)|+ ) => {
56 ($($parsed_dialect.dialect.is::<$dialect_type>())||+)
57 };
58}
59
60pub trait Dialect: Debug + Any {
94 fn dialect(&self) -> TypeId {
100 self.type_id()
101 }
102
103 fn is_delimited_identifier_start(&self, ch: char) -> bool {
109 ch == '"' || ch == '`'
110 }
111 fn is_proper_identifier_inside_quotes(&self, mut _chars: Peekable<Chars<'_>>) -> bool {
113 true
114 }
115 fn is_identifier_start(&self, ch: char) -> bool;
117 fn is_identifier_part(&self, ch: char) -> bool;
119 fn supports_filter_during_aggregation(&self) -> bool {
121 false
122 }
123 fn supports_within_after_array_aggregation(&self) -> bool {
128 false
129 }
130 fn supports_group_by_expr(&self) -> bool {
132 false
133 }
134 fn supports_substring_from_for_expr(&self) -> bool {
136 true
137 }
138 fn supports_in_empty_list(&self) -> bool {
140 false
141 }
142 fn supports_start_transaction_modifier(&self) -> bool {
144 false
145 }
146 fn convert_type_before_value(&self) -> bool {
149 false
150 }
151 fn parse_prefix(&self, _parser: &mut Parser) -> Option<Result<Expr, ParserError>> {
153 None
155 }
156 fn parse_infix(
158 &self,
159 _parser: &mut Parser,
160 _expr: &Expr,
161 _precedence: u8,
162 ) -> Option<Result<Expr, ParserError>> {
163 None
165 }
166 fn get_next_precedence(&self, _parser: &Parser) -> Option<Result<u8, ParserError>> {
168 None
170 }
171 fn parse_statement(&self, _parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
173 None
175 }
176}
177
178impl dyn Dialect {
179 #[inline]
180 pub fn is<T: Dialect>(&self) -> bool {
181 TypeId::of::<T>() == self.dialect()
183 }
184}
185
186pub fn dialect_from_str(dialect_name: impl AsRef<str>) -> Option<Box<dyn Dialect>> {
190 let dialect_name = dialect_name.as_ref();
191 match dialect_name.to_lowercase().as_str() {
192 "generic" => Some(Box::new(GenericDialect)),
193 "mysql" => Some(Box::new(MySqlDialect {})),
194 "postgresql" | "postgres" => Some(Box::new(PostgreSqlDialect {})),
195 "hive" => Some(Box::new(HiveDialect {})),
196 "sqlite" => Some(Box::new(SQLiteDialect {})),
197 "snowflake" => Some(Box::new(SnowflakeDialect)),
198 "redshift" => Some(Box::new(RedshiftSqlDialect {})),
199 "mssql" => Some(Box::new(MsSqlDialect {})),
200 "clickhouse" => Some(Box::new(ClickHouseDialect {})),
201 "bigquery" => Some(Box::new(BigQueryDialect)),
202 "ansi" => Some(Box::new(AnsiDialect {})),
203 "duckdb" => Some(Box::new(DuckDbDialect {})),
204 _ => None,
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::ansi::AnsiDialect;
211 use super::generic::GenericDialect;
212 use super::*;
213
214 struct DialectHolder<'a> {
215 dialect: &'a dyn Dialect,
216 }
217
218 #[test]
219 fn test_is_dialect() {
220 let generic_dialect: &dyn Dialect = &GenericDialect {};
221 let ansi_dialect: &dyn Dialect = &AnsiDialect {};
222
223 let generic_holder = DialectHolder {
224 dialect: generic_dialect,
225 };
226 let ansi_holder = DialectHolder {
227 dialect: ansi_dialect,
228 };
229
230 assert!(dialect_of!(generic_holder is GenericDialect | AnsiDialect),);
231 assert!(!dialect_of!(generic_holder is AnsiDialect));
232 assert!(dialect_of!(ansi_holder is AnsiDialect));
233 assert!(dialect_of!(ansi_holder is GenericDialect | AnsiDialect));
234 assert!(!dialect_of!(ansi_holder is GenericDialect | MsSqlDialect));
235 }
236
237 #[test]
238 fn test_dialect_from_str() {
239 assert!(parse_dialect("generic").is::<GenericDialect>());
240 assert!(parse_dialect("mysql").is::<MySqlDialect>());
241 assert!(parse_dialect("MySql").is::<MySqlDialect>());
242 assert!(parse_dialect("postgresql").is::<PostgreSqlDialect>());
243 assert!(parse_dialect("postgres").is::<PostgreSqlDialect>());
244 assert!(parse_dialect("hive").is::<HiveDialect>());
245 assert!(parse_dialect("sqlite").is::<SQLiteDialect>());
246 assert!(parse_dialect("snowflake").is::<SnowflakeDialect>());
247 assert!(parse_dialect("SnowFlake").is::<SnowflakeDialect>());
248 assert!(parse_dialect("MsSql").is::<MsSqlDialect>());
249 assert!(parse_dialect("clickhouse").is::<ClickHouseDialect>());
250 assert!(parse_dialect("ClickHouse").is::<ClickHouseDialect>());
251 assert!(parse_dialect("bigquery").is::<BigQueryDialect>());
252 assert!(parse_dialect("BigQuery").is::<BigQueryDialect>());
253 assert!(parse_dialect("ansi").is::<AnsiDialect>());
254 assert!(parse_dialect("ANSI").is::<AnsiDialect>());
255 assert!(parse_dialect("duckdb").is::<DuckDbDialect>());
256 assert!(parse_dialect("DuckDb").is::<DuckDbDialect>());
257
258 assert!(dialect_from_str("Unknown").is_none());
260 assert!(dialect_from_str("").is_none());
261 }
262
263 fn parse_dialect(v: &str) -> Box<dyn Dialect> {
264 dialect_from_str(v).unwrap()
265 }
266
267 #[test]
268 fn parse_with_wrapped_dialect() {
269 #[derive(Debug)]
273 struct WrappedDialect(MySqlDialect);
274
275 impl Dialect for WrappedDialect {
276 fn dialect(&self) -> std::any::TypeId {
277 self.0.dialect()
278 }
279
280 fn is_identifier_start(&self, ch: char) -> bool {
281 self.0.is_identifier_start(ch)
282 }
283
284 fn is_delimited_identifier_start(&self, ch: char) -> bool {
285 self.0.is_delimited_identifier_start(ch)
286 }
287
288 fn is_proper_identifier_inside_quotes(
289 &self,
290 chars: std::iter::Peekable<std::str::Chars<'_>>,
291 ) -> bool {
292 self.0.is_proper_identifier_inside_quotes(chars)
293 }
294
295 fn supports_filter_during_aggregation(&self) -> bool {
296 self.0.supports_filter_during_aggregation()
297 }
298
299 fn supports_within_after_array_aggregation(&self) -> bool {
300 self.0.supports_within_after_array_aggregation()
301 }
302
303 fn supports_group_by_expr(&self) -> bool {
304 self.0.supports_group_by_expr()
305 }
306
307 fn supports_substring_from_for_expr(&self) -> bool {
308 self.0.supports_substring_from_for_expr()
309 }
310
311 fn supports_in_empty_list(&self) -> bool {
312 self.0.supports_in_empty_list()
313 }
314
315 fn convert_type_before_value(&self) -> bool {
316 self.0.convert_type_before_value()
317 }
318
319 fn parse_prefix(
320 &self,
321 parser: &mut sqlparser::parser::Parser,
322 ) -> Option<Result<Expr, sqlparser::parser::ParserError>> {
323 self.0.parse_prefix(parser)
324 }
325
326 fn parse_infix(
327 &self,
328 parser: &mut sqlparser::parser::Parser,
329 expr: &Expr,
330 precedence: u8,
331 ) -> Option<Result<Expr, sqlparser::parser::ParserError>> {
332 self.0.parse_infix(parser, expr, precedence)
333 }
334
335 fn get_next_precedence(
336 &self,
337 parser: &sqlparser::parser::Parser,
338 ) -> Option<Result<u8, sqlparser::parser::ParserError>> {
339 self.0.get_next_precedence(parser)
340 }
341
342 fn parse_statement(
343 &self,
344 parser: &mut sqlparser::parser::Parser,
345 ) -> Option<Result<Statement, sqlparser::parser::ParserError>> {
346 self.0.parse_statement(parser)
347 }
348
349 fn is_identifier_part(&self, ch: char) -> bool {
350 self.0.is_identifier_part(ch)
351 }
352 }
353
354 #[allow(clippy::needless_raw_string_hashes)]
355 let statement = r#"SELECT 'Wayne\'s World'"#;
356 let res1 = Parser::parse_sql(&MySqlDialect {}, statement);
357 let res2 = Parser::parse_sql(&WrappedDialect(MySqlDialect {}), statement);
358 assert!(res1.is_ok());
359 assert_eq!(res1, res2);
360 }
361}