From e0cb0e72700313f532d8c50888fb96a7e5bdefa5 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Mon, 10 Apr 2023 15:12:56 -0400 Subject: [PATCH 1/3] Add `dialect_from_str` and improve `Dialect` documentation --- src/dialect/mod.rs | 83 ++++++++++++++++++++++++++++++++++++++++++-- src/dialect/mssql.rs | 1 + src/dialect/mysql.rs | 1 + src/lib.rs | 19 ++++++---- 4 files changed, 96 insertions(+), 8 deletions(-) diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index a13339a9d..e654076be 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -42,14 +42,44 @@ pub use self::sqlite::SQLiteDialect; pub use crate::keywords; use crate::parser::{Parser, ParserError}; -/// `dialect_of!(parser is SQLiteDialect | GenericDialect)` evaluates -/// to `true` if `parser.dialect` is one of the `Dialect`s specified. +/// `dialect_of!(parser Is SQLiteDialect | GenericDialect)` evaluates +/// to `true` if `parser.dialect` is one of the [`Dialect`]s specified. +/// +/// macro_rules! dialect_of { ( $parsed_dialect: ident is $($dialect_type: ty)|+ ) => { ($($parsed_dialect.dialect.is::<$dialect_type>())||+) }; } +/// Encapsulates the differences between SQL implementations. +/// +/// # SQL Dialects +/// SQL implementations deviatiate from one another, either due to +/// custom extensions or various historical reasons. This trait +/// encapsulates the parsing differences between dialects. +/// +/// # Examples +/// Most users create a [`Dialect`] directly, as shown on the [module +/// level documentation]: +/// +/// ``` +/// # use sqlparser::dialect::AnsiDialect; +/// let dialect = AnsiDialect {}; +/// ``` +/// +/// It is also possible to dynamically create a [`Dialect`] from its +/// name. For example: +/// +/// ``` +/// # use sqlparser::dialect::{AnsiDialect, dialect_from_str}; +/// let dialect = dialect_from_str("ansi").unwrap(); +/// +/// // Parsed dialect is an instance of `AnsiDialect`: +/// assert!(dialect.is::()); +/// ``` +/// +/// [module level documentation]: crate pub trait Dialect: Debug + Any { /// Determine if a character starts a quoted identifier. The default /// implementation, accepting "double quoted" ids is both ANSI-compliant @@ -113,6 +143,27 @@ impl dyn Dialect { } } +/// Returns the built in [`Dialect`] corresponding to `dialect_name`. +/// +/// See [`Dialect`] documentation for an example. +pub fn dialect_from_str(dialect_name: impl AsRef) -> Option> { + let dialect_name = dialect_name.as_ref(); + match dialect_name.to_lowercase().as_str() { + "generic" => Some(Box::new(GenericDialect)), + "mysql" => Some(Box::new(MySqlDialect {})), + "postgresql" | "postgres" => Some(Box::new(PostgreSqlDialect {})), + "hive" => Some(Box::new(HiveDialect {})), + "sqlite" => Some(Box::new(SQLiteDialect {})), + "snowflake" => Some(Box::new(SnowflakeDialect)), + "redshift" => Some(Box::new(RedshiftSqlDialect {})), + "mssql" => Some(Box::new(MsSqlDialect {})), + "clickhouse" => Some(Box::new(ClickHouseDialect {})), + "bigquery" => Some(Box::new(BigQueryDialect)), + "ansi" => Some(Box::new(AnsiDialect {})), + _ => None, + } +} + #[cfg(test)] mod tests { use super::ansi::AnsiDialect; @@ -141,4 +192,32 @@ mod tests { assert!(dialect_of!(ansi_holder is GenericDialect | AnsiDialect)); assert!(!dialect_of!(ansi_holder is GenericDialect | MsSqlDialect)); } + + #[test] + fn test_dialect_from_str() { + assert!(parse_dialect("generic").is::()); + assert!(parse_dialect("mysql").is::()); + assert!(parse_dialect("MySql").is::()); + assert!(parse_dialect("postgresql").is::()); + assert!(parse_dialect("postgres").is::()); + assert!(parse_dialect("hive").is::()); + assert!(parse_dialect("sqlite").is::()); + assert!(parse_dialect("snowflake").is::()); + assert!(parse_dialect("SnowFlake").is::()); + assert!(parse_dialect("MsSql").is::()); + assert!(parse_dialect("clickhouse").is::()); + assert!(parse_dialect("ClickHouse").is::()); + assert!(parse_dialect("bigquery").is::()); + assert!(parse_dialect("BigQuery").is::()); + assert!(parse_dialect("ansi").is::()); + assert!(parse_dialect("ANSI").is::()); + + // error cases + assert!(dialect_from_str("Unknown").is_none()); + assert!(dialect_from_str("").is_none()); + } + + fn parse_dialect(v: &str) -> Box { + dialect_from_str(v).unwrap() + } } diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs index a9056350d..6d1f49cd7 100644 --- a/src/dialect/mssql.rs +++ b/src/dialect/mssql.rs @@ -12,6 +12,7 @@ use crate::dialect::Dialect; +// [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server/) dialect #[derive(Debug)] pub struct MsSqlDialect {} diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index 82cbc5364..9408380d0 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -12,6 +12,7 @@ use crate::dialect::Dialect; +/// [MySQL](https://www.mysql.com/) #[derive(Debug)] pub struct MySqlDialect {} diff --git a/src/lib.rs b/src/lib.rs index 75209b054..5bcd32949 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,17 +10,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! SQL Parser for Rust +//! # SQL Parser for Rust //! //! This crate provides an ANSI:SQL 2011 lexer and parser that can parse SQL -//! into an Abstract Syntax Tree (AST). See the [sqlparser crates.io page] +//! into an Abstract Syntax Tree ([`AST`]). See the [sqlparser crates.io page] //! for more information. //! -//! See [`Parser::parse_sql`](crate::parser::Parser::parse_sql) and -//! [`Parser::new`](crate::parser::Parser::new) for the Parsing API -//! and the [`ast`](crate::ast) crate for the AST structure. +//! For more information: +//! 1. [`Parser::parse_sql`] and [`Parser::new`] for the Parsing API +//! 2. [`ast`] for the AST structure +//! 3. [`Dialect`] for supported SQL dialects //! -//! Example: +//! # Example //! //! ``` //! use sqlparser::dialect::GenericDialect; @@ -37,7 +38,13 @@ //! //! println!("AST: {:?}", ast); //! ``` +//! //! [sqlparser crates.io page]: https://crates.io/crates/sqlparser +//! [`Parser::parse_sql`]: crate::parser::Parser::parse_sql +//! [`Parser::new`]: crate::parser::Parser::new +//! [`AST`]: crate::ast +//! [`ast`]: crate::ast +//! [`Dialect`]: crate::dialect::Dialect #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::upper_case_acronyms)] From 6405fdbaf1ac314b9038dae42bf9a5f4a555989c Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Mon, 10 Apr 2023 15:26:17 -0400 Subject: [PATCH 2/3] cleanup --- src/dialect/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index e654076be..ac3e8b0ec 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -42,10 +42,10 @@ pub use self::sqlite::SQLiteDialect; pub use crate::keywords; use crate::parser::{Parser, ParserError}; +/// Convenience check if a [`Parser`] uses a certain dialect. +/// /// `dialect_of!(parser Is SQLiteDialect | GenericDialect)` evaluates /// to `true` if `parser.dialect` is one of the [`Dialect`]s specified. -/// -/// macro_rules! dialect_of { ( $parsed_dialect: ident is $($dialect_type: ty)|+ ) => { ($($parsed_dialect.dialect.is::<$dialect_type>())||+) From 42f7a6e53a153702114211414a3e0026dff4313a Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Mon, 10 Apr 2023 15:30:48 -0400 Subject: [PATCH 3/3] fix compilation with nostd --- src/dialect/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index ac3e8b0ec..5744ae65e 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -42,6 +42,9 @@ pub use self::sqlite::SQLiteDialect; pub use crate::keywords; use crate::parser::{Parser, ParserError}; +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; + /// Convenience check if a [`Parser`] uses a certain dialect. /// /// `dialect_of!(parser Is SQLiteDialect | GenericDialect)` evaluates