use crate::ast::NoExt;
use crate::ast::dialect::FeatureSet;
use crate::parser::Dialect;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct ClickHouse;
impl Dialect for ClickHouse {
type Ext = NoExt;
fn features(&self) -> &FeatureSet {
&FeatureSet::CLICKHOUSE
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{DataType, WrappedTypeKind};
use crate::dialect::test_support::{assert_full_grammar, first_column_type};
use crate::dialect::{Ansi, DuckDb, MySql, Postgres, Sqlite};
use crate::parse_with;
use crate::parser::Dialect;
#[test]
fn clickhouse_parses_the_full_m1_select_grammar() {
assert_full_grammar(ClickHouse);
}
fn rejects_under_every_oracle_preset(sql: &str) {
assert!(
parse_with(sql, crate::ParseConfig::new(Ansi)).is_err(),
"ANSI must reject the ClickHouse-only form {sql:?}",
);
assert!(
parse_with(sql, crate::ParseConfig::new(Postgres)).is_err(),
"PostgreSQL must reject the ClickHouse-only form {sql:?}",
);
assert!(
parse_with(sql, crate::ParseConfig::new(MySql)).is_err(),
"MySQL must reject the ClickHouse-only form {sql:?}",
);
assert!(
parse_with(sql, crate::ParseConfig::new(Sqlite)).is_err(),
"SQLite must reject the ClickHouse-only form {sql:?}",
);
assert!(
parse_with(sql, crate::ParseConfig::new(DuckDb)).is_err(),
"DuckDB must reject the ClickHouse-only form {sql:?}",
);
}
#[test]
fn clickhouse_parses_the_query_tails_that_oracle_presets_reject() {
for sql in [
"SELECT a FROM t LIMIT 5 BY a", "SELECT a FROM t LIMIT 5 OFFSET 2 BY a", "SELECT a FROM t SETTINGS max_threads = 8", "SELECT a FROM t FORMAT JSON", ] {
assert!(
parse_with(sql, crate::ParseConfig::new(ClickHouse)).is_ok(),
"ClickHouse parses {sql:?}"
);
rejects_under_every_oracle_preset(sql);
}
}
#[test]
fn clickhouse_type_constructors_with_type_bodies_reject_under_oracle_presets() {
for sql in [
"CREATE TABLE t (c Nullable(INT))",
"CREATE TABLE t (c LowCardinality(FixedString(16)))",
"CREATE TABLE t (c Nested(a INT, b INT))",
] {
assert!(
parse_with(sql, crate::ParseConfig::new(ClickHouse)).is_ok(),
"ClickHouse parses {sql:?}"
);
rejects_under_every_oracle_preset(sql);
}
}
#[test]
fn clickhouse_type_constructors_with_scalar_bodies_route_to_clickhouse_nodes() {
assert!(matches!(
first_column_type(ClickHouse, "CREATE TABLE t (c FixedString(16))"),
DataType::FixedString { .. }
));
assert!(matches!(
first_column_type(ClickHouse, "CREATE TABLE t (c DateTime64(3))"),
DataType::DateTime64 { .. }
));
assert!(matches!(
first_column_type(ClickHouse, "CREATE TABLE t (c Int256)"),
DataType::FixedWidthInt { signed: true, .. }
));
for sql in [
"CREATE TABLE t (c FixedString(16))",
"CREATE TABLE t (c DateTime64(3))",
"CREATE TABLE t (c Int256)",
] {
assert!(
matches!(first_column_type(Ansi, sql), DataType::UserDefined { .. }),
"ANSI reads {sql:?} as a generic user-defined type",
);
}
}
#[test]
fn clickhouse_wrapper_type_kinds_are_the_clickhouse_combinators() {
assert!(matches!(
first_column_type(ClickHouse, "CREATE TABLE t (c Nullable(INT))"),
DataType::Wrapped {
kind: WrappedTypeKind::Nullable,
..
}
));
assert!(matches!(
first_column_type(ClickHouse, "CREATE TABLE t (c LowCardinality(INT))"),
DataType::Wrapped {
kind: WrappedTypeKind::LowCardinality,
..
}
));
}
#[test]
fn clickhouse_quotes_identifiers_with_backticks_and_double_quotes() {
assert!(
parse_with(
r#"SELECT "a", `b` FROM t"#,
crate::ParseConfig::new(ClickHouse)
)
.is_ok(),
"ClickHouse accepts both `\"a\"` and `` `b` `` identifiers",
);
assert!(
parse_with("SELECT `b` FROM t", crate::ParseConfig::new(Ansi)).is_err(),
"ANSI rejects the backtick identifier ClickHouse accepts",
);
}
#[test]
fn clickhouse_features_are_the_clickhouse_preset() {
assert_eq!(ClickHouse.features(), &FeatureSet::CLICKHOUSE);
}
}