Add dialect_from_str and improve Dialect documentation (#848)

* Add `dialect_from_str` and improve `Dialect` documentation

* cleanup

* fix compilation with nostd
This commit is contained in:
Andrew Lamb 2023-04-27 11:37:11 -04:00 committed by GitHub
parent d8af92536c
commit 5ecf633e31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 99 additions and 8 deletions

View file

@ -42,14 +42,47 @@ pub use self::sqlite::SQLiteDialect;
pub use crate::keywords; pub use crate::keywords;
use crate::parser::{Parser, ParserError}; use crate::parser::{Parser, ParserError};
/// `dialect_of!(parser is SQLiteDialect | GenericDialect)` evaluates #[cfg(not(feature = "std"))]
/// to `true` if `parser.dialect` is one of the `Dialect`s specified. use alloc::boxed::Box;
/// 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 { macro_rules! dialect_of {
( $parsed_dialect: ident is $($dialect_type: ty)|+ ) => { ( $parsed_dialect: ident is $($dialect_type: ty)|+ ) => {
($($parsed_dialect.dialect.is::<$dialect_type>())||+) ($($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::<AnsiDialect>());
/// ```
///
/// [module level documentation]: crate
pub trait Dialect: Debug + Any { pub trait Dialect: Debug + Any {
/// Determine if a character starts a quoted identifier. The default /// Determine if a character starts a quoted identifier. The default
/// implementation, accepting "double quoted" ids is both ANSI-compliant /// implementation, accepting "double quoted" ids is both ANSI-compliant
@ -113,6 +146,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<str>) -> Option<Box<dyn Dialect>> {
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)] #[cfg(test)]
mod tests { mod tests {
use super::ansi::AnsiDialect; use super::ansi::AnsiDialect;
@ -141,4 +195,32 @@ mod tests {
assert!(dialect_of!(ansi_holder is GenericDialect | AnsiDialect)); assert!(dialect_of!(ansi_holder is GenericDialect | AnsiDialect));
assert!(!dialect_of!(ansi_holder is GenericDialect | MsSqlDialect)); assert!(!dialect_of!(ansi_holder is GenericDialect | MsSqlDialect));
} }
#[test]
fn test_dialect_from_str() {
assert!(parse_dialect("generic").is::<GenericDialect>());
assert!(parse_dialect("mysql").is::<MySqlDialect>());
assert!(parse_dialect("MySql").is::<MySqlDialect>());
assert!(parse_dialect("postgresql").is::<PostgreSqlDialect>());
assert!(parse_dialect("postgres").is::<PostgreSqlDialect>());
assert!(parse_dialect("hive").is::<HiveDialect>());
assert!(parse_dialect("sqlite").is::<SQLiteDialect>());
assert!(parse_dialect("snowflake").is::<SnowflakeDialect>());
assert!(parse_dialect("SnowFlake").is::<SnowflakeDialect>());
assert!(parse_dialect("MsSql").is::<MsSqlDialect>());
assert!(parse_dialect("clickhouse").is::<ClickHouseDialect>());
assert!(parse_dialect("ClickHouse").is::<ClickHouseDialect>());
assert!(parse_dialect("bigquery").is::<BigQueryDialect>());
assert!(parse_dialect("BigQuery").is::<BigQueryDialect>());
assert!(parse_dialect("ansi").is::<AnsiDialect>());
assert!(parse_dialect("ANSI").is::<AnsiDialect>());
// error cases
assert!(dialect_from_str("Unknown").is_none());
assert!(dialect_from_str("").is_none());
}
fn parse_dialect(v: &str) -> Box<dyn Dialect> {
dialect_from_str(v).unwrap()
}
} }

View file

@ -12,6 +12,7 @@
use crate::dialect::Dialect; use crate::dialect::Dialect;
// [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server/) dialect
#[derive(Debug)] #[derive(Debug)]
pub struct MsSqlDialect {} pub struct MsSqlDialect {}

View file

@ -12,6 +12,7 @@
use crate::dialect::Dialect; use crate::dialect::Dialect;
/// [MySQL](https://www.mysql.com/)
#[derive(Debug)] #[derive(Debug)]
pub struct MySqlDialect {} pub struct MySqlDialect {}

View file

@ -10,17 +10,18 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // 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 //! 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. //! for more information.
//! //!
//! See [`Parser::parse_sql`](crate::parser::Parser::parse_sql) and //! For more information:
//! [`Parser::new`](crate::parser::Parser::new) for the Parsing API //! 1. [`Parser::parse_sql`] and [`Parser::new`] for the Parsing API
//! and the [`ast`](crate::ast) crate for the AST structure. //! 2. [`ast`] for the AST structure
//! 3. [`Dialect`] for supported SQL dialects
//! //!
//! Example: //! # Example
//! //!
//! ``` //! ```
//! use sqlparser::dialect::GenericDialect; //! use sqlparser::dialect::GenericDialect;
@ -37,7 +38,13 @@
//! //!
//! println!("AST: {:?}", ast); //! println!("AST: {:?}", ast);
//! ``` //! ```
//!
//! [sqlparser crates.io page]: https://crates.io/crates/sqlparser //! [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)] #![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::upper_case_acronyms)] #![allow(clippy::upper_case_acronyms)]