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(order_by_clause().or_not())
172 .then(pivot_by_clause().or_not())
173 .then(limit_clause().or_not())
174 .map(
175 |(
176 (
177 (
178 (((((distinct, targets), from), where_clause), group_by), having),
179 order_by,
180 ),
181 pivot_by,
182 ),
183 limit,
184 )| {
185 SelectQuery {
186 distinct,
187 targets,
188 from,
189 where_clause,
190 group_by,
191 having,
192 pivot_by,
193 order_by,
194 limit,
195 }
196 },
197 )
198 })
199}
200
201fn from_clause<'a>() -> impl Parser<'a, ParserInput<'a>, FromClause, ParserExtra<'a>> + Clone {
203 ws1()
204 .ignore_then(kw("FROM"))
205 .ignore_then(ws1())
206 .ignore_then(from_modifiers())
207}
208
209fn targets<'a>() -> impl Parser<'a, ParserInput<'a>, Vec<Target>, ParserExtra<'a>> + Clone {
211 target()
212 .separated_by(ws().then(just(',')).then(ws()))
213 .at_least(1)
214 .collect()
215}
216
217fn target<'a>() -> impl Parser<'a, ParserInput<'a>, Target, ParserExtra<'a>> + Clone {
219 expr()
220 .then(
221 ws1()
222 .ignore_then(kw("AS"))
223 .ignore_then(ws1())
224 .ignore_then(identifier())
225 .or_not(),
226 )
227 .map(|(expr, alias)| Target { expr, alias })
228}
229
230fn from_modifiers<'a>() -> impl Parser<'a, ParserInput<'a>, FromClause, ParserExtra<'a>> + Clone {
232 let open_on = kw("OPEN")
233 .ignore_then(ws1())
234 .ignore_then(kw("ON"))
235 .ignore_then(ws1())
236 .ignore_then(date_literal())
237 .then_ignore(ws());
238
239 let close_on = kw("CLOSE")
240 .ignore_then(ws().then(kw("ON")).then(ws()).or_not())
241 .ignore_then(date_literal())
242 .then_ignore(ws());
243
244 let clear = kw("CLEAR").then_ignore(ws());
245
246 open_on
249 .or_not()
250 .then(close_on.or_not())
251 .then(clear.or_not().map(|c| c.is_some()))
252 .then(from_filter().or_not())
253 .map(|(((open_on, close_on), clear), filter)| FromClause {
254 open_on,
255 close_on,
256 clear,
257 filter,
258 subquery: None,
259 table_name: None,
260 })
261}
262
263fn from_filter<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
265 expr()
266}
267
268fn where_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
270 ws1()
271 .ignore_then(kw("WHERE"))
272 .ignore_then(ws1())
273 .ignore_then(expr())
274}
275
276fn group_by_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Vec<Expr>, ParserExtra<'a>> + Clone {
278 ws1()
279 .ignore_then(kw("GROUP"))
280 .ignore_then(ws1())
281 .ignore_then(kw("BY"))
282 .ignore_then(ws1())
283 .ignore_then(
284 expr()
285 .separated_by(ws().then(just(',')).then(ws()))
286 .at_least(1)
287 .collect(),
288 )
289}
290
291fn having_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
293 ws1()
294 .ignore_then(kw("HAVING"))
295 .ignore_then(ws1())
296 .ignore_then(expr())
297}
298
299fn pivot_by_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Vec<Expr>, ParserExtra<'a>> + Clone {
301 ws1()
302 .ignore_then(kw("PIVOT"))
303 .ignore_then(ws1())
304 .ignore_then(kw("BY"))
305 .ignore_then(ws1())
306 .ignore_then(
307 expr()
308 .separated_by(ws().then(just(',')).then(ws()))
309 .at_least(1)
310 .collect(),
311 )
312}
313
314fn order_by_clause<'a>() -> impl Parser<'a, ParserInput<'a>, Vec<OrderSpec>, ParserExtra<'a>> + Clone
316{
317 ws1()
318 .ignore_then(kw("ORDER"))
319 .ignore_then(ws1())
320 .ignore_then(kw("BY"))
321 .ignore_then(ws1())
322 .ignore_then(
323 order_spec()
324 .separated_by(ws().then(just(',')).then(ws()))
325 .at_least(1)
326 .collect(),
327 )
328}
329
330fn order_spec<'a>() -> impl Parser<'a, ParserInput<'a>, OrderSpec, ParserExtra<'a>> + Clone {
332 expr()
333 .then(
334 ws1()
335 .ignore_then(choice((
336 kw("ASC").to(SortDirection::Asc),
337 kw("DESC").to(SortDirection::Desc),
338 )))
339 .or_not(),
340 )
341 .map(|(expr, dir)| OrderSpec {
342 expr,
343 direction: dir.unwrap_or_default(),
344 })
345}
346
347fn limit_clause<'a>() -> impl Parser<'a, ParserInput<'a>, u64, ParserExtra<'a>> + Clone {
349 ws1()
350 .ignore_then(kw("LIMIT"))
351 .ignore_then(ws1())
352 .ignore_then(integer())
353 .map(|n| n as u64)
354}
355
356fn journal_query<'a>() -> impl Parser<'a, ParserInput<'a>, JournalQuery, ParserExtra<'a>> + Clone {
358 kw("JOURNAL")
359 .ignore_then(
360 ws1().ignore_then(string_literal()).or_not(),
362 )
363 .then(at_function().or_not())
364 .then(
365 ws1()
366 .ignore_then(kw("FROM"))
367 .ignore_then(ws1())
368 .ignore_then(from_modifiers())
369 .or_not(),
370 )
371 .map(|((account_pattern, at_function), from)| JournalQuery {
372 account_pattern: account_pattern.unwrap_or_default(),
373 at_function,
374 from,
375 })
376}
377
378fn balances_query<'a>() -> impl Parser<'a, ParserInput<'a>, BalancesQuery, ParserExtra<'a>> + Clone
380{
381 let at_fn = ws1().then(kw("AT")).rewind().ignore_then(at_function());
385
386 let from = ws1().then(kw("FROM")).rewind().ignore_then(
387 ws1()
388 .ignore_then(kw("FROM"))
389 .ignore_then(ws1())
390 .ignore_then(from_modifiers()),
391 );
392
393 kw("BALANCES")
394 .ignore_then(at_fn.or_not())
395 .then(from.or_not())
396 .then(where_clause().or_not())
397 .map(|((at_function, from), where_clause)| BalancesQuery {
398 at_function,
399 from,
400 where_clause,
401 })
402}
403
404fn print_query<'a>() -> impl Parser<'a, ParserInput<'a>, PrintQuery, ParserExtra<'a>> + Clone {
406 kw("PRINT")
407 .ignore_then(
408 ws1()
409 .ignore_then(kw("FROM"))
410 .ignore_then(ws1())
411 .ignore_then(from_modifiers())
412 .or_not(),
413 )
414 .map(|from| PrintQuery { from })
415}
416
417fn create_table_stmt<'a>() -> impl Parser<'a, ParserInput<'a>, CreateTableStmt, ParserExtra<'a>> {
419 let column_def = identifier()
421 .then(ws().ignore_then(identifier()).or_not())
422 .map(|(name, type_hint)| ColumnDef { name, type_hint });
423
424 let column_list = just('(')
425 .ignore_then(ws())
426 .ignore_then(
427 column_def
428 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
429 .collect::<Vec<_>>(),
430 )
431 .then_ignore(ws())
432 .then_ignore(just(')'));
433
434 let as_select = ws1()
435 .ignore_then(kw("AS"))
436 .ignore_then(ws1())
437 .ignore_then(select_query())
438 .map(Box::new);
439
440 kw("CREATE")
441 .ignore_then(ws1())
442 .ignore_then(kw("TABLE"))
443 .ignore_then(ws1())
444 .ignore_then(identifier())
445 .then(ws().ignore_then(column_list).or_not())
446 .then(as_select.or_not())
447 .map(|((table_name, columns), as_select)| CreateTableStmt {
448 table_name,
449 columns: columns.unwrap_or_default(),
450 as_select,
451 })
452}
453
454fn insert_stmt<'a>() -> impl Parser<'a, ParserInput<'a>, InsertStmt, ParserExtra<'a>> {
456 let column_list = just('(')
458 .ignore_then(ws())
459 .ignore_then(
460 identifier()
461 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
462 .collect::<Vec<_>>(),
463 )
464 .then_ignore(ws())
465 .then_ignore(just(')'));
466
467 let value_row = just('(')
469 .ignore_then(ws())
470 .ignore_then(
471 expr()
472 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
473 .collect::<Vec<_>>(),
474 )
475 .then_ignore(ws())
476 .then_ignore(just(')'));
477
478 let values_source = kw("VALUES")
479 .ignore_then(ws())
480 .ignore_then(
481 value_row
482 .separated_by(ws().ignore_then(just(',')).then_ignore(ws()))
483 .collect::<Vec<_>>(),
484 )
485 .map(InsertSource::Values);
486
487 let select_source = select_query().map(|sq| InsertSource::Select(Box::new(sq)));
489
490 let source = choice((values_source, select_source));
491
492 kw("INSERT")
493 .ignore_then(ws1())
494 .ignore_then(kw("INTO"))
495 .ignore_then(ws1())
496 .ignore_then(identifier())
497 .then(ws().ignore_then(column_list).or_not())
498 .then_ignore(ws())
499 .then(source)
500 .map(|((table_name, columns), source)| InsertStmt {
501 table_name,
502 columns,
503 source,
504 })
505}
506
507fn at_function<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
509 ws1()
510 .ignore_then(kw("AT"))
511 .ignore_then(ws1())
512 .ignore_then(identifier())
513}
514
515#[allow(clippy::large_stack_frames)]
517fn expr<'a>() -> Boxed<'a, 'a, ParserInput<'a>, Expr, ParserExtra<'a>> {
518 recursive(|expr| {
519 let primary = primary_expr(expr.clone()).boxed();
520
521 let unary = just('-')
523 .then_ignore(ws())
524 .or_not()
525 .then(primary)
526 .map(|(neg, e)| {
527 if neg.is_some() {
528 Expr::unary(UnaryOperator::Neg, e)
529 } else {
530 e
531 }
532 })
533 .boxed();
534
535 let multiplicative = unary
537 .clone()
538 .foldl(
539 ws().ignore_then(choice((
540 just('*').to(BinaryOperator::Mul),
541 just('/').to(BinaryOperator::Div),
542 just('%').to(BinaryOperator::Mod),
543 )))
544 .then_ignore(ws())
545 .then(unary)
546 .repeated(),
547 |left, (op, right)| Expr::binary(left, op, right),
548 )
549 .boxed();
550
551 let additive = multiplicative
553 .clone()
554 .foldl(
555 ws().ignore_then(choice((
556 just('+').to(BinaryOperator::Add),
557 just('-').to(BinaryOperator::Sub),
558 )))
559 .then_ignore(ws())
560 .then(multiplicative)
561 .repeated(),
562 |left, (op, right)| Expr::binary(left, op, right),
563 )
564 .boxed();
565
566 let comparison = additive
568 .clone()
569 .then(
570 choice((
571 ws1()
573 .ignore_then(kw("BETWEEN"))
574 .ignore_then(ws1())
575 .ignore_then(additive.clone())
576 .then_ignore(ws1())
577 .then_ignore(kw("AND"))
578 .then_ignore(ws1())
579 .then(additive.clone())
580 .map(|(low, high)| ComparisonSuffix::Between(low, high)),
581 ws1()
583 .ignore_then(kw("NOT"))
584 .ignore_then(ws1())
585 .ignore_then(kw("IN"))
586 .ignore_then(ws())
587 .ignore_then(choice((
588 set_literal(expr.clone()),
589 additive.clone(),
590 )))
591 .map(ComparisonSuffix::NotIn),
592 ws1()
594 .ignore_then(kw("IN"))
595 .ignore_then(ws())
596 .ignore_then(choice((
597 set_literal(expr.clone()),
598 additive.clone(),
599 )))
600 .map(ComparisonSuffix::In),
601 ws()
603 .ignore_then(comparison_op())
604 .then_ignore(ws())
605 .then(additive)
606 .map(|(op, right)| ComparisonSuffix::Binary(op, right)),
607 ))
608 .or_not(),
609 )
610 .map(|(left, suffix)| match suffix {
611 Some(ComparisonSuffix::Between(low, high)) => Expr::between(left, low, high),
612 Some(ComparisonSuffix::Binary(op, right)) => Expr::binary(left, op, right),
613 Some(ComparisonSuffix::In(right)) => Expr::binary(left, BinaryOperator::In, right),
614 Some(ComparisonSuffix::NotIn(right)) => {
615 Expr::binary(left, BinaryOperator::NotIn, right)
616 }
617 None => left,
618 })
619 .then(
621 ws1()
622 .ignore_then(kw("IS"))
623 .ignore_then(ws1())
624 .ignore_then(choice((
625 kw("NOT")
626 .ignore_then(ws1())
627 .ignore_then(kw("NULL"))
628 .to(UnaryOperator::IsNotNull),
629 kw("NULL").to(UnaryOperator::IsNull),
630 )))
631 .or_not(),
632 )
633 .map(|(expr, is_null)| {
634 if let Some(op) = is_null {
635 Expr::unary(op, expr)
636 } else {
637 expr
638 }
639 })
640 .boxed();
641
642 let not_expr = kw("NOT")
644 .ignore_then(ws1())
645 .repeated()
646 .collect::<Vec<_>>()
647 .then(comparison)
648 .map(|(nots, e)| {
649 nots.into_iter()
650 .fold(e, |acc, ()| Expr::unary(UnaryOperator::Not, acc))
651 })
652 .boxed();
653
654 let and_expr = not_expr
656 .clone()
657 .foldl(
658 ws1()
659 .ignore_then(kw("AND"))
660 .ignore_then(ws1())
661 .ignore_then(not_expr)
662 .repeated(),
663 |left, right| Expr::binary(left, BinaryOperator::And, right),
664 )
665 .boxed();
666
667 and_expr.clone().foldl(
669 ws1()
670 .ignore_then(kw("OR"))
671 .ignore_then(ws1())
672 .ignore_then(and_expr)
673 .repeated(),
674 |left, right| Expr::binary(left, BinaryOperator::Or, right),
675 )
676 })
677 .boxed()
678}
679
680fn comparison_op<'a>() -> impl Parser<'a, ParserInput<'a>, BinaryOperator, ParserExtra<'a>> + Clone
682{
683 choice((
684 just("!=").to(BinaryOperator::Ne),
686 just("!~").to(BinaryOperator::NotRegex),
687 just("<=").to(BinaryOperator::Le),
688 just(">=").to(BinaryOperator::Ge),
689 just('=').to(BinaryOperator::Eq),
691 just('<').to(BinaryOperator::Lt),
692 just('>').to(BinaryOperator::Gt),
693 just('~').to(BinaryOperator::Regex),
694 ))
695}
696
697fn set_literal<'a>(
707 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
708) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
709 just('(')
710 .ignore_then(ws())
711 .ignore_then(
712 expr.clone()
714 .then(
715 ws().ignore_then(just(',')).ignore_then(ws()).ignore_then(
719 expr.separated_by(ws().then(just(',')).then(ws()))
720 .allow_trailing()
721 .collect::<Vec<_>>(),
722 ),
723 )
724 .map(|(first, rest)| {
725 let mut elements = Vec::with_capacity(1 + rest.len());
726 elements.push(first);
727 elements.extend(rest);
728 elements
729 }),
730 )
731 .then_ignore(ws())
732 .then_ignore(just(')'))
733 .map(Expr::Set)
734}
735
736fn primary_expr<'a>(
738 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
739) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
740 choice((
741 just('(')
743 .ignore_then(ws())
744 .ignore_then(expr.clone())
745 .then_ignore(ws())
746 .then_ignore(just(')'))
747 .map(|e| Expr::Paren(Box::new(e))),
748 function_call_or_column(expr),
751 literal().map(Expr::Literal),
753 just('*').to(Expr::Wildcard),
755 ))
756}
757
758fn function_call_or_column<'a>(
760 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
761) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
762 identifier()
763 .then(
764 ws().ignore_then(just('('))
765 .ignore_then(ws())
766 .ignore_then(function_args(expr))
767 .then_ignore(ws())
768 .then_ignore(just(')'))
769 .or_not(),
770 )
771 .then(
772 ws1()
774 .ignore_then(kw("OVER"))
775 .ignore_then(ws())
776 .ignore_then(just('('))
777 .ignore_then(ws())
778 .ignore_then(window_spec())
779 .then_ignore(ws())
780 .then_ignore(just(')'))
781 .or_not(),
782 )
783 .map(|((name, args), over)| {
784 if let Some(args) = args {
785 if let Some(window_spec) = over {
786 Expr::Window(WindowFunction {
788 name,
789 args,
790 over: window_spec,
791 })
792 } else {
793 Expr::Function(FunctionCall { name, args })
795 }
796 } else {
797 Expr::Column(name)
798 }
799 })
800}
801
802fn window_spec<'a>() -> impl Parser<'a, ParserInput<'a>, WindowSpec, ParserExtra<'a>> + Clone {
804 let partition_by = kw("PARTITION")
805 .ignore_then(ws1())
806 .ignore_then(kw("BY"))
807 .ignore_then(ws1())
808 .ignore_then(
809 simple_arg()
810 .separated_by(ws().then(just(',')).then(ws()))
811 .at_least(1)
812 .collect::<Vec<_>>(),
813 )
814 .then_ignore(ws());
815
816 let window_order_by = kw("ORDER")
817 .ignore_then(ws1())
818 .ignore_then(kw("BY"))
819 .ignore_then(ws1())
820 .ignore_then(
821 window_order_spec()
822 .separated_by(ws().then(just(',')).then(ws()))
823 .at_least(1)
824 .collect::<Vec<_>>(),
825 );
826
827 partition_by
828 .or_not()
829 .then(window_order_by.or_not())
830 .map(|(partition_by, order_by)| WindowSpec {
831 partition_by,
832 order_by,
833 })
834}
835
836fn window_order_spec<'a>() -> impl Parser<'a, ParserInput<'a>, OrderSpec, ParserExtra<'a>> + Clone {
838 simple_arg()
839 .then(
840 ws1()
841 .ignore_then(choice((
842 kw("ASC").to(SortDirection::Asc),
843 kw("DESC").to(SortDirection::Desc),
844 )))
845 .or_not(),
846 )
847 .map(|(expr, dir)| OrderSpec {
848 expr,
849 direction: dir.unwrap_or_default(),
850 })
851}
852
853fn function_args<'a>(
855 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
856) -> impl Parser<'a, ParserInput<'a>, Vec<Expr>, ParserExtra<'a>> + Clone {
857 expr.separated_by(ws().then(just(',')).then(ws())).collect()
860}
861
862fn simple_arg<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
864 choice((
865 just('*').to(Expr::Wildcard),
866 identifier().map(Expr::Column),
867 literal().map(Expr::Literal),
868 ))
869}
870
871fn literal<'a>() -> impl Parser<'a, ParserInput<'a>, Literal, ParserExtra<'a>> + Clone {
873 choice((
874 kw("TRUE").to(Literal::Boolean(true)),
876 kw("FALSE").to(Literal::Boolean(false)),
877 kw("NULL").to(Literal::Null),
878 date_literal().map(Literal::Date),
880 number_literal(),
882 string_literal().map(Literal::String),
884 ))
885}
886
887fn identifier<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
889 text::ident().map(|s: &str| s.to_string())
890}
891
892fn table_identifier<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
895 choice((
896 just('#')
898 .ignore_then(text::ident())
899 .map(|s: &str| format!("#{s}")),
900 text::ident().map(|s: &str| s.to_string()),
902 ))
903}
904
905fn string_literal<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
907 let double_quoted = just('"')
909 .ignore_then(
910 none_of("\"\\")
911 .or(just('\\').ignore_then(any()))
912 .repeated()
913 .collect::<String>(),
914 )
915 .then_ignore(just('"'));
916
917 let single_quoted = just('\'')
919 .ignore_then(
920 none_of("'\\")
921 .or(just('\\').ignore_then(any()))
922 .repeated()
923 .collect::<String>(),
924 )
925 .then_ignore(just('\''));
926
927 choice((double_quoted, single_quoted))
928}
929
930fn date_literal<'a>() -> impl Parser<'a, ParserInput<'a>, NaiveDate, ParserExtra<'a>> + Clone {
932 digits()
933 .then_ignore(just('-'))
934 .then(digits())
935 .then_ignore(just('-'))
936 .then(digits())
937 .try_map(|((year, month), day): ((&str, &str), &str), span| {
938 let year: i32 = year
939 .parse()
940 .map_err(|_| Rich::custom(span, "invalid year"))?;
941 let month: u32 = month
942 .parse()
943 .map_err(|_| Rich::custom(span, "invalid month"))?;
944 let day: u32 = day.parse().map_err(|_| Rich::custom(span, "invalid day"))?;
945 rustledger_core::naive_date(year, month, day)
946 .ok_or_else(|| Rich::custom(span, "invalid date"))
947 })
948}
949
950fn number_literal<'a>() -> impl Parser<'a, ParserInput<'a>, Literal, ParserExtra<'a>> + Clone {
958 just('-')
959 .or_not()
960 .then(digits())
961 .then(just('.').then(digits()).or_not())
962 .try_map(
963 |((neg, int_part), frac_part): ((Option<char>, &str), Option<(char, &str)>), span| {
964 let mut s = String::new();
965 if neg.is_some() {
966 s.push('-');
967 }
968 s.push_str(int_part);
969 match frac_part {
970 None => match s.parse::<i64>() {
971 Ok(i) => Ok(Literal::Integer(i)),
972 Err(_) => Decimal::from_str(&s)
974 .map(Literal::Number)
975 .map_err(|_| Rich::custom(span, "invalid number")),
976 },
977 Some((_, frac)) => {
978 s.push('.');
979 s.push_str(frac);
980 Decimal::from_str(&s)
981 .map(Literal::Number)
982 .map_err(|_| Rich::custom(span, "invalid number"))
983 }
984 }
985 },
986 )
987}
988
989fn integer<'a>() -> impl Parser<'a, ParserInput<'a>, i64, ParserExtra<'a>> + Clone {
991 digits().try_map(|s: &str, span| {
992 s.parse::<i64>()
993 .map_err(|_| Rich::custom(span, "invalid integer"))
994 })
995}
996
997#[cfg(test)]
998mod tests {
999 use super::*;
1000 use rust_decimal_macros::dec;
1001
1002 #[test]
1003 fn test_simple_select() {
1004 let query = parse("SELECT * FROM year = 2024").unwrap();
1005 match query {
1006 Query::Select(sel) => {
1007 assert!(!sel.distinct);
1008 assert_eq!(sel.targets.len(), 1);
1009 assert!(matches!(sel.targets[0].expr, Expr::Wildcard));
1010 assert!(sel.from.is_some());
1011 }
1012 _ => panic!("Expected SELECT query"),
1013 }
1014 }
1015
1016 #[test]
1017 fn test_select_columns() {
1018 let query = parse("SELECT date, account, position").unwrap();
1019 match query {
1020 Query::Select(sel) => {
1021 assert_eq!(sel.targets.len(), 3);
1022 assert!(matches!(&sel.targets[0].expr, Expr::Column(c) if c == "date"));
1023 assert!(matches!(&sel.targets[1].expr, Expr::Column(c) if c == "account"));
1024 assert!(matches!(&sel.targets[2].expr, Expr::Column(c) if c == "position"));
1025 }
1026 _ => panic!("Expected SELECT query"),
1027 }
1028 }
1029
1030 #[test]
1031 fn test_select_with_alias() {
1032 let query = parse("SELECT SUM(position) AS total").unwrap();
1033 match query {
1034 Query::Select(sel) => {
1035 assert_eq!(sel.targets.len(), 1);
1036 assert_eq!(sel.targets[0].alias, Some("total".to_string()));
1037 match &sel.targets[0].expr {
1038 Expr::Function(f) => {
1039 assert_eq!(f.name, "SUM");
1040 assert_eq!(f.args.len(), 1);
1041 }
1042 _ => panic!("Expected function"),
1043 }
1044 }
1045 _ => panic!("Expected SELECT query"),
1046 }
1047 }
1048
1049 #[test]
1050 fn test_select_distinct() {
1051 let query = parse("SELECT DISTINCT account").unwrap();
1052 match query {
1053 Query::Select(sel) => {
1054 assert!(sel.distinct);
1055 }
1056 _ => panic!("Expected SELECT query"),
1057 }
1058 }
1059
1060 #[test]
1061 fn test_select_distinct_no_space() {
1062 let query = parse("SELECT DISTINCT(account) FROM postings").unwrap();
1064 match query {
1065 Query::Select(sel) => {
1066 assert!(sel.distinct);
1067 }
1068 _ => panic!("Expected SELECT query"),
1069 }
1070 }
1071
1072 #[test]
1073 fn test_select_distinct_coalesce_no_space() {
1074 let query = parse("SELECT DISTINCT(COALESCE(payee, narration)) as payee FROM transactions")
1076 .unwrap();
1077 match query {
1078 Query::Select(sel) => {
1079 assert!(sel.distinct);
1080 }
1081 _ => panic!("Expected SELECT query"),
1082 }
1083 }
1084
1085 #[test]
1086 fn test_where_clause() {
1087 let query = parse("SELECT * WHERE account ~ \"Expenses:\"").unwrap();
1088 match query {
1089 Query::Select(sel) => {
1090 assert!(sel.where_clause.is_some());
1091 match sel.where_clause.unwrap() {
1092 Expr::BinaryOp(op) => {
1093 assert_eq!(op.op, BinaryOperator::Regex);
1094 }
1095 _ => panic!("Expected binary op"),
1096 }
1097 }
1098 _ => panic!("Expected SELECT query"),
1099 }
1100 }
1101
1102 #[test]
1103 fn test_group_by() {
1104 let query = parse("SELECT account, SUM(position) GROUP BY account").unwrap();
1105 match query {
1106 Query::Select(sel) => {
1107 assert!(sel.group_by.is_some());
1108 assert_eq!(sel.group_by.unwrap().len(), 1);
1109 }
1110 _ => panic!("Expected SELECT query"),
1111 }
1112 }
1113
1114 #[test]
1115 fn test_order_by() {
1116 let query = parse("SELECT * ORDER BY date DESC, account ASC").unwrap();
1117 match query {
1118 Query::Select(sel) => {
1119 assert!(sel.order_by.is_some());
1120 let order = sel.order_by.unwrap();
1121 assert_eq!(order.len(), 2);
1122 assert_eq!(order[0].direction, SortDirection::Desc);
1123 assert_eq!(order[1].direction, SortDirection::Asc);
1124 }
1125 _ => panic!("Expected SELECT query"),
1126 }
1127 }
1128
1129 #[test]
1130 fn test_limit() {
1131 let query = parse("SELECT * LIMIT 100").unwrap();
1132 match query {
1133 Query::Select(sel) => {
1134 assert_eq!(sel.limit, Some(100));
1135 }
1136 _ => panic!("Expected SELECT query"),
1137 }
1138 }
1139
1140 #[test]
1141 fn test_from_open_close_clear() {
1142 let query = parse("SELECT * FROM OPEN ON 2024-01-01 CLOSE ON 2024-12-31 CLEAR").unwrap();
1143 match query {
1144 Query::Select(sel) => {
1145 let from = sel.from.unwrap();
1146 assert_eq!(
1147 from.open_on,
1148 Some(rustledger_core::naive_date(2024, 1, 1).unwrap())
1149 );
1150 assert_eq!(
1151 from.close_on,
1152 Some(rustledger_core::naive_date(2024, 12, 31).unwrap())
1153 );
1154 assert!(from.clear);
1155 }
1156 _ => panic!("Expected SELECT query"),
1157 }
1158 }
1159
1160 #[test]
1161 fn test_from_year_filter() {
1162 let query = parse("SELECT date, account FROM year = 2024").unwrap();
1163 match query {
1164 Query::Select(sel) => {
1165 let from = sel.from.unwrap();
1166 assert!(from.filter.is_some(), "FROM filter should be present");
1167 match from.filter.unwrap() {
1168 Expr::BinaryOp(op) => {
1169 assert_eq!(op.op, BinaryOperator::Eq);
1170 assert!(matches!(op.left, Expr::Column(ref c) if c == "year"));
1171 match op.right {
1173 Expr::Literal(Literal::Integer(n)) => assert_eq!(n, 2024),
1174 Expr::Literal(Literal::Number(n)) => assert_eq!(n, dec!(2024)),
1175 other => panic!("Expected numeric literal, got {other:?}"),
1176 }
1177 }
1178 other => panic!("Expected BinaryOp, got {other:?}"),
1179 }
1180 }
1181 _ => panic!("Expected SELECT query"),
1182 }
1183 }
1184
1185 #[test]
1186 fn test_journal_query() {
1187 let query = parse("JOURNAL \"Assets:Bank\" AT cost").unwrap();
1188 match query {
1189 Query::Journal(j) => {
1190 assert_eq!(j.account_pattern, "Assets:Bank");
1191 assert_eq!(j.at_function, Some("cost".to_string()));
1192 }
1193 _ => panic!("Expected JOURNAL query"),
1194 }
1195 }
1196
1197 #[test]
1198 fn test_balances_query() {
1199 let query = parse("BALANCES AT units FROM year = 2024").unwrap();
1200 match query {
1201 Query::Balances(b) => {
1202 assert_eq!(b.at_function, Some("units".to_string()));
1203 assert!(b.from.is_some());
1204 }
1205 _ => panic!("Expected BALANCES query"),
1206 }
1207 }
1208
1209 #[test]
1210 fn test_print_query() {
1211 let query = parse("PRINT").unwrap();
1212 assert!(matches!(query, Query::Print(_)));
1213 }
1214
1215 #[test]
1216 fn test_complex_expression() {
1217 let query = parse("SELECT * WHERE date >= 2024-01-01 AND account ~ \"Expenses:\"").unwrap();
1218 match query {
1219 Query::Select(sel) => match sel.where_clause.unwrap() {
1220 Expr::BinaryOp(op) => {
1221 assert_eq!(op.op, BinaryOperator::And);
1222 }
1223 _ => panic!("Expected AND"),
1224 },
1225 _ => panic!("Expected SELECT query"),
1226 }
1227 }
1228
1229 #[test]
1230 fn test_integer_literal_parsing() {
1231 let query = parse("SELECT * WHERE year = 2024").unwrap();
1232 match query {
1233 Query::Select(sel) => match sel.where_clause.unwrap() {
1234 Expr::BinaryOp(op) => match op.right {
1235 Expr::Literal(Literal::Integer(n)) => {
1236 assert_eq!(n, 2024);
1237 }
1238 _ => panic!("Expected integer literal"),
1239 },
1240 _ => panic!("Expected binary op"),
1241 },
1242 _ => panic!("Expected SELECT query"),
1243 }
1244 }
1245
1246 #[test]
1247 fn test_integer_vs_decimal_literal() {
1248 let q = parse("SELECT * WHERE x = 42").unwrap();
1250 let Query::Select(sel) = q else {
1251 panic!("expected SELECT");
1252 };
1253 let Expr::BinaryOp(op) = sel.where_clause.unwrap() else {
1254 panic!("expected binary op");
1255 };
1256 assert!(matches!(op.right, Expr::Literal(Literal::Integer(42))));
1257
1258 let q = parse("SELECT * WHERE x = 42.0").unwrap();
1260 let Query::Select(sel) = q else {
1261 panic!("expected SELECT");
1262 };
1263 let Expr::BinaryOp(op) = sel.where_clause.unwrap() else {
1264 panic!("expected binary op");
1265 };
1266 match op.right {
1267 Expr::Literal(Literal::Number(n)) => assert_eq!(n, dec!(42.0)),
1268 other => panic!("expected Number literal, got {other:?}"),
1269 }
1270 }
1271
1272 #[test]
1273 fn test_integer_overflow_falls_back_to_number() {
1274 let q = parse("SELECT * WHERE x = 99999999999999999999").unwrap();
1276 let Query::Select(sel) = q else {
1277 panic!("expected SELECT");
1278 };
1279 let Expr::BinaryOp(op) = sel.where_clause.unwrap() else {
1280 panic!("expected binary op");
1281 };
1282 assert!(matches!(op.right, Expr::Literal(Literal::Number(_))));
1283 }
1284
1285 #[test]
1286 fn test_negative_integer_literal() {
1287 let q = parse("SELECT * WHERE x = -42").unwrap();
1292 let Query::Select(sel) = q else {
1293 panic!("expected SELECT");
1294 };
1295 let Expr::BinaryOp(op) = sel.where_clause.unwrap() else {
1296 panic!("expected binary op");
1297 };
1298 match op.right {
1299 Expr::UnaryOp(unary) => {
1300 assert_eq!(unary.op, UnaryOperator::Neg);
1301 assert!(matches!(unary.operand, Expr::Literal(Literal::Integer(42))));
1302 }
1303 other => panic!("expected Unary(Neg, Integer(42)), got {other:?}"),
1304 }
1305 }
1306
1307 #[test]
1308 fn test_semicolon_optional() {
1309 assert!(parse("SELECT *").is_ok());
1310 assert!(parse("SELECT *;").is_ok());
1311 }
1312
1313 #[test]
1314 fn test_subquery_basic() {
1315 let query = parse("SELECT * FROM (SELECT account, position)").unwrap();
1316 match query {
1317 Query::Select(sel) => {
1318 assert!(sel.from.is_some());
1319 let from = sel.from.unwrap();
1320 assert!(from.subquery.is_some());
1321 let subquery = from.subquery.unwrap();
1322 assert_eq!(subquery.targets.len(), 2);
1323 }
1324 _ => panic!("Expected SELECT query"),
1325 }
1326 }
1327
1328 #[test]
1329 fn test_subquery_with_groupby() {
1330 let query = parse(
1331 "SELECT account, total FROM (SELECT account, SUM(position) AS total GROUP BY account)",
1332 )
1333 .unwrap();
1334 match query {
1335 Query::Select(sel) => {
1336 assert_eq!(sel.targets.len(), 2);
1337 let from = sel.from.unwrap();
1338 assert!(from.subquery.is_some());
1339 let subquery = from.subquery.unwrap();
1340 assert!(subquery.group_by.is_some());
1341 }
1342 _ => panic!("Expected SELECT query"),
1343 }
1344 }
1345
1346 #[test]
1347 fn test_subquery_with_outer_where() {
1348 let query =
1349 parse("SELECT * FROM (SELECT * WHERE year = 2024) WHERE account ~ \"Expenses:\"")
1350 .unwrap();
1351 match query {
1352 Query::Select(sel) => {
1353 assert!(sel.where_clause.is_some());
1355 let from = sel.from.unwrap();
1357 let subquery = from.subquery.unwrap();
1358 assert!(subquery.where_clause.is_some());
1359 }
1360 _ => panic!("Expected SELECT query"),
1361 }
1362 }
1363
1364 #[test]
1365 fn test_nested_subquery() {
1366 let query = parse("SELECT * FROM (SELECT * FROM (SELECT account))").unwrap();
1368 match query {
1369 Query::Select(sel) => {
1370 let from = sel.from.unwrap();
1371 let subquery1 = from.subquery.unwrap();
1372 let from2 = subquery1.from.unwrap();
1373 assert!(from2.subquery.is_some());
1374 }
1375 _ => panic!("Expected SELECT query"),
1376 }
1377 }
1378
1379 #[test]
1380 fn test_nested_function_calls() {
1381 let query = parse("SELECT units(sum(position))").unwrap();
1383 match query {
1384 Query::Select(sel) => {
1385 assert_eq!(sel.targets.len(), 1);
1386 match &sel.targets[0].expr {
1387 Expr::Function(outer) => {
1388 assert_eq!(outer.name, "units");
1389 assert_eq!(outer.args.len(), 1);
1390 match &outer.args[0] {
1391 Expr::Function(inner) => {
1392 assert_eq!(inner.name, "sum");
1393 assert_eq!(inner.args.len(), 1);
1394 assert!(
1395 matches!(&inner.args[0], Expr::Column(c) if c == "position")
1396 );
1397 }
1398 _ => panic!("Expected inner function call"),
1399 }
1400 }
1401 _ => panic!("Expected outer function call"),
1402 }
1403 }
1404 _ => panic!("Expected SELECT query"),
1405 }
1406 }
1407
1408 #[test]
1409 fn test_deeply_nested_function_calls() {
1410 let query = parse("SELECT foo(bar(baz(x)))").unwrap();
1412 match query {
1413 Query::Select(sel) => {
1414 assert_eq!(sel.targets.len(), 1);
1415 match &sel.targets[0].expr {
1416 Expr::Function(f1) => {
1417 assert_eq!(f1.name, "foo");
1418 match &f1.args[0] {
1419 Expr::Function(f2) => {
1420 assert_eq!(f2.name, "bar");
1421 match &f2.args[0] {
1422 Expr::Function(f3) => {
1423 assert_eq!(f3.name, "baz");
1424 assert!(matches!(&f3.args[0], Expr::Column(c) if c == "x"));
1425 }
1426 _ => panic!("Expected f3"),
1427 }
1428 }
1429 _ => panic!("Expected f2"),
1430 }
1431 }
1432 _ => panic!("Expected f1"),
1433 }
1434 }
1435 _ => panic!("Expected SELECT query"),
1436 }
1437 }
1438
1439 #[test]
1440 fn test_function_with_arithmetic() {
1441 let query = parse("SELECT sum(amount * 2)").unwrap();
1443 match query {
1444 Query::Select(sel) => match &sel.targets[0].expr {
1445 Expr::Function(f) => {
1446 assert_eq!(f.name, "sum");
1447 assert!(matches!(&f.args[0], Expr::BinaryOp(_)));
1448 }
1449 _ => panic!("Expected function"),
1450 },
1451 _ => panic!("Expected SELECT query"),
1452 }
1453 }
1454
1455 #[test]
1456 fn test_is_null() {
1457 let query = parse("SELECT * WHERE payee IS NULL").unwrap();
1458 match query {
1459 Query::Select(sel) => match sel.where_clause.unwrap() {
1460 Expr::UnaryOp(op) => {
1461 assert_eq!(op.op, UnaryOperator::IsNull);
1462 assert!(matches!(&op.operand, Expr::Column(c) if c == "payee"));
1463 }
1464 _ => panic!("Expected unary op"),
1465 },
1466 _ => panic!("Expected SELECT query"),
1467 }
1468 }
1469
1470 #[test]
1471 fn test_is_not_null() {
1472 let query = parse("SELECT * WHERE payee IS NOT NULL").unwrap();
1473 match query {
1474 Query::Select(sel) => match sel.where_clause.unwrap() {
1475 Expr::UnaryOp(op) => {
1476 assert_eq!(op.op, UnaryOperator::IsNotNull);
1477 assert!(matches!(&op.operand, Expr::Column(c) if c == "payee"));
1478 }
1479 _ => panic!("Expected unary op"),
1480 },
1481 _ => panic!("Expected SELECT query"),
1482 }
1483 }
1484
1485 #[test]
1486 fn test_not_regex() {
1487 let query = parse("SELECT * WHERE account !~ \"Assets:\"").unwrap();
1488 match query {
1489 Query::Select(sel) => match sel.where_clause.unwrap() {
1490 Expr::BinaryOp(op) => {
1491 assert_eq!(op.op, BinaryOperator::NotRegex);
1492 }
1493 _ => panic!("Expected binary op"),
1494 },
1495 _ => panic!("Expected SELECT query"),
1496 }
1497 }
1498
1499 #[test]
1500 fn test_modulo() {
1501 let query = parse("SELECT year % 4").unwrap();
1502 match query {
1503 Query::Select(sel) => match &sel.targets[0].expr {
1504 Expr::BinaryOp(op) => {
1505 assert_eq!(op.op, BinaryOperator::Mod);
1506 }
1507 _ => panic!("Expected binary op"),
1508 },
1509 _ => panic!("Expected SELECT query"),
1510 }
1511 }
1512
1513 #[test]
1514 fn test_between() {
1515 let query = parse("SELECT * WHERE year BETWEEN 2020 AND 2024").unwrap();
1516 match query {
1517 Query::Select(sel) => match sel.where_clause.unwrap() {
1518 Expr::Between { value, low, high } => {
1519 assert!(matches!(*value, Expr::Column(c) if c == "year"));
1520 assert!(matches!(*low, Expr::Literal(Literal::Integer(_))));
1521 assert!(matches!(*high, Expr::Literal(Literal::Integer(_))));
1522 }
1523 _ => panic!("Expected BETWEEN"),
1524 },
1525 _ => panic!("Expected SELECT query"),
1526 }
1527 }
1528
1529 #[test]
1530 fn test_not_in() {
1531 let query = parse("SELECT * WHERE account NOT IN tags").unwrap();
1532 match query {
1533 Query::Select(sel) => match sel.where_clause.unwrap() {
1534 Expr::BinaryOp(op) => {
1535 assert_eq!(op.op, BinaryOperator::NotIn);
1536 }
1537 _ => panic!("Expected binary op"),
1538 },
1539 _ => panic!("Expected SELECT query"),
1540 }
1541 }
1542
1543 #[test]
1544 fn test_in_set_literal() {
1545 let query = parse("SELECT * WHERE currency IN ('EUR', 'USD')").unwrap();
1547 match query {
1548 Query::Select(sel) => match sel.where_clause.unwrap() {
1549 Expr::BinaryOp(op) => {
1550 assert_eq!(op.op, BinaryOperator::In);
1551 match op.right {
1552 Expr::Set(elements) => {
1553 assert_eq!(elements.len(), 2);
1554 }
1555 _ => panic!("Expected Set"),
1556 }
1557 }
1558 _ => panic!("Expected binary op"),
1559 },
1560 _ => panic!("Expected SELECT query"),
1561 }
1562
1563 let query = parse("SELECT * WHERE currency IN ('EUR',)").unwrap();
1565 match query {
1566 Query::Select(sel) => match sel.where_clause.unwrap() {
1567 Expr::BinaryOp(op) => {
1568 assert_eq!(op.op, BinaryOperator::In);
1569 match op.right {
1570 Expr::Set(elements) => {
1571 assert_eq!(elements.len(), 1);
1572 }
1573 _ => panic!("Expected Set"),
1574 }
1575 }
1576 _ => panic!("Expected binary op"),
1577 },
1578 _ => panic!("Expected SELECT query"),
1579 }
1580
1581 let query = parse("SELECT * WHERE 'x' IN (tags)").unwrap();
1583 match query {
1584 Query::Select(sel) => match sel.where_clause.unwrap() {
1585 Expr::BinaryOp(op) => {
1586 assert_eq!(op.op, BinaryOperator::In);
1587 match op.right {
1589 Expr::Paren(inner) => match *inner {
1590 Expr::Column(name) => assert_eq!(name, "tags"),
1591 _ => panic!("Expected Column inside Paren"),
1592 },
1593 other => panic!("Expected Paren, got {other:?}"),
1594 }
1595 }
1596 _ => panic!("Expected binary op"),
1597 },
1598 _ => panic!("Expected SELECT query"),
1599 }
1600 }
1601
1602 #[test]
1603 fn test_string_arg_function() {
1604 let query = parse("SELECT foo(x)").unwrap();
1606 match query {
1607 Query::Select(sel) => match &sel.targets[0].expr {
1608 Expr::Function(f) => {
1609 assert_eq!(f.name, "foo");
1610 }
1611 _ => panic!("Expected function"),
1612 },
1613 _ => panic!("Expected SELECT query"),
1614 }
1615
1616 let query = parse("SELECT foo('bar')").unwrap();
1618 match query {
1619 Query::Select(sel) => match &sel.targets[0].expr {
1620 Expr::Function(f) => {
1621 assert_eq!(f.name, "foo");
1622 assert!(matches!(&f.args[0], Expr::Literal(Literal::String(s)) if s == "bar"));
1623 }
1624 _ => panic!("Expected function"),
1625 },
1626 _ => panic!("Expected SELECT query"),
1627 }
1628 }
1629
1630 #[test]
1631 fn test_meta_function() {
1632 let query = parse("SELECT meta('category')").unwrap();
1633 match query {
1634 Query::Select(sel) => match &sel.targets[0].expr {
1635 Expr::Function(f) => {
1636 assert_eq!(f.name.to_uppercase(), "META");
1637 assert_eq!(f.args.len(), 1);
1638 assert!(
1639 matches!(&f.args[0], Expr::Literal(Literal::String(s)) if s == "category")
1640 );
1641 }
1642 _ => panic!("Expected function"),
1643 },
1644 _ => panic!("Expected SELECT query"),
1645 }
1646 }
1647
1648 #[test]
1649 fn test_entry_meta_function() {
1650 let query = parse("SELECT entry_meta('source')").unwrap();
1651 match query {
1652 Query::Select(sel) => match &sel.targets[0].expr {
1653 Expr::Function(f) => {
1654 assert_eq!(f.name.to_uppercase(), "ENTRY_META");
1655 assert_eq!(f.args.len(), 1);
1656 }
1657 _ => panic!("Expected function"),
1658 },
1659 _ => panic!("Expected SELECT query"),
1660 }
1661 }
1662
1663 #[test]
1664 fn test_convert_function() {
1665 let query = parse("SELECT convert(position, 'USD')").unwrap();
1666 match query {
1667 Query::Select(sel) => match &sel.targets[0].expr {
1668 Expr::Function(f) => {
1669 assert_eq!(f.name.to_uppercase(), "CONVERT");
1670 assert_eq!(f.args.len(), 2);
1671 }
1672 _ => panic!("Expected function"),
1673 },
1674 _ => panic!("Expected SELECT query"),
1675 }
1676 }
1677
1678 #[test]
1679 fn test_type_cast_functions() {
1680 let query = parse("SELECT int(number)").unwrap();
1682 match query {
1683 Query::Select(sel) => match &sel.targets[0].expr {
1684 Expr::Function(f) => {
1685 assert_eq!(f.name.to_uppercase(), "INT");
1686 assert_eq!(f.args.len(), 1);
1687 }
1688 _ => panic!("Expected function"),
1689 },
1690 _ => panic!("Expected SELECT query"),
1691 }
1692
1693 let query = parse("SELECT decimal('123.45')").unwrap();
1695 match query {
1696 Query::Select(sel) => match &sel.targets[0].expr {
1697 Expr::Function(f) => {
1698 assert_eq!(f.name.to_uppercase(), "DECIMAL");
1699 }
1700 _ => panic!("Expected function"),
1701 },
1702 _ => panic!("Expected SELECT query"),
1703 }
1704
1705 let query = parse("SELECT str(123)").unwrap();
1707 match query {
1708 Query::Select(sel) => match &sel.targets[0].expr {
1709 Expr::Function(f) => {
1710 assert_eq!(f.name.to_uppercase(), "STR");
1711 }
1712 _ => panic!("Expected function"),
1713 },
1714 _ => panic!("Expected SELECT query"),
1715 }
1716
1717 let query = parse("SELECT bool(1)").unwrap();
1719 match query {
1720 Query::Select(sel) => match &sel.targets[0].expr {
1721 Expr::Function(f) => {
1722 assert_eq!(f.name.to_uppercase(), "BOOL");
1723 }
1724 _ => panic!("Expected function"),
1725 },
1726 _ => panic!("Expected SELECT query"),
1727 }
1728 }
1729
1730 #[test]
1731 fn test_system_table_prices() {
1732 let query = parse("SELECT date, currency, amount FROM #prices").unwrap();
1734 match query {
1735 Query::Select(sel) => {
1736 assert_eq!(sel.targets.len(), 3);
1737 assert!(matches!(&sel.targets[0].expr, Expr::Column(c) if c == "date"));
1738 assert!(matches!(&sel.targets[1].expr, Expr::Column(c) if c == "currency"));
1739 assert!(matches!(&sel.targets[2].expr, Expr::Column(c) if c == "amount"));
1740 let from = sel.from.unwrap();
1741 assert_eq!(from.table_name, Some("#prices".to_string()));
1742 }
1743 _ => panic!("Expected SELECT query"),
1744 }
1745 }
1746
1747 #[test]
1748 fn test_system_table_with_where() {
1749 let query = parse("SELECT * FROM #prices WHERE currency = 'EUR'").unwrap();
1751 match query {
1752 Query::Select(sel) => {
1753 let from = sel.from.unwrap();
1754 assert_eq!(from.table_name, Some("#prices".to_string()));
1755 assert!(sel.where_clause.is_some());
1756 }
1757 _ => panic!("Expected SELECT query"),
1758 }
1759 }
1760
1761 #[test]
1762 fn test_regular_table_identifier() {
1763 let query = parse("SELECT * FROM MyTable WHERE x = 1").unwrap();
1765 match query {
1766 Query::Select(sel) => {
1767 let from = sel.from.unwrap();
1768 assert_eq!(from.table_name, Some("MyTable".to_string()));
1769 }
1770 _ => panic!("Expected SELECT query"),
1771 }
1772 }
1773}