chryso_core/
sql_format.rs

1use crate::ast::{Expr, Statement};
2
3pub fn format_statement(statement: &Statement) -> String {
4    crate::ast::statement_to_sql(statement)
5}
6
7pub fn format_expr(expr: &Expr) -> String {
8    expr.to_sql()
9}
10
11#[cfg(test)]
12mod tests {
13    use super::format_statement;
14    use crate::ast::{
15        InsertSource, SelectItem, SelectStatement, Statement, TableFactor, TableRef,
16    };
17
18    #[test]
19    fn format_simple_select() {
20        let stmt = Statement::Select(SelectStatement {
21            distinct: false,
22            distinct_on: Vec::new(),
23            projection: vec![SelectItem {
24                expr: crate::ast::Expr::Identifier("id".to_string()),
25                alias: None,
26            }],
27            from: Some(TableRef {
28                factor: TableFactor::Table {
29                    name: "users".to_string(),
30                },
31                alias: None,
32                column_aliases: Vec::new(),
33                joins: Vec::new(),
34            }),
35            selection: None,
36            group_by: Vec::new(),
37            having: None,
38            order_by: Vec::new(),
39            limit: None,
40            offset: None,
41        });
42        let output = format_statement(&stmt);
43        assert_eq!(output, "select id from users");
44    }
45
46    #[test]
47    fn format_update_statement() {
48        let stmt = Statement::Update(crate::ast::UpdateStatement {
49            table: "users".to_string(),
50            assignments: vec![crate::ast::Assignment {
51                column: "name".to_string(),
52                value: crate::ast::Expr::Literal(crate::ast::Literal::String("bob".to_string())),
53            }],
54            selection: Some(crate::ast::Expr::BinaryOp {
55                left: Box::new(crate::ast::Expr::Identifier("id".to_string())),
56                op: crate::ast::BinaryOperator::Eq,
57                right: Box::new(crate::ast::Expr::Literal(crate::ast::Literal::Number(1.0))),
58            }),
59            returning: Vec::new(),
60        });
61        let output = format_statement(&stmt);
62        assert_eq!(output, "update users set name = 'bob' where id = 1");
63    }
64
65    #[test]
66    fn format_insert_multi_values() {
67        let stmt = Statement::Insert(crate::ast::InsertStatement {
68            table: "users".to_string(),
69            columns: vec!["id".to_string(), "name".to_string()],
70            source: InsertSource::Values(vec![
71                vec![
72                    crate::ast::Expr::Literal(crate::ast::Literal::Number(1.0)),
73                    crate::ast::Expr::Literal(crate::ast::Literal::String("alice".to_string())),
74                ],
75                vec![
76                    crate::ast::Expr::Literal(crate::ast::Literal::Number(2.0)),
77                    crate::ast::Expr::Literal(crate::ast::Literal::String("bob".to_string())),
78                ],
79            ]),
80            returning: Vec::new(),
81        });
82        let output = format_statement(&stmt);
83        assert_eq!(
84            output,
85            "insert into users (id, name) values (1, 'alice'), (2, 'bob')"
86        );
87    }
88
89    #[test]
90    fn format_insert_default_values() {
91        let stmt = Statement::Insert(crate::ast::InsertStatement {
92            table: "users".to_string(),
93            columns: Vec::new(),
94            source: InsertSource::DefaultValues,
95            returning: Vec::new(),
96        });
97        let output = format_statement(&stmt);
98        assert_eq!(output, "insert into users default values");
99    }
100
101    #[test]
102    fn format_insert_select() {
103        let select = Statement::Select(SelectStatement {
104            distinct: false,
105            distinct_on: Vec::new(),
106            projection: vec![SelectItem {
107                expr: crate::ast::Expr::Identifier("id".to_string()),
108                alias: None,
109            }],
110            from: Some(TableRef {
111                factor: TableFactor::Table {
112                    name: "staging".to_string(),
113                },
114                alias: None,
115                column_aliases: Vec::new(),
116                joins: Vec::new(),
117            }),
118            selection: None,
119            group_by: Vec::new(),
120            having: None,
121            order_by: Vec::new(),
122            limit: None,
123            offset: None,
124        });
125        let stmt = Statement::Insert(crate::ast::InsertStatement {
126            table: "users".to_string(),
127            columns: vec!["id".to_string()],
128            source: InsertSource::Query(Box::new(select)),
129            returning: Vec::new(),
130        });
131        let output = format_statement(&stmt);
132        assert_eq!(output, "insert into users (id) select id from staging");
133    }
134
135    #[test]
136    fn format_select_without_from() {
137        let stmt = Statement::Select(SelectStatement {
138            distinct: false,
139            distinct_on: Vec::new(),
140            projection: vec![SelectItem {
141                expr: crate::ast::Expr::Literal(crate::ast::Literal::Number(1.0)),
142                alias: None,
143            }],
144            from: None,
145            selection: None,
146            group_by: Vec::new(),
147            having: None,
148            order_by: Vec::new(),
149            limit: None,
150            offset: None,
151        });
152        let output = format_statement(&stmt);
153        assert_eq!(output, "select 1");
154    }
155
156    #[test]
157    fn format_select_without_from_with_order_limit() {
158        let stmt = Statement::Select(SelectStatement {
159            distinct: false,
160            distinct_on: Vec::new(),
161            projection: vec![SelectItem {
162                expr: crate::ast::Expr::Literal(crate::ast::Literal::Number(1.0)),
163                alias: None,
164            }],
165            from: None,
166            selection: None,
167            group_by: Vec::new(),
168            having: None,
169            order_by: vec![crate::ast::OrderByExpr {
170                expr: crate::ast::Expr::Literal(crate::ast::Literal::Number(1.0)),
171                asc: true,
172                nulls_first: None,
173            }],
174            limit: Some(2),
175            offset: Some(1),
176        });
177        let output = format_statement(&stmt);
178        assert_eq!(output, "select 1 order by 1 asc limit 2 offset 1");
179    }
180
181    #[test]
182    fn format_create_table_if_not_exists() {
183        let stmt = Statement::CreateTable(crate::ast::CreateTableStatement {
184            name: "users".to_string(),
185            if_not_exists: true,
186            columns: Vec::new(),
187        });
188        let output = format_statement(&stmt);
189        assert_eq!(output, "create table if not exists users");
190    }
191
192    #[test]
193    fn format_create_table_with_columns() {
194        let stmt = Statement::CreateTable(crate::ast::CreateTableStatement {
195            name: "users".to_string(),
196            if_not_exists: false,
197            columns: vec![
198                crate::ast::ColumnDef {
199                    name: "id".to_string(),
200                    data_type: "integer".to_string(),
201                },
202                crate::ast::ColumnDef {
203                    name: "name".to_string(),
204                    data_type: "varchar(20)".to_string(),
205                },
206            ],
207        });
208        let output = format_statement(&stmt);
209        assert_eq!(output, "create table users (id integer, name varchar(20))");
210    }
211}