use crate::ast::NoExt;
use crate::ast::dialect::FeatureSet;
use crate::parser::Dialect;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Databricks;
impl Dialect for Databricks {
type Ext = NoExt;
fn features(&self) -> &FeatureSet {
&FeatureSet::DATABRICKS
}
}
#[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 databricks_parses_the_full_m1_select_grammar() {
assert_full_grammar(Databricks);
}
fn rejects_under_every_oracle_preset(sql: &str) {
assert!(
parse_with(sql, Ansi).is_err(),
"ANSI must reject the Databricks-only form {sql:?}",
);
assert!(
parse_with(sql, Postgres).is_err(),
"PostgreSQL must reject the Databricks-only form {sql:?}",
);
assert!(
parse_with(sql, MySql).is_err(),
"MySQL must reject the Databricks-only form {sql:?}",
);
assert!(
parse_with(sql, Sqlite).is_err(),
"SQLite must reject the Databricks-only form {sql:?}",
);
assert!(
parse_with(sql, DuckDb).is_err(),
"DuckDB must reject the Databricks-only form {sql:?}",
);
}
fn rejects_under_the_non_duckdb_oracle_presets(sql: &str) {
assert!(parse_with(sql, Ansi).is_err(), "ANSI must reject {sql:?}");
assert!(
parse_with(sql, Postgres).is_err(),
"PostgreSQL must reject {sql:?}",
);
assert!(parse_with(sql, MySql).is_err(), "MySQL must reject {sql:?}");
assert!(
parse_with(sql, Sqlite).is_err(),
"SQLite must reject {sql:?}"
);
}
#[test]
fn databricks_parses_sided_semi_anti_joins_that_oracle_presets_reject() {
for sql in [
"SELECT * FROM a LEFT SEMI JOIN b ON a.x = b.x",
"SELECT * FROM a LEFT ANTI JOIN b ON a.x = b.x",
"SELECT * FROM a RIGHT SEMI JOIN b ON a.x = b.x",
"SELECT * FROM a RIGHT ANTI JOIN b USING (x)",
] {
assert!(
parse_with(sql, Databricks).is_ok(),
"Databricks parses {sql:?}"
);
rejects_under_every_oracle_preset(sql);
}
}
#[test]
fn databricks_parses_semi_structured_paths_that_oracle_presets_reject() {
for sql in ["SELECT src:customer", "SELECT src:customer[0].name"] {
assert!(
parse_with(sql, Databricks).is_ok(),
"Databricks parses {sql:?} as a semi-structured path"
);
rejects_under_the_non_duckdb_oracle_presets(sql);
assert!(
parse_with(sql, DuckDb).is_ok(),
"DuckDB reinterprets {sql:?} as a prefix colon alias, not a path",
);
}
let mid = "SELECT a + src:customer FROM t";
assert!(
parse_with(mid, Databricks).is_ok(),
"Databricks parses {mid:?}"
);
rejects_under_every_oracle_preset(mid);
}
#[test]
fn databricks_parses_qualify_group_and_order_by_all_that_the_non_duckdb_presets_reject() {
for sql in [
"SELECT a FROM t QUALIFY row_number() OVER () = 1",
"SELECT a, count(*) FROM t GROUP BY ALL",
"SELECT a FROM t ORDER BY ALL",
] {
assert!(
parse_with(sql, Databricks).is_ok(),
"Databricks parses {sql:?}"
);
rejects_under_the_non_duckdb_oracle_presets(sql);
assert!(
parse_with(sql, DuckDb).is_ok(),
"DuckDB shares {sql:?} (its own QUALIFY / GROUP BY ALL / ORDER BY ALL)",
);
}
}
#[test]
fn databricks_reserves_qualify_as_an_identifier() {
assert!(
parse_with(
"SELECT a FROM t QUALIFY row_number() OVER () = 1",
Databricks
)
.is_ok(),
"the bare `FROM t QUALIFY …` form parses the clause, not an alias",
);
assert!(
parse_with("SELECT qualify FROM t", Databricks).is_err(),
"Databricks rejects `qualify` as a column name (reserved)",
);
assert!(
parse_with("SELECT qualify FROM t", Ansi).is_ok(),
"ANSI reads `qualify` as an ordinary column name (unreserved)",
);
}
#[test]
fn databricks_quotes_identifiers_with_backticks() {
assert!(
parse_with("SELECT `a b` FROM t", Databricks).is_ok(),
"Databricks reads a backtick-quoted identifier",
);
assert!(
parse_with("SELECT `a b` FROM t", Ansi).is_err(),
"ANSI has no backtick identifier quote",
);
assert!(
parse_with("SELECT \"col\" FROM t", Databricks).is_ok(),
"Databricks reads a double-quoted identifier",
);
}
#[test]
fn databricks_features_are_the_databricks_preset() {
assert_eq!(Databricks.features(), &FeatureSet::DATABRICKS);
}
}