sqlparse/
lib.rs

1
2//! SQL Parser and Formatter for Rust
3//!
4//! Example code, see more on Github:
5//!
6//!
7//! ```
8//! use sqlparse::{FormatOption, Formatter};
9//!
10//! let sql = "SELECT a, b, 123, myfunc(b) \
11//!            FROM table_1 \
12//!            WHERE a > b AND b < 100 \
13//!            ORDER BY a DESC";
14//!
15//! let mut f = Formatter::default();
16//! let mut formatter = FormatOption::default();
17//! formatter.reindent = true;
18//! formatter.reindent_aligned = true;
19//! 
20//! let formatted_sql = f.format(sql, &mut formatter);
21//! println!("{}", formatted_sql);
22//!
23//! ```
24//! Output:
25//! 
26//! ```sql
27//! SELECT a,
28//!        b,
29//!        123,
30//!        myfunc(b)
31//!   FROM table_1
32//!  WHERE a > b
33//!    AND b < 100
34//!  ORDER BY a DESC
35//! ```
36
37mod engine;
38mod lexer;
39mod keywords;
40mod tokens;
41mod formatter;
42mod filters;
43mod trie;
44
45
46pub use tokens::TokenType;
47pub use lexer::{Token, TokenList};
48pub use formatter::{FormatOption};
49pub use engine::grouping::group_tokenlist;
50pub use trie::Trie;
51
52/// parse sql
53pub struct Parser {
54    stack: engine::FilterStack,
55}
56
57impl Default for Parser {
58    fn default() -> Self {
59        Self { stack: engine::FilterStack::new() }
60    }
61}
62
63// TODO: add option
64impl Parser {
65
66    pub fn new() -> Self {
67        Self { stack: engine::FilterStack::new() }
68    }
69
70    /// parse single sql statement
71    pub fn parse(&self, sql: &str) -> Vec<Token> {
72        self.stack.run(sql, true)
73    }
74
75    /// parse multiple sql statements
76    pub fn parse_multi(&self, sql: &str) -> Vec<Vec<Token>> {
77        self.stack.run_multi(sql, true)
78    }
79
80    pub fn parse_no_grouping(&self, sql: &str) -> Vec<Token> {
81        self.stack.run(sql, false)
82    }
83
84    pub fn parse_multi_no_grouping(&self, sql: &str) -> Vec<Vec<Token>> {
85        self.stack.run_multi(sql, false)
86    }
87}
88
89/// parse sql into tokens,
90/// only for test
91pub fn parse(sql: &str) -> Vec<Token> {
92    let stack = engine::FilterStack::new();
93    stack.run(sql, true)
94}
95
96/// parse multiple sqls into tokens,
97/// only for test
98pub fn parse_multi(sql: &str) -> Vec<Vec<Token>> {
99    let stack = engine::FilterStack::new();
100    stack.run_multi(sql, true)
101}
102
103/// parse sql into grouped tokens,
104/// only for test
105pub fn parse_no_grouping(sql: &str) -> Vec<Token> {
106    let stack = engine::FilterStack::new();
107    stack.run(sql, false)
108}
109
110/// format sql with multiple options
111pub struct Formatter {
112    stack: engine::FilterStack,
113}
114
115
116impl Default for Formatter {
117    fn default() -> Self {
118        Self { stack: engine::FilterStack::new() }
119    }
120}
121
122impl Formatter {
123
124    pub fn new(stack: engine::FilterStack) -> Self {
125        Self { stack }
126    }
127
128    /// do not use this function repeatly
129    pub fn format(&mut self, mut sql: &str, options: &mut formatter::FormatOption) -> String {
130        formatter::validate_options(options);
131        formatter::build_filter_stack(&mut self.stack, options);
132        if options.strip_whitespace { sql = sql.trim(); };
133        let tokens = self.stack.format(sql, options.grouping);
134        tokens.iter().map(|token| token.iter().map(|t| t.value.as_str()).collect::<String>()).collect::<Vec<_>>().join("\n")
135    }
136
137    pub fn build_filters(&mut self, options: &mut formatter::FormatOption) {
138        formatter::validate_options(options);
139        formatter::build_filter_stack(&mut self.stack, options);
140    }
141
142    pub fn format_sql(&mut self, sql: &str, options: &formatter::FormatOption) -> String {
143        let tokens = self.stack.format(sql, options.grouping);
144        tokens.iter().map(|token| token.iter().map(|t| t.value.as_str()).collect::<String>()).collect::<Vec<_>>().join("\n")
145    }
146}
147
148/// format sql to string,
149/// only for test
150pub fn format(mut sql: &str, options: &mut formatter::FormatOption) -> String {
151    let mut stack = engine::FilterStack::new();
152    formatter::validate_options(options);
153    formatter::build_filter_stack(&mut stack, options);
154    if options.strip_whitespace { sql = sql.trim(); };
155    let tokens = stack.format(sql, options.grouping);
156    // for token in &tokens{
157    //     println!("{:?}", token);
158    // }
159    tokens.iter().map(|token| token.iter().map(|t| t.value.as_str()).collect::<String>()).collect::<Vec<_>>().join("\n")
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165    use std::time::Instant;
166
167    #[test]
168    fn test_parse() {
169        let sql = "select * from users where id > 1 order by id;";
170        let tokens = parse(sql);
171        for token in tokens {
172            println!("{:?}", token);
173        }
174    }
175
176    #[test]
177    fn test_parse_identifier() {
178        let sql = "select * from sch.users;";
179        let tokens = parse(sql);
180        // let tokens = parse_no_grouping(sql);
181        for token in tokens {
182            println!("{:?} {}", token.typ, token.value);
183        }
184    }
185
186    #[test]
187    fn test_parser1() {
188        let sql= "SELECT article, MAX(price) AS price FROM shop GROUP BY article ORDER BY article;";
189        let p = Parser::default();
190        let now = Instant::now();
191        let _tokens = p.parse(sql);
192        let elapsed = now.elapsed();
193        println!("elapsed: {}ms", elapsed.as_millis());
194    }
195
196
197    #[test]
198    fn test_parser2() {
199        let sql= "s";
200        let p = Parser::default();
201        let tokens = p.parse(sql);
202        assert_eq!(tokens.len(), 1);
203        assert_eq!(tokens[0].typ, TokenType::Identifier);
204        println!("{:?}", tokens);
205    }
206
207    #[test]
208    fn test_parser3() {
209        let sql= "SELECT COUNT(CustomerID), Country FROM Customers GROUP BY Country HAVING COUNT(CustomerID) > 5 ORDER BY COUNT(CustomerID) DESC;";
210        let p = Parser::default();
211        let now = Instant::now();
212        let _tokens = p.parse(sql);
213        let elapsed = now.elapsed();
214        println!("elapsed: {}ms", elapsed.as_millis());
215    }
216
217}