Skip to main content

sqruff_lib_core/dialects/
init.rs

1use strum::IntoEnumIterator;
2use strum_macros::AsRefStr;
3
4use crate::value::Value;
5
6/// Trait for dialect-specific configuration.
7/// Each dialect implements this to parse and validate its configuration from raw config values.
8pub trait DialectConfig: Default + Clone + std::fmt::Debug {
9    /// Parse configuration from a Value (typically a Map from the config file's dialect section).
10    /// Returns the default configuration if parsing fails or if the input is None.
11    fn from_value(value: &Value) -> Self {
12        let _ = value;
13        Self::default()
14    }
15}
16
17/// Macro to generate a dialect config struct with `DialectConfig` impl and `config_options()`.
18///
19/// # Usage
20///
21/// ```ignore
22/// // Dialect with config options (all bool fields):
23/// sqruff_lib_core::dialect_config!(PostgresDialectConfig {
24///     /// Enable pg_trgm operators
25///     pg_trgm: "Enable parsing of pg_trgm trigram operators"
26/// });
27///
28/// // Dialect with no config options:
29/// sqruff_lib_core::dialect_config!(AnsiDialectConfig {});
30/// ```
31#[macro_export]
32macro_rules! dialect_config {
33    // With fields (all bool)
34    ($name:ident { $(
35        $(#[doc = $doc:expr])*
36        $field:ident : $desc:expr
37    ),* $(,)? }) => {
38        #[derive(Debug, Clone)]
39        pub struct $name {
40            $($(#[doc = $doc])* pub $field: bool,)*
41        }
42
43        impl Default for $name {
44            fn default() -> Self {
45                Self { $($field: false,)* }
46            }
47        }
48
49        impl $crate::dialects::init::DialectConfig for $name {
50            fn from_value(value: &$crate::value::Value) -> Self {
51                Self {
52                    $($field: value[stringify!($field)].to_bool(),)*
53                }
54            }
55        }
56
57        impl $name {
58            pub fn config_options() -> Vec<(&'static str, &'static str, &'static str)> {
59                vec![
60                    $((stringify!($field), $desc, "false"),)*
61                ]
62            }
63        }
64    };
65    // No fields
66    ($name:ident {}) => {
67        #[derive(Debug, Clone, Default)]
68        pub struct $name;
69
70        impl $crate::dialects::init::DialectConfig for $name {}
71
72        impl $name {
73            pub fn config_options() -> Vec<(&'static str, &'static str, &'static str)> {
74                vec![]
75            }
76        }
77    };
78}
79
80#[derive(
81    strum_macros::EnumString,
82    strum_macros::EnumIter,
83    AsRefStr,
84    Debug,
85    Clone,
86    Copy,
87    Default,
88    Ord,
89    PartialOrd,
90    Eq,
91    PartialEq,
92    Hash,
93)]
94#[strum(serialize_all = "snake_case")]
95pub enum DialectKind {
96    #[default]
97    Ansi,
98    Athena,
99    Bigquery,
100    Clickhouse,
101    Databricks,
102    Db2,
103    Duckdb,
104    Greenplum,
105    Mysql,
106    Oracle,
107    Postgres,
108    Redshift,
109    Snowflake,
110    Sparksql,
111    Sqlite,
112    Trino,
113    Tsql,
114}
115
116impl DialectKind {
117    /// Returns the human-readable name of the dialect.
118    pub fn name(&self) -> &'static str {
119        match self {
120            DialectKind::Ansi => "ansi",
121            DialectKind::Athena => "athena",
122            DialectKind::Bigquery => "bigquery",
123            DialectKind::Clickhouse => "clickhouse",
124            DialectKind::Databricks => "databricks",
125            DialectKind::Db2 => "db2",
126            DialectKind::Duckdb => "duckdb",
127            DialectKind::Greenplum => "greenplum",
128            DialectKind::Mysql => "mysql",
129            DialectKind::Oracle => "oracle",
130            DialectKind::Postgres => "postgres",
131            DialectKind::Redshift => "redshift",
132            DialectKind::Snowflake => "snowflake",
133            DialectKind::Sparksql => "sparksql",
134            DialectKind::Sqlite => "sqlite",
135            DialectKind::Trino => "trino",
136            DialectKind::Tsql => "tsql",
137        }
138    }
139
140    /// Returns a human-readable description of the dialect.
141    pub fn description(&self) -> &'static str {
142        match self {
143            DialectKind::Ansi => {
144                "Standard SQL syntax. The default dialect and base for all others."
145            }
146            DialectKind::Athena => "Amazon Athena SQL dialect for querying data in S3.",
147            DialectKind::Bigquery => {
148                "Google BigQuery SQL dialect for analytics and data warehousing."
149            }
150            DialectKind::Clickhouse => "ClickHouse SQL dialect for real-time analytics.",
151            DialectKind::Databricks => "Databricks SQL dialect for lakehouse analytics.",
152            DialectKind::Db2 => "IBM Db2 SQL dialect.",
153            DialectKind::Duckdb => "DuckDB SQL dialect for in-process analytical database.",
154            DialectKind::Greenplum => "Greenplum SQL dialect, a massively parallel Postgres.",
155            DialectKind::Mysql => "MySQL SQL dialect for the popular open-source database.",
156            DialectKind::Oracle => "Oracle SQL dialect for Oracle Database.",
157            DialectKind::Postgres => {
158                "PostgreSQL SQL dialect for the advanced open-source database."
159            }
160            DialectKind::Redshift => "Amazon Redshift SQL dialect for cloud data warehousing.",
161            DialectKind::Snowflake => "Snowflake SQL dialect for cloud data platform.",
162            DialectKind::Sparksql => "Apache Spark SQL dialect for big data processing.",
163            DialectKind::Sqlite => "SQLite SQL dialect for embedded database.",
164            DialectKind::Trino => "Trino (formerly PrestoSQL) dialect for distributed SQL queries.",
165            DialectKind::Tsql => "T-SQL dialect for Microsoft SQL Server and Azure SQL.",
166        }
167    }
168
169    /// Returns the configuration section header for this dialect.
170    /// Format: `[sqruff:dialect:{dialect_name}]`
171    pub fn config_section(&self) -> String {
172        format!("[sqruff:dialect:{}]", self.name())
173    }
174
175    /// Returns an optional URL to the official documentation for the dialect.
176    pub fn doc_url(&self) -> Option<&'static str> {
177        match self {
178            DialectKind::Ansi => None,
179            DialectKind::Athena => {
180                Some("https://docs.aws.amazon.com/athena/latest/ug/ddl-sql-reference.html")
181            }
182            DialectKind::Bigquery => {
183                Some("https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax")
184            }
185            DialectKind::Clickhouse => Some("https://clickhouse.com/docs/en/sql-reference/"),
186            DialectKind::Databricks => {
187                Some("https://docs.databricks.com/en/sql/language-manual/index.html")
188            }
189            DialectKind::Db2 => Some("https://www.ibm.com/docs/en/i/7.4?topic=overview-db2-i"),
190            DialectKind::Duckdb => Some("https://duckdb.org/docs/sql/introduction"),
191            DialectKind::Greenplum => {
192                Some("https://docs.vmware.com/en/VMware-Greenplum/index.html")
193            }
194            DialectKind::Mysql => Some("https://dev.mysql.com/doc/"),
195            DialectKind::Oracle => {
196                Some("https://www.oracle.com/database/technologies/appdev/sql.html")
197            }
198            DialectKind::Postgres => Some("https://www.postgresql.org/docs/current/sql.html"),
199            DialectKind::Redshift => {
200                Some("https://docs.aws.amazon.com/redshift/latest/dg/cm_chap_SQLCommandRef.html")
201            }
202            DialectKind::Snowflake => Some("https://docs.snowflake.com/en/sql-reference.html"),
203            DialectKind::Sparksql => Some("https://spark.apache.org/sql/"),
204            DialectKind::Sqlite => Some("https://www.sqlite.org/lang.html"),
205            DialectKind::Trino => Some("https://trino.io/docs/current/sql.html"),
206            DialectKind::Tsql => {
207                Some("https://learn.microsoft.com/en-us/sql/t-sql/language-reference")
208            }
209        }
210    }
211}
212
213/// Generate a readout of available dialects.
214pub fn dialect_readout() -> Vec<String> {
215    DialectKind::iter()
216        .map(|x| x.as_ref().to_string())
217        .collect()
218}
219
220#[cfg(test)]
221mod tests {
222    use super::DialectKind;
223
224    #[test]
225    fn dialect_readout_is_alphabetically_sorted() {
226        let readout = super::dialect_readout();
227
228        let mut sorted = readout.clone();
229        sorted.sort();
230
231        assert_eq!(readout, sorted);
232    }
233
234    #[test]
235    fn config_section_format() {
236        assert_eq!(
237            DialectKind::Snowflake.config_section(),
238            "[sqruff:dialect:snowflake]"
239        );
240        assert_eq!(
241            DialectKind::Bigquery.config_section(),
242            "[sqruff:dialect:bigquery]"
243        );
244        assert_eq!(DialectKind::Ansi.config_section(), "[sqruff:dialect:ansi]");
245    }
246}