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::with_capacity(1 + rest.len());
698 elements.push(first);
699 elements.extend(rest);
700 elements
701 }),
702 )
703 .then_ignore(ws())
704 .then_ignore(just(')'))
705 .map(Expr::Set)
706}
707
708fn primary_expr<'a>(
710 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
711) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
712 choice((
713 just('(')
715 .ignore_then(ws())
716 .ignore_then(expr.clone())
717 .then_ignore(ws())
718 .then_ignore(just(')'))
719 .map(|e| Expr::Paren(Box::new(e))),
720 function_call_or_column(expr),
723 literal().map(Expr::Literal),
725 just('*').to(Expr::Wildcard),
727 ))
728}
729
730fn function_call_or_column<'a>(
732 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
733) -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
734 identifier()
735 .then(
736 ws().ignore_then(just('('))
737 .ignore_then(ws())
738 .ignore_then(function_args(expr))
739 .then_ignore(ws())
740 .then_ignore(just(')'))
741 .or_not(),
742 )
743 .then(
744 ws1()
746 .ignore_then(kw("OVER"))
747 .ignore_then(ws())
748 .ignore_then(just('('))
749 .ignore_then(ws())
750 .ignore_then(window_spec())
751 .then_ignore(ws())
752 .then_ignore(just(')'))
753 .or_not(),
754 )
755 .map(|((name, args), over)| {
756 if let Some(args) = args {
757 if let Some(window_spec) = over {
758 Expr::Window(WindowFunction {
760 name,
761 args,
762 over: window_spec,
763 })
764 } else {
765 Expr::Function(FunctionCall { name, args })
767 }
768 } else {
769 Expr::Column(name)
770 }
771 })
772}
773
774fn window_spec<'a>() -> impl Parser<'a, ParserInput<'a>, WindowSpec, ParserExtra<'a>> + Clone {
776 let partition_by = kw("PARTITION")
777 .ignore_then(ws1())
778 .ignore_then(kw("BY"))
779 .ignore_then(ws1())
780 .ignore_then(
781 simple_arg()
782 .separated_by(ws().then(just(',')).then(ws()))
783 .at_least(1)
784 .collect::<Vec<_>>(),
785 )
786 .then_ignore(ws());
787
788 let window_order_by = kw("ORDER")
789 .ignore_then(ws1())
790 .ignore_then(kw("BY"))
791 .ignore_then(ws1())
792 .ignore_then(
793 window_order_spec()
794 .separated_by(ws().then(just(',')).then(ws()))
795 .at_least(1)
796 .collect::<Vec<_>>(),
797 );
798
799 partition_by
800 .or_not()
801 .then(window_order_by.or_not())
802 .map(|(partition_by, order_by)| WindowSpec {
803 partition_by,
804 order_by,
805 })
806}
807
808fn window_order_spec<'a>() -> impl Parser<'a, ParserInput<'a>, OrderSpec, ParserExtra<'a>> + Clone {
810 simple_arg()
811 .then(
812 ws1()
813 .ignore_then(choice((
814 kw("ASC").to(SortDirection::Asc),
815 kw("DESC").to(SortDirection::Desc),
816 )))
817 .or_not(),
818 )
819 .map(|(expr, dir)| OrderSpec {
820 expr,
821 direction: dir.unwrap_or_default(),
822 })
823}
824
825fn function_args<'a>(
827 expr: impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone + 'a,
828) -> impl Parser<'a, ParserInput<'a>, Vec<Expr>, ParserExtra<'a>> + Clone {
829 expr.separated_by(ws().then(just(',')).then(ws())).collect()
832}
833
834fn simple_arg<'a>() -> impl Parser<'a, ParserInput<'a>, Expr, ParserExtra<'a>> + Clone {
836 choice((
837 just('*').to(Expr::Wildcard),
838 identifier().map(Expr::Column),
839 literal().map(Expr::Literal),
840 ))
841}
842
843fn literal<'a>() -> impl Parser<'a, ParserInput<'a>, Literal, ParserExtra<'a>> + Clone {
845 choice((
846 kw("TRUE").to(Literal::Boolean(true)),
848 kw("FALSE").to(Literal::Boolean(false)),
849 kw("NULL").to(Literal::Null),
850 date_literal().map(Literal::Date),
852 decimal().map(Literal::Number),
854 string_literal().map(Literal::String),
856 ))
857}
858
859fn identifier<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
861 text::ident().map(|s: &str| s.to_string())
862}
863
864fn table_identifier<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
867 choice((
868 just('#')
870 .ignore_then(text::ident())
871 .map(|s: &str| format!("#{s}")),
872 text::ident().map(|s: &str| s.to_string()),
874 ))
875}
876
877fn string_literal<'a>() -> impl Parser<'a, ParserInput<'a>, String, ParserExtra<'a>> + Clone {
879 let double_quoted = just('"')
881 .ignore_then(
882 none_of("\"\\")
883 .or(just('\\').ignore_then(any()))
884 .repeated()
885 .collect::<String>(),
886 )
887 .then_ignore(just('"'));
888
889 let single_quoted = just('\'')
891 .ignore_then(
892 none_of("'\\")
893 .or(just('\\').ignore_then(any()))
894 .repeated()
895 .collect::<String>(),
896 )
897 .then_ignore(just('\''));
898
899 choice((double_quoted, single_quoted))
900}
901
902fn date_literal<'a>() -> impl Parser<'a, ParserInput<'a>, NaiveDate, ParserExtra<'a>> + Clone {
904 digits()
905 .then_ignore(just('-'))
906 .then(digits())
907 .then_ignore(just('-'))
908 .then(digits())
909 .try_map(|((year, month), day): ((&str, &str), &str), span| {
910 let year: i32 = year
911 .parse()
912 .map_err(|_| Rich::custom(span, "invalid year"))?;
913 let month: u32 = month
914 .parse()
915 .map_err(|_| Rich::custom(span, "invalid month"))?;
916 let day: u32 = day.parse().map_err(|_| Rich::custom(span, "invalid day"))?;
917 NaiveDate::from_ymd_opt(year, month, day)
918 .ok_or_else(|| Rich::custom(span, "invalid date"))
919 })
920}
921
922fn decimal<'a>() -> impl Parser<'a, ParserInput<'a>, Decimal, ParserExtra<'a>> + Clone {
924 just('-')
925 .or_not()
926 .then(digits())
927 .then(just('.').then(digits()).or_not())
928 .try_map(
929 |((neg, int_part), frac_part): ((Option<char>, &str), Option<(char, &str)>), span| {
930 let mut s = String::new();
931 if neg.is_some() {
932 s.push('-');
933 }
934 s.push_str(int_part);
935 if let Some((_, frac)) = frac_part {
936 s.push('.');
937 s.push_str(frac);
938 }
939 Decimal::from_str(&s).map_err(|_| Rich::custom(span, "invalid number"))
940 },
941 )
942}
943
944fn integer<'a>() -> impl Parser<'a, ParserInput<'a>, i64, ParserExtra<'a>> + Clone {
946 digits().try_map(|s: &str, span| {
947 s.parse::<i64>()
948 .map_err(|_| Rich::custom(span, "invalid integer"))
949 })
950}
951
952#[cfg(test)]
953mod tests {
954 use super::*;
955 use rust_decimal_macros::dec;
956
957 #[test]
958 fn test_simple_select() {
959 let query = parse("SELECT * FROM year = 2024").unwrap();
960 match query {
961 Query::Select(sel) => {
962 assert!(!sel.distinct);
963 assert_eq!(sel.targets.len(), 1);
964 assert!(matches!(sel.targets[0].expr, Expr::Wildcard));
965 assert!(sel.from.is_some());
966 }
967 _ => panic!("Expected SELECT query"),
968 }
969 }
970
971 #[test]
972 fn test_select_columns() {
973 let query = parse("SELECT date, account, position").unwrap();
974 match query {
975 Query::Select(sel) => {
976 assert_eq!(sel.targets.len(), 3);
977 assert!(matches!(&sel.targets[0].expr, Expr::Column(c) if c == "date"));
978 assert!(matches!(&sel.targets[1].expr, Expr::Column(c) if c == "account"));
979 assert!(matches!(&sel.targets[2].expr, Expr::Column(c) if c == "position"));
980 }
981 _ => panic!("Expected SELECT query"),
982 }
983 }
984
985 #[test]
986 fn test_select_with_alias() {
987 let query = parse("SELECT SUM(position) AS total").unwrap();
988 match query {
989 Query::Select(sel) => {
990 assert_eq!(sel.targets.len(), 1);
991 assert_eq!(sel.targets[0].alias, Some("total".to_string()));
992 match &sel.targets[0].expr {
993 Expr::Function(f) => {
994 assert_eq!(f.name, "SUM");
995 assert_eq!(f.args.len(), 1);
996 }
997 _ => panic!("Expected function"),
998 }
999 }
1000 _ => panic!("Expected SELECT query"),
1001 }
1002 }
1003
1004 #[test]
1005 fn test_select_distinct() {
1006 let query = parse("SELECT DISTINCT account").unwrap();
1007 match query {
1008 Query::Select(sel) => {
1009 assert!(sel.distinct);
1010 }
1011 _ => panic!("Expected SELECT query"),
1012 }
1013 }
1014
1015 #[test]
1016 fn test_select_distinct_no_space() {
1017 let query = parse("SELECT DISTINCT(account) FROM postings").unwrap();
1019 match query {
1020 Query::Select(sel) => {
1021 assert!(sel.distinct);
1022 }
1023 _ => panic!("Expected SELECT query"),
1024 }
1025 }
1026
1027 #[test]
1028 fn test_select_distinct_coalesce_no_space() {
1029 let query = parse("SELECT DISTINCT(COALESCE(payee, narration)) as payee FROM transactions")
1031 .unwrap();
1032 match query {
1033 Query::Select(sel) => {
1034 assert!(sel.distinct);
1035 }
1036 _ => panic!("Expected SELECT query"),
1037 }
1038 }
1039
1040 #[test]
1041 fn test_where_clause() {
1042 let query = parse("SELECT * WHERE account ~ \"Expenses:\"").unwrap();
1043 match query {
1044 Query::Select(sel) => {
1045 assert!(sel.where_clause.is_some());
1046 match sel.where_clause.unwrap() {
1047 Expr::BinaryOp(op) => {
1048 assert_eq!(op.op, BinaryOperator::Regex);
1049 }
1050 _ => panic!("Expected binary op"),
1051 }
1052 }
1053 _ => panic!("Expected SELECT query"),
1054 }
1055 }
1056
1057 #[test]
1058 fn test_group_by() {
1059 let query = parse("SELECT account, SUM(position) GROUP BY account").unwrap();
1060 match query {
1061 Query::Select(sel) => {
1062 assert!(sel.group_by.is_some());
1063 assert_eq!(sel.group_by.unwrap().len(), 1);
1064 }
1065 _ => panic!("Expected SELECT query"),
1066 }
1067 }
1068
1069 #[test]
1070 fn test_order_by() {
1071 let query = parse("SELECT * ORDER BY date DESC, account ASC").unwrap();
1072 match query {
1073 Query::Select(sel) => {
1074 assert!(sel.order_by.is_some());
1075 let order = sel.order_by.unwrap();
1076 assert_eq!(order.len(), 2);
1077 assert_eq!(order[0].direction, SortDirection::Desc);
1078 assert_eq!(order[1].direction, SortDirection::Asc);
1079 }
1080 _ => panic!("Expected SELECT query"),
1081 }
1082 }
1083
1084 #[test]
1085 fn test_limit() {
1086 let query = parse("SELECT * LIMIT 100").unwrap();
1087 match query {
1088 Query::Select(sel) => {
1089 assert_eq!(sel.limit, Some(100));
1090 }
1091 _ => panic!("Expected SELECT query"),
1092 }
1093 }
1094
1095 #[test]
1096 fn test_from_open_close_clear() {
1097 let query = parse("SELECT * FROM OPEN ON 2024-01-01 CLOSE ON 2024-12-31 CLEAR").unwrap();
1098 match query {
1099 Query::Select(sel) => {
1100 let from = sel.from.unwrap();
1101 assert_eq!(
1102 from.open_on,
1103 Some(NaiveDate::from_ymd_opt(2024, 1, 1).unwrap())
1104 );
1105 assert_eq!(
1106 from.close_on,
1107 Some(NaiveDate::from_ymd_opt(2024, 12, 31).unwrap())
1108 );
1109 assert!(from.clear);
1110 }
1111 _ => panic!("Expected SELECT query"),
1112 }
1113 }
1114
1115 #[test]
1116 fn test_from_year_filter() {
1117 let query = parse("SELECT date, account FROM year = 2024").unwrap();
1118 match query {
1119 Query::Select(sel) => {
1120 let from = sel.from.unwrap();
1121 assert!(from.filter.is_some(), "FROM filter should be present");
1122 match from.filter.unwrap() {
1123 Expr::BinaryOp(op) => {
1124 assert_eq!(op.op, BinaryOperator::Eq);
1125 assert!(matches!(op.left, Expr::Column(ref c) if c == "year"));
1126 match op.right {
1128 Expr::Literal(Literal::Integer(n)) => assert_eq!(n, 2024),
1129 Expr::Literal(Literal::Number(n)) => assert_eq!(n, dec!(2024)),
1130 other => panic!("Expected numeric literal, got {other:?}"),
1131 }
1132 }
1133 other => panic!("Expected BinaryOp, got {other:?}"),
1134 }
1135 }
1136 _ => panic!("Expected SELECT query"),
1137 }
1138 }
1139
1140 #[test]
1141 fn test_journal_query() {
1142 let query = parse("JOURNAL \"Assets:Bank\" AT cost").unwrap();
1143 match query {
1144 Query::Journal(j) => {
1145 assert_eq!(j.account_pattern, "Assets:Bank");
1146 assert_eq!(j.at_function, Some("cost".to_string()));
1147 }
1148 _ => panic!("Expected JOURNAL query"),
1149 }
1150 }
1151
1152 #[test]
1153 fn test_balances_query() {
1154 let query = parse("BALANCES AT units FROM year = 2024").unwrap();
1155 match query {
1156 Query::Balances(b) => {
1157 assert_eq!(b.at_function, Some("units".to_string()));
1158 assert!(b.from.is_some());
1159 }
1160 _ => panic!("Expected BALANCES query"),
1161 }
1162 }
1163
1164 #[test]
1165 fn test_print_query() {
1166 let query = parse("PRINT").unwrap();
1167 assert!(matches!(query, Query::Print(_)));
1168 }
1169
1170 #[test]
1171 fn test_complex_expression() {
1172 let query = parse("SELECT * WHERE date >= 2024-01-01 AND account ~ \"Expenses:\"").unwrap();
1173 match query {
1174 Query::Select(sel) => match sel.where_clause.unwrap() {
1175 Expr::BinaryOp(op) => {
1176 assert_eq!(op.op, BinaryOperator::And);
1177 }
1178 _ => panic!("Expected AND"),
1179 },
1180 _ => panic!("Expected SELECT query"),
1181 }
1182 }
1183
1184 #[test]
1185 fn test_number_literal() {
1186 let query = parse("SELECT * WHERE year = 2024").unwrap();
1187 match query {
1188 Query::Select(sel) => match sel.where_clause.unwrap() {
1189 Expr::BinaryOp(op) => match op.right {
1190 Expr::Literal(Literal::Number(n)) => {
1191 assert_eq!(n, dec!(2024));
1192 }
1193 _ => panic!("Expected number literal"),
1194 },
1195 _ => panic!("Expected binary op"),
1196 },
1197 _ => panic!("Expected SELECT query"),
1198 }
1199 }
1200
1201 #[test]
1202 fn test_semicolon_optional() {
1203 assert!(parse("SELECT *").is_ok());
1204 assert!(parse("SELECT *;").is_ok());
1205 }
1206
1207 #[test]
1208 fn test_subquery_basic() {
1209 let query = parse("SELECT * FROM (SELECT account, position)").unwrap();
1210 match query {
1211 Query::Select(sel) => {
1212 assert!(sel.from.is_some());
1213 let from = sel.from.unwrap();
1214 assert!(from.subquery.is_some());
1215 let subquery = from.subquery.unwrap();
1216 assert_eq!(subquery.targets.len(), 2);
1217 }
1218 _ => panic!("Expected SELECT query"),
1219 }
1220 }
1221
1222 #[test]
1223 fn test_subquery_with_groupby() {
1224 let query = parse(
1225 "SELECT account, total FROM (SELECT account, SUM(position) AS total GROUP BY account)",
1226 )
1227 .unwrap();
1228 match query {
1229 Query::Select(sel) => {
1230 assert_eq!(sel.targets.len(), 2);
1231 let from = sel.from.unwrap();
1232 assert!(from.subquery.is_some());
1233 let subquery = from.subquery.unwrap();
1234 assert!(subquery.group_by.is_some());
1235 }
1236 _ => panic!("Expected SELECT query"),
1237 }
1238 }
1239
1240 #[test]
1241 fn test_subquery_with_outer_where() {
1242 let query =
1243 parse("SELECT * FROM (SELECT * WHERE year = 2024) WHERE account ~ \"Expenses:\"")
1244 .unwrap();
1245 match query {
1246 Query::Select(sel) => {
1247 assert!(sel.where_clause.is_some());
1249 let from = sel.from.unwrap();
1251 let subquery = from.subquery.unwrap();
1252 assert!(subquery.where_clause.is_some());
1253 }
1254 _ => panic!("Expected SELECT query"),
1255 }
1256 }
1257
1258 #[test]
1259 fn test_nested_subquery() {
1260 let query = parse("SELECT * FROM (SELECT * FROM (SELECT account))").unwrap();
1262 match query {
1263 Query::Select(sel) => {
1264 let from = sel.from.unwrap();
1265 let subquery1 = from.subquery.unwrap();
1266 let from2 = subquery1.from.unwrap();
1267 assert!(from2.subquery.is_some());
1268 }
1269 _ => panic!("Expected SELECT query"),
1270 }
1271 }
1272
1273 #[test]
1274 fn test_nested_function_calls() {
1275 let query = parse("SELECT units(sum(position))").unwrap();
1277 match query {
1278 Query::Select(sel) => {
1279 assert_eq!(sel.targets.len(), 1);
1280 match &sel.targets[0].expr {
1281 Expr::Function(outer) => {
1282 assert_eq!(outer.name, "units");
1283 assert_eq!(outer.args.len(), 1);
1284 match &outer.args[0] {
1285 Expr::Function(inner) => {
1286 assert_eq!(inner.name, "sum");
1287 assert_eq!(inner.args.len(), 1);
1288 assert!(
1289 matches!(&inner.args[0], Expr::Column(c) if c == "position")
1290 );
1291 }
1292 _ => panic!("Expected inner function call"),
1293 }
1294 }
1295 _ => panic!("Expected outer function call"),
1296 }
1297 }
1298 _ => panic!("Expected SELECT query"),
1299 }
1300 }
1301
1302 #[test]
1303 fn test_deeply_nested_function_calls() {
1304 let query = parse("SELECT foo(bar(baz(x)))").unwrap();
1306 match query {
1307 Query::Select(sel) => {
1308 assert_eq!(sel.targets.len(), 1);
1309 match &sel.targets[0].expr {
1310 Expr::Function(f1) => {
1311 assert_eq!(f1.name, "foo");
1312 match &f1.args[0] {
1313 Expr::Function(f2) => {
1314 assert_eq!(f2.name, "bar");
1315 match &f2.args[0] {
1316 Expr::Function(f3) => {
1317 assert_eq!(f3.name, "baz");
1318 assert!(matches!(&f3.args[0], Expr::Column(c) if c == "x"));
1319 }
1320 _ => panic!("Expected f3"),
1321 }
1322 }
1323 _ => panic!("Expected f2"),
1324 }
1325 }
1326 _ => panic!("Expected f1"),
1327 }
1328 }
1329 _ => panic!("Expected SELECT query"),
1330 }
1331 }
1332
1333 #[test]
1334 fn test_function_with_arithmetic() {
1335 let query = parse("SELECT sum(amount * 2)").unwrap();
1337 match query {
1338 Query::Select(sel) => match &sel.targets[0].expr {
1339 Expr::Function(f) => {
1340 assert_eq!(f.name, "sum");
1341 assert!(matches!(&f.args[0], Expr::BinaryOp(_)));
1342 }
1343 _ => panic!("Expected function"),
1344 },
1345 _ => panic!("Expected SELECT query"),
1346 }
1347 }
1348
1349 #[test]
1350 fn test_is_null() {
1351 let query = parse("SELECT * WHERE payee IS NULL").unwrap();
1352 match query {
1353 Query::Select(sel) => match sel.where_clause.unwrap() {
1354 Expr::UnaryOp(op) => {
1355 assert_eq!(op.op, UnaryOperator::IsNull);
1356 assert!(matches!(&op.operand, Expr::Column(c) if c == "payee"));
1357 }
1358 _ => panic!("Expected unary op"),
1359 },
1360 _ => panic!("Expected SELECT query"),
1361 }
1362 }
1363
1364 #[test]
1365 fn test_is_not_null() {
1366 let query = parse("SELECT * WHERE payee IS NOT NULL").unwrap();
1367 match query {
1368 Query::Select(sel) => match sel.where_clause.unwrap() {
1369 Expr::UnaryOp(op) => {
1370 assert_eq!(op.op, UnaryOperator::IsNotNull);
1371 assert!(matches!(&op.operand, Expr::Column(c) if c == "payee"));
1372 }
1373 _ => panic!("Expected unary op"),
1374 },
1375 _ => panic!("Expected SELECT query"),
1376 }
1377 }
1378
1379 #[test]
1380 fn test_not_regex() {
1381 let query = parse("SELECT * WHERE account !~ \"Assets:\"").unwrap();
1382 match query {
1383 Query::Select(sel) => match sel.where_clause.unwrap() {
1384 Expr::BinaryOp(op) => {
1385 assert_eq!(op.op, BinaryOperator::NotRegex);
1386 }
1387 _ => panic!("Expected binary op"),
1388 },
1389 _ => panic!("Expected SELECT query"),
1390 }
1391 }
1392
1393 #[test]
1394 fn test_modulo() {
1395 let query = parse("SELECT year % 4").unwrap();
1396 match query {
1397 Query::Select(sel) => match &sel.targets[0].expr {
1398 Expr::BinaryOp(op) => {
1399 assert_eq!(op.op, BinaryOperator::Mod);
1400 }
1401 _ => panic!("Expected binary op"),
1402 },
1403 _ => panic!("Expected SELECT query"),
1404 }
1405 }
1406
1407 #[test]
1408 fn test_between() {
1409 let query = parse("SELECT * WHERE year BETWEEN 2020 AND 2024").unwrap();
1410 match query {
1411 Query::Select(sel) => match sel.where_clause.unwrap() {
1412 Expr::Between { value, low, high } => {
1413 assert!(matches!(*value, Expr::Column(c) if c == "year"));
1414 assert!(matches!(*low, Expr::Literal(Literal::Number(_))));
1415 assert!(matches!(*high, Expr::Literal(Literal::Number(_))));
1416 }
1417 _ => panic!("Expected BETWEEN"),
1418 },
1419 _ => panic!("Expected SELECT query"),
1420 }
1421 }
1422
1423 #[test]
1424 fn test_not_in() {
1425 let query = parse("SELECT * WHERE account NOT IN tags").unwrap();
1426 match query {
1427 Query::Select(sel) => match sel.where_clause.unwrap() {
1428 Expr::BinaryOp(op) => {
1429 assert_eq!(op.op, BinaryOperator::NotIn);
1430 }
1431 _ => panic!("Expected binary op"),
1432 },
1433 _ => panic!("Expected SELECT query"),
1434 }
1435 }
1436
1437 #[test]
1438 fn test_in_set_literal() {
1439 let query = parse("SELECT * WHERE currency IN ('EUR', 'USD')").unwrap();
1441 match query {
1442 Query::Select(sel) => match sel.where_clause.unwrap() {
1443 Expr::BinaryOp(op) => {
1444 assert_eq!(op.op, BinaryOperator::In);
1445 match op.right {
1446 Expr::Set(elements) => {
1447 assert_eq!(elements.len(), 2);
1448 }
1449 _ => panic!("Expected Set"),
1450 }
1451 }
1452 _ => panic!("Expected binary op"),
1453 },
1454 _ => panic!("Expected SELECT query"),
1455 }
1456
1457 let query = parse("SELECT * WHERE currency IN ('EUR',)").unwrap();
1459 match query {
1460 Query::Select(sel) => match sel.where_clause.unwrap() {
1461 Expr::BinaryOp(op) => {
1462 assert_eq!(op.op, BinaryOperator::In);
1463 match op.right {
1464 Expr::Set(elements) => {
1465 assert_eq!(elements.len(), 1);
1466 }
1467 _ => panic!("Expected Set"),
1468 }
1469 }
1470 _ => panic!("Expected binary op"),
1471 },
1472 _ => panic!("Expected SELECT query"),
1473 }
1474
1475 let query = parse("SELECT * WHERE 'x' IN (tags)").unwrap();
1477 match query {
1478 Query::Select(sel) => match sel.where_clause.unwrap() {
1479 Expr::BinaryOp(op) => {
1480 assert_eq!(op.op, BinaryOperator::In);
1481 match op.right {
1483 Expr::Paren(inner) => match *inner {
1484 Expr::Column(name) => assert_eq!(name, "tags"),
1485 _ => panic!("Expected Column inside Paren"),
1486 },
1487 other => panic!("Expected Paren, got {other:?}"),
1488 }
1489 }
1490 _ => panic!("Expected binary op"),
1491 },
1492 _ => panic!("Expected SELECT query"),
1493 }
1494 }
1495
1496 #[test]
1497 fn test_string_arg_function() {
1498 let query = parse("SELECT foo(x)").unwrap();
1500 match query {
1501 Query::Select(sel) => match &sel.targets[0].expr {
1502 Expr::Function(f) => {
1503 assert_eq!(f.name, "foo");
1504 }
1505 _ => panic!("Expected function"),
1506 },
1507 _ => panic!("Expected SELECT query"),
1508 }
1509
1510 let query = parse("SELECT foo('bar')").unwrap();
1512 match query {
1513 Query::Select(sel) => match &sel.targets[0].expr {
1514 Expr::Function(f) => {
1515 assert_eq!(f.name, "foo");
1516 assert!(matches!(&f.args[0], Expr::Literal(Literal::String(s)) if s == "bar"));
1517 }
1518 _ => panic!("Expected function"),
1519 },
1520 _ => panic!("Expected SELECT query"),
1521 }
1522 }
1523
1524 #[test]
1525 fn test_meta_function() {
1526 let query = parse("SELECT meta('category')").unwrap();
1527 match query {
1528 Query::Select(sel) => match &sel.targets[0].expr {
1529 Expr::Function(f) => {
1530 assert_eq!(f.name.to_uppercase(), "META");
1531 assert_eq!(f.args.len(), 1);
1532 assert!(
1533 matches!(&f.args[0], Expr::Literal(Literal::String(s)) if s == "category")
1534 );
1535 }
1536 _ => panic!("Expected function"),
1537 },
1538 _ => panic!("Expected SELECT query"),
1539 }
1540 }
1541
1542 #[test]
1543 fn test_entry_meta_function() {
1544 let query = parse("SELECT entry_meta('source')").unwrap();
1545 match query {
1546 Query::Select(sel) => match &sel.targets[0].expr {
1547 Expr::Function(f) => {
1548 assert_eq!(f.name.to_uppercase(), "ENTRY_META");
1549 assert_eq!(f.args.len(), 1);
1550 }
1551 _ => panic!("Expected function"),
1552 },
1553 _ => panic!("Expected SELECT query"),
1554 }
1555 }
1556
1557 #[test]
1558 fn test_convert_function() {
1559 let query = parse("SELECT convert(position, 'USD')").unwrap();
1560 match query {
1561 Query::Select(sel) => match &sel.targets[0].expr {
1562 Expr::Function(f) => {
1563 assert_eq!(f.name.to_uppercase(), "CONVERT");
1564 assert_eq!(f.args.len(), 2);
1565 }
1566 _ => panic!("Expected function"),
1567 },
1568 _ => panic!("Expected SELECT query"),
1569 }
1570 }
1571
1572 #[test]
1573 fn test_type_cast_functions() {
1574 let query = parse("SELECT int(number)").unwrap();
1576 match query {
1577 Query::Select(sel) => match &sel.targets[0].expr {
1578 Expr::Function(f) => {
1579 assert_eq!(f.name.to_uppercase(), "INT");
1580 assert_eq!(f.args.len(), 1);
1581 }
1582 _ => panic!("Expected function"),
1583 },
1584 _ => panic!("Expected SELECT query"),
1585 }
1586
1587 let query = parse("SELECT decimal('123.45')").unwrap();
1589 match query {
1590 Query::Select(sel) => match &sel.targets[0].expr {
1591 Expr::Function(f) => {
1592 assert_eq!(f.name.to_uppercase(), "DECIMAL");
1593 }
1594 _ => panic!("Expected function"),
1595 },
1596 _ => panic!("Expected SELECT query"),
1597 }
1598
1599 let query = parse("SELECT str(123)").unwrap();
1601 match query {
1602 Query::Select(sel) => match &sel.targets[0].expr {
1603 Expr::Function(f) => {
1604 assert_eq!(f.name.to_uppercase(), "STR");
1605 }
1606 _ => panic!("Expected function"),
1607 },
1608 _ => panic!("Expected SELECT query"),
1609 }
1610
1611 let query = parse("SELECT bool(1)").unwrap();
1613 match query {
1614 Query::Select(sel) => match &sel.targets[0].expr {
1615 Expr::Function(f) => {
1616 assert_eq!(f.name.to_uppercase(), "BOOL");
1617 }
1618 _ => panic!("Expected function"),
1619 },
1620 _ => panic!("Expected SELECT query"),
1621 }
1622 }
1623
1624 #[test]
1625 fn test_system_table_prices() {
1626 let query = parse("SELECT date, currency, amount FROM #prices").unwrap();
1628 match query {
1629 Query::Select(sel) => {
1630 assert_eq!(sel.targets.len(), 3);
1631 assert!(matches!(&sel.targets[0].expr, Expr::Column(c) if c == "date"));
1632 assert!(matches!(&sel.targets[1].expr, Expr::Column(c) if c == "currency"));
1633 assert!(matches!(&sel.targets[2].expr, Expr::Column(c) if c == "amount"));
1634 let from = sel.from.unwrap();
1635 assert_eq!(from.table_name, Some("#prices".to_string()));
1636 }
1637 _ => panic!("Expected SELECT query"),
1638 }
1639 }
1640
1641 #[test]
1642 fn test_system_table_with_where() {
1643 let query = parse("SELECT * FROM #prices WHERE currency = 'EUR'").unwrap();
1645 match query {
1646 Query::Select(sel) => {
1647 let from = sel.from.unwrap();
1648 assert_eq!(from.table_name, Some("#prices".to_string()));
1649 assert!(sel.where_clause.is_some());
1650 }
1651 _ => panic!("Expected SELECT query"),
1652 }
1653 }
1654
1655 #[test]
1656 fn test_regular_table_identifier() {
1657 let query = parse("SELECT * FROM MyTable WHERE x = 1").unwrap();
1659 match query {
1660 Query::Select(sel) => {
1661 let from = sel.from.unwrap();
1662 assert_eq!(from.table_name, Some("MyTable".to_string()));
1663 }
1664 _ => panic!("Expected SELECT query"),
1665 }
1666 }
1667}