sql_insight/
formatter.rs

1//! A Formatter that formats SQL into a standardized format.
2//!
3//! See [`format`](crate::format()) as the entry point for formatting SQL.
4
5use crate::error::Error;
6use sqlparser::dialect::Dialect;
7use sqlparser::parser::Parser;
8
9/// Convenience function to format SQL.
10///
11/// ## Example
12///
13/// ```rust
14/// use sql_insight::sqlparser::dialect::GenericDialect;
15///
16/// let dialect = GenericDialect {};
17/// let sql = "SELECT a FROM t1 \n WHERE b =   1";
18/// let result = sql_insight::format(&dialect, sql).unwrap();
19/// assert_eq!(result, ["SELECT a FROM t1 WHERE b = 1"]);
20/// ```
21pub fn format(dialect: &dyn Dialect, sql: &str) -> Result<Vec<String>, Error> {
22    Formatter::format(dialect, sql)
23}
24
25/// Formatter for SQL.
26#[derive(Debug, Default)]
27pub struct Formatter;
28
29impl Formatter {
30    /// Format SQL.
31    pub fn format(dialect: &dyn Dialect, sql: &str) -> Result<Vec<String>, Error> {
32        let statements = Parser::parse_sql(dialect, sql)?;
33        Ok(statements
34            .into_iter()
35            .map(|statement| statement.to_string())
36            .collect::<Vec<String>>())
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use crate::test_utils::all_dialects;
44
45    fn assert_format(sql: &str, expected: Vec<String>, dialects: Vec<Box<dyn Dialect>>) {
46        for dialect in dialects {
47            let result = Formatter::format(dialect.as_ref(), sql).unwrap();
48            assert_eq!(result, expected, "Failed for dialect: {dialect:?}")
49        }
50    }
51
52    #[test]
53    fn test_single_sql() {
54        let sql =
55            "SELECT a from t1   WHERE b=1 AND c in (2, (select * from b))\n  AND d LIKE '%foo'";
56        let expected = vec![
57            "SELECT a FROM t1 WHERE b = 1 AND c IN (2, (SELECT * FROM b)) AND d LIKE '%foo'".into(),
58        ];
59        assert_format(sql, expected, all_dialects());
60    }
61
62    #[test]
63    fn test_multiple_sql() {
64        let sql = "INSERT INTO   t2  \n (a) VALUES (4); UPDATE t1   SET b  = 2 \n WHERE a = 1; DELETE \n FROM t3   WHERE c = 3";
65        let expected = vec![
66            "INSERT INTO t2 (a) VALUES (4)".into(),
67            "UPDATE t1 SET b = 2 WHERE a = 1".into(),
68            "DELETE FROM t3 WHERE c = 3".into(),
69        ];
70        assert_format(sql, expected, all_dialects());
71    }
72
73    #[test]
74    fn test_sql_with_comments() {
75        let sql = "SELECT a FROM t1 WHERE b = 1; -- comment\nSELECT b FROM t2 WHERE c =  2  /* comment */";
76        let expected = vec![
77            "SELECT a FROM t1 WHERE b = 1".into(),
78            "SELECT b FROM t2 WHERE c = 2".into(),
79        ];
80        assert_format(sql, expected, all_dialects());
81    }
82}