diff --git a/src/dialect/ansi.rs b/src/dialect/ansi.rs index 89c8a9ea2..edc616b70 100644 --- a/src/dialect/ansi.rs +++ b/src/dialect/ansi.rs @@ -23,6 +23,11 @@ use crate::dialect::Dialect; pub struct AnsiDialect {} impl Dialect for AnsiDialect { + /// The SQL standard uses double quotes for delimited identifiers. + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('"') + } + fn is_identifier_start(&self, ch: char) -> bool { ch.is_ascii_lowercase() || ch.is_ascii_uppercase() } diff --git a/src/dialect/bigquery.rs b/src/dialect/bigquery.rs index 8fca51518..544be23e0 100644 --- a/src/dialect/bigquery.rs +++ b/src/dialect/bigquery.rs @@ -67,6 +67,11 @@ impl Dialect for BigQueryDialect { ch == '`' } + /// See + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('`') + } + fn supports_projection_trailing_commas(&self) -> bool { true } diff --git a/src/dialect/clickhouse.rs b/src/dialect/clickhouse.rs index 9061036f7..c81d953d1 100644 --- a/src/dialect/clickhouse.rs +++ b/src/dialect/clickhouse.rs @@ -32,6 +32,13 @@ impl Dialect for ClickHouseDialect { self.is_identifier_start(ch) || ch.is_ascii_digit() } + /// ClickHouse accepts both backticks and double quotes, but its own + /// formatter emits backticks by default. + /// See + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('`') + } + fn supports_string_literal_backslash_escape(&self) -> bool { true } diff --git a/src/dialect/databricks.rs b/src/dialect/databricks.rs index 679d335d7..3bc187a7f 100644 --- a/src/dialect/databricks.rs +++ b/src/dialect/databricks.rs @@ -31,6 +31,11 @@ impl Dialect for DatabricksDialect { matches!(ch, '`') } + /// See + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('`') + } + fn is_identifier_start(&self, ch: char) -> bool { matches!(ch, 'a'..='z' | 'A'..='Z' | '_') } diff --git a/src/dialect/duckdb.rs b/src/dialect/duckdb.rs index e70efd695..11946dd7d 100644 --- a/src/dialect/duckdb.rs +++ b/src/dialect/duckdb.rs @@ -36,6 +36,11 @@ impl Dialect for DuckDbDialect { ch.is_alphabetic() || ch.is_ascii_digit() || ch == '$' || ch == '_' } + /// See + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('"') + } + fn supports_filter_during_aggregation(&self) -> bool { true } diff --git a/src/dialect/hive.rs b/src/dialect/hive.rs index 6cbaef5ca..1a26f56ba 100644 --- a/src/dialect/hive.rs +++ b/src/dialect/hive.rs @@ -27,6 +27,11 @@ impl Dialect for HiveDialect { (ch == '"') || (ch == '`') } + /// See + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('`') + } + fn is_identifier_start(&self, ch: char) -> bool { ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '$' } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index efabfeb1f..f884b0549 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -2001,9 +2001,22 @@ mod tests { #[test] fn identifier_quote_style() { let tests: Vec<(&dyn Dialect, &str, Option)> = vec![ + (&AnsiDialect {}, "id", Some('"')), + (&BigQueryDialect {}, "id", Some('`')), + (&ClickHouseDialect {}, "id", Some('`')), + (&DatabricksDialect {}, "id", Some('`')), + (&DuckDbDialect {}, "id", Some('"')), (&GenericDialect {}, "id", None), - (&SQLiteDialect {}, "id", Some('`')), + (&HiveDialect {}, "id", Some('`')), + (&MsSqlDialect {}, "id", Some('[')), + (&MySqlDialect {}, "id", Some('`')), + (&OracleDialect {}, "id", Some('"')), (&PostgreSqlDialect {}, "id", Some('"')), + (&RedshiftSqlDialect {}, "id", Some('"')), + (&SnowflakeDialect {}, "id", Some('"')), + (&SQLiteDialect {}, "id", Some('`')), + (&SparkSqlDialect {}, "id", Some('`')), + (&TeradataDialect {}, "id", Some('"')), ]; for (dialect, ident, expected) in tests { diff --git a/src/dialect/redshift.rs b/src/dialect/redshift.rs index 25c2e9235..09c6d6944 100644 --- a/src/dialect/redshift.rs +++ b/src/dialect/redshift.rs @@ -92,6 +92,11 @@ impl Dialect for RedshiftSqlDialect { PostgreSqlDialect {}.is_identifier_part(ch) || ch == '#' } + /// See + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('"') + } + /// redshift has `CONVERT(type, value)` instead of `CONVERT(value, type)` /// fn convert_type_before_value(&self) -> bool { diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index b76cbc55e..0bedb12a5 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -140,6 +140,11 @@ impl Dialect for SnowflakeDialect { ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_' } + /// See + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('"') + } + fn supports_projection_trailing_commas(&self) -> bool { true } diff --git a/src/dialect/spark.rs b/src/dialect/spark.rs index 534d18015..2b32c6c23 100644 --- a/src/dialect/spark.rs +++ b/src/dialect/spark.rs @@ -36,6 +36,11 @@ impl Dialect for SparkSqlDialect { matches!(ch, '`') } + /// See + fn identifier_quote_style(&self, _identifier: &str) -> Option { + Some('`') + } + fn is_identifier_start(&self, ch: char) -> bool { matches!(ch, 'a'..='z' | 'A'..='Z' | '_') }