mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-22 15:04:04 +00:00
Add MSSQL dialect and fix up the postgres' identifier rules
The `@@version` test is MS' dialect of SQL, it seems, so test it with
its own dialect.
Update the rules for identifiers in Postresql dialect per documentation,
while we're at it. The current identifier rules in Postgresql dialect
were introduced in this commit - as a copy of generic rules, it seems:
810cd8e6cf (diff-2808df0fba0aed85f9d35c167bd6a5f1L138)
This commit is contained in:
parent
5047f2c02e
commit
304710d59a
8 changed files with 75 additions and 15 deletions
|
@ -5,7 +5,7 @@ pub struct GenericSqlDialect {}
|
|||
|
||||
impl Dialect for GenericSqlDialect {
|
||||
fn is_identifier_start(&self, ch: char) -> bool {
|
||||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '@'
|
||||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '#' || ch == '@'
|
||||
}
|
||||
|
||||
fn is_identifier_part(&self, ch: char) -> bool {
|
||||
|
@ -13,6 +13,8 @@ impl Dialect for GenericSqlDialect {
|
|||
|| (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| ch == '@'
|
||||
|| ch == '$'
|
||||
|| ch == '#'
|
||||
|| ch == '_'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
mod ansi_sql;
|
||||
mod generic_sql;
|
||||
pub mod keywords;
|
||||
mod mssql;
|
||||
mod postgresql;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use self::ansi_sql::AnsiSqlDialect;
|
||||
pub use self::generic_sql::GenericSqlDialect;
|
||||
pub use self::mssql::MsSqlDialect;
|
||||
pub use self::postgresql::PostgreSqlDialect;
|
||||
|
||||
pub trait Dialect: Debug {
|
||||
|
|
22
src/dialect/mssql.rs
Normal file
22
src/dialect/mssql.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use crate::dialect::Dialect;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MsSqlDialect {}
|
||||
|
||||
impl Dialect for MsSqlDialect {
|
||||
fn is_identifier_start(&self, ch: char) -> bool {
|
||||
// See https://docs.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-2017#rules-for-regular-identifiers
|
||||
// We don't support non-latin "letters" currently.
|
||||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '#' || ch == '@'
|
||||
}
|
||||
|
||||
fn is_identifier_part(&self, ch: char) -> bool {
|
||||
(ch >= 'a' && ch <= 'z')
|
||||
|| (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| ch == '@'
|
||||
|| ch == '$'
|
||||
|| ch == '#'
|
||||
|| ch == '_'
|
||||
}
|
||||
}
|
|
@ -5,14 +5,17 @@ pub struct PostgreSqlDialect {}
|
|||
|
||||
impl Dialect for PostgreSqlDialect {
|
||||
fn is_identifier_start(&self, ch: char) -> bool {
|
||||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '@'
|
||||
// See https://www.postgresql.org/docs/11/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
|
||||
// We don't yet support identifiers beginning with "letters with
|
||||
// diacritical marks and non-Latin letters"
|
||||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'
|
||||
}
|
||||
|
||||
fn is_identifier_part(&self, ch: char) -> bool {
|
||||
(ch >= 'a' && ch <= 'z')
|
||||
|| (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| ch == '@'
|
||||
|| ch == '$'
|
||||
|| ch == '_'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,7 @@ pub fn all_dialects() -> TestedDialects {
|
|||
dialects: vec![
|
||||
Box::new(GenericSqlDialect {}),
|
||||
Box::new(PostgreSqlDialect {}),
|
||||
Box::new(MsSqlDialect {}),
|
||||
Box::new(AnsiSqlDialect {}),
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1171,11 +1171,13 @@ fn parse_invalid_subquery_without_parens() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Parse results with PostgreSqlDialect are different from AnsiSqlDialect")]
|
||||
#[should_panic(
|
||||
expected = "Parse results with GenericSqlDialect are different from PostgreSqlDialect"
|
||||
)]
|
||||
fn ensure_multiple_dialects_are_tested() {
|
||||
// The SQL here must be parsed differently by different dialects.
|
||||
// At the time of writing, `@foo` is accepted as a valid identifier
|
||||
// by the generic and the postgresql dialect, but not by the ANSI one.
|
||||
// by the Generic and the MSSQL dialect, but not by Postgres and ANSI.
|
||||
let _ = parse_sql_statements("SELECT @foo");
|
||||
}
|
||||
|
||||
|
|
38
tests/sqlparser_mssql.rs
Normal file
38
tests/sqlparser_mssql.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
#![warn(clippy::all)]
|
||||
|
||||
use sqlparser::dialect::{GenericSqlDialect, MsSqlDialect};
|
||||
use sqlparser::sqlast::*;
|
||||
use sqlparser::test_utils::*;
|
||||
|
||||
#[test]
|
||||
fn parse_mssql_identifiers() {
|
||||
let sql = "SELECT @@version, _foo$123 FROM ##temp";
|
||||
let select = ms_and_generic().verified_only_select(sql);
|
||||
assert_eq!(
|
||||
&ASTNode::SQLIdentifier("@@version".to_string()),
|
||||
expr_from_projection(&select.projection[0]),
|
||||
);
|
||||
assert_eq!(
|
||||
&ASTNode::SQLIdentifier("_foo$123".to_string()),
|
||||
expr_from_projection(&select.projection[1]),
|
||||
);
|
||||
assert_eq!(2, select.projection.len());
|
||||
match select.relation {
|
||||
Some(TableFactor::Table { name, .. }) => {
|
||||
assert_eq!("##temp".to_string(), name.to_string());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn ms() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(MsSqlDialect {})],
|
||||
}
|
||||
}
|
||||
fn ms_and_generic() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(MsSqlDialect {}), Box::new(GenericSqlDialect {})],
|
||||
}
|
||||
}
|
|
@ -4,16 +4,6 @@ use sqlparser::dialect::{GenericSqlDialect, PostgreSqlDialect};
|
|||
use sqlparser::sqlast::*;
|
||||
use sqlparser::test_utils::*;
|
||||
|
||||
#[test]
|
||||
fn parse_select_version() {
|
||||
let sql = "SELECT @@version";
|
||||
let select = pg_and_generic().verified_only_select(sql);
|
||||
assert_eq!(
|
||||
&ASTNode::SQLIdentifier("@@version".to_string()),
|
||||
expr_from_projection(only(&select.projection)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_table_with_defaults() {
|
||||
let sql = "CREATE TABLE public.customer (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue