1mod lexer;
2mod sanitizer;
3mod writer;
4
5#[derive(Debug, PartialEq)]
6pub enum Keyword {
7 Select, From, Where, And, Or, Update, Set, Insert, Into, Values, Inner, Join, On, Limit, Offset, Between, Array, Other(BufferSlice),
25}
26
27#[derive(Debug, PartialEq)]
28pub enum Operator {
29 Arithmetic(ArithmeticOperator),
30 Logical(LogicalOperator),
31 Comparison(ComparisonOperator),
32 Bitwise(BitwiseOperator),
33 Json(JsonOperator),
34}
35
36#[derive(Debug, PartialEq)]
37pub enum ArithmeticOperator {
38 Multiply, Divide, Modulo, Plus, Minus, }
44
45#[derive(Debug, PartialEq)]
46pub enum LogicalOperator {
47 In, Not, Like, Ilike, Rlike, Glob, Match, Regexp, Then, Else, }
58
59#[derive(Debug, PartialEq)]
60pub enum ComparisonOperator {
61 Equal, Equal2, NullSafeEqual, GreaterThanOrEqual, LessThanOrEqual, EqualOrGreaterThan, EqualOrLessThan, EqualWithArrows, NotEqual, GreaterThan, LessThan, }
73
74#[derive(Debug, PartialEq)]
75pub enum BitwiseOperator {
76 LeftShift, RightShift, And, Or, }
81
82#[derive(Debug, PartialEq)]
83pub enum JsonOperator {
84 SpecifiedPath, SpecifiedPathAsText, }
87
88#[derive(Debug, PartialEq)]
89pub enum LiteralValueTypeIndicator {
90 Binary, Date, Time, Timestamp, X, ZeroX, B, ZeroB, N, Charset(BufferSlice), }
101
102#[derive(Debug, PartialEq)]
103pub struct BufferSlice {
104 pub start: usize,
105 pub end: usize,
106}
107
108impl BufferSlice {
109 pub fn new(start: usize, end: usize) -> BufferSlice {
110 BufferSlice { start, end }
111 }
112}
113
114#[derive(Debug, PartialEq)]
115pub enum Token {
116 Operator(Operator),
117 Keyword(Keyword),
118 LiteralValueTypeIndicator(LiteralValueTypeIndicator),
119 Backticked(BufferSlice),
120 DoubleQuoted(BufferSlice),
121 SingleQuoted(BufferSlice),
122 Numeric(BufferSlice),
123 Comment(BufferSlice),
124 Space,
125 Newline,
126 Dot,
127 Comma,
128 Wildcard,
129 ParentheseOpen,
130 ParentheseClose,
131 SquareBracketOpen,
132 SquareBracketClose,
133 Colon,
134 Semicolon,
135 Placeholder,
137 Ellipsis,
139 None,
141 Null,
142 True,
143 False,
144 NumberedPlaceholder(BufferSlice),
145 Unknown(char),
146}
147
148#[derive(Debug, PartialEq)]
149pub struct Sql {
150 buf: String,
151 pub tokens: Vec<Token>,
152}
153
154impl Sql {
155 pub fn buffer_content(&self, pos: &BufferSlice) -> &str {
156 let len = self.buf.len();
157 if pos.end < pos.start || pos.start > len || pos.end > len {
158 return "";
160 }
161 &self.buf[pos.start..pos.end]
162 }
163}
164
165pub fn lex(buf: String) -> Sql {
168 lexer::SqlLexer::new(buf).lex()
169}
170
171pub fn write(sql: Sql) -> String {
173 writer::SqlWriter::new(sql).write()
174}
175
176pub fn sanitize(sql: Sql) -> Sql {
178 sanitizer::SqlSanitizer::new(sql).sanitize()
179}
180
181pub fn sanitize_string(buf: String) -> String {
183 write(sanitize(lex(buf)))
184}
185
186#[cfg(test)]
187mod tests {
188 use super::Sql;
189 use super::{BufferSlice, ComparisonOperator, Keyword, Operator, Token};
190
191 #[test]
192 fn test_buffer_content() {
193 let sql = Sql {
194 buf: "SELECT `table`.* FROM `table` WHERE `id` = 'secret';".to_string(),
195 tokens: Vec::new(),
196 };
197 let buffer_position = BufferSlice::new(17, 21);
198
199 assert_eq!("FROM", sql.buffer_content(&buffer_position));
200 }
201
202 #[test]
203 fn test_buffer_content_multibyte_characters() {
204 let sql = Sql {
205 buf: "\"hæld\" ; 'jæld' ; `tæld`".to_string(),
206 tokens: Vec::new(),
207 };
208
209 assert_eq!("hæld", sql.buffer_content(&BufferSlice::new(1, 6)));
210 assert_eq!("jæld", sql.buffer_content(&BufferSlice::new(11, 16)));
211 assert_eq!("tæld", sql.buffer_content(&BufferSlice::new(21, 26)));
212 }
213
214 #[test]
215 fn test_buffer_content_wrong_order() {
216 let sql = Sql {
217 buf: "buffer content".to_string(),
218 tokens: Vec::new(),
219 };
220 let buffer_position = BufferSlice::new(6, 1);
221
222 assert_eq!("", sql.buffer_content(&buffer_position));
223 }
224
225 #[test]
226 fn test_buffer_content_out_of_bounds() {
227 let sql = Sql {
228 buf: "buffer content".to_string(),
229 tokens: Vec::new(),
230 };
231 let buffer_position = BufferSlice::new(100, 200);
232
233 assert_eq!("", sql.buffer_content(&buffer_position));
234 }
235
236 #[test]
237 fn test_buffer_content_out_of_bounds_partially() {
238 let sql = Sql {
239 buf: "buffer content".to_string(),
240 tokens: Vec::new(),
241 };
242 let buffer_position = BufferSlice::new(0, 200);
243
244 assert_eq!("", sql.buffer_content(&buffer_position));
245 }
246
247 #[test]
248 fn test_lex() {
249 let sql_buffer = "SELECT * FROM `table`";
250
251 let expected = vec![
252 Token::Keyword(Keyword::Select),
253 Token::Space,
254 Token::Wildcard,
255 Token::Space,
256 Token::Keyword(Keyword::From),
257 Token::Space,
258 Token::Backticked(BufferSlice::new(15, 20)),
259 ];
260
261 let sql = super::lex(sql_buffer.to_string());
262 assert_eq!(sql.buf, sql_buffer);
263 assert_eq!(sql.tokens, expected);
264 }
265
266 #[test]
267 fn test_write() {
268 let sql_buffer = "SELECT * FROM `table`";
269 assert_eq!(super::write(super::lex(sql_buffer.to_string())), sql_buffer);
270 }
271
272 #[test]
273 fn test_sanitize() {
274 let sql = super::sanitize(super::lex(
275 "SELECT * FROM `table` WHERE `id` = 1;".to_string(),
276 ));
277
278 let expected = vec![
279 Token::Keyword(Keyword::Select),
280 Token::Space,
281 Token::Wildcard,
282 Token::Space,
283 Token::Keyword(Keyword::From),
284 Token::Space,
285 Token::Backticked(BufferSlice::new(15, 20)),
286 Token::Space,
287 Token::Keyword(Keyword::Where),
288 Token::Space,
289 Token::Backticked(BufferSlice::new(29, 31)),
290 Token::Space,
291 Token::Operator(Operator::Comparison(ComparisonOperator::Equal)),
292 Token::Space,
293 Token::Placeholder,
294 Token::Semicolon,
295 ];
296
297 assert_eq!(sql.tokens, expected);
298 }
299
300 #[test]
301 fn test_sanitize_string() {
302 assert_eq!(
303 super::sanitize_string("SELECT * FROM `table` WHERE id = 1;".to_string()),
304 "SELECT * FROM `table` WHERE id = ?;"
305 );
306 }
307}