1use chumsky::prelude::*;
6use rust_decimal::Decimal;
7use std::str::FromStr;
8
9use crate::ast::{
10 BalancesQuery, BinaryOperator, ColumnDef, CreateTableStmt, Expr, FromClause, FunctionCall,
11 InsertSource, InsertStmt, JournalQuery, Literal, OrderSpec, PrintQuery, Query, SelectQuery,
12 SortDirection, Target, UnaryOperator, WindowFunction, WindowSpec,
13};
14use crate::error::{ParseError, ParseErrorKind};
15use rustledger_core::NaiveDate;
16
17type ParserInput<'a> = &'a str;
18type ParserExtra<'a> = extra::Err<Rich<'a, char>>;
19
20enum ComparisonSuffix {
22 Between(Expr, Expr),
23 Binary(BinaryOperator, Expr),
24 In(Expr),
26 NotIn(Expr),
28}
29
30pub fn parse(source: &str) -> Result<Query, ParseError> {
36 let (result, errs) = query_parser()
37 .then_ignore(ws())
38 .then_ignore(end())
39 .parse(source)
40 .into_output_errors();
41
42 if let Some(query) = result {
43 Ok(query)
44 } else {
45 let err = errs.first().map(|e| {
46 let kind = if e.found().is_none() {
47 ParseErrorKind::UnexpectedEof
48 } else {
49 ParseErrorKind::SyntaxError(e.to_string())
50 };
51 ParseError::new(kind, e.span().start)
52 });
53 Err(err.unwrap_or_else(|| ParseError::new(ParseErrorKind::UnexpectedEof, 0)))
54 }
55}
56
57fn ws<'a>() -> impl Parser<'a, ParserInput<'a>, (), ParserExtra<'a>> + Clone {
59 one_of(" \t\r\n").repeated().ignored()
60}
61
62fn ws1<'a>() -> impl Parser<'a, ParserInput<'a>, (), ParserExtra<'a>> + Clone {
64 one_of(" \t\r\n").repeated().at_least(1).ignored()
65}
66
67fn kw<'a>(keyword: &'static str) -> impl Parser<'a, ParserInput<'a>, (), ParserExtra<'a>> + Clone {
69 text::ident().try_map(move |s: &str, span| {
70 if s.eq_ignore_ascii_case(keyword) {
71 Ok(())
72 } else {
73 Err(Rich::custom(span, format!("expected keyword '{keyword}'")))
74 }
75 })
76}
77
78fn digits<'a>() -> impl Parser<'a, ParserInput<'a>, &'a str, ParserExtra<'a>> + Clone {
80 one_of("0123456789").repeated().at_least(1).to_slice()
81}
82
83fn query_parser<'a>() -> impl Parser<'a, ParserInput<'a>, Query, ParserExtra<'a>> {
85 ws().ignore_then(choice((
86 create_table_stmt().map(Query::CreateTable),
87 insert_stmt().map(Query::Insert),
88 select_query().map(|sq| Query::Select(Box::new(sq))),
89 journal_query().map(Query::Journal),
90 balances_query().map(Query::Balances),
91 print_query().map(Query::Print),
92 )))
93 .then_ignore(ws())
94 .then_ignore(just(';').or_not())
95}
96
97fn select_query<'a>() -> impl Parser<'a, ParserInput<'a>, SelectQuery, ParserExtra<'a>> {
99 recursive(|select_parser| {
100 let subquery_from = ws1()
102 .ignore_then(kw("FROM"))
103 .ignore_then(ws1())
104 .ignore_then(just('('))
105 .ignore_then(ws())
106 .ignore_then(select_parser)
107 .then_ignore(ws())
108 .then_ignore(just(')'))
109 .map(|sq| Some(FromClause::from_subquery(sq)));
110
111 let table_from = ws1()
115 .ignore_then(kw("FROM"))
116 .ignore_then(ws1())
117 .ignore_then(table_identifier().try_map(|name, span| {
118 if !name.starts_with('#') && name.contains(':') {
122 Err(Rich::custom(
123 span,
124 "table names cannot contain ':' - this looks like an account filter expression",
125 ))
126 } else {
127 Ok(name)
128 }
129 }))
130 .then_ignore(
131 ws().then(choice((
133 kw("WHERE").ignored(),
134 kw("GROUP").ignored(),
135 kw("ORDER").ignored(),
136 kw("HAVING").ignored(),
137 kw("LIMIT").ignored(),
138 kw("PIVOT").ignored(),
139 end().ignored(),
140 )))
141 .rewind(),
142 )
143 .map(|name| Some(FromClause::from_table(name)));
144
145 let regular_from = from_clause().map(Some);
147
148 kw("SELECT")
149 .ignore_then(ws1())
150 .ignore_then(
151 kw("DISTINCT")
152 .then_ignore(ws())
153 .or_not()
154 .map(|d| d.is_some()),
155 )
156 .then(targets())
157 .then(
158 subquery_from
159 .or(table_from)
160 .or(regular_from)
161 .or_not()
162 .map(std::option::Option::flatten),
163 )
164 .then(where_clause().or_not())
165 .then(group_by_clause().or_not())
166 .then(having_clause().or_not())
167 .then(pivot_by_clause().or_not())
168 .then(order_by_clause().or_not())
169 .then(limit_clause().or_not())
170 .map(
171 |(
172 (
173 (
174 (((((distinct, targets), from), where_clause), group_by), having),
175 pivot_by,
176 ),
177 order_by,
178 ),
179 limit,
180 )| {
181 SelectQuery {
182 distinct,
183 targets,
184 from,
185 where_clause,
186 group_by,
187 having,
188 pivot_by,
189 order_by,
190 limit,
191 }
192 },
193 )
194 })
195}
196
197fn from_clause<'a>() -> impl Parser<'a, ParserInput<'a>, FromClause, ParserExtra<'a>> + Clone {
199 ws1()
200 .ignore_then(kw("FROM"))
201 .ignore_then(ws1())
202 .ignore_then(from_modifiers())
203}
204
205fn targets<'a>() -> impl Parser<'a, ParserInput<'a>, Vec<Target>, ParserExtra<'a>> + Clone {
207 target()
208 .separated_by(ws().then(just(',')).then(ws()))
209 .at_least(1)
210 .collect()
211}
212
213fn target<'a>() -> impl Parser<'a, ParserInput<'a>, Target, ParserExtra<'a>> + Clone {
215 expr()
216 .then(
217 ws1()
218 .ignore_then(kw("AS"))
219 .ignore_then(ws1())
220 .ignore_then(identifier())
221 .or_not(),
222 )
223 .map(|(expr, alias)| Target { expr, alias })
224}
225
226fn from_modifiers<'a>() -> impl Parser<'a, ParserInput<'a>, FromClause, ParserExtra<'a>> + Clone {
228 let open_on = kw("OPEN")
229 .ignore_then(ws1())
230 .ignore_then(kw("ON"))
231 .ignore_then(ws1())
232 .ignore_then(date_literal())
233 .then_ignore(ws());
234
235 let close_on = kw("CLOSE")
236 .ignore_then(ws().then(kw("ON")).then(ws()).or_not())
237 .ignore_then(date_literal())
238 .then_ignore(ws());
239
240 let clear = kw("CLEAR").then_ignore(ws());
241
242 open_on
245 .or_not()
246 .then(close_on.or_not())
247 .then(clear.or_not().map(|c| c.is_some()))
248 .then(from_filter().or_not())
249 .map(|(((open_on, close_on), clear), filter)| FromClause {
250 open_on,
251 close_on,
252 clear,
253 filter,
254 subquery: None,
255 table_name: None,
256 })
257}
258
259fn from_filter<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
261 expr()
262}
263
264fn where_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
266 ws1()
267 .ignore_then(kw("WHERE"))
268 .ignore_then(ws1())
269 .ignore_then(expr())
270}
271
272fn group_by_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Vec<Expr>, ParserExtra<'a>> + Clone {
274 ws1()
275 .ignore_then(kw("GROUP"))
276 .ignore_then(ws1())
277 .ignore_then(kw("BY"))
278 .ignore_then(ws1())
279 .ignore_then(
280 expr()
281 .separated_by(ws().then(just(',')).then(ws()))
282 .at_least(1)
283 .collect(),
284 )
285}
286
287fn having_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
289 ws1()
290 .ignore_then(kw("HAVING"))
291 .ignore_then(ws1())
292 .ignore_then(expr())
293}
294
295fn pivot_by_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Vec<Expr>, ParserExtra<'a>> + Clone {
297 ws1()
298 .ignore_then(kw("PIVOT"))
299 .ignore_then(ws1())
300 .ignore_then(kw("BY"))
301 .ignore_then(ws1())
302 .ignore_then(
303 expr()
304 .separated_by(ws().then(just(',')).then(ws()))
305 .at_least(1)
306 .collect(),
307 )
308}
309
310fn order_by_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Vec<OrderSpec>, ParserExtra<'a>> + Clone
312{
313 ws1()
314 .ignore_then(kw("ORDER"))
315 .ignore_then(ws1())
316 .ignore_then(kw("BY"))
317 .ignore_then(ws1())
318 .ignore_then(
319 order_spec()
320 .separated_by(ws().then(just(',')).then(ws()))
321 .at_least(1)
322 .collect(),
323 )
324}
325
326fn order_spec<'a>() -> impl Parser<'a, ParserInput<'a>, OrderSpec, ParserExtra<'a>> + Clone {
328 expr()
329 .then(
330 ws1()
331 .ignore_then(choice((
332 kw("ASC").to(SortDirection::Asc),
333 kw("DESC").to(SortDirection::Desc),
334 )))
335 .or_not(),
336 )
337 .map(|(expr, dir)| OrderSpec {
338 expr,
339 direction: dir.unwrap_or_default(),
340 })
341}
342
343fn limit_clause<'a>() -> impl Parser<'a, ParserInput<'a>, u64, ParserExtra<'a>> + Clone {
345 ws1()
346 .ignore_then(kw("LIMIT"))
347 .ignore_then(ws1())
348 .ignore_then(integer())
349 .map(|n| n as u64)
350}
351
352fn journal_query<'a>() -> impl Parser<'a, ParserInput<'a>, JournalQuery, ParserExtra<'a>> + Clone {
354 kw("JOURNAL")
355 .ignore_then(
356 ws1().ignore_then(string_literal()).or_not(),
358 )
359 .then(at_function().or_not())
360 .then(
361 ws1()
362 .ignore_then(kw("FROM"))
363 .ignore_then(ws1())
364 .ignore_then(from_modifiers())
365 .or_not(),
366 )
367 .map(|((account_pattern, at_function), from)| JournalQuery {
368 account_pattern: account_pattern.unwrap_or_default(),
369 at_function,
370 from,
371 })
372}
373
374fn balances_query<'a>() -> impl Parser<'a, ParserInput<'a>, BalancesQuery, ParserExtra<'a>> + Clone
376{
377 kw("BALANCES")
378 .ignore_then(at_function().or_not())
379 .then(
380 ws1()
381 .ignore_then(kw("FROM"))
382 .ignore_then(ws1())
383 .ignore_then(from_modifiers())
384 .or_not(),
385 )
386 .map(|(at_function, from)| BalancesQuery { at_function, from })
387}
388
389fn print_query<'a>() -> impl Parser<'a, ParserInput<'a>, PrintQuery, ParserExtra<'a>> + Clone {
391 kw("PRINT")
392 .ignore_then(
393 ws1()
394 .ignore_then(kw("FROM"))
395 .ignore_then(ws1())
396 .ignore_then(from_modifiers())
397 .or_not(),
398 )
399 .map(|from| PrintQuery { from })
400}
401
402fn create_table_stmt<'a>() -> impl Parser<'a, ParserInput<'a>, CreateTableStmt, ParserExtra<'a>> {
404 let column_def = identifier()
406 .then(ws().ignore_then(identifier()).or_not())
407 .map(|(name, type_hint)| ColumnDef { name, type_hint });
408
409 let column_list = just('(')
410 .ignore_then(ws())
411 .ignore_then(
412 column_def
413 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
414 .collect::<Vec<_>>(),
415 )
416 .then_ignore(ws())
417 .then_ignore(just(')'));
418
419 let as_select = ws1()
420 .ignore_then(kw("AS"))
421 .ignore_then(ws1())
422 .ignore_then(select_query())
423 .map(Box::new);
424
425 kw("CREATE")
426 .ignore_then(ws1())
427 .ignore_then(kw("TABLE"))
428 .ignore_then(ws1())
429 .ignore_then(identifier())
430 .then(ws().ignore_then(column_list).or_not())
431 .then(as_select.or_not())
432 .map(|((table_name, columns), as_select)| CreateTableStmt {
433 table_name,
434 columns: columns.unwrap_or_default(),
435 as_select,
436 })
437}
438
439fn insert_stmt<'a>() -> impl Parser<'a, ParserInput<'a>, InsertStmt, ParserExtra<'a>> {
441 let column_list = just('(')
443 .ignore_then(ws())
444 .ignore_then(
445 identifier()
446 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
447 .collect::<Vec<_>>(),
448 )
449 .then_ignore(ws())
450 .then_ignore(just(')'));
451
452 let value_row = just('(')
454 .ignore_then(ws())
455 .ignore_then(
456 expr()
457 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
458 .collect::<Vec<_>>(),
459 )
460 .then_ignore(ws())
461 .then_ignore(just(')'));
462
463 let values_source = kw("VALUES")
464 .ignore_then(ws())
465 .ignore_then(
466 value_row
467 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
468 .collect::<Vec<_>>(),
469 )
470 .map(InsertSource::Values);
471
472 let select_source = select_query().map(|sq| InsertSource::Select(Box::new(sq)));
474
475 let source = choice((values_source, select_source));
476
477 kw("INSERT")
478 .ignore_then(ws1())
479 .ignore_then(kw("INTO"))
480 .ignore_then(ws1())
481 .ignore_then(identifier())
482 .then(ws().ignore_then(column_list).or_not())
483 .then_ignore(ws())
484 .then(source)
485 .map(|((table_name, columns), source)| InsertStmt {
486 table_name,
487 columns,
488 source,
489 })
490}
491
492fn at_function<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
494 ws1()
495 .ignore_then(kw("AT"))
496 .ignore_then(ws1())
497 .ignore_then(identifier())
498}
499
500#[allow(clippy::large_stack_frames)]
502fn expr<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
503 recursive(|expr| {
504 let primary = primary_expr(expr.clone());
505
506 let unary = just('-')
508 .then_ignore(ws())
509 .or_not()
510 .then(primary)
511 .map(|(neg, e)| {
512 if neg.is_some() {
513 Expr::unary(UnaryOperator::Neg, e)
514 } else {
515 e
516 }
517 });
518
519 let multiplicative = unary.clone().foldl(
521 ws().ignore_then(choice((
522 just('*').to(BinaryOperator::Mul),
523 just('/').to(BinaryOperator::Div),
524 just('%').to(BinaryOperator::Mod),
525 )))
526 .then_ignore(ws())
527 .then(unary)
528 .repeated(),
529 |left, (op, right)| Expr::binary(left, op, right),
530 );
531
532 let additive = multiplicative.clone().foldl(
534 ws().ignore_then(choice((
535 just('+').to(BinaryOperator::Add),
536 just('-').to(BinaryOperator::Sub),
537 )))
538 .then_ignore(ws())
539 .then(multiplicative)
540 .repeated(),
541 |left, (op, right)| Expr::binary(left, op, right),
542 );
543
544 let comparison = additive
546 .clone()
547 .then(
548 choice((
549 ws1()
551 .ignore_then(kw("BETWEEN"))
552 .ignore_then(ws1())
553 .ignore_then(additive.clone())
554 .then_ignore(ws1())
555 .then_ignore(kw("AND"))
556 .then_ignore(ws1())
557 .then(additive.clone())
558 .map(|(low, high)| ComparisonSuffix::Between(low, high)),
559 ws1()
561 .ignore_then(kw("NOT"))
562 .ignore_then(ws1())
563 .ignore_then(kw("IN"))
564 .ignore_then(ws())
565 .ignore_then(choice((
566 set_literal(expr.clone()),
567 additive.clone(),
568 )))
569 .map(ComparisonSuffix::NotIn),
570 ws1()
572 .ignore_then(kw("IN"))
573 .ignore_then(ws())
574 .ignore_then(choice((
575 set_literal(expr.clone()),
576 additive.clone(),
577 )))
578 .map(ComparisonSuffix::In),
579 ws()
581 .ignore_then(comparison_op())
582 .then_ignore(ws())
583 .then(additive)
584 .map(|(op, right)| ComparisonSuffix::Binary(op, right)),
585 ))
586 .or_not(),
587 )
588 .map(|(left, suffix)| match suffix {
589 Some(ComparisonSuffix::Between(low, high)) => Expr::between(left, low, high),
590 Some(ComparisonSuffix::Binary(op, right)) => Expr::binary(left, op, right),
591 Some(ComparisonSuffix::In(right)) => Expr::binary(left, BinaryOperator::In, right),
592 Some(ComparisonSuffix::NotIn(right)) => {
593 Expr::binary(left, BinaryOperator::NotIn, right)
594 }
595 None => left,
596 })
597 .then(
599 ws1()
600 .ignore_then(kw("IS"))
601 .ignore_then(ws1())
602 .ignore_then(choice((
603 kw("NOT")
604 .ignore_then(ws1())
605 .ignore_then(kw("NULL"))
606 .to(UnaryOperator::IsNotNull),
607 kw("NULL").to(UnaryOperator::IsNull),
608 )))
609 .or_not(),
610 )
611 .map(|(expr, is_null)| {
612 if let Some(op) = is_null {
613 Expr::unary(op, expr)
614 } else {
615 expr
616 }
617 });
618
619 let not_expr = kw("NOT")
621 .ignore_then(ws1())
622 .repeated()
623 .collect::<Vec<_>>()
624 .then(comparison)
625 .map(|(nots, e)| {
626 nots.into_iter()
627 .fold(e, |acc, ()| Expr::unary(UnaryOperator::Not, acc))
628 });
629
630 let and_expr = not_expr.clone().foldl(
632 ws1()
633 .ignore_then(kw("AND"))
634 .ignore_then(ws1())
635 .ignore_then(not_expr)
636 .repeated(),
637 |left, right| Expr::binary(left, BinaryOperator::And, right),
638 );
639
640 and_expr.clone().foldl(
642 ws1()
643 .ignore_then(kw("OR"))
644 .ignore_then(ws1())
645 .ignore_then(and_expr)
646 .repeated(),
647 |left, right| Expr::binary(left, BinaryOperator::Or, right),
648 )
649 })
650}
651
652fn comparison_op<'a>() -> impl Parser<'a, ParserInput<'a>, BinaryOperator, ParserExtra<'a>> + Clone
654{
655 choice((
656 just("!=").to(BinaryOperator::Ne),
658 just("!~").to(BinaryOperator::NotRegex),
659 just("<=").to(BinaryOperator::Le),
660 just(">=").to(BinaryOperator::Ge),
661 just('=').to(BinaryOperator::Eq),
663 just('<').to(BinaryOperator::Lt),
664 just('>').to(BinaryOperator::Gt),
665 just('~').to(BinaryOperator::Regex),
666 ))
667}
668
669fn set_literal<'a>(
679 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
680) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
681 just('(')
682 .ignore_then(ws())
683 .ignore_then(
684 expr.clone()
686 .then(
687 ws().ignore_then(just(',')).ignore_then(ws()).ignore_then(
691 expr.separated_by(ws().then(just(',')).then(ws()))
692 .allow_trailing()
693 .collect::<Vec<_>>(),
694 ),
695 )
696 .map(|(first, rest)| {
697 let mut elements = vec![first];
698 elements.extend(rest);
699 elements
700 }),
701 )
702 .then_ignore(ws())
703 .then_ignore(just(')'))
704 .map(Expr::Set)
705}
706
707fn primary_expr<'a>(
709 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
710) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
711 choice((
712 just('(')
714 .ignore_then(ws())
715 .ignore_then(expr.clone())
716 .then_ignore(ws())
717 .then_ignore(just(')'))
718 .map(|e| Expr::Paren(Box::new(e))),
719 function_call_or_column(expr),
722 literal().map(Expr::Literal),
724 just('*').to(Expr::Wildcard),
726 ))
727}
728
729fn function_call_or_column<'a>(
731 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
732) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
733 identifier()
734 .then(
735 ws().ignore_then(just('('))
736 .ignore_then(ws())
737 .ignore_then(function_args(expr))
738 .then_ignore(ws())
739 .then_ignore(just(')'))
740 .or_not(),
741 )
742 .then(
743 ws1()
745 .ignore_then(kw("OVER"))
746 .ignore_then(ws())
747 .ignore_then(just('('))
748 .ignore_then(ws())
749 .ignore_then(window_spec())
750 .then_ignore(ws())
751 .then_ignore(just(')'))
752 .or_not(),
753 )
754 .map(|((name, args), over)| {
755 if let Some(args) = args {
756 if let Some(window_spec) = over {
757 Expr::Window(WindowFunction {
759 name,
760 args,
761 over: window_spec,
762 })
763 } else {
764 Expr::Function(FunctionCall { name, args })
766 }
767 } else {
768 Expr::Column(name)
769 }
770 })
771}
772
773fn window_spec<'a>() -> impl Parser<'a, ParserInput<'a>, WindowSpec, ParserExtra<'a>> + Clone {
775 let partition_by = kw("PARTITION")
776 .ignore_then(ws1())
777 .ignore_then(kw("BY"))
778 .ignore_then(ws1())
779 .ignore_then(
780 simple_arg()
781 .separated_by(ws().then(just(',')).then(ws()))
782 .at_least(1)
783 .collect::<Vec<_>>(),
784 )
785 .then_ignore(ws());
786
787 let window_order_by = kw("ORDER")
788 .ignore_then(ws1())
789 .ignore_then(kw("BY"))
790 .ignore_then(ws1())
791 .ignore_then(
792 window_order_spec()
793 .separated_by(ws().then(just(',')).then(ws()))
794 .at_least(1)
795 .collect::<Vec<_>>(),
796 );
797
798 partition_by
799 .or_not()
800 .then(window_order_by.or_not())
801 .map(|(partition_by, order_by)| WindowSpec {
802 partition_by,
803 order_by,
804 })
805}
806
807fn window_order_spec<'a>() -> impl Parser<'a, ParserInput<'a>, OrderSpec, ParserExtra<'a>> + Clone {
809 simple_arg()
810 .then(
811 ws1()
812 .ignore_then(choice((
813 kw("ASC").to(SortDirection::Asc),
814 kw("DESC").to(SortDirection::Desc),
815 )))
816 .or_not(),
817 )
818 .map(|(expr, dir)| OrderSpec {
819 expr,
820 direction: dir.unwrap_or_default(),
821 })
822}
823
824fn function_args<'a>(
826 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
827) -> impl Parser<'a, ParserInput<'a>, Vec<Expr>, ParserExtra<'a>> + Clone {
828 expr.separated_by(ws().then(just(',')).then(ws())).collect()
831}
832
833fn simple_arg<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
835 choice((
836 just('*').to(Expr::Wildcard),
837 identifier().map(Expr::Column),
838 literal().map(Expr::Literal),
839 ))
840}
841
842fn literal<'a>() -> impl Parser<'a, ParserInput<'a>, Literal, ParserExtra<'a>> + Clone {
844 choice((
845 kw("TRUE").to(Literal::Boolean(true)),
847 kw("FALSE").to(Literal::Boolean(false)),
848 kw("NULL").to(Literal::Null),
849 date_literal().map(Literal::Date),
851 decimal().map(Literal::Number),
853 string_literal().map(Literal::String),
855 ))
856}
857
858fn identifier<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
860 text::ident().map(|s: &str| s.to_string())
861}
862
863fn table_identifier<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
866 choice((
867 just('#')
869 .ignore_then(text::ident())
870 .map(|s: &str| format!("#{s}")),
871 text::ident().map(|s: &str| s.to_string()),
873 ))
874}
875
876fn string_literal<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
878 let double_quoted = just('"')
880 .ignore_then(
881 none_of("\"\\")
882 .or(just('\\').ignore_then(any()))
883 .repeated()
884 .collect::<String>(),
885 )
886 .then_ignore(just('"'));
887
888 let single_quoted = just('\'')
890 .ignore_then(
891 none_of("'\\")
892 .or(just('\\').ignore_then(any()))
893 .repeated()
894 .collect::<String>(),
895 )
896 .then_ignore(just('\''));
897
898 choice((double_quoted, single_quoted))
899}
900
901fn date_literal<'a>() -> impl Parser<'a, ParserInput<'a>, NaiveDate, ParserExtra<'a>> + Clone {
903 digits()
904 .then_ignore(just('-'))
905 .then(digits())
906 .then_ignore(just('-'))
907 .then(digits())
908 .try_map(|((year, month), day): ((&str, &str), &str), span| {
909 let year: i32 = year
910 .parse()
911 .map_err(|_| Rich::custom(span, "invalid year"))?;
912 let month: u32 = month
913 .parse()
914 .map_err(|_| Rich::custom(span, "invalid month"))?;
915 let day: u32 = day.parse().map_err(|_| Rich::custom(span, "invalid day"))?;
916 NaiveDate::from_ymd_opt(year, month, day)
917 .ok_or_else(|| Rich::custom(span, "invalid date"))
918 })
919}
920
921fn decimal<'a>() -> impl Parser<'a, ParserInput<'a>, Decimal, ParserExtra<'a>> + Clone {
923 just('-')
924 .or_not()
925 .then(digits())
926 .then(just('.').then(digits()).or_not())
927 .try_map(
928 |((neg, int_part), frac_part): ((Option<char>, &str), Option<(char, &str)>), span| {
929 let mut s = String::new();
930 if neg.is_some() {
931 s.push('-');
932 }
933 s.push_str(int_part);
934 if let Some((_, frac)) = frac_part {
935 s.push('.');
936 s.push_str(frac);
937 }
938 Decimal::from_str(&s).map_err(|_| Rich::custom(span, "invalid number"))
939 },
940 )
941}
942
943fn integer<'a>() -> impl Parser<'a, ParserInput<'a>, i64, ParserExtra<'a>> + Clone {
945 digits().try_map(|s: &str, span| {
946 s.parse::<i64>()
947 .map_err(|_| Rich::custom(span, "invalid integer"))
948 })
949}
950
951#[cfg(test)]
952mod tests {
953 use super::*;
954 use rust_decimal_macros::dec;
955
956 #[test]
957 fn test_simple_select() {
958 let query = parse("SELECT * FROM year = 2024").unwrap();
959 match query {
960 Query::Select(sel) => {
961 assert!(!sel.distinct);
962 assert_eq!(sel.targets.len(), 1);
963 assert!(matches!(sel.targets[0].expr, Expr::Wildcard));
964 assert!(sel.from.is_some());
965 }
966 _ => panic!("Expected SELECT query"),
967 }
968 }
969
970 #[test]
971 fn test_select_columns() {
972 let query = parse("SELECT date, account, position").unwrap();
973 match query {
974 Query::Select(sel) => {
975 assert_eq!(sel.targets.len(), 3);
976 assert!(matches!(&sel.targets[0].expr, Expr::Column(c) if c == "date"));
977 assert!(matches!(&sel.targets[1].expr, Expr::Column(c) if c == "account"));
978 assert!(matches!(&sel.targets[2].expr, Expr::Column(c) if c == "position"));
979 }
980 _ => panic!("Expected SELECT query"),
981 }
982 }
983
984 #[test]
985 fn test_select_with_alias() {
986 let query = parse("SELECT SUM(position) AS total").unwrap();
987 match query {
988 Query::Select(sel) => {
989 assert_eq!(sel.targets.len(), 1);
990 assert_eq!(sel.targets[0].alias, Some("total".to_string()));
991 match &sel.targets[0].expr {
992 Expr::Function(f) => {
993 assert_eq!(f.name, "SUM");
994 assert_eq!(f.args.len(), 1);
995 }
996 _ => panic!("Expected function"),
997 }
998 }
999 _ => panic!("Expected SELECT query"),
1000 }
1001 }
1002
1003 #[test]
1004 fn test_select_distinct() {
1005 let query = parse("SELECT DISTINCT account").unwrap();
1006 match query {
1007 Query::Select(sel) => {
1008 assert!(sel.distinct);
1009 }
1010 _ => panic!("Expected SELECT query"),
1011 }
1012 }
1013
1014 #[test]
1015 fn test_select_distinct_no_space() {
1016 let query = parse("SELECT DISTINCT(account) FROM postings").unwrap();
1018 match query {
1019 Query::Select(sel) => {
1020 assert!(sel.distinct);
1021 }
1022 _ => panic!("Expected SELECT query"),
1023 }
1024 }
1025
1026 #[test]
1027 fn test_select_distinct_coalesce_no_space() {
1028 let query = parse("SELECT DISTINCT(COALESCE(payee, narration)) as payee FROM transactions")
1030 .unwrap();
1031 match query {
1032 Query::Select(sel) => {
1033 assert!(sel.distinct);
1034 }
1035 _ => panic!("Expected SELECT query"),
1036 }
1037 }
1038
1039 #[test]
1040 fn test_where_clause() {
1041 let query = parse("SELECT * WHERE account ~ \"Expenses:\"").unwrap();
1042 match query {
1043 Query::Select(sel) => {
1044 assert!(sel.where_clause.is_some());
1045 match sel.where_clause.unwrap() {
1046 Expr::BinaryOp(op) => {
1047 assert_eq!(op.op, BinaryOperator::Regex);
1048 }
1049 _ => panic!("Expected binary op"),
1050 }
1051 }
1052 _ => panic!("Expected SELECT query"),
1053 }
1054 }
1055
1056 #[test]
1057 fn test_group_by() {
1058 let query = parse("SELECT account, SUM(position) GROUP BY account").unwrap();
1059 match query {
1060 Query::Select(sel) => {
1061 assert!(sel.group_by.is_some());
1062 assert_eq!(sel.group_by.unwrap().len(), 1);
1063 }
1064 _ => panic!("Expected SELECT query"),
1065 }
1066 }
1067
1068 #[test]
1069 fn test_order_by() {
1070 let query = parse("SELECT * ORDER BY date DESC, account ASC").unwrap();
1071 match query {
1072 Query::Select(sel) => {
1073 assert!(sel.order_by.is_some());
1074 let order = sel.order_by.unwrap();
1075 assert_eq!(order.len(), 2);
1076 assert_eq!(order[0].direction, SortDirection::Desc);
1077 assert_eq!(order[1].direction, SortDirection::Asc);
1078 }
1079 _ => panic!("Expected SELECT query"),
1080 }
1081 }
1082
1083 #[test]
1084 fn test_limit() {
1085 let query = parse("SELECT * LIMIT 100").unwrap();
1086 match query {
1087 Query::Select(sel) => {
1088 assert_eq!(sel.limit, Some(100));
1089 }
1090 _ => panic!("Expected SELECT query"),
1091 }
1092 }
1093
1094 #[test]
1095 fn test_from_open_close_clear() {
1096 let query = parse("SELECT * FROM OPEN ON 2024-01-01 CLOSE ON 2024-12-31 CLEAR").unwrap();
1097 match query {
1098 Query::Select(sel) => {
1099 let from = sel.from.unwrap();
1100 assert_eq!(
1101 from.open_on,
1102 Some(NaiveDate::from_ymd_opt(2024, 1, 1).unwrap())
1103 );
1104 assert_eq!(
1105 from.close_on,
1106 Some(NaiveDate::from_ymd_opt(2024, 12, 31).unwrap())
1107 );
1108 assert!(from.clear);
1109 }
1110 _ => panic!("Expected SELECT query"),
1111 }
1112 }
1113
1114 #[test]
1115 fn test_from_year_filter() {
1116 let query = parse("SELECT date, account FROM year = 2024").unwrap();
1117 match query {
1118 Query::Select(sel) => {
1119 let from = sel.from.unwrap();
1120 assert!(from.filter.is_some(), "FROM filter should be present");
1121 match from.filter.unwrap() {
1122 Expr::BinaryOp(op) => {
1123 assert_eq!(op.op, BinaryOperator::Eq);
1124 assert!(matches!(op.left, Expr::Column(ref c) if c == "year"));
1125 match op.right {
1127 Expr::Literal(Literal::Integer(n)) => assert_eq!(n, 2024),
1128 Expr::Literal(Literal::Number(n)) => assert_eq!(n, dec!(2024)),
1129 other => panic!("Expected numeric literal, got {other:?}"),
1130 }
1131 }
1132 other => panic!("Expected BinaryOp, got {other:?}"),
1133 }
1134 }
1135 _ => panic!("Expected SELECT query"),
1136 }
1137 }
1138
1139 #[test]
1140 fn test_journal_query() {
1141 let query = parse("JOURNAL \"Assets:Bank\" AT cost").unwrap();
1142 match query {
1143 Query::Journal(j) => {
1144 assert_eq!(j.account_pattern, "Assets:Bank");
1145 assert_eq!(j.at_function, Some("cost".to_string()));
1146 }
1147 _ => panic!("Expected JOURNAL query"),
1148 }
1149 }
1150
1151 #[test]
1152 fn test_balances_query() {
1153 let query = parse("BALANCES AT units FROM year = 2024").unwrap();
1154 match query {
1155 Query::Balances(b) => {
1156 assert_eq!(b.at_function, Some("units".to_string()));
1157 assert!(b.from.is_some());
1158 }
1159 _ => panic!("Expected BALANCES query"),
1160 }
1161 }
1162
1163 #[test]
1164 fn test_print_query() {
1165 let query = parse("PRINT").unwrap();
1166 assert!(matches!(query, Query::Print(_)));
1167 }
1168
1169 #[test]
1170 fn test_complex_expression() {
1171 let query = parse("SELECT * WHERE date >= 2024-01-01 AND account ~ \"Expenses:\"").unwrap();
1172 match query {
1173 Query::Select(sel) => match sel.where_clause.unwrap() {
1174 Expr::BinaryOp(op) => {
1175 assert_eq!(op.op, BinaryOperator::And);
1176 }
1177 _ => panic!("Expected AND"),
1178 },
1179 _ => panic!("Expected SELECT query"),
1180 }
1181 }
1182
1183 #[test]
1184 fn test_number_literal() {
1185 let query = parse("SELECT * WHERE year = 2024").unwrap();
1186 match query {
1187 Query::Select(sel) => match sel.where_clause.unwrap() {
1188 Expr::BinaryOp(op) => match op.right {
1189 Expr::Literal(Literal::Number(n)) => {
1190 assert_eq!(n, dec!(2024));
1191 }
1192 _ => panic!("Expected number literal"),
1193 },
1194 _ => panic!("Expected binary op"),
1195 },
1196 _ => panic!("Expected SELECT query"),
1197 }
1198 }
1199
1200 #[test]
1201 fn test_semicolon_optional() {
1202 assert!(parse("SELECT *").is_ok());
1203 assert!(parse("SELECT *;").is_ok());
1204 }
1205
1206 #[test]
1207 fn test_subquery_basic() {
1208 let query = parse("SELECT * FROM (SELECT account, position)").unwrap();
1209 match query {
1210 Query::Select(sel) => {
1211 assert!(sel.from.is_some());
1212 let from = sel.from.unwrap();
1213 assert!(from.subquery.is_some());
1214 let subquery = from.subquery.unwrap();
1215 assert_eq!(subquery.targets.len(), 2);
1216 }
1217 _ => panic!("Expected SELECT query"),
1218 }
1219 }
1220
1221 #[test]
1222 fn test_subquery_with_groupby() {
1223 let query = parse(
1224 "SELECT account, total FROM (SELECT account, SUM(position) AS total GROUP BY account)",
1225 )
1226 .unwrap();
1227 match query {
1228 Query::Select(sel) => {
1229 assert_eq!(sel.targets.len(), 2);
1230 let from = sel.from.unwrap();
1231 assert!(from.subquery.is_some());
1232 let subquery = from.subquery.unwrap();
1233 assert!(subquery.group_by.is_some());
1234 }
1235 _ => panic!("Expected SELECT query"),
1236 }
1237 }
1238
1239 #[test]
1240 fn test_subquery_with_outer_where() {
1241 let query =
1242 parse("SELECT * FROM (SELECT * WHERE year = 2024) WHERE account ~ \"Expenses:\"")
1243 .unwrap();
1244 match query {
1245 Query::Select(sel) => {
1246 assert!(sel.where_clause.is_some());
1248 let from = sel.from.unwrap();
1250 let subquery = from.subquery.unwrap();
1251 assert!(subquery.where_clause.is_some());
1252 }
1253 _ => panic!("Expected SELECT query"),
1254 }
1255 }
1256
1257 #[test]
1258 fn test_nested_subquery() {
1259 let query = parse("SELECT * FROM (SELECT * FROM (SELECT account))").unwrap();
1261 match query {
1262 Query::Select(sel) => {
1263 let from = sel.from.unwrap();
1264 let subquery1 = from.subquery.unwrap();
1265 let from2 = subquery1.from.unwrap();
1266 assert!(from2.subquery.is_some());
1267 }
1268 _ => panic!("Expected SELECT query"),
1269 }
1270 }
1271
1272 #[test]
1273 fn test_nested_function_calls() {
1274 let query = parse("SELECT units(sum(position))").unwrap();
1276 match query {
1277 Query::Select(sel) => {
1278 assert_eq!(sel.targets.len(), 1);
1279 match &sel.targets[0].expr {
1280 Expr::Function(outer) => {
1281 assert_eq!(outer.name, "units");
1282 assert_eq!(outer.args.len(), 1);
1283 match &outer.args[0] {
1284 Expr::Function(inner) => {
1285 assert_eq!(inner.name, "sum");
1286 assert_eq!(inner.args.len(), 1);
1287 assert!(
1288 matches!(&inner.args[0], Expr::Column(c) if c == "position")
1289 );
1290 }
1291 _ => panic!("Expected inner function call"),
1292 }
1293 }
1294 _ => panic!("Expected outer function call"),
1295 }
1296 }
1297 _ => panic!("Expected SELECT query"),
1298 }
1299 }
1300
1301 #[test]
1302 fn test_deeply_nested_function_calls() {
1303 let query = parse("SELECT foo(bar(baz(x)))").unwrap();
1305 match query {
1306 Query::Select(sel) => {
1307 assert_eq!(sel.targets.len(), 1);
1308 match &sel.targets[0].expr {
1309 Expr::Function(f1) => {
1310 assert_eq!(f1.name, "foo");
1311 match &f1.args[0] {
1312 Expr::Function(f2) => {
1313 assert_eq!(f2.name, "bar");
1314 match &f2.args[0] {
1315 Expr::Function(f3) => {
1316 assert_eq!(f3.name, "baz");
1317 assert!(matches!(&f3.args[0], Expr::Column(c) if c == "x"));
1318 }
1319 _ => panic!("Expected f3"),
1320 }
1321 }
1322 _ => panic!("Expected f2"),
1323 }
1324 }
1325 _ => panic!("Expected f1"),
1326 }
1327 }
1328 _ => panic!("Expected SELECT query"),
1329 }
1330 }
1331
1332 #[test]
1333 fn test_function_with_arithmetic() {
1334 let query = parse("SELECT sum(amount * 2)").unwrap();
1336 match query {
1337 Query::Select(sel) => match &sel.targets[0].expr {
1338 Expr::Function(f) => {
1339 assert_eq!(f.name, "sum");
1340 assert!(matches!(&f.args[0], Expr::BinaryOp(_)));
1341 }
1342 _ => panic!("Expected function"),
1343 },
1344 _ => panic!("Expected SELECT query"),
1345 }
1346 }
1347
1348 #[test]
1349 fn test_is_null() {
1350 let query = parse("SELECT * WHERE payee IS NULL").unwrap();
1351 match query {
1352 Query::Select(sel) => match sel.where_clause.unwrap() {
1353 Expr::UnaryOp(op) => {
1354 assert_eq!(op.op, UnaryOperator::IsNull);
1355 assert!(matches!(&op.operand, Expr::Column(c) if c == "payee"));
1356 }
1357 _ => panic!("Expected unary op"),
1358 },
1359 _ => panic!("Expected SELECT query"),
1360 }
1361 }
1362
1363 #[test]
1364 fn test_is_not_null() {
1365 let query = parse("SELECT * WHERE payee IS NOT NULL").unwrap();
1366 match query {
1367 Query::Select(sel) => match sel.where_clause.unwrap() {
1368 Expr::UnaryOp(op) => {
1369 assert_eq!(op.op, UnaryOperator::IsNotNull);
1370 assert!(matches!(&op.operand, Expr::Column(c) if c == "payee"));
1371 }
1372 _ => panic!("Expected unary op"),
1373 },
1374 _ => panic!("Expected SELECT query"),
1375 }
1376 }
1377
1378 #[test]
1379 fn test_not_regex() {
1380 let query = parse("SELECT * WHERE account !~ \"Assets:\"").unwrap();
1381 match query {
1382 Query::Select(sel) => match sel.where_clause.unwrap() {
1383 Expr::BinaryOp(op) => {
1384 assert_eq!(op.op, BinaryOperator::NotRegex);
1385 }
1386 _ => panic!("Expected binary op"),
1387 },
1388 _ => panic!("Expected SELECT query"),
1389 }
1390 }
1391
1392 #[test]
1393 fn test_modulo() {
1394 let query = parse("SELECT year % 4").unwrap();
1395 match query {
1396 Query::Select(sel) => match &sel.targets[0].expr {
1397 Expr::BinaryOp(op) => {
1398 assert_eq!(op.op, BinaryOperator::Mod);
1399 }
1400 _ => panic!("Expected binary op"),
1401 },
1402 _ => panic!("Expected SELECT query"),
1403 }
1404 }
1405
1406 #[test]
1407 fn test_between() {
1408 let query = parse("SELECT * WHERE year BETWEEN 2020 AND 2024").unwrap();
1409 match query {
1410 Query::Select(sel) => match sel.where_clause.unwrap() {
1411 Expr::Between { value, low, high } => {
1412 assert!(matches!(*value, Expr::Column(c) if c == "year"));
1413 assert!(matches!(*low, Expr::Literal(Literal::Number(_))));
1414 assert!(matches!(*high, Expr::Literal(Literal::Number(_))));
1415 }
1416 _ => panic!("Expected BETWEEN"),
1417 },
1418 _ => panic!("Expected SELECT query"),
1419 }
1420 }
1421
1422 #[test]
1423 fn test_not_in() {
1424 let query = parse("SELECT * WHERE account NOT IN tags").unwrap();
1425 match query {
1426 Query::Select(sel) => match sel.where_clause.unwrap() {
1427 Expr::BinaryOp(op) => {
1428 assert_eq!(op.op, BinaryOperator::NotIn);
1429 }
1430 _ => panic!("Expected binary op"),
1431 },
1432 _ => panic!("Expected SELECT query"),
1433 }
1434 }
1435
1436 #[test]
1437 fn test_in_set_literal() {
1438 let query = parse("SELECT * WHERE currency IN ('EUR', 'USD')").unwrap();
1440 match query {
1441 Query::Select(sel) => match sel.where_clause.unwrap() {
1442 Expr::BinaryOp(op) => {
1443 assert_eq!(op.op, BinaryOperator::In);
1444 match op.right {
1445 Expr::Set(elements) => {
1446 assert_eq!(elements.len(), 2);
1447 }
1448 _ => panic!("Expected Set"),
1449 }
1450 }
1451 _ => panic!("Expected binary op"),
1452 },
1453 _ => panic!("Expected SELECT query"),
1454 }
1455
1456 let query = parse("SELECT * WHERE currency IN ('EUR',)").unwrap();
1458 match query {
1459 Query::Select(sel) => match sel.where_clause.unwrap() {
1460 Expr::BinaryOp(op) => {
1461 assert_eq!(op.op, BinaryOperator::In);
1462 match op.right {
1463 Expr::Set(elements) => {
1464 assert_eq!(elements.len(), 1);
1465 }
1466 _ => panic!("Expected Set"),
1467 }
1468 }
1469 _ => panic!("Expected binary op"),
1470 },
1471 _ => panic!("Expected SELECT query"),
1472 }
1473
1474 let query = parse("SELECT * WHERE 'x' IN (tags)").unwrap();
1476 match query {
1477 Query::Select(sel) => match sel.where_clause.unwrap() {
1478 Expr::BinaryOp(op) => {
1479 assert_eq!(op.op, BinaryOperator::In);
1480 match op.right {
1482 Expr::Paren(inner) => match *inner {
1483 Expr::Column(name) => assert_eq!(name, "tags"),
1484 _ => panic!("Expected Column inside Paren"),
1485 },
1486 other => panic!("Expected Paren, got {other:?}"),
1487 }
1488 }
1489 _ => panic!("Expected binary op"),
1490 },
1491 _ => panic!("Expected SELECT query"),
1492 }
1493 }
1494
1495 #[test]
1496 fn test_string_arg_function() {
1497 let query = parse("SELECT foo(x)").unwrap();
1499 match query {
1500 Query::Select(sel) => match &sel.targets[0].expr {
1501 Expr::Function(f) => {
1502 assert_eq!(f.name, "foo");
1503 }
1504 _ => panic!("Expected function"),
1505 },
1506 _ => panic!("Expected SELECT query"),
1507 }
1508
1509 let query = parse("SELECT foo('bar')").unwrap();
1511 match query {
1512 Query::Select(sel) => match &sel.targets[0].expr {
1513 Expr::Function(f) => {
1514 assert_eq!(f.name, "foo");
1515 assert!(matches!(&f.args[0], Expr::Literal(Literal::String(s)) if s == "bar"));
1516 }
1517 _ => panic!("Expected function"),
1518 },
1519 _ => panic!("Expected SELECT query"),
1520 }
1521 }
1522
1523 #[test]
1524 fn test_meta_function() {
1525 let query = parse("SELECT meta('category')").unwrap();
1526 match query {
1527 Query::Select(sel) => match &sel.targets[0].expr {
1528 Expr::Function(f) => {
1529 assert_eq!(f.name.to_uppercase(), "META");
1530 assert_eq!(f.args.len(), 1);
1531 assert!(
1532 matches!(&f.args[0], Expr::Literal(Literal::String(s)) if s == "category")
1533 );
1534 }
1535 _ => panic!("Expected function"),
1536 },
1537 _ => panic!("Expected SELECT query"),
1538 }
1539 }
1540
1541 #[test]
1542 fn test_entry_meta_function() {
1543 let query = parse("SELECT entry_meta('source')").unwrap();
1544 match query {
1545 Query::Select(sel) => match &sel.targets[0].expr {
1546 Expr::Function(f) => {
1547 assert_eq!(f.name.to_uppercase(), "ENTRY_META");
1548 assert_eq!(f.args.len(), 1);
1549 }
1550 _ => panic!("Expected function"),
1551 },
1552 _ => panic!("Expected SELECT query"),
1553 }
1554 }
1555
1556 #[test]
1557 fn test_convert_function() {
1558 let query = parse("SELECT convert(position, 'USD')").unwrap();
1559 match query {
1560 Query::Select(sel) => match &sel.targets[0].expr {
1561 Expr::Function(f) => {
1562 assert_eq!(f.name.to_uppercase(), "CONVERT");
1563 assert_eq!(f.args.len(), 2);
1564 }
1565 _ => panic!("Expected function"),
1566 },
1567 _ => panic!("Expected SELECT query"),
1568 }
1569 }
1570
1571 #[test]
1572 fn test_type_cast_functions() {
1573 let query = parse("SELECT int(number)").unwrap();
1575 match query {
1576 Query::Select(sel) => match &sel.targets[0].expr {
1577 Expr::Function(f) => {
1578 assert_eq!(f.name.to_uppercase(), "INT");
1579 assert_eq!(f.args.len(), 1);
1580 }
1581 _ => panic!("Expected function"),
1582 },
1583 _ => panic!("Expected SELECT query"),
1584 }
1585
1586 let query = parse("SELECT decimal('123.45')").unwrap();
1588 match query {
1589 Query::Select(sel) => match &sel.targets[0].expr {
1590 Expr::Function(f) => {
1591 assert_eq!(f.name.to_uppercase(), "DECIMAL");
1592 }
1593 _ => panic!("Expected function"),
1594 },
1595 _ => panic!("Expected SELECT query"),
1596 }
1597
1598 let query = parse("SELECT str(123)").unwrap();
1600 match query {
1601 Query::Select(sel) => match &sel.targets[0].expr {
1602 Expr::Function(f) => {
1603 assert_eq!(f.name.to_uppercase(), "STR");
1604 }
1605 _ => panic!("Expected function"),
1606 },
1607 _ => panic!("Expected SELECT query"),
1608 }
1609
1610 let query = parse("SELECT bool(1)").unwrap();
1612 match query {
1613 Query::Select(sel) => match &sel.targets[0].expr {
1614 Expr::Function(f) => {
1615 assert_eq!(f.name.to_uppercase(), "BOOL");
1616 }
1617 _ => panic!("Expected function"),
1618 },
1619 _ => panic!("Expected SELECT query"),
1620 }
1621 }
1622
1623 #[test]
1624 fn test_system_table_prices() {
1625 let query = parse("SELECT date, currency, amount FROM #prices").unwrap();
1627 match query {
1628 Query::Select(sel) => {
1629 assert_eq!(sel.targets.len(), 3);
1630 assert!(matches!(&sel.targets[0].expr, Expr::Column(c) if c == "date"));
1631 assert!(matches!(&sel.targets[1].expr, Expr::Column(c) if c == "currency"));
1632 assert!(matches!(&sel.targets[2].expr, Expr::Column(c) if c == "amount"));
1633 let from = sel.from.unwrap();
1634 assert_eq!(from.table_name, Some("#prices".to_string()));
1635 }
1636 _ => panic!("Expected SELECT query"),
1637 }
1638 }
1639
1640 #[test]
1641 fn test_system_table_with_where() {
1642 let query = parse("SELECT * FROM #prices WHERE currency = 'EUR'").unwrap();
1644 match query {
1645 Query::Select(sel) => {
1646 let from = sel.from.unwrap();
1647 assert_eq!(from.table_name, Some("#prices".to_string()));
1648 assert!(sel.where_clause.is_some());
1649 }
1650 _ => panic!("Expected SELECT query"),
1651 }
1652 }
1653
1654 #[test]
1655 fn test_regular_table_identifier() {
1656 let query = parse("SELECT * FROM MyTable WHERE x = 1").unwrap();
1658 match query {
1659 Query::Select(sel) => {
1660 let from = sel.from.unwrap();
1661 assert_eq!(from.table_name, Some("MyTable".to_string()));
1662 }
1663 _ => panic!("Expected SELECT query"),
1664 }
1665 }
1666}