1use std::str::FromStr;
2
3use nom::branch::alt;
4use nom::bytes::complete::{tag, tag_no_case, take_until, take_while, take_while1};
5use nom::character::complete::{alpha1, digit1, line_ending, multispace0, multispace1};
6use nom::character::is_alphanumeric;
7use nom::combinator::{map, not, opt, peek, recognize};
8use nom::error::{ErrorKind, ParseError};
9use nom::sequence::{delimited, pair, preceded, terminated, tuple};
10use nom::{IResult, InputLength, Parser};
11
12use base::column::Column;
13use base::{DefaultOrZeroOrOne, OrderType, ParseSQLError};
14
15pub struct CommonParser;
17
18impl CommonParser {
19 fn keyword_follow_char(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
20 peek(alt((
21 tag(" "),
22 tag("\n"),
23 tag(";"),
24 tag("("),
25 tag(")"),
26 tag("\t"),
27 tag(","),
28 tag("="),
29 CommonParser::eof,
30 )))(i)
31 }
32
33 fn keywords_part_1(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
34 alt((
35 terminated(tag_no_case("ABORT"), Self::keyword_follow_char),
36 terminated(tag_no_case("ACTION"), Self::keyword_follow_char),
37 terminated(tag_no_case("ADD"), Self::keyword_follow_char),
38 terminated(tag_no_case("AFTER"), Self::keyword_follow_char),
39 terminated(tag_no_case("ALL"), Self::keyword_follow_char),
40 terminated(tag_no_case("ALTER"), Self::keyword_follow_char),
41 terminated(tag_no_case("ANALYZE"), Self::keyword_follow_char),
42 terminated(tag_no_case("AND"), Self::keyword_follow_char),
43 terminated(tag_no_case("AS"), Self::keyword_follow_char),
44 terminated(tag_no_case("ASC"), Self::keyword_follow_char),
45 terminated(tag_no_case("ATTACH"), Self::keyword_follow_char),
46 terminated(tag_no_case("AUTOINCREMENT"), Self::keyword_follow_char),
47 terminated(tag_no_case("BEFORE"), Self::keyword_follow_char),
48 terminated(tag_no_case("BEGIN"), Self::keyword_follow_char),
49 terminated(tag_no_case("BETWEEN"), Self::keyword_follow_char),
50 terminated(tag_no_case("BY"), Self::keyword_follow_char),
51 terminated(tag_no_case("CASCADE"), Self::keyword_follow_char),
52 terminated(tag_no_case("CASE"), Self::keyword_follow_char),
53 terminated(tag_no_case("CAST"), Self::keyword_follow_char),
54 terminated(tag_no_case("CHECK"), Self::keyword_follow_char),
55 terminated(tag_no_case("COLLATE"), Self::keyword_follow_char),
56 ))(i)
57 }
58
59 fn keywords_part_2(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
60 alt((
61 terminated(tag_no_case("COLUMN"), Self::keyword_follow_char),
62 terminated(tag_no_case("COMMIT"), Self::keyword_follow_char),
63 terminated(tag_no_case("CONFLICT"), Self::keyword_follow_char),
64 terminated(tag_no_case("CONSTRAINT"), Self::keyword_follow_char),
65 terminated(tag_no_case("CREATE"), Self::keyword_follow_char),
66 terminated(tag_no_case("CROSS"), Self::keyword_follow_char),
67 terminated(tag_no_case("CURRENT_DATE"), Self::keyword_follow_char),
68 terminated(tag_no_case("CURRENT_TIME"), Self::keyword_follow_char),
69 terminated(tag_no_case("CURRENT_TIMESTAMP"), Self::keyword_follow_char),
70 terminated(tag_no_case("DATABASE"), Self::keyword_follow_char),
71 terminated(tag_no_case("DEFAULT"), Self::keyword_follow_char),
72 terminated(tag_no_case("DEFERRABLE"), Self::keyword_follow_char),
73 terminated(tag_no_case("DEFERRED"), Self::keyword_follow_char),
74 terminated(tag_no_case("DELETE"), Self::keyword_follow_char),
75 terminated(tag_no_case("DESC"), Self::keyword_follow_char),
76 terminated(tag_no_case("DETACH"), Self::keyword_follow_char),
77 terminated(tag_no_case("DISTINCT"), Self::keyword_follow_char),
78 terminated(tag_no_case("DROP"), Self::keyword_follow_char),
79 terminated(tag_no_case("EACH"), Self::keyword_follow_char),
80 terminated(tag_no_case("ELSE"), Self::keyword_follow_char),
81 terminated(tag_no_case("END"), Self::keyword_follow_char),
82 ))(i)
83 }
84
85 fn keywords_part_3(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
86 alt((
87 terminated(tag_no_case("ESCAPE"), Self::keyword_follow_char),
88 terminated(tag_no_case("EXCEPT"), Self::keyword_follow_char),
89 terminated(tag_no_case("EXCLUSIVE"), Self::keyword_follow_char),
90 terminated(tag_no_case("EXISTS"), Self::keyword_follow_char),
91 terminated(tag_no_case("EXPLAIN"), Self::keyword_follow_char),
92 terminated(tag_no_case("FAIL"), Self::keyword_follow_char),
93 terminated(tag_no_case("FOR"), Self::keyword_follow_char),
94 terminated(tag_no_case("FOREIGN"), Self::keyword_follow_char),
95 terminated(tag_no_case("FROM"), Self::keyword_follow_char),
96 terminated(tag_no_case("FULL"), Self::keyword_follow_char),
97 terminated(tag_no_case("FULLTEXT"), Self::keyword_follow_char),
98 terminated(tag_no_case("GLOB"), Self::keyword_follow_char),
99 terminated(tag_no_case("GROUP"), Self::keyword_follow_char),
100 terminated(tag_no_case("HAVING"), Self::keyword_follow_char),
101 terminated(tag_no_case("IF"), Self::keyword_follow_char),
102 terminated(tag_no_case("IGNORE"), Self::keyword_follow_char),
103 terminated(tag_no_case("IMMEDIATE"), Self::keyword_follow_char),
104 terminated(tag_no_case("IN"), Self::keyword_follow_char),
105 terminated(tag_no_case("INDEX"), Self::keyword_follow_char),
106 terminated(tag_no_case("INDEXED"), Self::keyword_follow_char),
107 terminated(tag_no_case("INITIALLY"), Self::keyword_follow_char),
108 ))(i)
109 }
110
111 fn keywords_part_4(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
112 alt((
113 terminated(tag_no_case("INNER"), Self::keyword_follow_char),
114 terminated(tag_no_case("INSERT"), Self::keyword_follow_char),
115 terminated(tag_no_case("INSTEAD"), Self::keyword_follow_char),
116 terminated(tag_no_case("INTERSECT"), Self::keyword_follow_char),
117 terminated(tag_no_case("INTO"), Self::keyword_follow_char),
118 terminated(tag_no_case("IS"), Self::keyword_follow_char),
119 terminated(tag_no_case("ISNULL"), Self::keyword_follow_char),
120 terminated(tag_no_case("ORDER"), Self::keyword_follow_char),
121 terminated(tag_no_case("JOIN"), Self::keyword_follow_char),
122 terminated(tag_no_case("KEY"), Self::keyword_follow_char),
123 terminated(tag_no_case("LEFT"), Self::keyword_follow_char),
124 terminated(tag_no_case("LIKE"), Self::keyword_follow_char),
125 terminated(tag_no_case("LIMIT"), Self::keyword_follow_char),
126 terminated(tag_no_case("MATCH"), Self::keyword_follow_char),
127 terminated(tag_no_case("NATURAL"), Self::keyword_follow_char),
128 terminated(tag_no_case("NO"), Self::keyword_follow_char),
129 terminated(tag_no_case("NOT"), Self::keyword_follow_char),
130 terminated(tag_no_case("NOTNULL"), Self::keyword_follow_char),
131 terminated(tag_no_case("NULL"), Self::keyword_follow_char),
132 terminated(tag_no_case("OF"), Self::keyword_follow_char),
133 terminated(tag_no_case("OFFSET"), Self::keyword_follow_char),
134 ))(i)
135 }
136
137 fn keywords_part_5(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
138 alt((
139 terminated(tag_no_case("ON"), Self::keyword_follow_char),
140 terminated(tag_no_case("OR"), Self::keyword_follow_char),
141 terminated(tag_no_case("OUTER"), Self::keyword_follow_char),
142 terminated(tag_no_case("PLAN"), Self::keyword_follow_char),
143 terminated(tag_no_case("PRAGMA"), Self::keyword_follow_char),
144 terminated(tag_no_case("PRIMARY"), Self::keyword_follow_char),
145 terminated(tag_no_case("QUERY"), Self::keyword_follow_char),
146 terminated(tag_no_case("RAISE"), Self::keyword_follow_char),
147 terminated(tag_no_case("RECURSIVE"), Self::keyword_follow_char),
148 terminated(tag_no_case("REFERENCES"), Self::keyword_follow_char),
149 terminated(tag_no_case("REGEXP"), Self::keyword_follow_char),
150 terminated(tag_no_case("REINDEX"), Self::keyword_follow_char),
151 terminated(tag_no_case("RELEASE"), Self::keyword_follow_char),
152 terminated(tag_no_case("RENAME"), Self::keyword_follow_char),
153 terminated(tag_no_case("REPLACE"), Self::keyword_follow_char),
154 terminated(tag_no_case("RESTRICT"), Self::keyword_follow_char),
155 terminated(tag_no_case("RIGHT"), Self::keyword_follow_char),
156 terminated(tag_no_case("ROLLBACK"), Self::keyword_follow_char),
157 terminated(tag_no_case("ROW"), Self::keyword_follow_char),
158 terminated(tag_no_case("SAVEPOINT"), Self::keyword_follow_char),
159 terminated(tag_no_case("SELECT"), Self::keyword_follow_char),
160 ))(i)
161 }
162
163 fn keywords_part_6(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
164 alt((
165 terminated(tag_no_case("SET"), Self::keyword_follow_char),
166 terminated(tag_no_case("SPATIAL"), Self::keyword_follow_char),
167 terminated(tag_no_case("TABLE"), Self::keyword_follow_char),
168 terminated(tag_no_case("TEMP"), Self::keyword_follow_char),
169 terminated(tag_no_case("TEMPORARY"), Self::keyword_follow_char),
170 terminated(tag_no_case("THEN"), Self::keyword_follow_char),
171 terminated(tag_no_case("TO"), Self::keyword_follow_char),
172 terminated(tag_no_case("TRANSACTION"), Self::keyword_follow_char),
173 terminated(tag_no_case("TRIGGER"), Self::keyword_follow_char),
174 terminated(tag_no_case("UNION"), Self::keyword_follow_char),
175 terminated(tag_no_case("UNIQUE"), Self::keyword_follow_char),
176 terminated(tag_no_case("UPDATE"), Self::keyword_follow_char),
177 terminated(tag_no_case("USING"), Self::keyword_follow_char),
178 terminated(tag_no_case("VACUUM"), Self::keyword_follow_char),
179 terminated(tag_no_case("VALUES"), Self::keyword_follow_char),
180 terminated(tag_no_case("VIEW"), Self::keyword_follow_char),
181 terminated(tag_no_case("VIRTUAL"), Self::keyword_follow_char),
182 terminated(tag_no_case("WHEN"), Self::keyword_follow_char),
183 terminated(tag_no_case("WHERE"), Self::keyword_follow_char),
184 terminated(tag_no_case("WITH"), Self::keyword_follow_char),
185 terminated(tag_no_case("WITHOUT"), Self::keyword_follow_char),
186 ))(i)
187 }
188
189 pub fn sql_keyword(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
191 alt((
192 Self::keywords_part_1,
193 Self::keywords_part_2,
194 Self::keywords_part_3,
195 Self::keywords_part_4,
196 Self::keywords_part_5,
197 Self::keywords_part_6,
198 ))(i)
199 }
200
201 pub fn opt_index_name(i: &str) -> IResult<&str, Option<String>, ParseSQLError<&str>> {
203 opt(map(
204 delimited(multispace1, CommonParser::sql_identifier, multispace0),
205 String::from,
206 ))(i)
207 }
208
209 #[allow(clippy::type_complexity)]
210 pub fn index_col_name(
211 i: &str,
212 ) -> IResult<&str, (Column, Option<u16>, Option<OrderType>), ParseSQLError<&str>> {
213 let (remaining_input, (column, len_u8, order)) = tuple((
214 terminated(Column::without_alias, multispace0),
215 opt(delimited(tag("("), digit1, tag(")"))),
216 opt(OrderType::parse),
217 ))(i)?;
218 let len = len_u8.map(|l| u16::from_str(l).unwrap());
219
220 Ok((remaining_input, (column, len, order)))
221 }
222
223 #[inline]
224 fn is_sql_identifier(chr: char) -> bool {
225 is_alphanumeric(chr as u8) || chr == '_' || chr == '@'
226 }
227
228 pub fn opt_delimited<I: Clone, O1, O2, O3, E: ParseError<I>, F, G, H>(
230 mut first: F,
231 mut second: G,
232 mut third: H,
233 ) -> impl FnMut(I) -> IResult<I, O2, E>
234 where
235 F: Parser<I, O1, E>,
236 G: Parser<I, O2, E>,
237 H: Parser<I, O3, E>,
238 {
239 move |input: I| {
240 let inp = input.clone();
241 match second.parse(input) {
242 Ok((i, o)) => Ok((i, o)),
243 _ => {
244 let (inp, _) = first.parse(inp)?;
245 let (inp, o2) = second.parse(inp)?;
246 third.parse(inp).map(|(i, _)| (i, o2))
247 }
248 }
249 }
250 }
251
252 fn precision_helper(i: &str) -> IResult<&str, (u8, Option<u8>), ParseSQLError<&str>> {
253 let (remaining_input, (m, d)) = tuple((
254 digit1,
255 opt(preceded(tag(","), preceded(multispace0, digit1))),
256 ))(i)?;
257
258 Ok((
259 remaining_input,
260 (m.parse().unwrap(), d.map(|r| r.parse().unwrap())),
261 ))
262 }
263
264 pub fn precision(i: &str) -> IResult<&str, (u8, Option<u8>), ParseSQLError<&str>> {
265 delimited(tag("("), Self::precision_helper, tag(")"))(i)
266 }
267
268 pub fn delim_digit(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
269 delimited(tag("("), digit1, tag(")"))(i)
270 }
271
272 pub fn sql_identifier(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
273 alt((
274 alt((
275 preceded(
276 not(peek(CommonParser::sql_keyword)),
277 recognize(pair(alpha1, take_while(Self::is_sql_identifier))),
278 ),
279 recognize(pair(tag("_"), take_while1(Self::is_sql_identifier))),
280 recognize(pair(tag("@"), take_while1(Self::is_sql_identifier))),
282 )),
283 delimited(tag("`"), take_while1(Self::is_sql_identifier), tag("`")),
284 delimited(tag("["), take_while1(Self::is_sql_identifier), tag("]")),
285 ))(i)
286 }
287
288 pub fn unsigned_number(i: &str) -> IResult<&str, u64, ParseSQLError<&str>> {
290 map(digit1, |d| FromStr::from_str(d).unwrap())(i)
291 }
292
293 pub fn eof<I: Copy + InputLength, E: ParseError<I>>(input: I) -> IResult<I, I, E> {
294 if input.input_len() == 0 {
295 Ok((input, input))
296 } else {
297 Err(nom::Err::Error(E::from_error_kind(input, ErrorKind::Eof)))
298 }
299 }
300
301 pub fn statement_terminator(i: &str) -> IResult<&str, (), ParseSQLError<&str>> {
303 let (remaining_input, _) = delimited(
304 multispace0,
305 alt((tag(";"), line_ending, CommonParser::eof)),
306 multispace0,
307 )(i)?;
308 Ok((remaining_input, ()))
309 }
310
311 pub fn as_alias(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
313 map(
314 tuple((
315 multispace1,
316 opt(pair(tag_no_case("AS"), multispace1)),
317 CommonParser::sql_identifier,
319 )),
320 |a| a.2,
321 )(i)
322 }
323
324 pub fn ws_sep_comma(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
325 delimited(multispace0, tag(","), multispace0)(i)
326 }
327
328 pub(crate) fn ws_sep_equals(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
329 delimited(multispace0, tag("="), multispace0)(i)
330 }
331
332 pub fn parse_comment(i: &str) -> IResult<&str, String, ParseSQLError<&str>> {
337 alt((
338 map(
339 preceded(
340 delimited(multispace0, tag_no_case("COMMENT"), multispace1),
341 delimited(tag("'"), take_until("'"), tag("'")),
342 ),
343 String::from,
344 ),
345 map(
346 preceded(
347 delimited(multispace0, tag_no_case("COMMENT"), multispace1),
348 delimited(tag("\""), take_until("\""), tag("\"")),
349 ),
350 String::from,
351 ),
352 ))(i)
353 }
354
355 pub fn parse_if_exists(i: &str) -> IResult<&str, Option<&str>, ParseSQLError<&str>> {
357 opt(delimited(
358 multispace0,
359 delimited(tag_no_case("IF"), multispace1, tag_no_case("EXISTS")),
360 multispace0,
361 ))(i)
362 }
363
364 pub fn parse_quoted_string(i: &str) -> IResult<&str, String, ParseSQLError<&str>> {
366 alt((
367 map(delimited(tag("'"), take_until("'"), tag("'")), String::from),
368 map(
369 delimited(tag("\""), take_until("\""), tag("\"")),
370 String::from,
371 ),
372 ))(i)
373 }
374
375 pub fn parse_quoted_string_value_with_key(
377 i: &str,
378 key: String,
379 ) -> IResult<&str, String, ParseSQLError<&str>> {
380 alt((
381 map(
382 tuple((
383 tag_no_case(key.as_str()),
384 multispace1,
385 CommonParser::parse_quoted_string,
386 )),
387 |(_, _, value)| value,
388 ),
389 map(
390 tuple((
391 tag_no_case(key.as_str()),
392 multispace0,
393 tag("="),
394 multispace0,
395 CommonParser::parse_quoted_string,
396 )),
397 |(_, _, _, _, value)| value,
398 ),
399 ))(i)
400 }
401
402 pub fn parse_string_value_with_key(
404 i: &str,
405 key: String,
406 ) -> IResult<&str, String, ParseSQLError<&str>> {
407 alt((
408 map(
409 tuple((tag_no_case(key.as_str()), multispace1, Self::sql_identifier)),
410 |(_, _, value)| String::from(value),
411 ),
412 map(
413 tuple((
414 tag_no_case(key.as_str()),
415 multispace0,
416 tag("="),
417 multispace0,
418 Self::sql_identifier,
419 )),
420 |(_, _, _, _, value)| String::from(value),
421 ),
422 ))(i)
423 }
424
425 pub fn parse_digit_value_with_key(
427 i: &str,
428 key: String,
429 ) -> IResult<&str, String, ParseSQLError<&str>> {
430 alt((
431 map(
432 tuple((tag_no_case(key.as_str()), multispace1, Self::sql_identifier)),
433 |(_, _, value)| String::from(value),
434 ),
435 map(
436 tuple((
437 tag_no_case(key.as_str()),
438 multispace0,
439 tag("="),
440 multispace0,
441 digit1,
442 )),
443 |(_, _, _, _, value)| String::from(value),
444 ),
445 ))(i)
446 }
447
448 pub fn parse_default_value_with_key(
450 i: &str,
451 key: String,
452 ) -> IResult<&str, DefaultOrZeroOrOne, ParseSQLError<&str>> {
453 alt((
454 map(
455 tuple((
456 tag_no_case(key.as_str()),
457 multispace1,
458 DefaultOrZeroOrOne::parse,
459 )),
460 |(_, _, value)| value,
461 ),
462 map(
463 tuple((
464 tag_no_case(key.as_str()),
465 multispace0,
466 tag("="),
467 multispace0,
468 DefaultOrZeroOrOne::parse,
469 )),
470 |(_, _, _, _, value)| value,
471 ),
472 ))(i)
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use nom::bytes::complete::tag;
479 use nom::IResult;
480
481 use base::CommonParser;
482
483 #[test]
484 fn parse_sql_identifiers() {
485 let id1 = "foo";
486 let id2 = "f_o_o";
487 let id3 = "foo12";
488 let id4 = ":fo oo";
489 let id5 = "primary ";
490 let id6 = "`primary`";
491
492 assert!(CommonParser::sql_identifier(id1).is_ok());
493 assert!(CommonParser::sql_identifier(id2).is_ok());
494 assert!(CommonParser::sql_identifier(id3).is_ok());
495 assert!(CommonParser::sql_identifier(id4).is_err());
496 assert!(CommonParser::sql_identifier(id5).is_err());
497 assert!(CommonParser::sql_identifier(id6).is_ok());
498 }
499
500 fn test_opt_delimited_fn_call(i: &str) -> IResult<&str, &str> {
501 CommonParser::opt_delimited(tag("("), tag("abc"), tag(")"))(i)
502 }
503
504 #[test]
505 fn parse_opt_delimited() {
506 assert_eq!(test_opt_delimited_fn_call("abc"), Ok(("", "abc")));
507 assert_eq!(test_opt_delimited_fn_call("(abc)"), Ok(("", "abc")));
508 assert!(test_opt_delimited_fn_call("(abc").is_err());
509 assert_eq!(test_opt_delimited_fn_call("abc)"), Ok((")", "abc")));
510 assert!(test_opt_delimited_fn_call("ab").is_err());
511 }
512
513 #[test]
514 fn parse_comment() {
515 let res = CommonParser::parse_comment(" COMMENT 'test'");
516 assert_eq!(res.unwrap().1, "test");
517
518 let res = CommonParser::parse_comment(" COMMENT \"test\"");
519 assert_eq!(res.unwrap().1, "test");
520 }
521
522 #[test]
523 fn parse_statement_terminator() {
524 let res = CommonParser::statement_terminator(" ; ");
525 assert_eq!(res, Ok(("", ())));
526 }
527}