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 let at_fn = ws1().then(kw("AT")).rewind().ignore_then(at_function());
381
382 let from = ws1().then(kw("FROM")).rewind().ignore_then(
383 ws1()
384 .ignore_then(kw("FROM"))
385 .ignore_then(ws1())
386 .ignore_then(from_modifiers()),
387 );
388
389 kw("BALANCES")
390 .ignore_then(at_fn.or_not())
391 .then(from.or_not())
392 .then(where_clause().or_not())
393 .map(|((at_function, from), where_clause)| BalancesQuery {
394 at_function,
395 from,
396 where_clause,
397 })
398}
399
400fn print_query<'a>() -> impl Parser<'a, ParserInput<'a>, PrintQuery, ParserExtra<'a>> + Clone {
402 kw("PRINT")
403 .ignore_then(
404 ws1()
405 .ignore_then(kw("FROM"))
406 .ignore_then(ws1())
407 .ignore_then(from_modifiers())
408 .or_not(),
409 )
410 .map(|from| PrintQuery { from })
411}
412
413fn create_table_stmt<'a>() -> impl Parser<'a, ParserInput<'a>, CreateTableStmt, ParserExtra<'a>> {
415 let column_def = identifier()
417 .then(ws().ignore_then(identifier()).or_not())
418 .map(|(name, type_hint)| ColumnDef { name, type_hint });
419
420 let column_list = just('(')
421 .ignore_then(ws())
422 .ignore_then(
423 column_def
424 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
425 .collect::<Vec<_>>(),
426 )
427 .then_ignore(ws())
428 .then_ignore(just(')'));
429
430 let as_select = ws1()
431 .ignore_then(kw("AS"))
432 .ignore_then(ws1())
433 .ignore_then(select_query())
434 .map(Box::new);
435
436 kw("CREATE")
437 .ignore_then(ws1())
438 .ignore_then(kw("TABLE"))
439 .ignore_then(ws1())
440 .ignore_then(identifier())
441 .then(ws().ignore_then(column_list).or_not())
442 .then(as_select.or_not())
443 .map(|((table_name, columns), as_select)| CreateTableStmt {
444 table_name,
445 columns: columns.unwrap_or_default(),
446 as_select,
447 })
448}
449
450fn insert_stmt<'a>() -> impl Parser<'a, ParserInput<'a>, InsertStmt, ParserExtra<'a>> {
452 let column_list = just('(')
454 .ignore_then(ws())
455 .ignore_then(
456 identifier()
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 value_row = just('(')
465 .ignore_then(ws())
466 .ignore_then(
467 expr()
468 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
469 .collect::<Vec<_>>(),
470 )
471 .then_ignore(ws())
472 .then_ignore(just(')'));
473
474 let values_source = kw("VALUES")
475 .ignore_then(ws())
476 .ignore_then(
477 value_row
478 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
479 .collect::<Vec<_>>(),
480 )
481 .map(InsertSource::Values);
482
483 let select_source = select_query().map(|sq| InsertSource::Select(Box::new(sq)));
485
486 let source = choice((values_source, select_source));
487
488 kw("INSERT")
489 .ignore_then(ws1())
490 .ignore_then(kw("INTO"))
491 .ignore_then(ws1())
492 .ignore_then(identifier())
493 .then(ws().ignore_then(column_list).or_not())
494 .then_ignore(ws())
495 .then(source)
496 .map(|((table_name, columns), source)| InsertStmt {
497 table_name,
498 columns,
499 source,
500 })
501}
502
503fn at_function<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
505 ws1()
506 .ignore_then(kw("AT"))
507 .ignore_then(ws1())
508 .ignore_then(identifier())
509}
510
511#[allow(clippy::large_stack_frames)]
513fn expr<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
514 recursive(|expr| {
515 let primary = primary_expr(expr.clone());
516
517 let unary = just('-')
519 .then_ignore(ws())
520 .or_not()
521 .then(primary)
522 .map(|(neg, e)| {
523 if neg.is_some() {
524 Expr::unary(UnaryOperator::Neg, e)
525 } else {
526 e
527 }
528 });
529
530 let multiplicative = unary.clone().foldl(
532 ws().ignore_then(choice((
533 just('*').to(BinaryOperator::Mul),
534 just('/').to(BinaryOperator::Div),
535 just('%').to(BinaryOperator::Mod),
536 )))
537 .then_ignore(ws())
538 .then(unary)
539 .repeated(),
540 |left, (op, right)| Expr::binary(left, op, right),
541 );
542
543 let additive = multiplicative.clone().foldl(
545 ws().ignore_then(choice((
546 just('+').to(BinaryOperator::Add),
547 just('-').to(BinaryOperator::Sub),
548 )))
549 .then_ignore(ws())
550 .then(multiplicative)
551 .repeated(),
552 |left, (op, right)| Expr::binary(left, op, right),
553 );
554
555 let comparison = additive
557 .clone()
558 .then(
559 choice((
560 ws1()
562 .ignore_then(kw("BETWEEN"))
563 .ignore_then(ws1())
564 .ignore_then(additive.clone())
565 .then_ignore(ws1())
566 .then_ignore(kw("AND"))
567 .then_ignore(ws1())
568 .then(additive.clone())
569 .map(|(low, high)| ComparisonSuffix::Between(low, high)),
570 ws1()
572 .ignore_then(kw("NOT"))
573 .ignore_then(ws1())
574 .ignore_then(kw("IN"))
575 .ignore_then(ws())
576 .ignore_then(choice((
577 set_literal(expr.clone()),
578 additive.clone(),
579 )))
580 .map(ComparisonSuffix::NotIn),
581 ws1()
583 .ignore_then(kw("IN"))
584 .ignore_then(ws())
585 .ignore_then(choice((
586 set_literal(expr.clone()),
587 additive.clone(),
588 )))
589 .map(ComparisonSuffix::In),
590 ws()
592 .ignore_then(comparison_op())
593 .then_ignore(ws())
594 .then(additive)
595 .map(|(op, right)| ComparisonSuffix::Binary(op, right)),
596 ))
597 .or_not(),
598 )
599 .map(|(left, suffix)| match suffix {
600 Some(ComparisonSuffix::Between(low, high)) => Expr::between(left, low, high),
601 Some(ComparisonSuffix::Binary(op, right)) => Expr::binary(left, op, right),
602 Some(ComparisonSuffix::In(right)) => Expr::binary(left, BinaryOperator::In, right),
603 Some(ComparisonSuffix::NotIn(right)) => {
604 Expr::binary(left, BinaryOperator::NotIn, right)
605 }
606 None => left,
607 })
608 .then(
610 ws1()
611 .ignore_then(kw("IS"))
612 .ignore_then(ws1())
613 .ignore_then(choice((
614 kw("NOT")
615 .ignore_then(ws1())
616 .ignore_then(kw("NULL"))
617 .to(UnaryOperator::IsNotNull),
618 kw("NULL").to(UnaryOperator::IsNull),
619 )))
620 .or_not(),
621 )
622 .map(|(expr, is_null)| {
623 if let Some(op) = is_null {
624 Expr::unary(op, expr)
625 } else {
626 expr
627 }
628 });
629
630 let not_expr = kw("NOT")
632 .ignore_then(ws1())
633 .repeated()
634 .collect::<Vec<_>>()
635 .then(comparison)
636 .map(|(nots, e)| {
637 nots.into_iter()
638 .fold(e, |acc, ()| Expr::unary(UnaryOperator::Not, acc))
639 });
640
641 let and_expr = not_expr.clone().foldl(
643 ws1()
644 .ignore_then(kw("AND"))
645 .ignore_then(ws1())
646 .ignore_then(not_expr)
647 .repeated(),
648 |left, right| Expr::binary(left, BinaryOperator::And, right),
649 );
650
651 and_expr.clone().foldl(
653 ws1()
654 .ignore_then(kw("OR"))
655 .ignore_then(ws1())
656 .ignore_then(and_expr)
657 .repeated(),
658 |left, right| Expr::binary(left, BinaryOperator::Or, right),
659 )
660 })
661}
662
663fn comparison_op<'a>() -> impl Parser<'a, ParserInput<'a>, BinaryOperator, ParserExtra<'a>> + Clone
665{
666 choice((
667 just("!=").to(BinaryOperator::Ne),
669 just("!~").to(BinaryOperator::NotRegex),
670 just("<=").to(BinaryOperator::Le),
671 just(">=").to(BinaryOperator::Ge),
672 just('=').to(BinaryOperator::Eq),
674 just('<').to(BinaryOperator::Lt),
675 just('>').to(BinaryOperator::Gt),
676 just('~').to(BinaryOperator::Regex),
677 ))
678}
679
680fn set_literal<'a>(
690 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
691) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
692 just('(')
693 .ignore_then(ws())
694 .ignore_then(
695 expr.clone()
697 .then(
698 ws().ignore_then(just(',')).ignore_then(ws()).ignore_then(
702 expr.separated_by(ws().then(just(',')).then(ws()))
703 .allow_trailing()
704 .collect::<Vec<_>>(),
705 ),
706 )
707 .map(|(first, rest)| {
708 let mut elements = Vec::with_capacity(1 + rest.len());
709 elements.push(first);
710 elements.extend(rest);
711 elements
712 }),
713 )
714 .then_ignore(ws())
715 .then_ignore(just(')'))
716 .map(Expr::Set)
717}
718
719fn primary_expr<'a>(
721 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
722) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
723 choice((
724 just('(')
726 .ignore_then(ws())
727 .ignore_then(expr.clone())
728 .then_ignore(ws())
729 .then_ignore(just(')'))
730 .map(|e| Expr::Paren(Box::new(e))),
731 function_call_or_column(expr),
734 literal().map(Expr::Literal),
736 just('*').to(Expr::Wildcard),
738 ))
739}
740
741fn function_call_or_column<'a>(
743 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
744) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
745 identifier()
746 .then(
747 ws().ignore_then(just('('))
748 .ignore_then(ws())
749 .ignore_then(function_args(expr))
750 .then_ignore(ws())
751 .then_ignore(just(')'))
752 .or_not(),
753 )
754 .then(
755 ws1()
757 .ignore_then(kw("OVER"))
758 .ignore_then(ws())
759 .ignore_then(just('('))
760 .ignore_then(ws())
761 .ignore_then(window_spec())
762 .then_ignore(ws())
763 .then_ignore(just(')'))
764 .or_not(),
765 )
766 .map(|((name, args), over)| {
767 if let Some(args) = args {
768 if let Some(window_spec) = over {
769 Expr::Window(WindowFunction {
771 name,
772 args,
773 over: window_spec,
774 })
775 } else {
776 Expr::Function(FunctionCall { name, args })
778 }
779 } else {
780 Expr::Column(name)
781 }
782 })
783}
784
785fn window_spec<'a>() -> impl Parser<'a, ParserInput<'a>, WindowSpec, ParserExtra<'a>> + Clone {
787 let partition_by = kw("PARTITION")
788 .ignore_then(ws1())
789 .ignore_then(kw("BY"))
790 .ignore_then(ws1())
791 .ignore_then(
792 simple_arg()
793 .separated_by(ws().then(just(',')).then(ws()))
794 .at_least(1)
795 .collect::<Vec<_>>(),
796 )
797 .then_ignore(ws());
798
799 let window_order_by = kw("ORDER")
800 .ignore_then(ws1())
801 .ignore_then(kw("BY"))
802 .ignore_then(ws1())
803 .ignore_then(
804 window_order_spec()
805 .separated_by(ws().then(just(',')).then(ws()))
806 .at_least(1)
807 .collect::<Vec<_>>(),
808 );
809
810 partition_by
811 .or_not()
812 .then(window_order_by.or_not())
813 .map(|(partition_by, order_by)| WindowSpec {
814 partition_by,
815 order_by,
816 })
817}
818
819fn window_order_spec<'a>() -> impl Parser<'a, ParserInput<'a>, OrderSpec, ParserExtra<'a>> + Clone {
821 simple_arg()
822 .then(
823 ws1()
824 .ignore_then(choice((
825 kw("ASC").to(SortDirection::Asc),
826 kw("DESC").to(SortDirection::Desc),
827 )))
828 .or_not(),
829 )
830 .map(|(expr, dir)| OrderSpec {
831 expr,
832 direction: dir.unwrap_or_default(),
833 })
834}
835
836fn function_args<'a>(
838 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
839) -> impl Parser<'a, ParserInput<'a>, Vec<Expr>, ParserExtra<'a>> + Clone {
840 expr.separated_by(ws().then(just(',')).then(ws())).collect()
843}
844
845fn simple_arg<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
847 choice((
848 just('*').to(Expr::Wildcard),
849 identifier().map(Expr::Column),
850 literal().map(Expr::Literal),
851 ))
852}
853
854fn literal<'a>() -> impl Parser<'a, ParserInput<'a>, Literal, ParserExtra<'a>> + Clone {
856 choice((
857 kw("TRUE").to(Literal::Boolean(true)),
859 kw("FALSE").to(Literal::Boolean(false)),
860 kw("NULL").to(Literal::Null),
861 date_literal().map(Literal::Date),
863 decimal().map(Literal::Number),
865 string_literal().map(Literal::String),
867 ))
868}
869
870fn identifier<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
872 text::ident().map(|s: &str| s.to_string())
873}
874
875fn table_identifier<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
878 choice((
879 just('#')
881 .ignore_then(text::ident())
882 .map(|s: &str| format!("#{s}")),
883 text::ident().map(|s: &str| s.to_string()),
885 ))
886}
887
888fn string_literal<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
890 let double_quoted = just('"')
892 .ignore_then(
893 none_of("\"\\")
894 .or(just('\\').ignore_then(any()))
895 .repeated()
896 .collect::<String>(),
897 )
898 .then_ignore(just('"'));
899
900 let single_quoted = just('\'')
902 .ignore_then(
903 none_of("'\\")
904 .or(just('\\').ignore_then(any()))
905 .repeated()
906 .collect::<String>(),
907 )
908 .then_ignore(just('\''));
909
910 choice((double_quoted, single_quoted))
911}
912
913fn date_literal<'a>() -> impl Parser<'a, ParserInput<'a>, NaiveDate, ParserExtra<'a>> + Clone {
915 digits()
916 .then_ignore(just('-'))
917 .then(digits())
918 .then_ignore(just('-'))
919 .then(digits())
920 .try_map(|((year, month), day): ((&str, &str), &str), span| {
921 let year: i32 = year
922 .parse()
923 .map_err(|_| Rich::custom(span, "invalid year"))?;
924 let month: u32 = month
925 .parse()
926 .map_err(|_| Rich::custom(span, "invalid month"))?;
927 let day: u32 = day.parse().map_err(|_| Rich::custom(span, "invalid day"))?;
928 rustledger_core::naive_date(year, month, day)
929 .ok_or_else(|| Rich::custom(span, "invalid date"))
930 })
931}
932
933fn decimal<'a>() -> impl Parser<'a, ParserInput<'a>, Decimal, ParserExtra<'a>> + Clone {
935 just('-')
936 .or_not()
937 .then(digits())
938 .then(just('.').then(digits()).or_not())
939 .try_map(
940 |((neg, int_part), frac_part): ((Option<char>, &str), Option<(char, &str)>), span| {
941 let mut s = String::new();
942 if neg.is_some() {
943 s.push('-');
944 }
945 s.push_str(int_part);
946 if let Some((_, frac)) = frac_part {
947 s.push('.');
948 s.push_str(frac);
949 }
950 Decimal::from_str(&s).map_err(|_| Rich::custom(span, "invalid number"))
951 },
952 )
953}
954
955fn integer<'a>() -> impl Parser<'a, ParserInput<'a>, i64, ParserExtra<'a>> + Clone {
957 digits().try_map(|s: &str, span| {
958 s.parse::<i64>()
959 .map_err(|_| Rich::custom(span, "invalid integer"))
960 })
961}
962
963#[cfg(test)]
964mod tests {
965 use super::*;
966 use rust_decimal_macros::dec;
967
968 #[test]
969 fn test_simple_select() {
970 let query = parse("SELECT * FROM year = 2024").unwrap();
971 match query {
972 Query::Select(sel) => {
973 assert!(!sel.distinct);
974 assert_eq!(sel.targets.len(), 1);
975 assert!(matches!(sel.targets[0].expr, Expr::Wildcard));
976 assert!(sel.from.is_some());
977 }
978 _ => panic!("Expected SELECT query"),
979 }
980 }
981
982 #[test]
983 fn test_select_columns() {
984 let query = parse("SELECT date, account, position").unwrap();
985 match query {
986 Query::Select(sel) => {
987 assert_eq!(sel.targets.len(), 3);
988 assert!(matches!(&sel.targets[0].expr, Expr::Column(c) if c == "date"));
989 assert!(matches!(&sel.targets[1].expr, Expr::Column(c) if c == "account"));
990 assert!(matches!(&sel.targets[2].expr, Expr::Column(c) if c == "position"));
991 }
992 _ => panic!("Expected SELECT query"),
993 }
994 }
995
996 #[test]
997 fn test_select_with_alias() {
998 let query = parse("SELECT SUM(position) AS total").unwrap();
999 match query {
1000 Query::Select(sel) => {
1001 assert_eq!(sel.targets.len(), 1);
1002 assert_eq!(sel.targets[0].alias, Some("total".to_string()));
1003 match &sel.targets[0].expr {
1004 Expr::Function(f) => {
1005 assert_eq!(f.name, "SUM");
1006 assert_eq!(f.args.len(), 1);
1007 }
1008 _ => panic!("Expected function"),
1009 }
1010 }
1011 _ => panic!("Expected SELECT query"),
1012 }
1013 }
1014
1015 #[test]
1016 fn test_select_distinct() {
1017 let query = parse("SELECT DISTINCT account").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_no_space() {
1028 let query = parse("SELECT DISTINCT(account) FROM postings").unwrap();
1030 match query {
1031 Query::Select(sel) => {
1032 assert!(sel.distinct);
1033 }
1034 _ => panic!("Expected SELECT query"),
1035 }
1036 }
1037
1038 #[test]
1039 fn test_select_distinct_coalesce_no_space() {
1040 let query = parse("SELECT DISTINCT(COALESCE(payee, narration)) as payee FROM transactions")
1042 .unwrap();
1043 match query {
1044 Query::Select(sel) => {
1045 assert!(sel.distinct);
1046 }
1047 _ => panic!("Expected SELECT query"),
1048 }
1049 }
1050
1051 #[test]
1052 fn test_where_clause() {
1053 let query = parse("SELECT * WHERE account ~ \"Expenses:\"").unwrap();
1054 match query {
1055 Query::Select(sel) => {
1056 assert!(sel.where_clause.is_some());
1057 match sel.where_clause.unwrap() {
1058 Expr::BinaryOp(op) => {
1059 assert_eq!(op.op, BinaryOperator::Regex);
1060 }
1061 _ => panic!("Expected binary op"),
1062 }
1063 }
1064 _ => panic!("Expected SELECT query"),
1065 }
1066 }
1067
1068 #[test]
1069 fn test_group_by() {
1070 let query = parse("SELECT account, SUM(position) GROUP BY account").unwrap();
1071 match query {
1072 Query::Select(sel) => {
1073 assert!(sel.group_by.is_some());
1074 assert_eq!(sel.group_by.unwrap().len(), 1);
1075 }
1076 _ => panic!("Expected SELECT query"),
1077 }
1078 }
1079
1080 #[test]
1081 fn test_order_by() {
1082 let query = parse("SELECT * ORDER BY date DESC, account ASC").unwrap();
1083 match query {
1084 Query::Select(sel) => {
1085 assert!(sel.order_by.is_some());
1086 let order = sel.order_by.unwrap();
1087 assert_eq!(order.len(), 2);
1088 assert_eq!(order[0].direction, SortDirection::Desc);
1089 assert_eq!(order[1].direction, SortDirection::Asc);
1090 }
1091 _ => panic!("Expected SELECT query"),
1092 }
1093 }
1094
1095 #[test]
1096 fn test_limit() {
1097 let query = parse("SELECT * LIMIT 100").unwrap();
1098 match query {
1099 Query::Select(sel) => {
1100 assert_eq!(sel.limit, Some(100));
1101 }
1102 _ => panic!("Expected SELECT query"),
1103 }
1104 }
1105
1106 #[test]
1107 fn test_from_open_close_clear() {
1108 let query = parse("SELECT * FROM OPEN ON 2024-01-01 CLOSE ON 2024-12-31 CLEAR").unwrap();
1109 match query {
1110 Query::Select(sel) => {
1111 let from = sel.from.unwrap();
1112 assert_eq!(
1113 from.open_on,
1114 Some(rustledger_core::naive_date(2024, 1, 1).unwrap())
1115 );
1116 assert_eq!(
1117 from.close_on,
1118 Some(rustledger_core::naive_date(2024, 12, 31).unwrap())
1119 );
1120 assert!(from.clear);
1121 }
1122 _ => panic!("Expected SELECT query"),
1123 }
1124 }
1125
1126 #[test]
1127 fn test_from_year_filter() {
1128 let query = parse("SELECT date, account FROM year = 2024").unwrap();
1129 match query {
1130 Query::Select(sel) => {
1131 let from = sel.from.unwrap();
1132 assert!(from.filter.is_some(), "FROM filter should be present");
1133 match from.filter.unwrap() {
1134 Expr::BinaryOp(op) => {
1135 assert_eq!(op.op, BinaryOperator::Eq);
1136 assert!(matches!(op.left, Expr::Column(ref c) if c == "year"));
1137 match op.right {
1139 Expr::Literal(Literal::Integer(n)) => assert_eq!(n, 2024),
1140 Expr::Literal(Literal::Number(n)) => assert_eq!(n, dec!(2024)),
1141 other => panic!("Expected numeric literal, got {other:?}"),
1142 }
1143 }
1144 other => panic!("Expected BinaryOp, got {other:?}"),
1145 }
1146 }
1147 _ => panic!("Expected SELECT query"),
1148 }
1149 }
1150
1151 #[test]
1152 fn test_journal_query() {
1153 let query = parse("JOURNAL \"Assets:Bank\" AT cost").unwrap();
1154 match query {
1155 Query::Journal(j) => {
1156 assert_eq!(j.account_pattern, "Assets:Bank");
1157 assert_eq!(j.at_function, Some("cost".to_string()));
1158 }
1159 _ => panic!("Expected JOURNAL query"),
1160 }
1161 }
1162
1163 #[test]
1164 fn test_balances_query() {
1165 let query = parse("BALANCES AT units FROM year = 2024").unwrap();
1166 match query {
1167 Query::Balances(b) => {
1168 assert_eq!(b.at_function, Some("units".to_string()));
1169 assert!(b.from.is_some());
1170 }
1171 _ => panic!("Expected BALANCES query"),
1172 }
1173 }
1174
1175 #[test]
1176 fn test_print_query() {
1177 let query = parse("PRINT").unwrap();
1178 assert!(matches!(query, Query::Print(_)));
1179 }
1180
1181 #[test]
1182 fn test_complex_expression() {
1183 let query = parse("SELECT * WHERE date >= 2024-01-01 AND account ~ \"Expenses:\"").unwrap();
1184 match query {
1185 Query::Select(sel) => match sel.where_clause.unwrap() {
1186 Expr::BinaryOp(op) => {
1187 assert_eq!(op.op, BinaryOperator::And);
1188 }
1189 _ => panic!("Expected AND"),
1190 },
1191 _ => panic!("Expected SELECT query"),
1192 }
1193 }
1194
1195 #[test]
1196 fn test_number_literal() {
1197 let query = parse("SELECT * WHERE year = 2024").unwrap();
1198 match query {
1199 Query::Select(sel) => match sel.where_clause.unwrap() {
1200 Expr::BinaryOp(op) => match op.right {
1201 Expr::Literal(Literal::Number(n)) => {
1202 assert_eq!(n, dec!(2024));
1203 }
1204 _ => panic!("Expected number literal"),
1205 },
1206 _ => panic!("Expected binary op"),
1207 },
1208 _ => panic!("Expected SELECT query"),
1209 }
1210 }
1211
1212 #[test]
1213 fn test_semicolon_optional() {
1214 assert!(parse("SELECT *").is_ok());
1215 assert!(parse("SELECT *;").is_ok());
1216 }
1217
1218 #[test]
1219 fn test_subquery_basic() {
1220 let query = parse("SELECT * FROM (SELECT account, position)").unwrap();
1221 match query {
1222 Query::Select(sel) => {
1223 assert!(sel.from.is_some());
1224 let from = sel.from.unwrap();
1225 assert!(from.subquery.is_some());
1226 let subquery = from.subquery.unwrap();
1227 assert_eq!(subquery.targets.len(), 2);
1228 }
1229 _ => panic!("Expected SELECT query"),
1230 }
1231 }
1232
1233 #[test]
1234 fn test_subquery_with_groupby() {
1235 let query = parse(
1236 "SELECT account, total FROM (SELECT account, SUM(position) AS total GROUP BY account)",
1237 )
1238 .unwrap();
1239 match query {
1240 Query::Select(sel) => {
1241 assert_eq!(sel.targets.len(), 2);
1242 let from = sel.from.unwrap();
1243 assert!(from.subquery.is_some());
1244 let subquery = from.subquery.unwrap();
1245 assert!(subquery.group_by.is_some());
1246 }
1247 _ => panic!("Expected SELECT query"),
1248 }
1249 }
1250
1251 #[test]
1252 fn test_subquery_with_outer_where() {
1253 let query =
1254 parse("SELECT * FROM (SELECT * WHERE year = 2024) WHERE account ~ \"Expenses:\"")
1255 .unwrap();
1256 match query {
1257 Query::Select(sel) => {
1258 assert!(sel.where_clause.is_some());
1260 let from = sel.from.unwrap();
1262 let subquery = from.subquery.unwrap();
1263 assert!(subquery.where_clause.is_some());
1264 }
1265 _ => panic!("Expected SELECT query"),
1266 }
1267 }
1268
1269 #[test]
1270 fn test_nested_subquery() {
1271 let query = parse("SELECT * FROM (SELECT * FROM (SELECT account))").unwrap();
1273 match query {
1274 Query::Select(sel) => {
1275 let from = sel.from.unwrap();
1276 let subquery1 = from.subquery.unwrap();
1277 let from2 = subquery1.from.unwrap();
1278 assert!(from2.subquery.is_some());
1279 }
1280 _ => panic!("Expected SELECT query"),
1281 }
1282 }
1283
1284 #[test]
1285 fn test_nested_function_calls() {
1286 let query = parse("SELECT units(sum(position))").unwrap();
1288 match query {
1289 Query::Select(sel) => {
1290 assert_eq!(sel.targets.len(), 1);
1291 match &sel.targets[0].expr {
1292 Expr::Function(outer) => {
1293 assert_eq!(outer.name, "units");
1294 assert_eq!(outer.args.len(), 1);
1295 match &outer.args[0] {
1296 Expr::Function(inner) => {
1297 assert_eq!(inner.name, "sum");
1298 assert_eq!(inner.args.len(), 1);
1299 assert!(
1300 matches!(&inner.args[0], Expr::Column(c) if c == "position")
1301 );
1302 }
1303 _ => panic!("Expected inner function call"),
1304 }
1305 }
1306 _ => panic!("Expected outer function call"),
1307 }
1308 }
1309 _ => panic!("Expected SELECT query"),
1310 }
1311 }
1312
1313 #[test]
1314 fn test_deeply_nested_function_calls() {
1315 let query = parse("SELECT foo(bar(baz(x)))").unwrap();
1317 match query {
1318 Query::Select(sel) => {
1319 assert_eq!(sel.targets.len(), 1);
1320 match &sel.targets[0].expr {
1321 Expr::Function(f1) => {
1322 assert_eq!(f1.name, "foo");
1323 match &f1.args[0] {
1324 Expr::Function(f2) => {
1325 assert_eq!(f2.name, "bar");
1326 match &f2.args[0] {
1327 Expr::Function(f3) => {
1328 assert_eq!(f3.name, "baz");
1329 assert!(matches!(&f3.args[0], Expr::Column(c) if c == "x"));
1330 }
1331 _ => panic!("Expected f3"),
1332 }
1333 }
1334 _ => panic!("Expected f2"),
1335 }
1336 }
1337 _ => panic!("Expected f1"),
1338 }
1339 }
1340 _ => panic!("Expected SELECT query"),
1341 }
1342 }
1343
1344 #[test]
1345 fn test_function_with_arithmetic() {
1346 let query = parse("SELECT sum(amount * 2)").unwrap();
1348 match query {
1349 Query::Select(sel) => match &sel.targets[0].expr {
1350 Expr::Function(f) => {
1351 assert_eq!(f.name, "sum");
1352 assert!(matches!(&f.args[0], Expr::BinaryOp(_)));
1353 }
1354 _ => panic!("Expected function"),
1355 },
1356 _ => panic!("Expected SELECT query"),
1357 }
1358 }
1359
1360 #[test]
1361 fn test_is_null() {
1362 let query = parse("SELECT * WHERE payee IS NULL").unwrap();
1363 match query {
1364 Query::Select(sel) => match sel.where_clause.unwrap() {
1365 Expr::UnaryOp(op) => {
1366 assert_eq!(op.op, UnaryOperator::IsNull);
1367 assert!(matches!(&op.operand, Expr::Column(c) if c == "payee"));
1368 }
1369 _ => panic!("Expected unary op"),
1370 },
1371 _ => panic!("Expected SELECT query"),
1372 }
1373 }
1374
1375 #[test]
1376 fn test_is_not_null() {
1377 let query = parse("SELECT * WHERE payee IS NOT NULL").unwrap();
1378 match query {
1379 Query::Select(sel) => match sel.where_clause.unwrap() {
1380 Expr::UnaryOp(op) => {
1381 assert_eq!(op.op, UnaryOperator::IsNotNull);
1382 assert!(matches!(&op.operand, Expr::Column(c) if c == "payee"));
1383 }
1384 _ => panic!("Expected unary op"),
1385 },
1386 _ => panic!("Expected SELECT query"),
1387 }
1388 }
1389
1390 #[test]
1391 fn test_not_regex() {
1392 let query = parse("SELECT * WHERE account !~ \"Assets:\"").unwrap();
1393 match query {
1394 Query::Select(sel) => match sel.where_clause.unwrap() {
1395 Expr::BinaryOp(op) => {
1396 assert_eq!(op.op, BinaryOperator::NotRegex);
1397 }
1398 _ => panic!("Expected binary op"),
1399 },
1400 _ => panic!("Expected SELECT query"),
1401 }
1402 }
1403
1404 #[test]
1405 fn test_modulo() {
1406 let query = parse("SELECT year % 4").unwrap();
1407 match query {
1408 Query::Select(sel) => match &sel.targets[0].expr {
1409 Expr::BinaryOp(op) => {
1410 assert_eq!(op.op, BinaryOperator::Mod);
1411 }
1412 _ => panic!("Expected binary op"),
1413 },
1414 _ => panic!("Expected SELECT query"),
1415 }
1416 }
1417
1418 #[test]
1419 fn test_between() {
1420 let query = parse("SELECT * WHERE year BETWEEN 2020 AND 2024").unwrap();
1421 match query {
1422 Query::Select(sel) => match sel.where_clause.unwrap() {
1423 Expr::Between { value, low, high } => {
1424 assert!(matches!(*value, Expr::Column(c) if c == "year"));
1425 assert!(matches!(*low, Expr::Literal(Literal::Number(_))));
1426 assert!(matches!(*high, Expr::Literal(Literal::Number(_))));
1427 }
1428 _ => panic!("Expected BETWEEN"),
1429 },
1430 _ => panic!("Expected SELECT query"),
1431 }
1432 }
1433
1434 #[test]
1435 fn test_not_in() {
1436 let query = parse("SELECT * WHERE account NOT IN tags").unwrap();
1437 match query {
1438 Query::Select(sel) => match sel.where_clause.unwrap() {
1439 Expr::BinaryOp(op) => {
1440 assert_eq!(op.op, BinaryOperator::NotIn);
1441 }
1442 _ => panic!("Expected binary op"),
1443 },
1444 _ => panic!("Expected SELECT query"),
1445 }
1446 }
1447
1448 #[test]
1449 fn test_in_set_literal() {
1450 let query = parse("SELECT * WHERE currency IN ('EUR', 'USD')").unwrap();
1452 match query {
1453 Query::Select(sel) => match sel.where_clause.unwrap() {
1454 Expr::BinaryOp(op) => {
1455 assert_eq!(op.op, BinaryOperator::In);
1456 match op.right {
1457 Expr::Set(elements) => {
1458 assert_eq!(elements.len(), 2);
1459 }
1460 _ => panic!("Expected Set"),
1461 }
1462 }
1463 _ => panic!("Expected binary op"),
1464 },
1465 _ => panic!("Expected SELECT query"),
1466 }
1467
1468 let query = parse("SELECT * WHERE currency IN ('EUR',)").unwrap();
1470 match query {
1471 Query::Select(sel) => match sel.where_clause.unwrap() {
1472 Expr::BinaryOp(op) => {
1473 assert_eq!(op.op, BinaryOperator::In);
1474 match op.right {
1475 Expr::Set(elements) => {
1476 assert_eq!(elements.len(), 1);
1477 }
1478 _ => panic!("Expected Set"),
1479 }
1480 }
1481 _ => panic!("Expected binary op"),
1482 },
1483 _ => panic!("Expected SELECT query"),
1484 }
1485
1486 let query = parse("SELECT * WHERE 'x' IN (tags)").unwrap();
1488 match query {
1489 Query::Select(sel) => match sel.where_clause.unwrap() {
1490 Expr::BinaryOp(op) => {
1491 assert_eq!(op.op, BinaryOperator::In);
1492 match op.right {
1494 Expr::Paren(inner) => match *inner {
1495 Expr::Column(name) => assert_eq!(name, "tags"),
1496 _ => panic!("Expected Column inside Paren"),
1497 },
1498 other => panic!("Expected Paren, got {other:?}"),
1499 }
1500 }
1501 _ => panic!("Expected binary op"),
1502 },
1503 _ => panic!("Expected SELECT query"),
1504 }
1505 }
1506
1507 #[test]
1508 fn test_string_arg_function() {
1509 let query = parse("SELECT foo(x)").unwrap();
1511 match query {
1512 Query::Select(sel) => match &sel.targets[0].expr {
1513 Expr::Function(f) => {
1514 assert_eq!(f.name, "foo");
1515 }
1516 _ => panic!("Expected function"),
1517 },
1518 _ => panic!("Expected SELECT query"),
1519 }
1520
1521 let query = parse("SELECT foo('bar')").unwrap();
1523 match query {
1524 Query::Select(sel) => match &sel.targets[0].expr {
1525 Expr::Function(f) => {
1526 assert_eq!(f.name, "foo");
1527 assert!(matches!(&f.args[0], Expr::Literal(Literal::String(s)) if s == "bar"));
1528 }
1529 _ => panic!("Expected function"),
1530 },
1531 _ => panic!("Expected SELECT query"),
1532 }
1533 }
1534
1535 #[test]
1536 fn test_meta_function() {
1537 let query = parse("SELECT meta('category')").unwrap();
1538 match query {
1539 Query::Select(sel) => match &sel.targets[0].expr {
1540 Expr::Function(f) => {
1541 assert_eq!(f.name.to_uppercase(), "META");
1542 assert_eq!(f.args.len(), 1);
1543 assert!(
1544 matches!(&f.args[0], Expr::Literal(Literal::String(s)) if s == "category")
1545 );
1546 }
1547 _ => panic!("Expected function"),
1548 },
1549 _ => panic!("Expected SELECT query"),
1550 }
1551 }
1552
1553 #[test]
1554 fn test_entry_meta_function() {
1555 let query = parse("SELECT entry_meta('source')").unwrap();
1556 match query {
1557 Query::Select(sel) => match &sel.targets[0].expr {
1558 Expr::Function(f) => {
1559 assert_eq!(f.name.to_uppercase(), "ENTRY_META");
1560 assert_eq!(f.args.len(), 1);
1561 }
1562 _ => panic!("Expected function"),
1563 },
1564 _ => panic!("Expected SELECT query"),
1565 }
1566 }
1567
1568 #[test]
1569 fn test_convert_function() {
1570 let query = parse("SELECT convert(position, 'USD')").unwrap();
1571 match query {
1572 Query::Select(sel) => match &sel.targets[0].expr {
1573 Expr::Function(f) => {
1574 assert_eq!(f.name.to_uppercase(), "CONVERT");
1575 assert_eq!(f.args.len(), 2);
1576 }
1577 _ => panic!("Expected function"),
1578 },
1579 _ => panic!("Expected SELECT query"),
1580 }
1581 }
1582
1583 #[test]
1584 fn test_type_cast_functions() {
1585 let query = parse("SELECT int(number)").unwrap();
1587 match query {
1588 Query::Select(sel) => match &sel.targets[0].expr {
1589 Expr::Function(f) => {
1590 assert_eq!(f.name.to_uppercase(), "INT");
1591 assert_eq!(f.args.len(), 1);
1592 }
1593 _ => panic!("Expected function"),
1594 },
1595 _ => panic!("Expected SELECT query"),
1596 }
1597
1598 let query = parse("SELECT decimal('123.45')").unwrap();
1600 match query {
1601 Query::Select(sel) => match &sel.targets[0].expr {
1602 Expr::Function(f) => {
1603 assert_eq!(f.name.to_uppercase(), "DECIMAL");
1604 }
1605 _ => panic!("Expected function"),
1606 },
1607 _ => panic!("Expected SELECT query"),
1608 }
1609
1610 let query = parse("SELECT str(123)").unwrap();
1612 match query {
1613 Query::Select(sel) => match &sel.targets[0].expr {
1614 Expr::Function(f) => {
1615 assert_eq!(f.name.to_uppercase(), "STR");
1616 }
1617 _ => panic!("Expected function"),
1618 },
1619 _ => panic!("Expected SELECT query"),
1620 }
1621
1622 let query = parse("SELECT bool(1)").unwrap();
1624 match query {
1625 Query::Select(sel) => match &sel.targets[0].expr {
1626 Expr::Function(f) => {
1627 assert_eq!(f.name.to_uppercase(), "BOOL");
1628 }
1629 _ => panic!("Expected function"),
1630 },
1631 _ => panic!("Expected SELECT query"),
1632 }
1633 }
1634
1635 #[test]
1636 fn test_system_table_prices() {
1637 let query = parse("SELECT date, currency, amount FROM #prices").unwrap();
1639 match query {
1640 Query::Select(sel) => {
1641 assert_eq!(sel.targets.len(), 3);
1642 assert!(matches!(&sel.targets[0].expr, Expr::Column(c) if c == "date"));
1643 assert!(matches!(&sel.targets[1].expr, Expr::Column(c) if c == "currency"));
1644 assert!(matches!(&sel.targets[2].expr, Expr::Column(c) if c == "amount"));
1645 let from = sel.from.unwrap();
1646 assert_eq!(from.table_name, Some("#prices".to_string()));
1647 }
1648 _ => panic!("Expected SELECT query"),
1649 }
1650 }
1651
1652 #[test]
1653 fn test_system_table_with_where() {
1654 let query = parse("SELECT * FROM #prices WHERE currency = 'EUR'").unwrap();
1656 match query {
1657 Query::Select(sel) => {
1658 let from = sel.from.unwrap();
1659 assert_eq!(from.table_name, Some("#prices".to_string()));
1660 assert!(sel.where_clause.is_some());
1661 }
1662 _ => panic!("Expected SELECT query"),
1663 }
1664 }
1665
1666 #[test]
1667 fn test_regular_table_identifier() {
1668 let query = parse("SELECT * FROM MyTable WHERE x = 1").unwrap();
1670 match query {
1671 Query::Select(sel) => {
1672 let from = sel.from.unwrap();
1673 assert_eq!(from.table_name, Some("MyTable".to_string()));
1674 }
1675 _ => panic!("Expected SELECT query"),
1676 }
1677 }
1678}