use crate::ast::NoExt;
use crate::ast::dialect::FeatureSet;
use crate::parser::Dialect;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Mssql;
impl Dialect for Mssql {
type Ext = NoExt;
fn features(&self) -> &FeatureSet {
&FeatureSet::MSSQL
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dialect::test_support::assert_full_grammar;
use crate::dialect::{Ansi, DuckDb, MySql, Postgres, Sqlite};
use crate::parse_with;
use crate::parser::Dialect;
#[test]
fn mssql_parses_the_full_m1_select_grammar() {
assert_full_grammar(Mssql);
}
fn rejects_under_every_oracle_preset(sql: &str) {
assert!(
parse_with(sql, Ansi).is_err(),
"ANSI must reject the MSSQL-only form {sql:?}",
);
assert!(
parse_with(sql, Postgres).is_err(),
"PostgreSQL must reject the MSSQL-only form {sql:?}",
);
assert!(
parse_with(sql, MySql).is_err(),
"MySQL must reject the MSSQL-only form {sql:?}",
);
assert!(
parse_with(sql, Sqlite).is_err(),
"SQLite must reject the MSSQL-only form {sql:?}",
);
assert!(
parse_with(sql, DuckDb).is_err(),
"DuckDB must reject the MSSQL-only form {sql:?}",
);
}
#[test]
fn mssql_parses_cross_and_outer_apply_that_every_oracle_preset_rejects() {
for sql in [
"SELECT * FROM a CROSS APPLY (SELECT 1) AS t",
"SELECT * FROM a OUTER APPLY b",
"SELECT * FROM a CROSS APPLY (SELECT b.x FROM b WHERE b.id = a.id) AS c",
] {
assert!(parse_with(sql, Mssql).is_ok(), "MSSQL parses {sql:?}");
rejects_under_every_oracle_preset(sql);
}
}
#[test]
fn mssql_parses_money_literals_that_every_oracle_preset_rejects() {
for sql in ["SELECT $1234.56", "SELECT $.5", "SELECT a + $9.99 FROM t"] {
assert!(parse_with(sql, Mssql).is_ok(), "MSSQL parses {sql:?}");
rejects_under_every_oracle_preset(sql);
}
}
#[test]
fn mssql_quotes_identifiers_with_brackets_and_double_quotes() {
let bracket = "SELECT [a b] FROM t";
assert!(parse_with(bracket, Mssql).is_ok(), "MSSQL reads `[a b]`");
assert!(
parse_with(bracket, Ansi).is_err(),
"ANSI has no bracket identifier quote",
);
assert!(
parse_with(bracket, Postgres).is_err(),
"PostgreSQL has no bracket identifier quote",
);
assert!(
parse_with(bracket, MySql).is_err(),
"MySQL quotes with backticks, not brackets",
);
assert!(
parse_with(bracket, DuckDb).is_err(),
"DuckDB has no bracket identifier quote",
);
assert!(
parse_with(bracket, Sqlite).is_ok(),
"SQLite shares the `[name]` bracket quote",
);
assert!(
parse_with("SELECT \"col\" FROM t", Mssql).is_ok(),
"MSSQL reads a double-quoted identifier",
);
}
#[test]
fn mssql_parses_at_name_parameters() {
for sql in ["SELECT * FROM t WHERE a = @p", "SELECT @p"] {
assert!(parse_with(sql, Mssql).is_ok(), "MSSQL parses {sql:?}");
assert!(
parse_with(sql, Ansi).is_err(),
"ANSI has no `@` sigil, so it rejects {sql:?}",
);
assert!(
parse_with(sql, DuckDb).is_ok(),
"DuckDB reads {sql:?} as a prefix `@` operator",
);
assert!(
parse_with(sql, Postgres).is_ok(),
"PostgreSQL reads {sql:?} as a prefix `@` operator",
);
}
}
#[test]
fn mssql_parses_national_string_literals() {
let national = "SELECT N'hi'";
assert!(
parse_with(national, Mssql).is_ok(),
"MSSQL reads `N'hi'` as a national-character string",
);
assert!(
parse_with(national, Sqlite).is_ok(),
"SQLite reads `N'hi'` as the column `N` aliased by the bare string `'hi'`",
);
}
#[test]
fn mssql_features_are_the_mssql_preset() {
assert_eq!(Mssql.features(), &FeatureSet::MSSQL);
}
}