use crate::ast::NoExt;
use crate::ast::dialect::FeatureSet;
use crate::parser::Dialect;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Hive;
impl Dialect for Hive {
type Ext = NoExt;
fn features(&self) -> &FeatureSet {
&FeatureSet::HIVE
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Expr, SelectItem};
use crate::dialect::test_support::assert_full_grammar;
use crate::dialect::{Ansi, DuckDb, MySql, Postgres, Sqlite};
use crate::parse_with;
use crate::parser::{Dialect, Parsed};
#[test]
fn hive_parses_the_full_m1_select_grammar() {
assert_full_grammar(Hive);
}
fn rejects_under_every_oracle_preset(sql: &str) {
assert!(
parse_with(sql, crate::ParseConfig::new(Ansi)).is_err(),
"ANSI must reject the Hive-only form {sql:?}",
);
assert!(
parse_with(sql, crate::ParseConfig::new(Postgres)).is_err(),
"PostgreSQL must reject the Hive-only form {sql:?}",
);
assert!(
parse_with(sql, crate::ParseConfig::new(MySql)).is_err(),
"MySQL must reject the Hive-only form {sql:?}",
);
assert!(
parse_with(sql, crate::ParseConfig::new(Sqlite)).is_err(),
"SQLite must reject the Hive-only form {sql:?}",
);
assert!(
parse_with(sql, crate::ParseConfig::new(DuckDb)).is_err(),
"DuckDB must reject the Hive-only form {sql:?}",
);
}
#[test]
fn hive_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, crate::ParseConfig::new(Hive)).is_ok(),
"Hive parses {sql:?}"
);
rejects_under_every_oracle_preset(sql);
}
}
#[test]
fn hive_quotes_identifiers_with_backticks_not_double_quotes() {
let backtick = "SELECT `a b` FROM t";
assert!(
parse_with(backtick, crate::ParseConfig::new(Hive)).is_ok(),
"Hive reads `` `a b` `` as an identifier",
);
assert!(
parse_with(backtick, crate::ParseConfig::new(Ansi)).is_err(),
"ANSI has no backtick identifier quote",
);
assert!(
parse_with(backtick, crate::ParseConfig::new(Postgres)).is_err(),
"PostgreSQL has no backtick identifier quote",
);
assert!(
parse_with(backtick, crate::ParseConfig::new(DuckDb)).is_err(),
"DuckDB has no backtick identifier quote",
);
assert!(
parse_with(backtick, crate::ParseConfig::new(MySql)).is_ok(),
"MySQL shares the backtick identifier quote",
);
assert!(
parse_with(backtick, crate::ParseConfig::new(Sqlite)).is_ok(),
"SQLite shares the backtick identifier quote (MySQL compat)",
);
}
#[test]
fn hive_reads_double_quotes_as_strings_not_identifiers() {
let parsed = parse_with("SELECT \"hi\"", crate::ParseConfig::new(Hive))
.expect("Hive parses `\"hi\"`");
let SelectItem::Expr { expr, .. } = &select_projection(&parsed)[0] else {
panic!("expected a bare projection expression");
};
let Expr::Literal { literal, .. } = expr else {
panic!("Hive must read `\"hi\"` as a string literal, got {expr:?}");
};
assert_eq!(literal.as_str(parsed.source()).expect("string value"), "hi",);
let ansi_parsed = parse_with("SELECT \"hi\"", crate::ParseConfig::new(Ansi))
.expect("ANSI parses `\"hi\"`");
let SelectItem::Expr { expr, .. } = &select_projection(&ansi_parsed)[0] else {
panic!("expected a bare projection expression");
};
assert!(
!matches!(expr, Expr::Literal { .. }),
"ANSI must read `\"hi\"` as an identifier, not a string literal",
);
}
#[test]
fn hive_features_are_the_hive_preset() {
assert_eq!(Hive.features(), &FeatureSet::HIVE);
}
fn select_projection(parsed: &Parsed) -> &[SelectItem<NoExt>] {
use crate::ast::{SetExpr, Statement};
let Statement::Query { query, .. } = &parsed.statements()[0] else {
panic!("expected a query statement");
};
let SetExpr::Select { select, .. } = &query.body else {
panic!("expected a plain SELECT body");
};
&select.projection
}
}