1use crate::ast::*;
2use crate::lexer::lex;
3use crate::token::Token;
4
5const MAX_NESTING_DEPTH: usize = 64;
9
10#[derive(Debug)]
12pub enum ParseError {
13 Lex { message: String, position: usize },
15 UnexpectedToken { expected: String, got: String },
17 NestingDepthExceeded { max: usize },
19 Unsupported { feature: String },
21 Syntax { message: String },
23}
24
25impl ParseError {
26 pub fn message(&self) -> String {
28 self.to_string()
29 }
30}
31
32impl std::fmt::Display for ParseError {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self {
35 Self::Lex { message, position } => write!(f, "at position {position}: {message}"),
36 Self::UnexpectedToken { expected, got } => {
37 write!(f, "expected {expected}, got {got}")
38 }
39 Self::NestingDepthExceeded { max } => {
40 write!(f, "query nesting depth exceeds maximum of {max}")
41 }
42 Self::Unsupported { feature } => write!(f, "{feature}"),
43 Self::Syntax { message } => write!(f, "{message}"),
44 }
45 }
46}
47
48impl std::error::Error for ParseError {}
49
50fn token_to_scalar_fn(tok: &Token) -> ScalarFn {
51 match tok {
52 Token::Upper => ScalarFn::Upper,
53 Token::Lower => ScalarFn::Lower,
54 Token::Length => ScalarFn::Length,
55 Token::Trim => ScalarFn::Trim,
56 Token::Substring => ScalarFn::Substring,
57 Token::Concat => ScalarFn::Concat,
58 Token::Abs => ScalarFn::Abs,
59 Token::Round => ScalarFn::Round,
60 Token::Ceil => ScalarFn::Ceil,
61 Token::Floor => ScalarFn::Floor,
62 Token::Sqrt => ScalarFn::Sqrt,
63 Token::Pow => ScalarFn::Pow,
64 Token::Now => ScalarFn::Now,
65 Token::Extract => ScalarFn::Extract,
66 Token::DateAdd => ScalarFn::DateAdd,
67 Token::DateDiff => ScalarFn::DateDiff,
68 _ => unreachable!(),
69 }
70}
71
72struct Parser {
73 tokens: Vec<Token>,
74 pos: usize,
75 depth: usize,
76}
77
78pub fn parse(input: &str) -> Result<Statement, ParseError> {
100 let tokens = lex(input).map_err(|e| ParseError::Lex {
101 message: e.message,
102 position: e.position,
103 })?;
104 let mut parser = Parser {
105 tokens,
106 pos: 0,
107 depth: 0,
108 };
109 let stmt = parser.parse_statement()?;
110 if !matches!(parser.peek(), Token::Eof) {
116 return Err(ParseError::Syntax {
117 message: format!(
118 "unexpected trailing token: {}",
119 parser.peek().display_name()
120 ),
121 });
122 }
123 Ok(stmt)
124}
125
126fn substitute_projection_aliases(expr: Expr, fields: &[ProjectionField]) -> Expr {
131 match expr {
132 Expr::Field(ref name) => {
133 for f in fields {
134 if f.alias.as_deref() == Some(name.as_str()) {
135 return f.expr.clone();
136 }
137 }
138 expr
139 }
140 Expr::BinaryOp(l, op, r) => Expr::BinaryOp(
141 Box::new(substitute_projection_aliases(*l, fields)),
142 op,
143 Box::new(substitute_projection_aliases(*r, fields)),
144 ),
145 Expr::UnaryOp(op, inner) => {
146 Expr::UnaryOp(op, Box::new(substitute_projection_aliases(*inner, fields)))
147 }
148 Expr::Coalesce(l, r) => Expr::Coalesce(
149 Box::new(substitute_projection_aliases(*l, fields)),
150 Box::new(substitute_projection_aliases(*r, fields)),
151 ),
152 Expr::InList {
153 expr: e,
154 list,
155 negated,
156 } => Expr::InList {
157 expr: Box::new(substitute_projection_aliases(*e, fields)),
158 list: list
159 .into_iter()
160 .map(|i| substitute_projection_aliases(i, fields))
161 .collect(),
162 negated,
163 },
164 Expr::ScalarFunc(f, args) => Expr::ScalarFunc(
165 f,
166 args.into_iter()
167 .map(|a| substitute_projection_aliases(a, fields))
168 .collect(),
169 ),
170 other => other,
171 }
172}
173
174impl Parser {
175 fn peek(&self) -> &Token {
176 &self.tokens[self.pos]
177 }
178
179 fn advance(&mut self) -> Token {
180 let t = self.tokens[self.pos].clone();
181 self.pos += 1;
182 t
183 }
184
185 fn expect(&mut self, expected: &Token) -> Result<(), ParseError> {
186 let t = self.advance();
187 if &t == expected {
188 Ok(())
189 } else {
190 Err(ParseError::UnexpectedToken {
191 expected: expected.display_name(),
192 got: t.display_name(),
193 })
194 }
195 }
196
197 fn unexpected(&self, expected: &str, got: &Token) -> ParseError {
199 ParseError::UnexpectedToken {
200 expected: expected.into(),
201 got: got.display_name(),
202 }
203 }
204
205 fn parse_statement(&mut self) -> Result<Statement, ParseError> {
206 self.depth += 1;
207 if self.depth > MAX_NESTING_DEPTH {
208 self.depth -= 1;
209 return Err(ParseError::NestingDepthExceeded {
210 max: MAX_NESTING_DEPTH,
211 });
212 }
213 if matches!(self.peek(), Token::Explain) {
214 self.advance();
215 let inner = self.parse_statement()?;
216 self.depth -= 1;
217 return Ok(Statement::Explain(Box::new(inner)));
218 }
219 let stmt = match self.peek() {
220 Token::Insert => self.parse_insert(),
221 Token::Upsert => self.parse_upsert(),
222 Token::Type => self.parse_create_type(),
223 Token::Alter => self.parse_alter_table(),
224 Token::Drop => self.parse_drop_or_drop_view(),
225 Token::Materialized => self.parse_create_view(),
226 Token::Refresh => self.parse_refresh_view(),
227 Token::Begin => {
228 self.advance();
229 if *self.peek() == Token::Transaction {
231 self.advance();
232 }
233 return Ok(Statement::Begin);
234 }
235 Token::Commit => {
236 self.advance();
237 return Ok(Statement::Commit);
238 }
239 Token::Rollback => {
240 self.advance();
241 return Ok(Statement::Rollback);
242 }
243 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
244 self.parse_aggregate_query()
245 }
246 Token::Ident(_) => self.parse_query_or_mutation(),
247 Token::Update => Err(ParseError::Syntax {
248 message: "'update' cannot start a statement — in PowQL, use pipeline syntax: \
249 TableName filter ... update { ... }"
250 .into(),
251 }),
252 Token::Delete => Err(ParseError::Syntax {
253 message: "'delete' cannot start a statement — in PowQL, use pipeline syntax: \
254 TableName filter ... delete"
255 .into(),
256 }),
257 _ => Err(self.unexpected("statement", self.peek())),
258 }?;
259 let result = self.maybe_parse_union(stmt);
261 self.depth -= 1;
262 result
263 }
264
265 fn parse_query_or_mutation(&mut self) -> Result<Statement, ParseError> {
266 let source = match self.advance() {
267 Token::Ident(name) => name,
268 t => {
269 return Err(ParseError::UnexpectedToken {
270 expected: "type name".into(),
271 got: t.display_name(),
272 })
273 }
274 };
275 let alias = self.try_parse_alias();
276 let joins = self.parse_joins()?;
277
278 let mut filter = None;
282 let mut order = None;
283 let mut limit = None;
284 let mut offset = None;
285 let mut projection = None;
286 let mut distinct = false;
287 let mut group_by = None;
288
289 loop {
290 match self.peek() {
291 Token::Distinct => {
292 self.advance();
293 distinct = true;
294 }
295 Token::Group => {
296 self.advance();
297 group_by = Some(self.parse_group_by()?);
298 }
299 Token::Filter => {
300 self.advance();
301 filter = Some(self.parse_expr()?);
302 }
303 Token::Order => {
304 self.advance();
305 order = Some(self.parse_order()?);
306 }
307 Token::Limit => {
308 self.advance();
309 limit = Some(self.parse_expr()?);
310 }
311 Token::Offset => {
312 self.advance();
313 offset = Some(self.parse_expr()?);
314 }
315 Token::LBrace => {
316 projection = Some(self.parse_projection()?);
317 }
318 Token::Having => {
319 self.advance();
321 let having_expr = self.parse_expr()?;
322 let group = group_by.as_mut().ok_or_else(|| ParseError::Syntax {
323 message: "having without group by".into(),
324 })?;
325 let rewritten = match projection.as_ref() {
326 Some(fields) => substitute_projection_aliases(having_expr, fields),
327 None => having_expr,
328 };
329 group.having = Some(match group.having.take() {
330 Some(existing) => {
331 Expr::BinaryOp(Box::new(existing), BinOp::And, Box::new(rewritten))
332 }
333 None => rewritten,
334 });
335 }
336 Token::Update => {
337 if !joins.is_empty() {
338 return Err(ParseError::Unsupported {
339 feature: "update on a joined query is not supported".into(),
340 });
341 }
342 self.advance();
343 let assignments = self.parse_assignments()?;
344 return Ok(Statement::UpdateQuery(UpdateExpr {
345 source,
346 filter,
347 assignments,
348 }));
349 }
350 Token::Delete => {
351 if !joins.is_empty() {
352 return Err(ParseError::Unsupported {
353 feature: "delete on a joined query is not supported".into(),
354 });
355 }
356 self.advance();
357 return Ok(Statement::DeleteQuery(DeleteExpr { source, filter }));
358 }
359 _ => break,
360 }
361 }
362
363 Ok(Statement::Query(QueryExpr {
364 source,
365 alias,
366 joins,
367 filter,
368 order,
369 limit,
370 offset,
371 projection,
372 aggregation: None,
373 distinct,
374 group_by,
375 }))
376 }
377
378 fn parse_query_tail(&mut self, source: String) -> Result<QueryExpr, ParseError> {
384 let alias = self.try_parse_alias();
385 let joins = self.parse_joins()?;
386 let mut filter = None;
387 let mut order = None;
388 let mut limit = None;
389 let mut offset = None;
390 let mut projection = None;
391 let mut distinct = false;
392 let mut group_by = None;
393
394 loop {
395 match self.peek() {
396 Token::Distinct => {
397 self.advance();
398 distinct = true;
399 }
400 Token::Group => {
401 self.advance();
402 group_by = Some(self.parse_group_by()?);
403 }
404 Token::Filter => {
405 self.advance();
406 filter = Some(self.parse_expr()?);
407 }
408 Token::Order => {
409 self.advance();
410 order = Some(self.parse_order()?);
411 }
412 Token::Limit => {
413 self.advance();
414 limit = Some(self.parse_expr()?);
415 }
416 Token::Offset => {
417 self.advance();
418 offset = Some(self.parse_expr()?);
419 }
420 Token::LBrace => {
421 projection = Some(self.parse_projection()?);
422 }
423 Token::Having => {
424 self.advance();
430 let having_expr = self.parse_expr()?;
431 let group = group_by.as_mut().ok_or_else(|| ParseError::Syntax {
432 message: "having without group by".into(),
433 })?;
434 let rewritten = match projection.as_ref() {
435 Some(fields) => substitute_projection_aliases(having_expr, fields),
436 None => having_expr,
437 };
438 group.having = Some(match group.having.take() {
439 Some(existing) => {
440 Expr::BinaryOp(Box::new(existing), BinOp::And, Box::new(rewritten))
441 }
442 None => rewritten,
443 });
444 }
445 _ => break,
446 }
447 }
448
449 Ok(QueryExpr {
450 source,
451 alias,
452 joins,
453 filter,
454 order,
455 limit,
456 offset,
457 projection,
458 aggregation: None,
459 distinct,
460 group_by,
461 })
462 }
463
464 fn try_parse_alias(&mut self) -> Option<String> {
468 if *self.peek() == Token::As {
469 self.advance();
470 if let Token::Ident(name) = self.peek().clone() {
471 self.advance();
472 return Some(name);
473 }
474 }
475 None
476 }
477
478 fn parse_joins(&mut self) -> Result<Vec<JoinClause>, ParseError> {
485 let mut joins = Vec::new();
486 loop {
487 let kind = match self.peek() {
488 Token::Join => {
489 self.advance();
490 JoinKind::Inner
491 }
492 Token::Inner => {
493 self.advance();
494 self.expect(&Token::Join)?;
495 JoinKind::Inner
496 }
497 Token::LeftKw => {
498 self.advance();
499 if *self.peek() == Token::Outer {
500 self.advance();
501 }
502 self.expect(&Token::Join)?;
503 JoinKind::LeftOuter
504 }
505 Token::RightKw => {
506 self.advance();
507 if *self.peek() == Token::Outer {
508 self.advance();
509 }
510 self.expect(&Token::Join)?;
511 JoinKind::RightOuter
512 }
513 Token::Cross => {
514 self.advance();
515 self.expect(&Token::Join)?;
516 JoinKind::Cross
517 }
518 _ => break,
519 };
520
521 let source = match self.advance() {
522 Token::Ident(name) => name,
523 t => {
524 return Err(ParseError::UnexpectedToken {
525 expected: "type name after join".into(),
526 got: t.display_name(),
527 });
528 }
529 };
530 let alias = self.try_parse_alias();
531 let on = if kind == JoinKind::Cross {
532 None
533 } else if *self.peek() == Token::On {
534 self.advance();
535 Some(self.parse_expr()?)
536 } else {
537 return Err(ParseError::Syntax {
538 message: format!("expected `on <expr>` after join {source}"),
539 });
540 };
541
542 joins.push(JoinClause {
543 kind,
544 source,
545 alias,
546 on,
547 });
548 }
549 Ok(joins)
550 }
551
552 fn parse_insert(&mut self) -> Result<Statement, ParseError> {
553 self.expect(&Token::Insert)?;
554 let target = match self.advance() {
555 Token::Ident(name) => name,
556 t => {
557 return Err(ParseError::UnexpectedToken {
558 expected: "type name".into(),
559 got: t.display_name(),
560 })
561 }
562 };
563 let mut rows = vec![self.parse_assignments()?];
567 while *self.peek() == Token::Comma {
568 self.advance(); rows.push(self.parse_assignments()?);
570 }
571 Ok(Statement::Insert(InsertExpr { target, rows }))
572 }
573
574 fn parse_upsert(&mut self) -> Result<Statement, ParseError> {
576 self.expect(&Token::Upsert)?;
577 let target = match self.advance() {
578 Token::Ident(name) => name,
579 t => {
580 return Err(ParseError::UnexpectedToken {
581 expected: "type name".into(),
582 got: t.display_name(),
583 })
584 }
585 };
586 self.expect(&Token::On)?;
587 let key_column = match self.advance() {
588 Token::DotIdent(name) => name,
589 t => {
590 return Err(ParseError::UnexpectedToken {
591 expected: ".key_column".into(),
592 got: t.display_name(),
593 })
594 }
595 };
596 let assignments = self.parse_assignments()?;
597 let on_conflict = if *self.peek() == Token::On {
598 self.advance(); self.expect(&Token::Conflict)?;
600 self.parse_assignments()?
601 } else {
602 Vec::new()
603 };
604 Ok(Statement::Upsert(UpsertExpr {
605 target,
606 key_column,
607 assignments,
608 on_conflict,
609 }))
610 }
611
612 fn parse_assignments(&mut self) -> Result<Vec<Assignment>, ParseError> {
613 self.expect(&Token::LBrace)?;
614 let mut assignments = Vec::new();
615 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
616 let field = match self.advance() {
617 Token::Ident(name) => name,
618 t => {
619 return Err(ParseError::UnexpectedToken {
620 expected: "field name".into(),
621 got: t.display_name(),
622 })
623 }
624 };
625 self.expect(&Token::Assign)?;
626 let value = self.parse_expr()?;
627 assignments.push(Assignment { field, value });
628 if *self.peek() == Token::Comma {
629 self.advance();
630 }
631 }
632 self.expect(&Token::RBrace)?;
633 Ok(assignments)
634 }
635
636 fn parse_projection(&mut self) -> Result<Vec<ProjectionField>, ParseError> {
637 self.expect(&Token::LBrace)?;
638 let mut fields = Vec::new();
639 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
640 let first = self.advance();
641 if *self.peek() == Token::Colon {
642 self.advance();
644 let alias = match first {
645 Token::Ident(name) => name,
646 _ => {
647 return Err(ParseError::Syntax {
648 message: "expected alias name".into(),
649 })
650 }
651 };
652 let expr = self.parse_expr()?;
653 fields.push(ProjectionField {
654 alias: Some(alias),
655 expr,
656 });
657 } else {
658 let expr = match first {
659 Token::Ident(name) => {
663 if let Token::DotIdent(field) = self.peek().clone() {
664 self.advance();
665 Expr::QualifiedField {
666 qualifier: name,
667 field,
668 }
669 } else {
670 Expr::Field(name)
671 }
672 }
673 Token::DotIdent(name) => Expr::Field(name),
674 Token::RowNumber | Token::Rank | Token::DenseRank => {
675 let wfunc = match first {
676 Token::RowNumber => WindowFunc::RowNumber,
677 Token::Rank => WindowFunc::Rank,
678 Token::DenseRank => WindowFunc::DenseRank,
679 _ => {
680 return Err(ParseError::Syntax {
681 message: "unexpected window function token".into(),
682 })
683 }
684 };
685 self.expect(&Token::LParen)?;
686 self.expect(&Token::RParen)?;
687 let (partition_by, order_by) = self.parse_over_clause()?;
688 Expr::Window {
689 function: wfunc,
690 args: vec![],
691 partition_by,
692 order_by,
693 }
694 }
695 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
696 let mut func = match first {
697 Token::Count => AggFunc::Count,
698 Token::Avg => AggFunc::Avg,
699 Token::Sum => AggFunc::Sum,
700 Token::Min => AggFunc::Min,
701 Token::Max => AggFunc::Max,
702 _ => {
703 return Err(ParseError::Syntax {
704 message: "unexpected aggregate token".into(),
705 })
706 }
707 };
708 self.expect(&Token::LParen)?;
709 if func == AggFunc::Count && *self.peek() == Token::Star {
711 self.advance();
712 self.expect(&Token::RParen)?;
713 if *self.peek() == Token::Over {
715 let (partition_by, order_by) = self.parse_over_clause()?;
716 Expr::Window {
717 function: WindowFunc::Count,
718 args: vec![Expr::Field("*".into())],
719 partition_by,
720 order_by,
721 }
722 } else {
723 Expr::FunctionCall(
724 AggFunc::Count,
725 Box::new(Expr::Field("*".into())),
726 )
727 }
728 } else {
729 if func == AggFunc::Count && *self.peek() == Token::Distinct {
731 self.advance();
732 func = AggFunc::CountDistinct;
733 }
734 let inner = self.parse_expr()?;
735 self.expect(&Token::RParen)?;
736 if *self.peek() == Token::Over {
738 let wfunc =
739 match func {
740 AggFunc::Count => WindowFunc::Count,
741 AggFunc::Avg => WindowFunc::Avg,
742 AggFunc::Sum => WindowFunc::Sum,
743 AggFunc::Min => WindowFunc::Min,
744 AggFunc::Max => WindowFunc::Max,
745 _ => return Err(ParseError::Unsupported {
746 feature:
747 "count(distinct ...) over (...) is not supported"
748 .into(),
749 }),
750 };
751 let (partition_by, order_by) = self.parse_over_clause()?;
752 Expr::Window {
753 function: wfunc,
754 args: vec![inner],
755 partition_by,
756 order_by,
757 }
758 } else {
759 Expr::FunctionCall(func, Box::new(inner))
760 }
761 }
762 }
763 Token::Upper
764 | Token::Lower
765 | Token::Length
766 | Token::Trim
767 | Token::Substring
768 | Token::Concat
769 | Token::Abs
770 | Token::Round
771 | Token::Ceil
772 | Token::Floor
773 | Token::Sqrt
774 | Token::Pow
775 | Token::Now
776 | Token::Extract
777 | Token::DateAdd
778 | Token::DateDiff => {
779 let func = token_to_scalar_fn(&first);
780 self.expect(&Token::LParen)?;
781 let mut args = Vec::new();
782 while !matches!(self.peek(), Token::RParen | Token::Eof) {
783 args.push(self.parse_expr()?);
784 if *self.peek() == Token::Comma {
785 self.advance();
786 }
787 }
788 self.expect(&Token::RParen)?;
789 Expr::ScalarFunc(func, args)
790 }
791 Token::Cast => {
792 self.expect(&Token::LParen)?;
793 let inner = self.parse_expr()?;
794 self.expect(&Token::Comma)?;
795 let cast_type = self.parse_cast_type()?;
796 self.expect(&Token::RParen)?;
797 Expr::Cast(Box::new(inner), cast_type)
798 }
799 Token::Case => {
800 let mut whens = Vec::new();
801 while *self.peek() == Token::When {
802 self.advance();
803 let condition = self.parse_expr()?;
804 self.expect(&Token::Then)?;
805 let result = self.parse_expr()?;
806 whens.push((Box::new(condition), Box::new(result)));
807 }
808 let else_expr = if *self.peek() == Token::Else {
809 self.advance();
810 Some(Box::new(self.parse_expr()?))
811 } else {
812 None
813 };
814 self.expect(&Token::End)?;
815 Expr::Case { whens, else_expr }
816 }
817 _ => {
818 return Err(ParseError::UnexpectedToken {
819 expected: "field".into(),
820 got: first.display_name(),
821 })
822 }
823 };
824 fields.push(ProjectionField { alias: None, expr });
825 }
826 if *self.peek() == Token::Comma {
827 self.advance();
828 }
829 }
830 self.expect(&Token::RBrace)?;
831 Ok(fields)
832 }
833
834 fn parse_over_clause(&mut self) -> Result<(Vec<String>, Vec<OrderKey>), ParseError> {
837 self.expect(&Token::Over)?;
838 self.expect(&Token::LParen)?;
839 let mut partition_by = Vec::new();
840 let mut order_by = Vec::new();
841 if *self.peek() == Token::Partition {
842 self.advance();
843 while let Token::DotIdent(name) = self.peek() {
844 let name = name.clone();
845 self.advance();
846 partition_by.push(name);
847 if *self.peek() == Token::Comma {
848 if matches!(self.tokens.get(self.pos + 1), Some(Token::DotIdent(_))) {
852 self.advance();
853 } else {
854 break;
855 }
856 } else {
857 break;
858 }
859 }
860 }
861 if *self.peek() == Token::Order {
862 self.advance();
863 while let Token::DotIdent(name) = self.peek() {
864 let field = name.clone();
865 self.advance();
866 let descending = match self.peek() {
867 Token::Desc => {
868 self.advance();
869 true
870 }
871 Token::Asc => {
872 self.advance();
873 false
874 }
875 _ => false,
876 };
877 order_by.push(OrderKey { field, descending });
878 if *self.peek() == Token::Comma {
879 self.advance();
880 } else {
881 break;
882 }
883 }
884 }
885 self.expect(&Token::RParen)?;
886 Ok((partition_by, order_by))
887 }
888
889 fn parse_cast_type(&mut self) -> Result<CastType, ParseError> {
891 match self.advance() {
892 Token::StringLit(s) => match s.as_str() {
893 "int" | "Int" | "INT" => Ok(CastType::Int),
894 "float" | "Float" | "FLOAT" => Ok(CastType::Float),
895 "str" | "Str" | "STR" | "string" | "String" => Ok(CastType::Str),
896 "bool" | "Bool" | "BOOL" | "boolean" => Ok(CastType::Bool),
897 "datetime" | "DateTime" | "DATETIME" => Ok(CastType::DateTime),
898 other => Err(ParseError::Syntax {
899 message: format!("invalid cast type: \"{other}\""),
900 }),
901 },
902 t => Err(ParseError::UnexpectedToken {
903 expected: "string literal for cast type".into(),
904 got: t.display_name(),
905 }),
906 }
907 }
908
909 fn parse_order(&mut self) -> Result<OrderClause, ParseError> {
910 let mut keys = Vec::new();
911 loop {
912 let field = match self.advance() {
913 Token::DotIdent(name) => name,
914 t => {
915 return Err(ParseError::UnexpectedToken {
916 expected: ".field after order".into(),
917 got: t.display_name(),
918 })
919 }
920 };
921 let descending = match self.peek() {
922 Token::Desc => {
923 self.advance();
924 true
925 }
926 Token::Asc => {
927 self.advance();
928 false
929 }
930 _ => false,
931 };
932 keys.push(OrderKey { field, descending });
933 if *self.peek() == Token::Comma {
934 self.advance();
935 } else {
936 break;
937 }
938 }
939 Ok(OrderClause { keys })
940 }
941
942 fn parse_aggregate_query(&mut self) -> Result<Statement, ParseError> {
943 let mut func = match self.advance() {
944 Token::Count => AggFunc::Count,
945 Token::Avg => AggFunc::Avg,
946 Token::Sum => AggFunc::Sum,
947 Token::Min => AggFunc::Min,
948 Token::Max => AggFunc::Max,
949 t => {
950 return Err(ParseError::UnexpectedToken {
951 expected: "aggregate function".into(),
952 got: t.display_name(),
953 })
954 }
955 };
956 self.expect(&Token::LParen)?;
957 if func == AggFunc::Count && *self.peek() == Token::Distinct {
959 self.advance();
960 func = AggFunc::CountDistinct;
961 }
962 let source = match self.advance() {
963 Token::Ident(name) => name,
964 t => {
965 return Err(ParseError::UnexpectedToken {
966 expected: "type name".into(),
967 got: t.display_name(),
968 })
969 }
970 };
971 let mut query = self.parse_query_tail(source)?;
975 self.expect(&Token::RParen)?;
976
977 let mut agg_field: Option<String> = None;
984 if func != AggFunc::Count {
985 if let Some(proj) = &query.projection {
986 if proj.len() == 1 && proj[0].alias.is_none() {
987 if let Expr::Field(name) = &proj[0].expr {
988 agg_field = Some(name.clone());
989 }
990 }
991 }
992 if agg_field.is_some() {
993 query.projection = None;
994 }
995 }
996 query.aggregation = Some(AggregateExpr {
997 function: func,
998 field: agg_field,
999 });
1000 Ok(Statement::Query(query))
1001 }
1002
1003 fn parse_expr(&mut self) -> Result<Expr, ParseError> {
1004 self.depth += 1;
1005 if self.depth > MAX_NESTING_DEPTH {
1006 self.depth -= 1;
1007 return Err(ParseError::NestingDepthExceeded {
1008 max: MAX_NESTING_DEPTH,
1009 });
1010 }
1011 let result = self.parse_or_expr();
1012 self.depth -= 1;
1013 result
1014 }
1015
1016 fn parse_or_expr(&mut self) -> Result<Expr, ParseError> {
1017 let mut left = self.parse_and_expr()?;
1018 while *self.peek() == Token::Or {
1019 self.advance();
1020 let right = self.parse_and_expr()?;
1021 left = Expr::BinaryOp(Box::new(left), BinOp::Or, Box::new(right));
1022 }
1023 Ok(left)
1024 }
1025
1026 fn parse_and_expr(&mut self) -> Result<Expr, ParseError> {
1027 let mut left = self.parse_comparison()?;
1028 while *self.peek() == Token::And {
1029 self.advance();
1030 let right = self.parse_comparison()?;
1031 left = Expr::BinaryOp(Box::new(left), BinOp::And, Box::new(right));
1032 }
1033 Ok(left)
1034 }
1035
1036 fn parse_comparison(&mut self) -> Result<Expr, ParseError> {
1037 let left = self.parse_additive()?;
1038
1039 if *self.peek() == Token::Is {
1041 self.advance();
1042 if *self.peek() == Token::Not {
1043 self.advance();
1044 self.expect(&Token::Null)?;
1045 return Ok(Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(left)));
1046 } else {
1047 self.expect(&Token::Null)?;
1048 return Ok(Expr::UnaryOp(UnaryOp::IsNull, Box::new(left)));
1049 }
1050 }
1051
1052 match self.peek() {
1055 Token::In => {
1056 self.advance();
1057 return self.parse_in_list(left, false);
1058 }
1059 Token::Like => {
1060 self.advance();
1061 let pattern = self.parse_additive()?;
1062 return Ok(Expr::BinaryOp(
1063 Box::new(left),
1064 BinOp::Like,
1065 Box::new(pattern),
1066 ));
1067 }
1068 Token::Between => {
1069 self.advance();
1070 return self.parse_between(left, false);
1071 }
1072 Token::Not => {
1073 let next = self.tokens.get(self.pos + 1);
1077 match next {
1078 Some(Token::In) => {
1079 self.advance(); self.advance(); return self.parse_in_list(left, true);
1082 }
1083 Some(Token::Like) => {
1084 self.advance(); self.advance(); let pattern = self.parse_additive()?;
1087 let like = Expr::BinaryOp(Box::new(left), BinOp::Like, Box::new(pattern));
1088 return Ok(Expr::UnaryOp(UnaryOp::Not, Box::new(like)));
1089 }
1090 Some(Token::Between) => {
1091 self.advance(); self.advance(); return self.parse_between(left, true);
1094 }
1095 _ => {}
1096 }
1097 }
1098 _ => {}
1099 }
1100
1101 let op = match self.peek() {
1102 Token::Eq => BinOp::Eq,
1103 Token::Neq => BinOp::Neq,
1104 Token::Lt => BinOp::Lt,
1105 Token::Gt => BinOp::Gt,
1106 Token::Lte => BinOp::Lte,
1107 Token::Gte => BinOp::Gte,
1108 _ => return Ok(left),
1109 };
1110 self.advance();
1111 if *self.peek() == Token::Null {
1115 match op {
1116 BinOp::Eq => {
1117 self.advance();
1118 return Ok(Expr::UnaryOp(UnaryOp::IsNull, Box::new(left)));
1119 }
1120 BinOp::Neq => {
1121 self.advance();
1122 return Ok(Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(left)));
1123 }
1124 _ => {}
1125 }
1126 }
1127 let right = self.parse_additive()?;
1128 Ok(Expr::BinaryOp(Box::new(left), op, Box::new(right)))
1129 }
1130
1131 fn parse_in_list(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
1136 self.expect(&Token::LParen)?;
1137 if let Token::Ident(_) = self.peek() {
1139 let after = self.tokens.get(self.pos + 1);
1142 let is_subquery = !matches!(after, Some(Token::Comma) | Some(Token::RParen));
1143 if is_subquery {
1144 let source = match self.advance() {
1145 Token::Ident(name) => name,
1146 _ => unreachable!(),
1147 };
1148 let subquery = self.parse_query_tail(source)?;
1149 self.expect(&Token::RParen)?;
1150 return Ok(Expr::InSubquery {
1151 expr: Box::new(expr),
1152 subquery: Box::new(subquery),
1153 negated,
1154 });
1155 }
1156 }
1157 let mut list = Vec::new();
1158 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1159 list.push(self.parse_expr()?);
1160 if *self.peek() == Token::Comma {
1161 self.advance();
1162 }
1163 }
1164 self.expect(&Token::RParen)?;
1165 Ok(Expr::InList {
1166 expr: Box::new(expr),
1167 list,
1168 negated,
1169 })
1170 }
1171
1172 fn try_parse_exists_subquery(&mut self) -> Result<Option<QueryExpr>, ParseError> {
1180 if *self.peek() != Token::LParen {
1181 return Ok(None);
1182 }
1183 let after_lparen = self.tokens.get(self.pos + 1);
1187 if !matches!(after_lparen, Some(Token::Ident(_))) {
1188 return Ok(None);
1189 }
1190 self.expect(&Token::LParen)?;
1191 let source = match self.advance() {
1192 Token::Ident(name) => name,
1193 _ => unreachable!(),
1194 };
1195 let subquery = self.parse_query_tail(source)?;
1196 self.expect(&Token::RParen)?;
1197 Ok(Some(subquery))
1198 }
1199
1200 fn parse_between(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
1204 let low = self.parse_additive()?;
1205 self.expect(&Token::And)?;
1206 let high = self.parse_additive()?;
1207 if negated {
1208 Ok(Expr::BinaryOp(
1210 Box::new(Expr::BinaryOp(
1211 Box::new(expr.clone()),
1212 BinOp::Lt,
1213 Box::new(low),
1214 )),
1215 BinOp::Or,
1216 Box::new(Expr::BinaryOp(Box::new(expr), BinOp::Gt, Box::new(high))),
1217 ))
1218 } else {
1219 Ok(Expr::BinaryOp(
1221 Box::new(Expr::BinaryOp(
1222 Box::new(expr.clone()),
1223 BinOp::Gte,
1224 Box::new(low),
1225 )),
1226 BinOp::And,
1227 Box::new(Expr::BinaryOp(Box::new(expr), BinOp::Lte, Box::new(high))),
1228 ))
1229 }
1230 }
1231
1232 fn parse_group_by(&mut self) -> Result<GroupByClause, ParseError> {
1234 let mut keys = Vec::new();
1235 while let Token::DotIdent(name) = self.peek() {
1236 let name = name.clone();
1237 self.advance();
1238 keys.push(name);
1239 if *self.peek() == Token::Comma {
1240 self.advance();
1241 } else {
1242 break;
1243 }
1244 }
1245 if keys.is_empty() {
1246 return Err(ParseError::Syntax {
1247 message: "expected at least one .field after group".into(),
1248 });
1249 }
1250 let having = if *self.peek() == Token::Having {
1251 self.advance();
1252 Some(self.parse_expr()?)
1253 } else {
1254 None
1255 };
1256 Ok(GroupByClause { keys, having })
1257 }
1258
1259 fn parse_additive(&mut self) -> Result<Expr, ParseError> {
1260 let mut left = self.parse_multiplicative()?;
1261 loop {
1262 let op = match self.peek() {
1263 Token::Plus => BinOp::Add,
1264 Token::Minus => BinOp::Sub,
1265 Token::Coalesce => {
1266 self.advance();
1267 let right = self.parse_multiplicative()?;
1268 left = Expr::Coalesce(Box::new(left), Box::new(right));
1269 continue;
1270 }
1271 _ => break,
1272 };
1273 self.advance();
1274 let right = self.parse_multiplicative()?;
1275 left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
1276 }
1277 Ok(left)
1278 }
1279
1280 fn parse_multiplicative(&mut self) -> Result<Expr, ParseError> {
1281 let mut left = self.parse_primary()?;
1282 loop {
1283 let op = match self.peek() {
1284 Token::Star => BinOp::Mul,
1285 Token::Slash => BinOp::Div,
1286 _ => break,
1287 };
1288 self.advance();
1289 let right = self.parse_primary()?;
1290 left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
1291 }
1292 Ok(left)
1293 }
1294
1295 fn parse_primary(&mut self) -> Result<Expr, ParseError> {
1296 match self.peek().clone() {
1297 Token::DotIdent(name) => {
1298 self.advance();
1299 Ok(Expr::Field(name))
1300 }
1301 Token::IntLit(v) => {
1302 self.advance();
1303 Ok(Expr::Literal(Literal::Int(v)))
1304 }
1305 Token::FloatLit(v) => {
1306 self.advance();
1307 Ok(Expr::Literal(Literal::Float(v)))
1308 }
1309 Token::StringLit(v) => {
1310 self.advance();
1311 Ok(Expr::Literal(Literal::String(v)))
1312 }
1313 Token::BoolLit(v) => {
1314 self.advance();
1315 Ok(Expr::Literal(Literal::Bool(v)))
1316 }
1317 Token::Param(name) => {
1318 self.advance();
1319 Ok(Expr::Param(name))
1320 }
1321 Token::Null => {
1322 self.advance();
1323 Ok(Expr::Null)
1324 }
1325 Token::Not => {
1326 self.advance();
1327 if *self.peek() == Token::Exists {
1328 self.advance();
1329 if let Some(sub) = self.try_parse_exists_subquery()? {
1333 return Ok(Expr::ExistsSubquery {
1334 subquery: Box::new(sub),
1335 negated: true,
1336 });
1337 }
1338 let expr = self.parse_primary()?;
1339 Ok(Expr::UnaryOp(UnaryOp::NotExists, Box::new(expr)))
1340 } else {
1341 let expr = self.parse_primary()?;
1342 Ok(Expr::UnaryOp(UnaryOp::Not, Box::new(expr)))
1343 }
1344 }
1345 Token::Exists => {
1346 self.advance();
1347 if let Some(sub) = self.try_parse_exists_subquery()? {
1351 return Ok(Expr::ExistsSubquery {
1352 subquery: Box::new(sub),
1353 negated: false,
1354 });
1355 }
1356 let expr = self.parse_primary()?;
1357 Ok(Expr::UnaryOp(UnaryOp::Exists, Box::new(expr)))
1358 }
1359 Token::LParen => {
1360 self.advance();
1361 let expr = self.parse_expr()?;
1362 self.expect(&Token::RParen)?;
1363 Ok(expr)
1364 }
1365 Token::Ident(name) => {
1366 self.advance();
1367 if let Token::DotIdent(field) = self.peek().clone() {
1371 self.advance();
1372 return Ok(Expr::QualifiedField {
1373 qualifier: name,
1374 field,
1375 });
1376 }
1377 Ok(Expr::Field(name))
1378 }
1379 Token::RowNumber | Token::Rank | Token::DenseRank => {
1381 let wfunc = match self.advance() {
1382 Token::RowNumber => WindowFunc::RowNumber,
1383 Token::Rank => WindowFunc::Rank,
1384 Token::DenseRank => WindowFunc::DenseRank,
1385 _ => {
1386 return Err(ParseError::Syntax {
1387 message: "unexpected window function token".into(),
1388 })
1389 }
1390 };
1391 self.expect(&Token::LParen)?;
1392 self.expect(&Token::RParen)?;
1393 let (partition_by, order_by) = self.parse_over_clause()?;
1394 Ok(Expr::Window {
1395 function: wfunc,
1396 args: vec![],
1397 partition_by,
1398 order_by,
1399 })
1400 }
1401 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1405 let mut func = match self.advance() {
1406 Token::Count => AggFunc::Count,
1407 Token::Avg => AggFunc::Avg,
1408 Token::Sum => AggFunc::Sum,
1409 Token::Min => AggFunc::Min,
1410 Token::Max => AggFunc::Max,
1411 _ => {
1412 return Err(ParseError::Syntax {
1413 message: "unexpected aggregate token".into(),
1414 })
1415 }
1416 };
1417 self.expect(&Token::LParen)?;
1418 if func == AggFunc::Count && *self.peek() == Token::Star {
1420 self.advance();
1421 self.expect(&Token::RParen)?;
1422 if *self.peek() == Token::Over {
1424 let (partition_by, order_by) = self.parse_over_clause()?;
1425 return Ok(Expr::Window {
1426 function: WindowFunc::Count,
1427 args: vec![Expr::Field("*".into())],
1428 partition_by,
1429 order_by,
1430 });
1431 }
1432 return Ok(Expr::FunctionCall(
1433 AggFunc::Count,
1434 Box::new(Expr::Field("*".into())),
1435 ));
1436 }
1437 if func == AggFunc::Count && *self.peek() == Token::Distinct {
1439 self.advance();
1440 func = AggFunc::CountDistinct;
1441 }
1442 let inner = self.parse_expr()?;
1443 self.expect(&Token::RParen)?;
1444 if *self.peek() == Token::Over {
1446 let wfunc = match func {
1447 AggFunc::Count => WindowFunc::Count,
1448 AggFunc::Avg => WindowFunc::Avg,
1449 AggFunc::Sum => WindowFunc::Sum,
1450 AggFunc::Min => WindowFunc::Min,
1451 AggFunc::Max => WindowFunc::Max,
1452 _ => {
1453 return Err(ParseError::Unsupported {
1454 feature: "count(distinct ...) over (...) is not supported".into(),
1455 })
1456 }
1457 };
1458 let (partition_by, order_by) = self.parse_over_clause()?;
1459 return Ok(Expr::Window {
1460 function: wfunc,
1461 args: vec![inner],
1462 partition_by,
1463 order_by,
1464 });
1465 }
1466 Ok(Expr::FunctionCall(func, Box::new(inner)))
1467 }
1468 Token::Upper
1469 | Token::Lower
1470 | Token::Length
1471 | Token::Trim
1472 | Token::Substring
1473 | Token::Concat
1474 | Token::Abs
1475 | Token::Round
1476 | Token::Ceil
1477 | Token::Floor
1478 | Token::Sqrt
1479 | Token::Pow
1480 | Token::Now
1481 | Token::Extract
1482 | Token::DateAdd
1483 | Token::DateDiff => {
1484 let tok = self.advance();
1485 let func = token_to_scalar_fn(&tok);
1486 self.expect(&Token::LParen)?;
1487 let mut args = Vec::new();
1488 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1489 args.push(self.parse_expr()?);
1490 if *self.peek() == Token::Comma {
1491 self.advance();
1492 }
1493 }
1494 self.expect(&Token::RParen)?;
1495 Ok(Expr::ScalarFunc(func, args))
1496 }
1497 Token::Cast => {
1498 self.advance();
1499 self.expect(&Token::LParen)?;
1500 let inner = self.parse_expr()?;
1501 self.expect(&Token::Comma)?;
1502 let cast_type = self.parse_cast_type()?;
1503 self.expect(&Token::RParen)?;
1504 Ok(Expr::Cast(Box::new(inner), cast_type))
1505 }
1506 Token::Case => {
1507 self.advance();
1508 let mut whens = Vec::new();
1509 while *self.peek() == Token::When {
1510 self.advance();
1511 let condition = self.parse_expr()?;
1512 self.expect(&Token::Then)?;
1513 let result = self.parse_expr()?;
1514 whens.push((Box::new(condition), Box::new(result)));
1515 }
1516 let else_expr = if *self.peek() == Token::Else {
1517 self.advance();
1518 Some(Box::new(self.parse_expr()?))
1519 } else {
1520 None
1521 };
1522 self.expect(&Token::End)?;
1523 Ok(Expr::Case { whens, else_expr })
1524 }
1525 t => Err(ParseError::Syntax {
1526 message: format!("unexpected token in expression: {}", t.display_name()),
1527 }),
1528 }
1529 }
1530
1531 fn parse_alter_table(&mut self) -> Result<Statement, ParseError> {
1534 self.expect(&Token::Alter)?;
1535 let table = match self.advance() {
1536 Token::Ident(name) => name,
1537 t => {
1538 return Err(ParseError::UnexpectedToken {
1539 expected: "table name after alter".into(),
1540 got: t.display_name(),
1541 })
1542 }
1543 };
1544 match self.peek() {
1545 Token::Add => {
1546 self.advance();
1547 if *self.peek() == Token::Index {
1549 self.advance();
1550 let column = match self.advance() {
1551 Token::DotIdent(n) => n,
1552 t => {
1553 return Err(ParseError::UnexpectedToken {
1554 expected: ".<column> after add index".into(),
1555 got: t.display_name(),
1556 })
1557 }
1558 };
1559 return Ok(Statement::AlterTable(AlterTableExpr {
1560 table,
1561 action: AlterAction::AddIndex { column },
1562 }));
1563 }
1564 if *self.peek() == Token::Column {
1566 self.advance();
1567 }
1568 let required = if *self.peek() == Token::Required {
1569 self.advance();
1570 true
1571 } else {
1572 false
1573 };
1574 let name = match self.advance() {
1575 Token::Ident(n) => n,
1576 t => {
1577 return Err(ParseError::UnexpectedToken {
1578 expected: "column name".into(),
1579 got: t.display_name(),
1580 })
1581 }
1582 };
1583 self.expect(&Token::Colon)?;
1584 let type_name = match self.advance() {
1585 Token::Ident(n) => n,
1586 t => {
1587 return Err(ParseError::UnexpectedToken {
1588 expected: "type name".into(),
1589 got: t.display_name(),
1590 })
1591 }
1592 };
1593 Ok(Statement::AlterTable(AlterTableExpr {
1594 table,
1595 action: AlterAction::AddColumn {
1596 name,
1597 type_name,
1598 required,
1599 },
1600 }))
1601 }
1602 Token::Drop => {
1603 self.advance();
1604 if *self.peek() == Token::Column {
1606 self.advance();
1607 }
1608 let name = match self.advance() {
1609 Token::Ident(n) => n,
1610 t => {
1611 return Err(ParseError::UnexpectedToken {
1612 expected: "column name".into(),
1613 got: t.display_name(),
1614 })
1615 }
1616 };
1617 Ok(Statement::AlterTable(AlterTableExpr {
1618 table,
1619 action: AlterAction::DropColumn { name },
1620 }))
1621 }
1622 t => Err(ParseError::UnexpectedToken {
1623 expected: "add or drop after alter <table>".into(),
1624 got: t.display_name(),
1625 }),
1626 }
1627 }
1628
1629 fn parse_drop_or_drop_view(&mut self) -> Result<Statement, ParseError> {
1631 self.expect(&Token::Drop)?;
1632 if *self.peek() == Token::View {
1633 self.advance(); let name = match self.advance() {
1635 Token::Ident(name) => name,
1636 t => {
1637 return Err(ParseError::UnexpectedToken {
1638 expected: "view name after drop view".into(),
1639 got: t.display_name(),
1640 })
1641 }
1642 };
1643 return Ok(Statement::DropView(DropViewExpr { name }));
1644 }
1645 let table = match self.advance() {
1646 Token::Ident(name) => name,
1647 t => {
1648 return Err(ParseError::UnexpectedToken {
1649 expected: "table name after drop".into(),
1650 got: t.display_name(),
1651 })
1652 }
1653 };
1654 Ok(Statement::DropTable(DropTableExpr { table }))
1655 }
1656
1657 fn parse_create_view(&mut self) -> Result<Statement, ParseError> {
1662 self.expect(&Token::Materialized)?;
1663 let name = match self.advance() {
1664 Token::Ident(name) => name,
1665 t => {
1666 return Err(ParseError::UnexpectedToken {
1667 expected: "view name after materialize".into(),
1668 got: t.display_name(),
1669 })
1670 }
1671 };
1672 self.expect(&Token::As)?;
1673 let query_start = self.pos;
1675 let source = match self.advance() {
1676 Token::Ident(s) => s,
1677 t => {
1678 return Err(ParseError::UnexpectedToken {
1679 expected: "source table name".into(),
1680 got: t.display_name(),
1681 })
1682 }
1683 };
1684 let query = self.parse_query_tail(source)?;
1685 let query_text = tokens_to_text(&self.tokens[query_start..self.pos]);
1687 Ok(Statement::CreateView(CreateViewExpr {
1688 name,
1689 query,
1690 query_text,
1691 }))
1692 }
1693
1694 fn maybe_parse_union(&mut self, left: Statement) -> Result<Statement, ParseError> {
1697 if *self.peek() != Token::Union {
1698 return Ok(left);
1699 }
1700 if !matches!(left, Statement::Query(_) | Statement::Union(_)) {
1701 return Err(ParseError::Syntax {
1702 message: "UNION requires a query on the left side".into(),
1703 });
1704 }
1705 self.advance(); let all = if let Token::Ident(s) = self.peek() {
1707 if s == "all" {
1708 self.advance();
1709 true
1710 } else {
1711 false
1712 }
1713 } else {
1714 false
1715 };
1716 let right = self.parse_single_query()?;
1718 let union = Statement::Union(UnionExpr {
1719 left: Box::new(left),
1720 right: Box::new(right),
1721 all,
1722 });
1723 self.maybe_parse_union(union)
1725 }
1726
1727 fn parse_single_query(&mut self) -> Result<Statement, ParseError> {
1729 match self.peek() {
1730 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1731 self.parse_aggregate_query()
1732 }
1733 Token::Ident(_) => self.parse_query_or_mutation(),
1734 _ => Err(ParseError::Syntax {
1735 message: format!(
1736 "expected query after UNION, got {}",
1737 self.peek().display_name()
1738 ),
1739 }),
1740 }
1741 }
1742
1743 fn parse_refresh_view(&mut self) -> Result<Statement, ParseError> {
1745 self.expect(&Token::Refresh)?;
1746 let name = match self.advance() {
1747 Token::Ident(name) => name,
1748 t => {
1749 return Err(ParseError::UnexpectedToken {
1750 expected: "view name after refresh".into(),
1751 got: t.display_name(),
1752 })
1753 }
1754 };
1755 Ok(Statement::RefreshView(RefreshViewExpr { name }))
1756 }
1757
1758 fn parse_create_type(&mut self) -> Result<Statement, ParseError> {
1759 self.expect(&Token::Type)?;
1760 let name = match self.advance() {
1761 Token::Ident(n) => n,
1762 t => {
1763 return Err(ParseError::UnexpectedToken {
1764 expected: "type name".into(),
1765 got: t.display_name(),
1766 })
1767 }
1768 };
1769 self.expect(&Token::LBrace)?;
1770 let mut fields = Vec::new();
1771 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1772 let required = if *self.peek() == Token::Required {
1773 self.advance();
1774 true
1775 } else {
1776 false
1777 };
1778 let field_name = match self.advance() {
1779 Token::Ident(n) => n,
1780 t => {
1781 return Err(ParseError::UnexpectedToken {
1782 expected: "field name".into(),
1783 got: t.display_name(),
1784 })
1785 }
1786 };
1787 self.expect(&Token::Colon)?;
1788 let type_name = match self.advance() {
1789 Token::Ident(n) => n,
1790 t => {
1791 return Err(ParseError::UnexpectedToken {
1792 expected: "type name".into(),
1793 got: t.display_name(),
1794 })
1795 }
1796 };
1797 fields.push(FieldDef {
1798 name: field_name,
1799 type_name,
1800 required,
1801 });
1802 if *self.peek() == Token::Comma {
1803 self.advance();
1804 }
1805 }
1806 self.expect(&Token::RBrace)?;
1807 Ok(Statement::CreateType(CreateTypeExpr { name, fields }))
1808 }
1809}
1810
1811fn tokens_to_text(tokens: &[Token]) -> String {
1815 let mut out = String::with_capacity(64);
1816 for tok in tokens {
1817 if !out.is_empty() && !matches!(tok, Token::Eof) {
1818 out.push(' ');
1819 }
1820 match tok {
1821 Token::Ident(s) => out.push_str(s),
1822 Token::DotIdent(s) => {
1823 out.push('.');
1824 out.push_str(s);
1825 }
1826 Token::IntLit(v) => out.push_str(&v.to_string()),
1827 Token::FloatLit(v) => out.push_str(&v.to_string()),
1828 Token::StringLit(s) => {
1829 out.push('"');
1830 out.push_str(s);
1831 out.push('"');
1832 }
1833 Token::BoolLit(v) => out.push_str(if *v { "true" } else { "false" }),
1834 Token::Param(s) => {
1835 out.push('$');
1836 out.push_str(s);
1837 }
1838 Token::Type => out.push_str("type"),
1839 Token::Filter => out.push_str("filter"),
1840 Token::Order => out.push_str("order"),
1841 Token::Limit => out.push_str("limit"),
1842 Token::Offset => out.push_str("offset"),
1843 Token::Insert => out.push_str("insert"),
1844 Token::Update => out.push_str("update"),
1845 Token::Delete => out.push_str("delete"),
1846 Token::Upsert => out.push_str("upsert"),
1847 Token::Conflict => out.push_str("conflict"),
1848 Token::Select => out.push_str("select"),
1849 Token::Required => out.push_str("required"),
1850 Token::Multi => out.push_str("multi"),
1851 Token::Link => out.push_str("link"),
1852 Token::Index => out.push_str("index"),
1853 Token::On => out.push_str("on"),
1854 Token::Asc => out.push_str("asc"),
1855 Token::Desc => out.push_str("desc"),
1856 Token::And => out.push_str("and"),
1857 Token::Or => out.push_str("or"),
1858 Token::Not => out.push_str("not"),
1859 Token::Exists => out.push_str("exists"),
1860 Token::Let => out.push_str("let"),
1861 Token::As => out.push_str("as"),
1862 Token::Match => out.push_str("match"),
1863 Token::Group => out.push_str("group"),
1864 Token::Join => out.push_str("join"),
1865 Token::Inner => out.push_str("inner"),
1866 Token::LeftKw => out.push_str("left"),
1867 Token::RightKw => out.push_str("right"),
1868 Token::Outer => out.push_str("outer"),
1869 Token::Cross => out.push_str("cross"),
1870 Token::Transaction => out.push_str("transaction"),
1871 Token::Begin => out.push_str("begin"),
1872 Token::Commit => out.push_str("commit"),
1873 Token::Rollback => out.push_str("rollback"),
1874 Token::View => out.push_str("view"),
1875 Token::Materialized => out.push_str("materialized"),
1876 Token::Refresh => out.push_str("refresh"),
1877 Token::Union => out.push_str("union"),
1878 Token::Having => out.push_str("having"),
1879 Token::Distinct => out.push_str("distinct"),
1880 Token::In => out.push_str("in"),
1881 Token::Between => out.push_str("between"),
1882 Token::Like => out.push_str("like"),
1883 Token::Count => out.push_str("count"),
1884 Token::Avg => out.push_str("avg"),
1885 Token::Sum => out.push_str("sum"),
1886 Token::Min => out.push_str("min"),
1887 Token::Max => out.push_str("max"),
1888 Token::Is => out.push_str("is"),
1889 Token::Null => out.push_str("null"),
1890 Token::Upper => out.push_str("upper"),
1891 Token::Lower => out.push_str("lower"),
1892 Token::Length => out.push_str("length"),
1893 Token::Trim => out.push_str("trim"),
1894 Token::Substring => out.push_str("substring"),
1895 Token::Concat => out.push_str("concat"),
1896 Token::Abs => out.push_str("abs"),
1897 Token::Round => out.push_str("round"),
1898 Token::Ceil => out.push_str("ceil"),
1899 Token::Floor => out.push_str("floor"),
1900 Token::Sqrt => out.push_str("sqrt"),
1901 Token::Pow => out.push_str("pow"),
1902 Token::Now => out.push_str("now"),
1903 Token::Extract => out.push_str("extract"),
1904 Token::DateAdd => out.push_str("date_add"),
1905 Token::DateDiff => out.push_str("date_diff"),
1906 Token::Cast => out.push_str("cast"),
1907 Token::Case => out.push_str("case"),
1908 Token::When => out.push_str("when"),
1909 Token::Then => out.push_str("then"),
1910 Token::Else => out.push_str("else"),
1911 Token::End => out.push_str("end"),
1912 Token::Over => out.push_str("over"),
1913 Token::Partition => out.push_str("partition"),
1914 Token::RowNumber => out.push_str("row_number"),
1915 Token::Rank => out.push_str("rank"),
1916 Token::DenseRank => out.push_str("dense_rank"),
1917 Token::Alter => out.push_str("alter"),
1918 Token::Drop => out.push_str("drop"),
1919 Token::Add => out.push_str("add"),
1920 Token::Column => out.push_str("column"),
1921 Token::Eq => out.push('='),
1922 Token::Neq => out.push_str("!="),
1923 Token::Lt => out.push('<'),
1924 Token::Gt => out.push('>'),
1925 Token::Lte => out.push_str("<="),
1926 Token::Gte => out.push_str(">="),
1927 Token::Assign => out.push_str(":="),
1928 Token::Arrow => out.push_str("->"),
1929 Token::Pipe => out.push('|'),
1930 Token::Coalesce => out.push_str("??"),
1931 Token::Plus => out.push('+'),
1932 Token::Minus => out.push('-'),
1933 Token::Star => out.push('*'),
1934 Token::Slash => out.push('/'),
1935 Token::LBrace => out.push('{'),
1936 Token::RBrace => out.push('}'),
1937 Token::LParen => out.push('('),
1938 Token::RParen => out.push(')'),
1939 Token::Comma => out.push(','),
1940 Token::Colon => out.push(':'),
1941 Token::Dot => out.push('.'),
1942 Token::Explain => out.push_str("explain"),
1943 Token::Eof => {}
1944 }
1945 }
1946 out
1947}
1948
1949#[cfg(test)]
1950mod tests {
1951 use super::*;
1952 #[test]
1953 fn test_parse_simple_query() {
1954 let stmt = parse("User").unwrap();
1955 match stmt {
1956 Statement::Query(q) => {
1957 assert_eq!(q.source, "User");
1958 assert!(q.filter.is_none());
1959 assert!(q.projection.is_none());
1960 }
1961 _ => panic!("expected query"),
1962 }
1963 }
1964
1965 #[test]
1966 fn test_parse_filter() {
1967 let stmt = parse("User filter .age > 30").unwrap();
1968 match stmt {
1969 Statement::Query(q) => {
1970 assert_eq!(q.source, "User");
1971 assert!(q.filter.is_some());
1972 }
1973 _ => panic!("expected query"),
1974 }
1975 }
1976
1977 #[test]
1978 fn test_parse_projection() {
1979 let stmt = parse("User { name, email }").unwrap();
1980 match stmt {
1981 Statement::Query(q) => {
1982 let proj = q.projection.unwrap();
1983 assert_eq!(proj.len(), 2);
1984 }
1985 _ => panic!("expected query"),
1986 }
1987 }
1988
1989 #[test]
1990 fn test_parse_filter_order_limit() {
1991 let stmt = parse("User filter .age > 30 order .name desc limit 10").unwrap();
1992 match stmt {
1993 Statement::Query(q) => {
1994 assert!(q.filter.is_some());
1995 let order = q.order.unwrap();
1996 assert_eq!(order.keys.len(), 1);
1997 assert_eq!(order.keys[0].field, "name");
1998 assert!(order.keys[0].descending);
1999 assert!(q.limit.is_some());
2000 }
2001 _ => panic!("expected query"),
2002 }
2003 }
2004
2005 #[test]
2006 fn test_parse_insert() {
2007 let stmt = parse(r#"insert User { name := "Alice", age := 30 }"#).unwrap();
2008 match stmt {
2009 Statement::Insert(ins) => {
2010 assert_eq!(ins.target, "User");
2011 assert_eq!(ins.rows.len(), 1);
2012 assert_eq!(ins.rows[0].len(), 2);
2013 assert_eq!(ins.rows[0][0].field, "name");
2014 assert_eq!(ins.rows[0][1].field, "age");
2015 }
2016 _ => panic!("expected insert"),
2017 }
2018 }
2019
2020 #[test]
2021 fn test_parse_insert_multi_row() {
2022 let stmt =
2023 parse(r#"insert User { name := "Alice", age := 30 }, { name := "Bob", age := 25 }, { name := "Cy" }"#)
2024 .unwrap();
2025 match stmt {
2026 Statement::Insert(ins) => {
2027 assert_eq!(ins.target, "User");
2028 assert_eq!(ins.rows.len(), 3);
2029 assert_eq!(ins.rows[0].len(), 2);
2030 assert_eq!(ins.rows[1][0].field, "name");
2031 assert_eq!(ins.rows[2].len(), 1);
2032 assert_eq!(ins.rows[2][0].field, "name");
2033 }
2034 _ => panic!("expected insert"),
2035 }
2036 }
2037
2038 #[test]
2039 fn test_parse_update() {
2040 let stmt = parse(r#"User filter .email = "alice@ex.com" update { age := 31 }"#).unwrap();
2041 match stmt {
2042 Statement::UpdateQuery(upd) => {
2043 assert_eq!(upd.source, "User");
2044 assert!(upd.filter.is_some());
2045 assert_eq!(upd.assignments.len(), 1);
2046 }
2047 _ => panic!("expected update"),
2048 }
2049 }
2050
2051 #[test]
2052 fn test_parse_delete() {
2053 let stmt = parse("User filter .age < 18 delete").unwrap();
2054 match stmt {
2055 Statement::DeleteQuery(del) => {
2056 assert_eq!(del.source, "User");
2057 assert!(del.filter.is_some());
2058 }
2059 _ => panic!("expected delete"),
2060 }
2061 }
2062
2063 #[test]
2064 fn test_parse_count() {
2065 let stmt = parse("count(User)").unwrap();
2066 match stmt {
2067 Statement::Query(q) => {
2068 let agg = q.aggregation.unwrap();
2069 assert_eq!(agg.function, AggFunc::Count);
2070 assert!(q.filter.is_none());
2071 }
2072 _ => panic!("expected query with aggregation"),
2073 }
2074 }
2075
2076 #[test]
2077 fn test_parse_count_with_filter() {
2078 let stmt = parse("count(User filter .age > 30)").unwrap();
2081 match stmt {
2082 Statement::Query(q) => {
2083 assert_eq!(q.source, "User");
2084 let agg = q.aggregation.unwrap();
2085 assert_eq!(agg.function, AggFunc::Count);
2086 assert!(q.filter.is_some(), "filter should have been parsed");
2087 }
2088 _ => panic!("expected query with aggregation"),
2089 }
2090 }
2091
2092 #[test]
2093 fn test_parse_count_with_filter_and_limit() {
2094 let stmt = parse("count(User filter .age > 30 limit 100)").unwrap();
2095 match stmt {
2096 Statement::Query(q) => {
2097 assert_eq!(q.source, "User");
2098 assert!(q.filter.is_some());
2099 assert!(q.limit.is_some());
2100 assert_eq!(q.aggregation.unwrap().function, AggFunc::Count);
2101 }
2102 _ => panic!("expected query with aggregation"),
2103 }
2104 }
2105
2106 #[test]
2107 fn test_parse_create_type() {
2108 let stmt = parse("type User { required name: str, age: int }").unwrap();
2109 match stmt {
2110 Statement::CreateType(ct) => {
2111 assert_eq!(ct.name, "User");
2112 assert_eq!(ct.fields.len(), 2);
2113 assert!(ct.fields[0].required);
2114 assert!(!ct.fields[1].required);
2115 }
2116 _ => panic!("expected create type"),
2117 }
2118 }
2119
2120 #[test]
2121 fn test_parse_sum_with_field_projection() {
2122 let stmt = parse("sum(User filter .age > 30 { .age })").unwrap();
2125 match stmt {
2126 Statement::Query(q) => {
2127 let agg = q.aggregation.expect("aggregate");
2128 assert_eq!(agg.function, AggFunc::Sum);
2129 assert_eq!(agg.field.as_deref(), Some("age"));
2130 assert!(
2131 q.projection.is_none(),
2132 "projection should be lifted into agg.field"
2133 );
2134 }
2135 _ => panic!("expected query"),
2136 }
2137 }
2138
2139 #[test]
2140 fn test_parse_avg_min_max_with_field() {
2141 for (src, expected) in [
2142 ("avg(User { .age })", AggFunc::Avg),
2143 ("min(User { .age })", AggFunc::Min),
2144 ("max(User { .age })", AggFunc::Max),
2145 ] {
2146 let stmt = parse(src).unwrap();
2147 match stmt {
2148 Statement::Query(q) => {
2149 let agg = q.aggregation.unwrap();
2150 assert_eq!(agg.function, expected, "func mismatch for {src}");
2151 assert_eq!(
2152 agg.field.as_deref(),
2153 Some("age"),
2154 "field mismatch for {src}"
2155 );
2156 assert!(
2157 q.projection.is_none(),
2158 "projection should be cleared for {src}"
2159 );
2160 }
2161 _ => panic!("expected query for {src}"),
2162 }
2163 }
2164 }
2165
2166 #[test]
2167 fn test_parse_count_leaves_projection_alone() {
2168 let stmt = parse("count(User { .age })").unwrap();
2171 match stmt {
2172 Statement::Query(q) => {
2173 let agg = q.aggregation.unwrap();
2174 assert_eq!(agg.function, AggFunc::Count);
2175 assert!(agg.field.is_none());
2176 assert!(q.projection.is_some(), "count must not eat projection");
2177 }
2178 _ => panic!("expected query"),
2179 }
2180 }
2181
2182 #[test]
2187 fn test_parse_source_alias() {
2188 let stmt = parse("User as u filter u.age > 30").unwrap();
2189 match stmt {
2190 Statement::Query(q) => {
2191 assert_eq!(q.source, "User");
2192 assert_eq!(q.alias.as_deref(), Some("u"));
2193 assert!(q.joins.is_empty());
2194 match q.filter.unwrap() {
2195 Expr::BinaryOp(l, BinOp::Gt, _) => match *l {
2196 Expr::QualifiedField { qualifier, field } => {
2197 assert_eq!(qualifier, "u");
2198 assert_eq!(field, "age");
2199 }
2200 other => panic!("expected qualified field, got {other:?}"),
2201 },
2202 other => panic!("expected >, got {other:?}"),
2203 }
2204 }
2205 _ => panic!("expected query"),
2206 }
2207 }
2208
2209 #[test]
2210 fn test_parse_inner_join_on() {
2211 let stmt = parse("User as u inner join Order as o on u.id = o.user_id").unwrap();
2212 match stmt {
2213 Statement::Query(q) => {
2214 assert_eq!(q.source, "User");
2215 assert_eq!(q.alias.as_deref(), Some("u"));
2216 assert_eq!(q.joins.len(), 1);
2217 let j = &q.joins[0];
2218 assert_eq!(j.kind, JoinKind::Inner);
2219 assert_eq!(j.source, "Order");
2220 assert_eq!(j.alias.as_deref(), Some("o"));
2221 let on = j.on.as_ref().expect("on clause");
2222 match on {
2223 Expr::BinaryOp(l, BinOp::Eq, r) => {
2224 assert!(matches!(**l, Expr::QualifiedField { .. }));
2225 assert!(matches!(**r, Expr::QualifiedField { .. }));
2226 }
2227 other => panic!("expected eq, got {other:?}"),
2228 }
2229 }
2230 _ => panic!("expected query"),
2231 }
2232 }
2233
2234 #[test]
2235 fn test_parse_bare_join_defaults_to_inner() {
2236 let stmt = parse("User join Order on User.id = Order.user_id").unwrap();
2237 match stmt {
2238 Statement::Query(q) => {
2239 assert_eq!(q.joins.len(), 1);
2240 assert_eq!(q.joins[0].kind, JoinKind::Inner);
2241 }
2242 _ => panic!("expected query"),
2243 }
2244 }
2245
2246 #[test]
2247 fn test_parse_left_outer_join() {
2248 let stmt = parse("User as u left outer join Order as o on u.id = o.user_id").unwrap();
2249 match stmt {
2250 Statement::Query(q) => {
2251 assert_eq!(q.joins.len(), 1);
2252 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2253 }
2254 _ => panic!("expected query"),
2255 }
2256 }
2257
2258 #[test]
2259 fn test_parse_left_join_without_outer_keyword() {
2260 let stmt = parse("User as u left join Order as o on u.id = o.user_id").unwrap();
2262 match stmt {
2263 Statement::Query(q) => {
2264 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2265 }
2266 _ => panic!("expected query"),
2267 }
2268 }
2269
2270 #[test]
2271 fn test_parse_right_join() {
2272 let stmt = parse("User as u right join Order as o on u.id = o.user_id").unwrap();
2273 match stmt {
2274 Statement::Query(q) => {
2275 assert_eq!(q.joins[0].kind, JoinKind::RightOuter);
2276 }
2277 _ => panic!("expected query"),
2278 }
2279 }
2280
2281 #[test]
2282 fn test_parse_cross_join_has_no_on() {
2283 let stmt = parse("User cross join Order").unwrap();
2284 match stmt {
2285 Statement::Query(q) => {
2286 assert_eq!(q.joins[0].kind, JoinKind::Cross);
2287 assert!(q.joins[0].on.is_none());
2288 }
2289 _ => panic!("expected query"),
2290 }
2291 }
2292
2293 #[test]
2294 fn test_parse_multi_join_chain() {
2295 let stmt = parse(
2296 "User as u join Order as o on u.id = o.user_id \
2297 join Product as p on o.product_id = p.id",
2298 )
2299 .unwrap();
2300 match stmt {
2301 Statement::Query(q) => {
2302 assert_eq!(q.joins.len(), 2);
2303 assert_eq!(q.joins[0].source, "Order");
2304 assert_eq!(q.joins[1].source, "Product");
2305 }
2306 _ => panic!("expected query"),
2307 }
2308 }
2309
2310 #[test]
2311 fn test_parse_join_with_filter_tail() {
2312 let stmt = parse(
2314 "User as u join Order as o on u.id = o.user_id \
2315 filter o.total > 100 order .name limit 10",
2316 )
2317 .unwrap();
2318 match stmt {
2319 Statement::Query(q) => {
2320 assert_eq!(q.joins.len(), 1);
2321 assert!(q.filter.is_some());
2322 assert!(q.order.is_some());
2323 assert!(q.limit.is_some());
2324 }
2325 _ => panic!("expected query"),
2326 }
2327 }
2328
2329 #[test]
2330 fn test_parse_join_requires_on_for_inner() {
2331 let err = parse("User join Order").unwrap_err();
2333 assert!(
2334 err.message().contains("on"),
2335 "expected on-clause error, got {:?}",
2336 err.message()
2337 );
2338 }
2339
2340 #[test]
2341 fn test_parse_update_on_joined_query_errors() {
2342 let err =
2345 parse("User as u join Order as o on u.id = o.user_id update { age := 1 }").unwrap_err();
2346 assert!(err.message().contains("update"));
2347 }
2348
2349 #[test]
2350 fn test_parse_delete_on_joined_query_errors() {
2351 let err = parse("User as u join Order as o on u.id = o.user_id delete").unwrap_err();
2352 assert!(err.message().contains("delete"));
2353 }
2354
2355 #[test]
2358 fn test_parse_distinct() {
2359 let stmt = parse("User distinct { .name }").unwrap();
2360 match stmt {
2361 Statement::Query(q) => {
2362 assert!(q.distinct);
2363 assert!(q.projection.is_some());
2364 }
2365 _ => panic!("expected query"),
2366 }
2367 }
2368
2369 #[test]
2370 fn test_parse_in_list() {
2371 let stmt = parse(r#"User filter .name in ("Alice", "Bob")"#).unwrap();
2372 match stmt {
2373 Statement::Query(q) => match q.filter.unwrap() {
2374 Expr::InList {
2375 expr,
2376 list,
2377 negated,
2378 } => {
2379 assert!(!negated);
2380 assert!(matches!(*expr, Expr::Field(f) if f == "name"));
2381 assert_eq!(list.len(), 2);
2382 }
2383 other => panic!("expected InList, got {other:?}"),
2384 },
2385 _ => panic!("expected query"),
2386 }
2387 }
2388
2389 #[test]
2390 fn test_parse_not_in_list() {
2391 let stmt = parse("User filter .age not in (1, 2, 3)").unwrap();
2392 match stmt {
2393 Statement::Query(q) => match q.filter.unwrap() {
2394 Expr::InList { negated, list, .. } => {
2395 assert!(negated);
2396 assert_eq!(list.len(), 3);
2397 }
2398 other => panic!("expected InList, got {other:?}"),
2399 },
2400 _ => panic!("expected query"),
2401 }
2402 }
2403
2404 #[test]
2405 fn test_parse_between() {
2406 let stmt = parse("User filter .age between 10 and 20").unwrap();
2408 match stmt {
2409 Statement::Query(q) => {
2410 match q.filter.unwrap() {
2411 Expr::BinaryOp(_, BinOp::And, _) => {} other => panic!("expected And (desugared between), got {other:?}"),
2413 }
2414 }
2415 _ => panic!("expected query"),
2416 }
2417 }
2418
2419 #[test]
2420 fn test_parse_not_between() {
2421 let stmt = parse("User filter .age not between 10 and 20").unwrap();
2423 match stmt {
2424 Statement::Query(q) => {
2425 match q.filter.unwrap() {
2426 Expr::BinaryOp(_, BinOp::Or, _) => {} other => panic!("expected Or (desugared not between), got {other:?}"),
2428 }
2429 }
2430 _ => panic!("expected query"),
2431 }
2432 }
2433
2434 #[test]
2435 fn test_parse_like() {
2436 let stmt = parse(r#"User filter .name like "A%""#).unwrap();
2437 match stmt {
2438 Statement::Query(q) => match q.filter.unwrap() {
2439 Expr::BinaryOp(l, BinOp::Like, r) => {
2440 assert!(matches!(*l, Expr::Field(f) if f == "name"));
2441 assert!(matches!(*r, Expr::Literal(Literal::String(s)) if s == "A%"));
2442 }
2443 other => panic!("expected Like, got {other:?}"),
2444 },
2445 _ => panic!("expected query"),
2446 }
2447 }
2448
2449 #[test]
2450 fn test_parse_not_like() {
2451 let stmt = parse(r#"User filter .name not like "A%""#).unwrap();
2452 match stmt {
2453 Statement::Query(q) => match q.filter.unwrap() {
2454 Expr::UnaryOp(UnaryOp::Not, inner) => {
2455 assert!(matches!(*inner, Expr::BinaryOp(_, BinOp::Like, _)));
2456 }
2457 other => panic!("expected Not(Like), got {other:?}"),
2458 },
2459 _ => panic!("expected query"),
2460 }
2461 }
2462
2463 #[test]
2466 fn test_parse_group_by_single_key() {
2467 let stmt = parse("User group .status { .status, n: count(.name) }").unwrap();
2468 match stmt {
2469 Statement::Query(q) => {
2470 let gb = q.group_by.unwrap();
2471 assert_eq!(gb.keys, vec!["status"]);
2472 assert!(gb.having.is_none());
2473 let proj = q.projection.unwrap();
2474 assert_eq!(proj.len(), 2);
2475 assert!(matches!(
2476 &proj[1].expr,
2477 Expr::FunctionCall(AggFunc::Count, _)
2478 ));
2479 assert_eq!(proj[1].alias.as_deref(), Some("n"));
2480 }
2481 _ => panic!("expected query"),
2482 }
2483 }
2484
2485 #[test]
2486 fn test_parse_group_by_multi_key() {
2487 let stmt = parse("User group .status, .age { .status, .age }").unwrap();
2488 match stmt {
2489 Statement::Query(q) => {
2490 let gb = q.group_by.unwrap();
2491 assert_eq!(gb.keys, vec!["status", "age"]);
2492 }
2493 _ => panic!("expected query"),
2494 }
2495 }
2496
2497 #[test]
2498 fn test_parse_group_by_having() {
2499 let stmt = parse("User group .status having count(.name) > 1 { .status }").unwrap();
2500 match stmt {
2501 Statement::Query(q) => {
2502 let gb = q.group_by.unwrap();
2503 assert_eq!(gb.keys, vec!["status"]);
2504 assert!(gb.having.is_some());
2505 match gb.having.unwrap() {
2507 Expr::BinaryOp(l, BinOp::Gt, _) => {
2508 assert!(matches!(*l, Expr::FunctionCall(AggFunc::Count, _)));
2509 }
2510 other => panic!("expected BinaryOp, got {other:?}"),
2511 }
2512 }
2513 _ => panic!("expected query"),
2514 }
2515 }
2516
2517 #[test]
2518 fn test_parse_aggregate_in_projection() {
2519 let stmt = parse("User group .status { .status, count(.name), sum(.age) }").unwrap();
2521 match stmt {
2522 Statement::Query(q) => {
2523 let proj = q.projection.unwrap();
2524 assert_eq!(proj.len(), 3);
2525 assert!(matches!(
2526 &proj[1].expr,
2527 Expr::FunctionCall(AggFunc::Count, _)
2528 ));
2529 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Sum, _)));
2530 }
2531 _ => panic!("expected query"),
2532 }
2533 }
2534
2535 #[test]
2536 fn test_parse_aggregate_in_aliased_projection() {
2537 let stmt = parse("User group .status { .status, total: count(.name), average: avg(.age) }")
2538 .unwrap();
2539 match stmt {
2540 Statement::Query(q) => {
2541 let proj = q.projection.unwrap();
2542 assert_eq!(proj[1].alias.as_deref(), Some("total"));
2543 assert!(matches!(
2544 &proj[1].expr,
2545 Expr::FunctionCall(AggFunc::Count, _)
2546 ));
2547 assert_eq!(proj[2].alias.as_deref(), Some("average"));
2548 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Avg, _)));
2549 }
2550 _ => panic!("expected query"),
2551 }
2552 }
2553
2554 #[test]
2557 fn test_parse_is_null() {
2558 let stmt = parse("User filter .age is null").unwrap();
2559 match stmt {
2560 Statement::Query(q) => {
2561 let filter = q.filter.unwrap();
2562 assert_eq!(
2563 filter,
2564 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2565 );
2566 }
2567 _ => panic!("expected query"),
2568 }
2569 }
2570
2571 #[test]
2572 fn test_parse_is_not_null() {
2573 let stmt = parse("User filter .age is not null").unwrap();
2574 match stmt {
2575 Statement::Query(q) => {
2576 let filter = q.filter.unwrap();
2577 assert_eq!(
2578 filter,
2579 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2580 );
2581 }
2582 _ => panic!("expected query"),
2583 }
2584 }
2585
2586 #[test]
2587 fn test_parse_eq_null_desugars_to_is_null() {
2588 let stmt = parse("User filter .age = null").unwrap();
2589 match stmt {
2590 Statement::Query(q) => {
2591 let filter = q.filter.unwrap();
2592 assert_eq!(
2593 filter,
2594 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2595 );
2596 }
2597 _ => panic!("expected query"),
2598 }
2599 }
2600
2601 #[test]
2602 fn test_parse_neq_null_desugars_to_is_not_null() {
2603 let stmt = parse("User filter .age != null").unwrap();
2604 match stmt {
2605 Statement::Query(q) => {
2606 let filter = q.filter.unwrap();
2607 assert_eq!(
2608 filter,
2609 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2610 );
2611 }
2612 _ => panic!("expected query"),
2613 }
2614 }
2615
2616 #[test]
2617 fn test_parse_null_comparisons_parse_ok() {
2618 assert!(parse("User filter .age < null").is_ok());
2622 assert!(parse("User filter .age >= null").is_ok());
2623 }
2624
2625 #[test]
2626 fn test_parse_count_star_expr() {
2627 let stmt = parse("User filter count(*) > 0").unwrap();
2628 match stmt {
2629 Statement::Query(q) => {
2630 let filter = q.filter.unwrap();
2631 match filter {
2632 Expr::BinaryOp(left, BinOp::Gt, _) => {
2633 assert_eq!(
2634 *left,
2635 Expr::FunctionCall(AggFunc::Count, Box::new(Expr::Field("*".into())))
2636 );
2637 }
2638 _ => panic!("expected comparison"),
2639 }
2640 }
2641 _ => panic!("expected query"),
2642 }
2643 }
2644
2645 #[test]
2648 fn test_parse_upper_in_filter() {
2649 let stmt = parse(r#"User filter upper(.name) = "ALICE""#).unwrap();
2650 match stmt {
2651 Statement::Query(q) => {
2652 let f = q.filter.unwrap();
2653 match f {
2654 Expr::BinaryOp(left, BinOp::Eq, _right) => {
2655 assert!(matches!(*left, Expr::ScalarFunc(ScalarFn::Upper, _)));
2656 }
2657 _ => panic!("expected binary op with upper"),
2658 }
2659 }
2660 _ => panic!("expected query"),
2661 }
2662 }
2663
2664 #[test]
2665 fn test_parse_substring() {
2666 let stmt = parse("User { sub: substring(.name, 1, 3) }").unwrap();
2667 match stmt {
2668 Statement::Query(q) => {
2669 let proj = q.projection.unwrap();
2670 match &proj[0].expr {
2671 Expr::ScalarFunc(ScalarFn::Substring, args) => {
2672 assert_eq!(args.len(), 3);
2673 }
2674 other => panic!("expected ScalarFunc Substring, got {other:?}"),
2675 }
2676 }
2677 _ => panic!("expected query"),
2678 }
2679 }
2680
2681 #[test]
2682 fn test_parse_concat() {
2683 let stmt = parse(r#"User { full: concat(.name, " - ", .email) }"#).unwrap();
2684 match stmt {
2685 Statement::Query(q) => {
2686 let proj = q.projection.unwrap();
2687 match &proj[0].expr {
2688 Expr::ScalarFunc(ScalarFn::Concat, args) => {
2689 assert_eq!(args.len(), 3);
2690 }
2691 other => panic!("expected ScalarFunc Concat, got {other:?}"),
2692 }
2693 }
2694 _ => panic!("expected query"),
2695 }
2696 }
2697
2698 #[test]
2701 fn test_parse_case_single_when() {
2702 let stmt = parse(r#"User filter case when .age > 30 then true else false end"#).unwrap();
2703 match stmt {
2704 Statement::Query(q) => {
2705 let filter = q.filter.unwrap();
2706 match filter {
2707 Expr::Case { whens, else_expr } => {
2708 assert_eq!(whens.len(), 1);
2709 assert!(else_expr.is_some());
2710 }
2711 other => panic!("expected Case expr, got {other:?}"),
2712 }
2713 }
2714 _ => panic!("expected query"),
2715 }
2716 }
2717
2718 #[test]
2719 fn test_parse_case_multiple_whens() {
2720 let stmt = parse(
2721 r#"User { label: case when .age > 30 then "senior" when .age > 20 then "adult" else "young" end }"#
2722 ).unwrap();
2723 match stmt {
2724 Statement::Query(q) => {
2725 let proj = q.projection.unwrap();
2726 match &proj[0].expr {
2727 Expr::Case { whens, else_expr } => {
2728 assert_eq!(whens.len(), 2);
2729 assert!(else_expr.is_some());
2730 }
2731 other => panic!("expected Case expr, got {other:?}"),
2732 }
2733 }
2734 _ => panic!("expected query"),
2735 }
2736 }
2737
2738 #[test]
2739 fn test_parse_case_without_else() {
2740 let stmt = parse(r#"User filter case when .age > 30 then true end"#).unwrap();
2741 match stmt {
2742 Statement::Query(q) => {
2743 let filter = q.filter.unwrap();
2744 match filter {
2745 Expr::Case { whens, else_expr } => {
2746 assert_eq!(whens.len(), 1);
2747 assert!(else_expr.is_none());
2748 }
2749 other => panic!("expected Case expr, got {other:?}"),
2750 }
2751 }
2752 _ => panic!("expected query"),
2753 }
2754 }
2755
2756 #[test]
2759 fn test_parse_mul_expr() {
2760 let stmt = parse("User filter .price * .quantity > 100").unwrap();
2761 match stmt {
2762 Statement::Query(q) => {
2763 let filter = q.filter.unwrap();
2764 match filter {
2765 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2766 Expr::BinaryOp(_, BinOp::Mul, _) => {}
2767 other => panic!("expected Mul, got {other:?}"),
2768 },
2769 other => panic!("expected BinaryOp Gt, got {other:?}"),
2770 }
2771 }
2772 _ => panic!("expected query"),
2773 }
2774 }
2775
2776 #[test]
2777 fn test_parse_div_expr() {
2778 let stmt = parse("User { ratio: .total / .count }").unwrap();
2779 match stmt {
2780 Statement::Query(q) => {
2781 let proj = q.projection.unwrap();
2782 assert_eq!(proj[0].alias.as_deref(), Some("ratio"));
2783 match &proj[0].expr {
2784 Expr::BinaryOp(_, BinOp::Div, _) => {}
2785 other => panic!("expected Div, got {other:?}"),
2786 }
2787 }
2788 _ => panic!("expected query"),
2789 }
2790 }
2791
2792 #[test]
2793 fn test_parse_mul_div_precedence() {
2794 let stmt = parse("User filter .a + .b * .c > 0").unwrap();
2796 match stmt {
2797 Statement::Query(q) => {
2798 let filter = q.filter.unwrap();
2799 match filter {
2800 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2801 Expr::BinaryOp(_, BinOp::Add, right) => {
2802 assert!(matches!(*right, Expr::BinaryOp(_, BinOp::Mul, _)));
2803 }
2804 other => panic!("expected Add, got {other:?}"),
2805 },
2806 other => panic!("expected Gt, got {other:?}"),
2807 }
2808 }
2809 _ => panic!("expected query"),
2810 }
2811 }
2812
2813 #[test]
2816 fn test_parse_multi_order() {
2817 let stmt = parse("User order .name asc, .age desc").unwrap();
2818 match stmt {
2819 Statement::Query(q) => {
2820 let order = q.order.unwrap();
2821 assert_eq!(order.keys.len(), 2);
2822 assert_eq!(order.keys[0].field, "name");
2823 assert!(!order.keys[0].descending);
2824 assert_eq!(order.keys[1].field, "age");
2825 assert!(order.keys[1].descending);
2826 }
2827 _ => panic!("expected query"),
2828 }
2829 }
2830
2831 #[test]
2832 fn test_parse_order_default_asc() {
2833 let stmt = parse("User order .name").unwrap();
2834 match stmt {
2835 Statement::Query(q) => {
2836 let order = q.order.unwrap();
2837 assert_eq!(order.keys.len(), 1);
2838 assert!(!order.keys[0].descending);
2839 }
2840 _ => panic!("expected query"),
2841 }
2842 }
2843
2844 #[test]
2847 fn test_parse_alter_add_column() {
2848 let stmt = parse("alter User add column status: str").unwrap();
2849 match stmt {
2850 Statement::AlterTable(at) => {
2851 assert_eq!(at.table, "User");
2852 match at.action {
2853 AlterAction::AddColumn {
2854 name,
2855 type_name,
2856 required,
2857 } => {
2858 assert_eq!(name, "status");
2859 assert_eq!(type_name, "str");
2860 assert!(!required);
2861 }
2862 other => panic!("expected AddColumn, got {other:?}"),
2863 }
2864 }
2865 other => panic!("expected AlterTable, got {other:?}"),
2866 }
2867 }
2868
2869 #[test]
2870 fn test_parse_alter_add_required_column() {
2871 let stmt = parse("alter User add required status: str").unwrap();
2872 match stmt {
2873 Statement::AlterTable(at) => match at.action {
2874 AlterAction::AddColumn { required, .. } => assert!(required),
2875 other => panic!("expected AddColumn, got {other:?}"),
2876 },
2877 other => panic!("expected AlterTable, got {other:?}"),
2878 }
2879 }
2880
2881 #[test]
2882 fn test_parse_alter_drop_column() {
2883 let stmt = parse("alter User drop column status").unwrap();
2884 match stmt {
2885 Statement::AlterTable(at) => {
2886 assert_eq!(at.table, "User");
2887 match at.action {
2888 AlterAction::DropColumn { name } => assert_eq!(name, "status"),
2889 other => panic!("expected DropColumn, got {other:?}"),
2890 }
2891 }
2892 other => panic!("expected AlterTable, got {other:?}"),
2893 }
2894 }
2895
2896 #[test]
2897 fn test_parse_alter_drop_without_column_keyword() {
2898 let stmt = parse("alter User drop status").unwrap();
2899 match stmt {
2900 Statement::AlterTable(at) => match at.action {
2901 AlterAction::DropColumn { name } => assert_eq!(name, "status"),
2902 other => panic!("expected DropColumn, got {other:?}"),
2903 },
2904 other => panic!("expected AlterTable, got {other:?}"),
2905 }
2906 }
2907
2908 #[test]
2909 fn test_parse_drop_table() {
2910 let stmt = parse("drop User").unwrap();
2911 match stmt {
2912 Statement::DropTable(dt) => assert_eq!(dt.table, "User"),
2913 other => panic!("expected DropTable, got {other:?}"),
2914 }
2915 }
2916
2917 #[test]
2920 fn test_parse_in_subquery() {
2921 let stmt = parse("User filter .name in (VIP { .name })").unwrap();
2922 match stmt {
2923 Statement::Query(q) => {
2924 let filter = q.filter.unwrap();
2925 match filter {
2926 Expr::InSubquery {
2927 expr,
2928 subquery,
2929 negated,
2930 } => {
2931 assert!(!negated);
2932 assert!(matches!(*expr, Expr::Field(ref f) if f == "name"));
2933 assert_eq!(subquery.source, "VIP");
2934 }
2935 other => panic!("expected InSubquery, got {other:?}"),
2936 }
2937 }
2938 _ => panic!("expected query"),
2939 }
2940 }
2941
2942 #[test]
2943 fn test_parse_not_in_subquery() {
2944 let stmt = parse("User filter .id not in (Order { .user_id })").unwrap();
2945 match stmt {
2946 Statement::Query(q) => match q.filter.unwrap() {
2947 Expr::InSubquery { negated, .. } => assert!(negated),
2948 other => panic!("expected InSubquery, got {other:?}"),
2949 },
2950 _ => panic!("expected query"),
2951 }
2952 }
2953
2954 #[test]
2955 fn test_parse_in_literal_list_still_works() {
2956 let stmt = parse("User filter .age in (25, 30, 35)").unwrap();
2958 match stmt {
2959 Statement::Query(q) => match q.filter.unwrap() {
2960 Expr::InList { list, negated, .. } => {
2961 assert!(!negated);
2962 assert_eq!(list.len(), 3);
2963 }
2964 other => panic!("expected InList, got {other:?}"),
2965 },
2966 _ => panic!("expected query"),
2967 }
2968 }
2969
2970 #[test]
2973 fn test_parse_create_view() {
2974 let stmt = parse("materialize OldUsers as User filter .age > 28").unwrap();
2975 match stmt {
2976 Statement::CreateView(cv) => {
2977 assert_eq!(cv.name, "OldUsers");
2978 assert_eq!(cv.query.source, "User");
2979 assert!(cv.query.filter.is_some());
2980 assert!(!cv.query_text.is_empty());
2981 }
2982 _ => panic!("expected CreateView"),
2983 }
2984 }
2985
2986 #[test]
2987 fn test_parse_create_view_with_projection() {
2988 let stmt = parse("materialize UserNames as User { .name }").unwrap();
2989 match stmt {
2990 Statement::CreateView(cv) => {
2991 assert_eq!(cv.name, "UserNames");
2992 assert!(cv.query.projection.is_some());
2993 }
2994 _ => panic!("expected CreateView"),
2995 }
2996 }
2997
2998 #[test]
2999 fn test_parse_refresh_view() {
3000 let stmt = parse("refresh OldUsers").unwrap();
3001 match stmt {
3002 Statement::RefreshView(rv) => {
3003 assert_eq!(rv.name, "OldUsers");
3004 }
3005 _ => panic!("expected RefreshView"),
3006 }
3007 }
3008
3009 #[test]
3010 fn test_parse_drop_view() {
3011 let stmt = parse("drop view OldUsers").unwrap();
3012 match stmt {
3013 Statement::DropView(dv) => {
3014 assert_eq!(dv.name, "OldUsers");
3015 }
3016 _ => panic!("expected DropView"),
3017 }
3018 }
3019
3020 #[test]
3021 fn test_parse_drop_table_still_works() {
3022 let stmt = parse("drop Users").unwrap();
3023 match stmt {
3024 Statement::DropTable(dt) => {
3025 assert_eq!(dt.table, "Users");
3026 }
3027 _ => panic!("expected DropTable"),
3028 }
3029 }
3030
3031 #[test]
3032 fn test_parse_union() {
3033 let stmt = parse("User union Order").unwrap();
3034 match stmt {
3035 Statement::Union(u) => {
3036 assert!(!u.all);
3037 match *u.left {
3038 Statement::Query(_) => {}
3039 _ => panic!("expected Query on left"),
3040 }
3041 match *u.right {
3042 Statement::Query(_) => {}
3043 _ => panic!("expected Query on right"),
3044 }
3045 }
3046 _ => panic!("expected Union"),
3047 }
3048 }
3049
3050 #[test]
3051 fn test_parse_union_all() {
3052 let stmt = parse("User union all Order").unwrap();
3053 match stmt {
3054 Statement::Union(u) => {
3055 assert!(u.all, "expected UNION ALL");
3056 match *u.left {
3057 Statement::Query(_) => {}
3058 _ => panic!("expected Query on left"),
3059 }
3060 match *u.right {
3061 Statement::Query(_) => {}
3062 _ => panic!("expected Query on right"),
3063 }
3064 }
3065 _ => panic!("expected Union"),
3066 }
3067 }
3068
3069 #[test]
3070 fn test_parse_union_chain() {
3071 let stmt = parse("User union Order union Product").unwrap();
3073 match stmt {
3074 Statement::Union(outer) => {
3075 assert!(!outer.all);
3076 match *outer.right {
3078 Statement::Query(q) => assert_eq!(q.source, "Product"),
3079 _ => panic!("expected Query(Product) on right"),
3080 }
3081 match *outer.left {
3083 Statement::Union(inner) => {
3084 assert!(!inner.all);
3085 match *inner.left {
3086 Statement::Query(q) => assert_eq!(q.source, "User"),
3087 _ => panic!("expected Query(User)"),
3088 }
3089 match *inner.right {
3090 Statement::Query(q) => assert_eq!(q.source, "Order"),
3091 _ => panic!("expected Query(Order)"),
3092 }
3093 }
3094 _ => panic!("expected inner Union"),
3095 }
3096 }
3097 _ => panic!("expected Union"),
3098 }
3099 }
3100
3101 #[test]
3102 fn test_parse_union_with_filter() {
3103 let stmt = parse("User filter .age > 10 union Order filter .total > 50").unwrap();
3104 match stmt {
3105 Statement::Union(u) => {
3106 assert!(!u.all);
3107 match *u.left {
3109 Statement::Query(q) => {
3110 assert_eq!(q.source, "User");
3111 assert!(q.filter.is_some());
3112 }
3113 _ => panic!("expected Query on left"),
3114 }
3115 match *u.right {
3116 Statement::Query(q) => {
3117 assert_eq!(q.source, "Order");
3118 assert!(q.filter.is_some());
3119 }
3120 _ => panic!("expected Query on right"),
3121 }
3122 }
3123 _ => panic!("expected Union"),
3124 }
3125 }
3126
3127 #[test]
3128 fn test_parse_count_distinct_standalone() {
3129 let stmt = parse("count(distinct User { .name })").unwrap();
3130 match stmt {
3131 Statement::Query(q) => {
3132 let agg = q.aggregation.unwrap();
3133 assert_eq!(agg.function, AggFunc::CountDistinct);
3134 assert_eq!(agg.field.as_deref(), Some("name"));
3135 }
3136 _ => panic!("expected Query"),
3137 }
3138 }
3139
3140 #[test]
3141 fn test_parse_count_distinct_in_projection() {
3142 let stmt = parse("User group .dept { .dept, count(distinct .name) }").unwrap();
3143 match stmt {
3144 Statement::Query(q) => {
3145 let proj = q.projection.unwrap();
3146 assert_eq!(proj.len(), 2);
3147 match &proj[1].expr {
3148 Expr::FunctionCall(func, _) => {
3149 assert_eq!(*func, AggFunc::CountDistinct);
3150 }
3151 _ => panic!("expected FunctionCall"),
3152 }
3153 }
3154 _ => panic!("expected Query"),
3155 }
3156 }
3157
3158 #[test]
3161 fn test_parse_window_row_number_order() {
3162 let stmt = parse("User { .name, rn: row_number() over (order .age) }").unwrap();
3163 match stmt {
3164 Statement::Query(q) => {
3165 let proj = q.projection.unwrap();
3166 assert_eq!(proj.len(), 2);
3167 assert_eq!(proj[1].alias.as_deref(), Some("rn"));
3168 match &proj[1].expr {
3169 Expr::Window {
3170 function,
3171 args,
3172 partition_by,
3173 order_by,
3174 } => {
3175 assert_eq!(*function, WindowFunc::RowNumber);
3176 assert!(args.is_empty());
3177 assert!(partition_by.is_empty());
3178 assert_eq!(order_by.len(), 1);
3179 assert_eq!(order_by[0].field, "age");
3180 assert!(!order_by[0].descending);
3181 }
3182 other => panic!("expected Window, got {other:?}"),
3183 }
3184 }
3185 _ => panic!("expected query"),
3186 }
3187 }
3188
3189 #[test]
3190 fn test_parse_window_sum_partition_order() {
3191 let stmt =
3192 parse("User { .name, s: sum(.salary) over (partition .dept order .salary) }").unwrap();
3193 match stmt {
3194 Statement::Query(q) => {
3195 let proj = q.projection.unwrap();
3196 assert_eq!(proj.len(), 2);
3197 assert_eq!(proj[1].alias.as_deref(), Some("s"));
3198 match &proj[1].expr {
3199 Expr::Window {
3200 function,
3201 args,
3202 partition_by,
3203 order_by,
3204 } => {
3205 assert_eq!(*function, WindowFunc::Sum);
3206 assert_eq!(args.len(), 1);
3207 assert!(matches!(&args[0], Expr::Field(f) if f == "salary"));
3208 assert_eq!(partition_by, &["dept"]);
3209 assert_eq!(order_by.len(), 1);
3210 assert_eq!(order_by[0].field, "salary");
3211 assert!(!order_by[0].descending);
3212 }
3213 other => panic!("expected Window, got {other:?}"),
3214 }
3215 }
3216 _ => panic!("expected query"),
3217 }
3218 }
3219
3220 #[test]
3221 fn test_parse_window_rank_desc() {
3222 let stmt =
3223 parse("User { .dept, .salary, r: rank() over (partition .dept order .salary desc) }")
3224 .unwrap();
3225 match stmt {
3226 Statement::Query(q) => {
3227 let proj = q.projection.unwrap();
3228 assert_eq!(proj.len(), 3);
3229 match &proj[2].expr {
3230 Expr::Window {
3231 function,
3232 partition_by,
3233 order_by,
3234 ..
3235 } => {
3236 assert_eq!(*function, WindowFunc::Rank);
3237 assert_eq!(partition_by, &["dept"]);
3238 assert_eq!(order_by.len(), 1);
3239 assert!(order_by[0].descending);
3240 }
3241 other => panic!("expected Window, got {other:?}"),
3242 }
3243 }
3244 _ => panic!("expected query"),
3245 }
3246 }
3247
3248 #[test]
3249 fn test_parse_window_dense_rank() {
3250 let stmt = parse("User { .name, dr: dense_rank() over (order .score desc) }").unwrap();
3251 match stmt {
3252 Statement::Query(q) => {
3253 let proj = q.projection.unwrap();
3254 assert_eq!(proj.len(), 2);
3255 match &proj[1].expr {
3256 Expr::Window { function, .. } => {
3257 assert_eq!(*function, WindowFunc::DenseRank);
3258 }
3259 other => panic!("expected Window, got {other:?}"),
3260 }
3261 }
3262 _ => panic!("expected query"),
3263 }
3264 }
3265
3266 #[test]
3267 fn test_parse_sum_without_over_is_aggregate() {
3268 let stmt = parse("User group .dept { .dept, total: sum(.salary) }").unwrap();
3270 match stmt {
3271 Statement::Query(q) => {
3272 let proj = q.projection.unwrap();
3273 assert_eq!(proj.len(), 2);
3274 match &proj[1].expr {
3275 Expr::FunctionCall(AggFunc::Sum, _) => {} other => panic!("expected FunctionCall(Sum), got {other:?}"),
3277 }
3278 }
3279 _ => panic!("expected query"),
3280 }
3281 }
3282
3283 #[test]
3284 fn test_nesting_depth_limit() {
3285 let mut query = String::from("User filter ");
3287 for _ in 0..70 {
3288 query.push('(');
3289 }
3290 query.push_str(".age > 1");
3291 for _ in 0..70 {
3292 query.push(')');
3293 }
3294 let result = parse(&query);
3295 assert!(result.is_err());
3296 let err = result.unwrap_err();
3297 assert!(
3298 err.message().contains("nesting depth"),
3299 "expected nesting depth error, got: {}",
3300 err.message()
3301 );
3302 }
3303
3304 #[test]
3305 fn test_moderate_nesting_succeeds() {
3306 let mut query = String::from("User filter ");
3308 for _ in 0..10 {
3309 query.push('(');
3310 }
3311 query.push_str(".age > 1");
3312 for _ in 0..10 {
3313 query.push(')');
3314 }
3315 assert!(parse(&query).is_ok());
3316 }
3317
3318 #[test]
3322 fn test_parse_fuzz_repro_projection_eof() {
3323 let err = parse("nn{").expect_err("unterminated projection must error, not panic");
3324 let _ = err.message();
3325 }
3326
3327 #[test]
3330 fn test_parse_fuzz_repro_short_projection_eof() {
3331 let err = parse("z{").expect_err("unterminated projection must error, not panic");
3332 let _ = err.message();
3333 }
3334
3335 #[test]
3336 fn test_update_at_statement_start_gives_helpful_error() {
3337 let err =
3338 parse(r#"update User filter .name = "Alice" { age := 31 }"#).expect_err("should fail");
3339 let msg = err.message();
3340 assert!(
3341 msg.contains("pipeline syntax"),
3342 "error should mention pipeline syntax, got: {msg}"
3343 );
3344 assert!(
3345 msg.contains("update"),
3346 "error should mention 'update', got: {msg}"
3347 );
3348 }
3349
3350 #[test]
3351 fn test_delete_at_statement_start_gives_helpful_error() {
3352 let err = parse("delete User filter .age < 18").expect_err("should fail");
3353 let msg = err.message();
3354 assert!(
3355 msg.contains("pipeline syntax"),
3356 "error should mention pipeline syntax, got: {msg}"
3357 );
3358 assert!(
3359 msg.contains("delete"),
3360 "error should mention 'delete', got: {msg}"
3361 );
3362 }
3363}