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 assignments = self.parse_assignments()?;
564 Ok(Statement::Insert(InsertExpr {
565 target,
566 assignments,
567 }))
568 }
569
570 fn parse_upsert(&mut self) -> Result<Statement, ParseError> {
572 self.expect(&Token::Upsert)?;
573 let target = match self.advance() {
574 Token::Ident(name) => name,
575 t => {
576 return Err(ParseError::UnexpectedToken {
577 expected: "type name".into(),
578 got: t.display_name(),
579 })
580 }
581 };
582 self.expect(&Token::On)?;
583 let key_column = match self.advance() {
584 Token::DotIdent(name) => name,
585 t => {
586 return Err(ParseError::UnexpectedToken {
587 expected: ".key_column".into(),
588 got: t.display_name(),
589 })
590 }
591 };
592 let assignments = self.parse_assignments()?;
593 let on_conflict = if *self.peek() == Token::On {
594 self.advance(); self.expect(&Token::Conflict)?;
596 self.parse_assignments()?
597 } else {
598 Vec::new()
599 };
600 Ok(Statement::Upsert(UpsertExpr {
601 target,
602 key_column,
603 assignments,
604 on_conflict,
605 }))
606 }
607
608 fn parse_assignments(&mut self) -> Result<Vec<Assignment>, ParseError> {
609 self.expect(&Token::LBrace)?;
610 let mut assignments = Vec::new();
611 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
612 let field = match self.advance() {
613 Token::Ident(name) => name,
614 t => {
615 return Err(ParseError::UnexpectedToken {
616 expected: "field name".into(),
617 got: t.display_name(),
618 })
619 }
620 };
621 self.expect(&Token::Assign)?;
622 let value = self.parse_expr()?;
623 assignments.push(Assignment { field, value });
624 if *self.peek() == Token::Comma {
625 self.advance();
626 }
627 }
628 self.expect(&Token::RBrace)?;
629 Ok(assignments)
630 }
631
632 fn parse_projection(&mut self) -> Result<Vec<ProjectionField>, ParseError> {
633 self.expect(&Token::LBrace)?;
634 let mut fields = Vec::new();
635 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
636 let first = self.advance();
637 if *self.peek() == Token::Colon {
638 self.advance();
640 let alias = match first {
641 Token::Ident(name) => name,
642 _ => {
643 return Err(ParseError::Syntax {
644 message: "expected alias name".into(),
645 })
646 }
647 };
648 let expr = self.parse_expr()?;
649 fields.push(ProjectionField {
650 alias: Some(alias),
651 expr,
652 });
653 } else {
654 let expr = match first {
655 Token::Ident(name) => {
659 if let Token::DotIdent(field) = self.peek().clone() {
660 self.advance();
661 Expr::QualifiedField {
662 qualifier: name,
663 field,
664 }
665 } else {
666 Expr::Field(name)
667 }
668 }
669 Token::DotIdent(name) => Expr::Field(name),
670 Token::RowNumber | Token::Rank | Token::DenseRank => {
671 let wfunc = match first {
672 Token::RowNumber => WindowFunc::RowNumber,
673 Token::Rank => WindowFunc::Rank,
674 Token::DenseRank => WindowFunc::DenseRank,
675 _ => {
676 return Err(ParseError::Syntax {
677 message: "unexpected window function token".into(),
678 })
679 }
680 };
681 self.expect(&Token::LParen)?;
682 self.expect(&Token::RParen)?;
683 let (partition_by, order_by) = self.parse_over_clause()?;
684 Expr::Window {
685 function: wfunc,
686 args: vec![],
687 partition_by,
688 order_by,
689 }
690 }
691 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
692 let mut func = match first {
693 Token::Count => AggFunc::Count,
694 Token::Avg => AggFunc::Avg,
695 Token::Sum => AggFunc::Sum,
696 Token::Min => AggFunc::Min,
697 Token::Max => AggFunc::Max,
698 _ => {
699 return Err(ParseError::Syntax {
700 message: "unexpected aggregate token".into(),
701 })
702 }
703 };
704 self.expect(&Token::LParen)?;
705 if func == AggFunc::Count && *self.peek() == Token::Star {
707 self.advance();
708 self.expect(&Token::RParen)?;
709 if *self.peek() == Token::Over {
711 let (partition_by, order_by) = self.parse_over_clause()?;
712 Expr::Window {
713 function: WindowFunc::Count,
714 args: vec![Expr::Field("*".into())],
715 partition_by,
716 order_by,
717 }
718 } else {
719 Expr::FunctionCall(
720 AggFunc::Count,
721 Box::new(Expr::Field("*".into())),
722 )
723 }
724 } else {
725 if func == AggFunc::Count && *self.peek() == Token::Distinct {
727 self.advance();
728 func = AggFunc::CountDistinct;
729 }
730 let inner = self.parse_expr()?;
731 self.expect(&Token::RParen)?;
732 if *self.peek() == Token::Over {
734 let wfunc =
735 match func {
736 AggFunc::Count => WindowFunc::Count,
737 AggFunc::Avg => WindowFunc::Avg,
738 AggFunc::Sum => WindowFunc::Sum,
739 AggFunc::Min => WindowFunc::Min,
740 AggFunc::Max => WindowFunc::Max,
741 _ => return Err(ParseError::Unsupported {
742 feature:
743 "count(distinct ...) over (...) is not supported"
744 .into(),
745 }),
746 };
747 let (partition_by, order_by) = self.parse_over_clause()?;
748 Expr::Window {
749 function: wfunc,
750 args: vec![inner],
751 partition_by,
752 order_by,
753 }
754 } else {
755 Expr::FunctionCall(func, Box::new(inner))
756 }
757 }
758 }
759 Token::Upper
760 | Token::Lower
761 | Token::Length
762 | Token::Trim
763 | Token::Substring
764 | Token::Concat
765 | Token::Abs
766 | Token::Round
767 | Token::Ceil
768 | Token::Floor
769 | Token::Sqrt
770 | Token::Pow
771 | Token::Now
772 | Token::Extract
773 | Token::DateAdd
774 | Token::DateDiff => {
775 let func = token_to_scalar_fn(&first);
776 self.expect(&Token::LParen)?;
777 let mut args = Vec::new();
778 while !matches!(self.peek(), Token::RParen | Token::Eof) {
779 args.push(self.parse_expr()?);
780 if *self.peek() == Token::Comma {
781 self.advance();
782 }
783 }
784 self.expect(&Token::RParen)?;
785 Expr::ScalarFunc(func, args)
786 }
787 Token::Cast => {
788 self.expect(&Token::LParen)?;
789 let inner = self.parse_expr()?;
790 self.expect(&Token::Comma)?;
791 let cast_type = self.parse_cast_type()?;
792 self.expect(&Token::RParen)?;
793 Expr::Cast(Box::new(inner), cast_type)
794 }
795 Token::Case => {
796 let mut whens = Vec::new();
797 while *self.peek() == Token::When {
798 self.advance();
799 let condition = self.parse_expr()?;
800 self.expect(&Token::Then)?;
801 let result = self.parse_expr()?;
802 whens.push((Box::new(condition), Box::new(result)));
803 }
804 let else_expr = if *self.peek() == Token::Else {
805 self.advance();
806 Some(Box::new(self.parse_expr()?))
807 } else {
808 None
809 };
810 self.expect(&Token::End)?;
811 Expr::Case { whens, else_expr }
812 }
813 _ => {
814 return Err(ParseError::UnexpectedToken {
815 expected: "field".into(),
816 got: first.display_name(),
817 })
818 }
819 };
820 fields.push(ProjectionField { alias: None, expr });
821 }
822 if *self.peek() == Token::Comma {
823 self.advance();
824 }
825 }
826 self.expect(&Token::RBrace)?;
827 Ok(fields)
828 }
829
830 fn parse_over_clause(&mut self) -> Result<(Vec<String>, Vec<OrderKey>), ParseError> {
833 self.expect(&Token::Over)?;
834 self.expect(&Token::LParen)?;
835 let mut partition_by = Vec::new();
836 let mut order_by = Vec::new();
837 if *self.peek() == Token::Partition {
838 self.advance();
839 while let Token::DotIdent(name) = self.peek() {
840 let name = name.clone();
841 self.advance();
842 partition_by.push(name);
843 if *self.peek() == Token::Comma {
844 if matches!(self.tokens.get(self.pos + 1), Some(Token::DotIdent(_))) {
848 self.advance();
849 } else {
850 break;
851 }
852 } else {
853 break;
854 }
855 }
856 }
857 if *self.peek() == Token::Order {
858 self.advance();
859 while let Token::DotIdent(name) = self.peek() {
860 let field = name.clone();
861 self.advance();
862 let descending = match self.peek() {
863 Token::Desc => {
864 self.advance();
865 true
866 }
867 Token::Asc => {
868 self.advance();
869 false
870 }
871 _ => false,
872 };
873 order_by.push(OrderKey { field, descending });
874 if *self.peek() == Token::Comma {
875 self.advance();
876 } else {
877 break;
878 }
879 }
880 }
881 self.expect(&Token::RParen)?;
882 Ok((partition_by, order_by))
883 }
884
885 fn parse_cast_type(&mut self) -> Result<CastType, ParseError> {
887 match self.advance() {
888 Token::StringLit(s) => match s.as_str() {
889 "int" | "Int" | "INT" => Ok(CastType::Int),
890 "float" | "Float" | "FLOAT" => Ok(CastType::Float),
891 "str" | "Str" | "STR" | "string" | "String" => Ok(CastType::Str),
892 "bool" | "Bool" | "BOOL" | "boolean" => Ok(CastType::Bool),
893 "datetime" | "DateTime" | "DATETIME" => Ok(CastType::DateTime),
894 other => Err(ParseError::Syntax {
895 message: format!("invalid cast type: \"{other}\""),
896 }),
897 },
898 t => Err(ParseError::UnexpectedToken {
899 expected: "string literal for cast type".into(),
900 got: t.display_name(),
901 }),
902 }
903 }
904
905 fn parse_order(&mut self) -> Result<OrderClause, ParseError> {
906 let mut keys = Vec::new();
907 loop {
908 let field = match self.advance() {
909 Token::DotIdent(name) => name,
910 t => {
911 return Err(ParseError::UnexpectedToken {
912 expected: ".field after order".into(),
913 got: t.display_name(),
914 })
915 }
916 };
917 let descending = match self.peek() {
918 Token::Desc => {
919 self.advance();
920 true
921 }
922 Token::Asc => {
923 self.advance();
924 false
925 }
926 _ => false,
927 };
928 keys.push(OrderKey { field, descending });
929 if *self.peek() == Token::Comma {
930 self.advance();
931 } else {
932 break;
933 }
934 }
935 Ok(OrderClause { keys })
936 }
937
938 fn parse_aggregate_query(&mut self) -> Result<Statement, ParseError> {
939 let mut func = match self.advance() {
940 Token::Count => AggFunc::Count,
941 Token::Avg => AggFunc::Avg,
942 Token::Sum => AggFunc::Sum,
943 Token::Min => AggFunc::Min,
944 Token::Max => AggFunc::Max,
945 t => {
946 return Err(ParseError::UnexpectedToken {
947 expected: "aggregate function".into(),
948 got: t.display_name(),
949 })
950 }
951 };
952 self.expect(&Token::LParen)?;
953 if func == AggFunc::Count && *self.peek() == Token::Distinct {
955 self.advance();
956 func = AggFunc::CountDistinct;
957 }
958 let source = match self.advance() {
959 Token::Ident(name) => name,
960 t => {
961 return Err(ParseError::UnexpectedToken {
962 expected: "type name".into(),
963 got: t.display_name(),
964 })
965 }
966 };
967 let mut query = self.parse_query_tail(source)?;
971 self.expect(&Token::RParen)?;
972
973 let mut agg_field: Option<String> = None;
980 if func != AggFunc::Count {
981 if let Some(proj) = &query.projection {
982 if proj.len() == 1 && proj[0].alias.is_none() {
983 if let Expr::Field(name) = &proj[0].expr {
984 agg_field = Some(name.clone());
985 }
986 }
987 }
988 if agg_field.is_some() {
989 query.projection = None;
990 }
991 }
992 query.aggregation = Some(AggregateExpr {
993 function: func,
994 field: agg_field,
995 });
996 Ok(Statement::Query(query))
997 }
998
999 fn parse_expr(&mut self) -> Result<Expr, ParseError> {
1000 self.depth += 1;
1001 if self.depth > MAX_NESTING_DEPTH {
1002 self.depth -= 1;
1003 return Err(ParseError::NestingDepthExceeded {
1004 max: MAX_NESTING_DEPTH,
1005 });
1006 }
1007 let result = self.parse_or_expr();
1008 self.depth -= 1;
1009 result
1010 }
1011
1012 fn parse_or_expr(&mut self) -> Result<Expr, ParseError> {
1013 let mut left = self.parse_and_expr()?;
1014 while *self.peek() == Token::Or {
1015 self.advance();
1016 let right = self.parse_and_expr()?;
1017 left = Expr::BinaryOp(Box::new(left), BinOp::Or, Box::new(right));
1018 }
1019 Ok(left)
1020 }
1021
1022 fn parse_and_expr(&mut self) -> Result<Expr, ParseError> {
1023 let mut left = self.parse_comparison()?;
1024 while *self.peek() == Token::And {
1025 self.advance();
1026 let right = self.parse_comparison()?;
1027 left = Expr::BinaryOp(Box::new(left), BinOp::And, Box::new(right));
1028 }
1029 Ok(left)
1030 }
1031
1032 fn parse_comparison(&mut self) -> Result<Expr, ParseError> {
1033 let left = self.parse_additive()?;
1034
1035 if *self.peek() == Token::Is {
1037 self.advance();
1038 if *self.peek() == Token::Not {
1039 self.advance();
1040 self.expect(&Token::Null)?;
1041 return Ok(Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(left)));
1042 } else {
1043 self.expect(&Token::Null)?;
1044 return Ok(Expr::UnaryOp(UnaryOp::IsNull, Box::new(left)));
1045 }
1046 }
1047
1048 match self.peek() {
1051 Token::In => {
1052 self.advance();
1053 return self.parse_in_list(left, false);
1054 }
1055 Token::Like => {
1056 self.advance();
1057 let pattern = self.parse_additive()?;
1058 return Ok(Expr::BinaryOp(
1059 Box::new(left),
1060 BinOp::Like,
1061 Box::new(pattern),
1062 ));
1063 }
1064 Token::Between => {
1065 self.advance();
1066 return self.parse_between(left, false);
1067 }
1068 Token::Not => {
1069 let next = self.tokens.get(self.pos + 1);
1073 match next {
1074 Some(Token::In) => {
1075 self.advance(); self.advance(); return self.parse_in_list(left, true);
1078 }
1079 Some(Token::Like) => {
1080 self.advance(); self.advance(); let pattern = self.parse_additive()?;
1083 let like = Expr::BinaryOp(Box::new(left), BinOp::Like, Box::new(pattern));
1084 return Ok(Expr::UnaryOp(UnaryOp::Not, Box::new(like)));
1085 }
1086 Some(Token::Between) => {
1087 self.advance(); self.advance(); return self.parse_between(left, true);
1090 }
1091 _ => {}
1092 }
1093 }
1094 _ => {}
1095 }
1096
1097 let op = match self.peek() {
1098 Token::Eq => BinOp::Eq,
1099 Token::Neq => BinOp::Neq,
1100 Token::Lt => BinOp::Lt,
1101 Token::Gt => BinOp::Gt,
1102 Token::Lte => BinOp::Lte,
1103 Token::Gte => BinOp::Gte,
1104 _ => return Ok(left),
1105 };
1106 self.advance();
1107 if *self.peek() == Token::Null {
1111 match op {
1112 BinOp::Eq => {
1113 self.advance();
1114 return Ok(Expr::UnaryOp(UnaryOp::IsNull, Box::new(left)));
1115 }
1116 BinOp::Neq => {
1117 self.advance();
1118 return Ok(Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(left)));
1119 }
1120 _ => {}
1121 }
1122 }
1123 let right = self.parse_additive()?;
1124 Ok(Expr::BinaryOp(Box::new(left), op, Box::new(right)))
1125 }
1126
1127 fn parse_in_list(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
1132 self.expect(&Token::LParen)?;
1133 if let Token::Ident(_) = self.peek() {
1135 let after = self.tokens.get(self.pos + 1);
1138 let is_subquery = !matches!(after, Some(Token::Comma) | Some(Token::RParen));
1139 if is_subquery {
1140 let source = match self.advance() {
1141 Token::Ident(name) => name,
1142 _ => unreachable!(),
1143 };
1144 let subquery = self.parse_query_tail(source)?;
1145 self.expect(&Token::RParen)?;
1146 return Ok(Expr::InSubquery {
1147 expr: Box::new(expr),
1148 subquery: Box::new(subquery),
1149 negated,
1150 });
1151 }
1152 }
1153 let mut list = Vec::new();
1154 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1155 list.push(self.parse_expr()?);
1156 if *self.peek() == Token::Comma {
1157 self.advance();
1158 }
1159 }
1160 self.expect(&Token::RParen)?;
1161 Ok(Expr::InList {
1162 expr: Box::new(expr),
1163 list,
1164 negated,
1165 })
1166 }
1167
1168 fn try_parse_exists_subquery(&mut self) -> Result<Option<QueryExpr>, ParseError> {
1176 if *self.peek() != Token::LParen {
1177 return Ok(None);
1178 }
1179 let after_lparen = self.tokens.get(self.pos + 1);
1183 if !matches!(after_lparen, Some(Token::Ident(_))) {
1184 return Ok(None);
1185 }
1186 self.expect(&Token::LParen)?;
1187 let source = match self.advance() {
1188 Token::Ident(name) => name,
1189 _ => unreachable!(),
1190 };
1191 let subquery = self.parse_query_tail(source)?;
1192 self.expect(&Token::RParen)?;
1193 Ok(Some(subquery))
1194 }
1195
1196 fn parse_between(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
1200 let low = self.parse_additive()?;
1201 self.expect(&Token::And)?;
1202 let high = self.parse_additive()?;
1203 if negated {
1204 Ok(Expr::BinaryOp(
1206 Box::new(Expr::BinaryOp(
1207 Box::new(expr.clone()),
1208 BinOp::Lt,
1209 Box::new(low),
1210 )),
1211 BinOp::Or,
1212 Box::new(Expr::BinaryOp(Box::new(expr), BinOp::Gt, Box::new(high))),
1213 ))
1214 } else {
1215 Ok(Expr::BinaryOp(
1217 Box::new(Expr::BinaryOp(
1218 Box::new(expr.clone()),
1219 BinOp::Gte,
1220 Box::new(low),
1221 )),
1222 BinOp::And,
1223 Box::new(Expr::BinaryOp(Box::new(expr), BinOp::Lte, Box::new(high))),
1224 ))
1225 }
1226 }
1227
1228 fn parse_group_by(&mut self) -> Result<GroupByClause, ParseError> {
1230 let mut keys = Vec::new();
1231 while let Token::DotIdent(name) = self.peek() {
1232 let name = name.clone();
1233 self.advance();
1234 keys.push(name);
1235 if *self.peek() == Token::Comma {
1236 self.advance();
1237 } else {
1238 break;
1239 }
1240 }
1241 if keys.is_empty() {
1242 return Err(ParseError::Syntax {
1243 message: "expected at least one .field after group".into(),
1244 });
1245 }
1246 let having = if *self.peek() == Token::Having {
1247 self.advance();
1248 Some(self.parse_expr()?)
1249 } else {
1250 None
1251 };
1252 Ok(GroupByClause { keys, having })
1253 }
1254
1255 fn parse_additive(&mut self) -> Result<Expr, ParseError> {
1256 let mut left = self.parse_multiplicative()?;
1257 loop {
1258 let op = match self.peek() {
1259 Token::Plus => BinOp::Add,
1260 Token::Minus => BinOp::Sub,
1261 Token::Coalesce => {
1262 self.advance();
1263 let right = self.parse_multiplicative()?;
1264 left = Expr::Coalesce(Box::new(left), Box::new(right));
1265 continue;
1266 }
1267 _ => break,
1268 };
1269 self.advance();
1270 let right = self.parse_multiplicative()?;
1271 left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
1272 }
1273 Ok(left)
1274 }
1275
1276 fn parse_multiplicative(&mut self) -> Result<Expr, ParseError> {
1277 let mut left = self.parse_primary()?;
1278 loop {
1279 let op = match self.peek() {
1280 Token::Star => BinOp::Mul,
1281 Token::Slash => BinOp::Div,
1282 _ => break,
1283 };
1284 self.advance();
1285 let right = self.parse_primary()?;
1286 left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
1287 }
1288 Ok(left)
1289 }
1290
1291 fn parse_primary(&mut self) -> Result<Expr, ParseError> {
1292 match self.peek().clone() {
1293 Token::DotIdent(name) => {
1294 self.advance();
1295 Ok(Expr::Field(name))
1296 }
1297 Token::IntLit(v) => {
1298 self.advance();
1299 Ok(Expr::Literal(Literal::Int(v)))
1300 }
1301 Token::FloatLit(v) => {
1302 self.advance();
1303 Ok(Expr::Literal(Literal::Float(v)))
1304 }
1305 Token::StringLit(v) => {
1306 self.advance();
1307 Ok(Expr::Literal(Literal::String(v)))
1308 }
1309 Token::BoolLit(v) => {
1310 self.advance();
1311 Ok(Expr::Literal(Literal::Bool(v)))
1312 }
1313 Token::Param(name) => {
1314 self.advance();
1315 Ok(Expr::Param(name))
1316 }
1317 Token::Null => {
1318 self.advance();
1319 Ok(Expr::Null)
1320 }
1321 Token::Not => {
1322 self.advance();
1323 if *self.peek() == Token::Exists {
1324 self.advance();
1325 if let Some(sub) = self.try_parse_exists_subquery()? {
1329 return Ok(Expr::ExistsSubquery {
1330 subquery: Box::new(sub),
1331 negated: true,
1332 });
1333 }
1334 let expr = self.parse_primary()?;
1335 Ok(Expr::UnaryOp(UnaryOp::NotExists, Box::new(expr)))
1336 } else {
1337 let expr = self.parse_primary()?;
1338 Ok(Expr::UnaryOp(UnaryOp::Not, Box::new(expr)))
1339 }
1340 }
1341 Token::Exists => {
1342 self.advance();
1343 if let Some(sub) = self.try_parse_exists_subquery()? {
1347 return Ok(Expr::ExistsSubquery {
1348 subquery: Box::new(sub),
1349 negated: false,
1350 });
1351 }
1352 let expr = self.parse_primary()?;
1353 Ok(Expr::UnaryOp(UnaryOp::Exists, Box::new(expr)))
1354 }
1355 Token::LParen => {
1356 self.advance();
1357 let expr = self.parse_expr()?;
1358 self.expect(&Token::RParen)?;
1359 Ok(expr)
1360 }
1361 Token::Ident(name) => {
1362 self.advance();
1363 if let Token::DotIdent(field) = self.peek().clone() {
1367 self.advance();
1368 return Ok(Expr::QualifiedField {
1369 qualifier: name,
1370 field,
1371 });
1372 }
1373 Ok(Expr::Field(name))
1374 }
1375 Token::RowNumber | Token::Rank | Token::DenseRank => {
1377 let wfunc = match self.advance() {
1378 Token::RowNumber => WindowFunc::RowNumber,
1379 Token::Rank => WindowFunc::Rank,
1380 Token::DenseRank => WindowFunc::DenseRank,
1381 _ => {
1382 return Err(ParseError::Syntax {
1383 message: "unexpected window function token".into(),
1384 })
1385 }
1386 };
1387 self.expect(&Token::LParen)?;
1388 self.expect(&Token::RParen)?;
1389 let (partition_by, order_by) = self.parse_over_clause()?;
1390 Ok(Expr::Window {
1391 function: wfunc,
1392 args: vec![],
1393 partition_by,
1394 order_by,
1395 })
1396 }
1397 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1401 let mut func = match self.advance() {
1402 Token::Count => AggFunc::Count,
1403 Token::Avg => AggFunc::Avg,
1404 Token::Sum => AggFunc::Sum,
1405 Token::Min => AggFunc::Min,
1406 Token::Max => AggFunc::Max,
1407 _ => {
1408 return Err(ParseError::Syntax {
1409 message: "unexpected aggregate token".into(),
1410 })
1411 }
1412 };
1413 self.expect(&Token::LParen)?;
1414 if func == AggFunc::Count && *self.peek() == Token::Star {
1416 self.advance();
1417 self.expect(&Token::RParen)?;
1418 if *self.peek() == Token::Over {
1420 let (partition_by, order_by) = self.parse_over_clause()?;
1421 return Ok(Expr::Window {
1422 function: WindowFunc::Count,
1423 args: vec![Expr::Field("*".into())],
1424 partition_by,
1425 order_by,
1426 });
1427 }
1428 return Ok(Expr::FunctionCall(
1429 AggFunc::Count,
1430 Box::new(Expr::Field("*".into())),
1431 ));
1432 }
1433 if func == AggFunc::Count && *self.peek() == Token::Distinct {
1435 self.advance();
1436 func = AggFunc::CountDistinct;
1437 }
1438 let inner = self.parse_expr()?;
1439 self.expect(&Token::RParen)?;
1440 if *self.peek() == Token::Over {
1442 let wfunc = match func {
1443 AggFunc::Count => WindowFunc::Count,
1444 AggFunc::Avg => WindowFunc::Avg,
1445 AggFunc::Sum => WindowFunc::Sum,
1446 AggFunc::Min => WindowFunc::Min,
1447 AggFunc::Max => WindowFunc::Max,
1448 _ => {
1449 return Err(ParseError::Unsupported {
1450 feature: "count(distinct ...) over (...) is not supported".into(),
1451 })
1452 }
1453 };
1454 let (partition_by, order_by) = self.parse_over_clause()?;
1455 return Ok(Expr::Window {
1456 function: wfunc,
1457 args: vec![inner],
1458 partition_by,
1459 order_by,
1460 });
1461 }
1462 Ok(Expr::FunctionCall(func, Box::new(inner)))
1463 }
1464 Token::Upper
1465 | Token::Lower
1466 | Token::Length
1467 | Token::Trim
1468 | Token::Substring
1469 | Token::Concat
1470 | Token::Abs
1471 | Token::Round
1472 | Token::Ceil
1473 | Token::Floor
1474 | Token::Sqrt
1475 | Token::Pow
1476 | Token::Now
1477 | Token::Extract
1478 | Token::DateAdd
1479 | Token::DateDiff => {
1480 let tok = self.advance();
1481 let func = token_to_scalar_fn(&tok);
1482 self.expect(&Token::LParen)?;
1483 let mut args = Vec::new();
1484 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1485 args.push(self.parse_expr()?);
1486 if *self.peek() == Token::Comma {
1487 self.advance();
1488 }
1489 }
1490 self.expect(&Token::RParen)?;
1491 Ok(Expr::ScalarFunc(func, args))
1492 }
1493 Token::Cast => {
1494 self.advance();
1495 self.expect(&Token::LParen)?;
1496 let inner = self.parse_expr()?;
1497 self.expect(&Token::Comma)?;
1498 let cast_type = self.parse_cast_type()?;
1499 self.expect(&Token::RParen)?;
1500 Ok(Expr::Cast(Box::new(inner), cast_type))
1501 }
1502 Token::Case => {
1503 self.advance();
1504 let mut whens = Vec::new();
1505 while *self.peek() == Token::When {
1506 self.advance();
1507 let condition = self.parse_expr()?;
1508 self.expect(&Token::Then)?;
1509 let result = self.parse_expr()?;
1510 whens.push((Box::new(condition), Box::new(result)));
1511 }
1512 let else_expr = if *self.peek() == Token::Else {
1513 self.advance();
1514 Some(Box::new(self.parse_expr()?))
1515 } else {
1516 None
1517 };
1518 self.expect(&Token::End)?;
1519 Ok(Expr::Case { whens, else_expr })
1520 }
1521 t => Err(ParseError::Syntax {
1522 message: format!("unexpected token in expression: {}", t.display_name()),
1523 }),
1524 }
1525 }
1526
1527 fn parse_alter_table(&mut self) -> Result<Statement, ParseError> {
1530 self.expect(&Token::Alter)?;
1531 let table = match self.advance() {
1532 Token::Ident(name) => name,
1533 t => {
1534 return Err(ParseError::UnexpectedToken {
1535 expected: "table name after alter".into(),
1536 got: t.display_name(),
1537 })
1538 }
1539 };
1540 match self.peek() {
1541 Token::Add => {
1542 self.advance();
1543 if *self.peek() == Token::Index {
1545 self.advance();
1546 let column = match self.advance() {
1547 Token::DotIdent(n) => n,
1548 t => {
1549 return Err(ParseError::UnexpectedToken {
1550 expected: ".<column> after add index".into(),
1551 got: t.display_name(),
1552 })
1553 }
1554 };
1555 return Ok(Statement::AlterTable(AlterTableExpr {
1556 table,
1557 action: AlterAction::AddIndex { column },
1558 }));
1559 }
1560 if *self.peek() == Token::Column {
1562 self.advance();
1563 }
1564 let required = if *self.peek() == Token::Required {
1565 self.advance();
1566 true
1567 } else {
1568 false
1569 };
1570 let name = match self.advance() {
1571 Token::Ident(n) => n,
1572 t => {
1573 return Err(ParseError::UnexpectedToken {
1574 expected: "column name".into(),
1575 got: t.display_name(),
1576 })
1577 }
1578 };
1579 self.expect(&Token::Colon)?;
1580 let type_name = match self.advance() {
1581 Token::Ident(n) => n,
1582 t => {
1583 return Err(ParseError::UnexpectedToken {
1584 expected: "type name".into(),
1585 got: t.display_name(),
1586 })
1587 }
1588 };
1589 Ok(Statement::AlterTable(AlterTableExpr {
1590 table,
1591 action: AlterAction::AddColumn {
1592 name,
1593 type_name,
1594 required,
1595 },
1596 }))
1597 }
1598 Token::Drop => {
1599 self.advance();
1600 if *self.peek() == Token::Column {
1602 self.advance();
1603 }
1604 let name = match self.advance() {
1605 Token::Ident(n) => n,
1606 t => {
1607 return Err(ParseError::UnexpectedToken {
1608 expected: "column name".into(),
1609 got: t.display_name(),
1610 })
1611 }
1612 };
1613 Ok(Statement::AlterTable(AlterTableExpr {
1614 table,
1615 action: AlterAction::DropColumn { name },
1616 }))
1617 }
1618 t => Err(ParseError::UnexpectedToken {
1619 expected: "add or drop after alter <table>".into(),
1620 got: t.display_name(),
1621 }),
1622 }
1623 }
1624
1625 fn parse_drop_or_drop_view(&mut self) -> Result<Statement, ParseError> {
1627 self.expect(&Token::Drop)?;
1628 if *self.peek() == Token::View {
1629 self.advance(); let name = match self.advance() {
1631 Token::Ident(name) => name,
1632 t => {
1633 return Err(ParseError::UnexpectedToken {
1634 expected: "view name after drop view".into(),
1635 got: t.display_name(),
1636 })
1637 }
1638 };
1639 return Ok(Statement::DropView(DropViewExpr { name }));
1640 }
1641 let table = match self.advance() {
1642 Token::Ident(name) => name,
1643 t => {
1644 return Err(ParseError::UnexpectedToken {
1645 expected: "table name after drop".into(),
1646 got: t.display_name(),
1647 })
1648 }
1649 };
1650 Ok(Statement::DropTable(DropTableExpr { table }))
1651 }
1652
1653 fn parse_create_view(&mut self) -> Result<Statement, ParseError> {
1658 self.expect(&Token::Materialized)?;
1659 let name = match self.advance() {
1660 Token::Ident(name) => name,
1661 t => {
1662 return Err(ParseError::UnexpectedToken {
1663 expected: "view name after materialize".into(),
1664 got: t.display_name(),
1665 })
1666 }
1667 };
1668 self.expect(&Token::As)?;
1669 let query_start = self.pos;
1671 let source = match self.advance() {
1672 Token::Ident(s) => s,
1673 t => {
1674 return Err(ParseError::UnexpectedToken {
1675 expected: "source table name".into(),
1676 got: t.display_name(),
1677 })
1678 }
1679 };
1680 let query = self.parse_query_tail(source)?;
1681 let query_text = tokens_to_text(&self.tokens[query_start..self.pos]);
1683 Ok(Statement::CreateView(CreateViewExpr {
1684 name,
1685 query,
1686 query_text,
1687 }))
1688 }
1689
1690 fn maybe_parse_union(&mut self, left: Statement) -> Result<Statement, ParseError> {
1693 if *self.peek() != Token::Union {
1694 return Ok(left);
1695 }
1696 if !matches!(left, Statement::Query(_) | Statement::Union(_)) {
1697 return Err(ParseError::Syntax {
1698 message: "UNION requires a query on the left side".into(),
1699 });
1700 }
1701 self.advance(); let all = if let Token::Ident(s) = self.peek() {
1703 if s == "all" {
1704 self.advance();
1705 true
1706 } else {
1707 false
1708 }
1709 } else {
1710 false
1711 };
1712 let right = self.parse_single_query()?;
1714 let union = Statement::Union(UnionExpr {
1715 left: Box::new(left),
1716 right: Box::new(right),
1717 all,
1718 });
1719 self.maybe_parse_union(union)
1721 }
1722
1723 fn parse_single_query(&mut self) -> Result<Statement, ParseError> {
1725 match self.peek() {
1726 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1727 self.parse_aggregate_query()
1728 }
1729 Token::Ident(_) => self.parse_query_or_mutation(),
1730 _ => Err(ParseError::Syntax {
1731 message: format!(
1732 "expected query after UNION, got {}",
1733 self.peek().display_name()
1734 ),
1735 }),
1736 }
1737 }
1738
1739 fn parse_refresh_view(&mut self) -> Result<Statement, ParseError> {
1741 self.expect(&Token::Refresh)?;
1742 let name = match self.advance() {
1743 Token::Ident(name) => name,
1744 t => {
1745 return Err(ParseError::UnexpectedToken {
1746 expected: "view name after refresh".into(),
1747 got: t.display_name(),
1748 })
1749 }
1750 };
1751 Ok(Statement::RefreshView(RefreshViewExpr { name }))
1752 }
1753
1754 fn parse_create_type(&mut self) -> Result<Statement, ParseError> {
1755 self.expect(&Token::Type)?;
1756 let name = match self.advance() {
1757 Token::Ident(n) => n,
1758 t => {
1759 return Err(ParseError::UnexpectedToken {
1760 expected: "type name".into(),
1761 got: t.display_name(),
1762 })
1763 }
1764 };
1765 self.expect(&Token::LBrace)?;
1766 let mut fields = Vec::new();
1767 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1768 let required = if *self.peek() == Token::Required {
1769 self.advance();
1770 true
1771 } else {
1772 false
1773 };
1774 let field_name = match self.advance() {
1775 Token::Ident(n) => n,
1776 t => {
1777 return Err(ParseError::UnexpectedToken {
1778 expected: "field name".into(),
1779 got: t.display_name(),
1780 })
1781 }
1782 };
1783 self.expect(&Token::Colon)?;
1784 let type_name = match self.advance() {
1785 Token::Ident(n) => n,
1786 t => {
1787 return Err(ParseError::UnexpectedToken {
1788 expected: "type name".into(),
1789 got: t.display_name(),
1790 })
1791 }
1792 };
1793 fields.push(FieldDef {
1794 name: field_name,
1795 type_name,
1796 required,
1797 });
1798 if *self.peek() == Token::Comma {
1799 self.advance();
1800 }
1801 }
1802 self.expect(&Token::RBrace)?;
1803 Ok(Statement::CreateType(CreateTypeExpr { name, fields }))
1804 }
1805}
1806
1807fn tokens_to_text(tokens: &[Token]) -> String {
1811 let mut out = String::with_capacity(64);
1812 for tok in tokens {
1813 if !out.is_empty() && !matches!(tok, Token::Eof) {
1814 out.push(' ');
1815 }
1816 match tok {
1817 Token::Ident(s) => out.push_str(s),
1818 Token::DotIdent(s) => {
1819 out.push('.');
1820 out.push_str(s);
1821 }
1822 Token::IntLit(v) => out.push_str(&v.to_string()),
1823 Token::FloatLit(v) => out.push_str(&v.to_string()),
1824 Token::StringLit(s) => {
1825 out.push('"');
1826 out.push_str(s);
1827 out.push('"');
1828 }
1829 Token::BoolLit(v) => out.push_str(if *v { "true" } else { "false" }),
1830 Token::Param(s) => {
1831 out.push('$');
1832 out.push_str(s);
1833 }
1834 Token::Type => out.push_str("type"),
1835 Token::Filter => out.push_str("filter"),
1836 Token::Order => out.push_str("order"),
1837 Token::Limit => out.push_str("limit"),
1838 Token::Offset => out.push_str("offset"),
1839 Token::Insert => out.push_str("insert"),
1840 Token::Update => out.push_str("update"),
1841 Token::Delete => out.push_str("delete"),
1842 Token::Upsert => out.push_str("upsert"),
1843 Token::Conflict => out.push_str("conflict"),
1844 Token::Select => out.push_str("select"),
1845 Token::Required => out.push_str("required"),
1846 Token::Multi => out.push_str("multi"),
1847 Token::Link => out.push_str("link"),
1848 Token::Index => out.push_str("index"),
1849 Token::On => out.push_str("on"),
1850 Token::Asc => out.push_str("asc"),
1851 Token::Desc => out.push_str("desc"),
1852 Token::And => out.push_str("and"),
1853 Token::Or => out.push_str("or"),
1854 Token::Not => out.push_str("not"),
1855 Token::Exists => out.push_str("exists"),
1856 Token::Let => out.push_str("let"),
1857 Token::As => out.push_str("as"),
1858 Token::Match => out.push_str("match"),
1859 Token::Group => out.push_str("group"),
1860 Token::Join => out.push_str("join"),
1861 Token::Inner => out.push_str("inner"),
1862 Token::LeftKw => out.push_str("left"),
1863 Token::RightKw => out.push_str("right"),
1864 Token::Outer => out.push_str("outer"),
1865 Token::Cross => out.push_str("cross"),
1866 Token::Transaction => out.push_str("transaction"),
1867 Token::Begin => out.push_str("begin"),
1868 Token::Commit => out.push_str("commit"),
1869 Token::Rollback => out.push_str("rollback"),
1870 Token::View => out.push_str("view"),
1871 Token::Materialized => out.push_str("materialized"),
1872 Token::Refresh => out.push_str("refresh"),
1873 Token::Union => out.push_str("union"),
1874 Token::Having => out.push_str("having"),
1875 Token::Distinct => out.push_str("distinct"),
1876 Token::In => out.push_str("in"),
1877 Token::Between => out.push_str("between"),
1878 Token::Like => out.push_str("like"),
1879 Token::Count => out.push_str("count"),
1880 Token::Avg => out.push_str("avg"),
1881 Token::Sum => out.push_str("sum"),
1882 Token::Min => out.push_str("min"),
1883 Token::Max => out.push_str("max"),
1884 Token::Is => out.push_str("is"),
1885 Token::Null => out.push_str("null"),
1886 Token::Upper => out.push_str("upper"),
1887 Token::Lower => out.push_str("lower"),
1888 Token::Length => out.push_str("length"),
1889 Token::Trim => out.push_str("trim"),
1890 Token::Substring => out.push_str("substring"),
1891 Token::Concat => out.push_str("concat"),
1892 Token::Abs => out.push_str("abs"),
1893 Token::Round => out.push_str("round"),
1894 Token::Ceil => out.push_str("ceil"),
1895 Token::Floor => out.push_str("floor"),
1896 Token::Sqrt => out.push_str("sqrt"),
1897 Token::Pow => out.push_str("pow"),
1898 Token::Now => out.push_str("now"),
1899 Token::Extract => out.push_str("extract"),
1900 Token::DateAdd => out.push_str("date_add"),
1901 Token::DateDiff => out.push_str("date_diff"),
1902 Token::Cast => out.push_str("cast"),
1903 Token::Case => out.push_str("case"),
1904 Token::When => out.push_str("when"),
1905 Token::Then => out.push_str("then"),
1906 Token::Else => out.push_str("else"),
1907 Token::End => out.push_str("end"),
1908 Token::Over => out.push_str("over"),
1909 Token::Partition => out.push_str("partition"),
1910 Token::RowNumber => out.push_str("row_number"),
1911 Token::Rank => out.push_str("rank"),
1912 Token::DenseRank => out.push_str("dense_rank"),
1913 Token::Alter => out.push_str("alter"),
1914 Token::Drop => out.push_str("drop"),
1915 Token::Add => out.push_str("add"),
1916 Token::Column => out.push_str("column"),
1917 Token::Eq => out.push('='),
1918 Token::Neq => out.push_str("!="),
1919 Token::Lt => out.push('<'),
1920 Token::Gt => out.push('>'),
1921 Token::Lte => out.push_str("<="),
1922 Token::Gte => out.push_str(">="),
1923 Token::Assign => out.push_str(":="),
1924 Token::Arrow => out.push_str("->"),
1925 Token::Pipe => out.push('|'),
1926 Token::Coalesce => out.push_str("??"),
1927 Token::Plus => out.push('+'),
1928 Token::Minus => out.push('-'),
1929 Token::Star => out.push('*'),
1930 Token::Slash => out.push('/'),
1931 Token::LBrace => out.push('{'),
1932 Token::RBrace => out.push('}'),
1933 Token::LParen => out.push('('),
1934 Token::RParen => out.push(')'),
1935 Token::Comma => out.push(','),
1936 Token::Colon => out.push(':'),
1937 Token::Dot => out.push('.'),
1938 Token::Explain => out.push_str("explain"),
1939 Token::Eof => {}
1940 }
1941 }
1942 out
1943}
1944
1945#[cfg(test)]
1946mod tests {
1947 use super::*;
1948 #[test]
1949 fn test_parse_simple_query() {
1950 let stmt = parse("User").unwrap();
1951 match stmt {
1952 Statement::Query(q) => {
1953 assert_eq!(q.source, "User");
1954 assert!(q.filter.is_none());
1955 assert!(q.projection.is_none());
1956 }
1957 _ => panic!("expected query"),
1958 }
1959 }
1960
1961 #[test]
1962 fn test_parse_filter() {
1963 let stmt = parse("User filter .age > 30").unwrap();
1964 match stmt {
1965 Statement::Query(q) => {
1966 assert_eq!(q.source, "User");
1967 assert!(q.filter.is_some());
1968 }
1969 _ => panic!("expected query"),
1970 }
1971 }
1972
1973 #[test]
1974 fn test_parse_projection() {
1975 let stmt = parse("User { name, email }").unwrap();
1976 match stmt {
1977 Statement::Query(q) => {
1978 let proj = q.projection.unwrap();
1979 assert_eq!(proj.len(), 2);
1980 }
1981 _ => panic!("expected query"),
1982 }
1983 }
1984
1985 #[test]
1986 fn test_parse_filter_order_limit() {
1987 let stmt = parse("User filter .age > 30 order .name desc limit 10").unwrap();
1988 match stmt {
1989 Statement::Query(q) => {
1990 assert!(q.filter.is_some());
1991 let order = q.order.unwrap();
1992 assert_eq!(order.keys.len(), 1);
1993 assert_eq!(order.keys[0].field, "name");
1994 assert!(order.keys[0].descending);
1995 assert!(q.limit.is_some());
1996 }
1997 _ => panic!("expected query"),
1998 }
1999 }
2000
2001 #[test]
2002 fn test_parse_insert() {
2003 let stmt = parse(r#"insert User { name := "Alice", age := 30 }"#).unwrap();
2004 match stmt {
2005 Statement::Insert(ins) => {
2006 assert_eq!(ins.target, "User");
2007 assert_eq!(ins.assignments.len(), 2);
2008 assert_eq!(ins.assignments[0].field, "name");
2009 assert_eq!(ins.assignments[1].field, "age");
2010 }
2011 _ => panic!("expected insert"),
2012 }
2013 }
2014
2015 #[test]
2016 fn test_parse_update() {
2017 let stmt = parse(r#"User filter .email = "alice@ex.com" update { age := 31 }"#).unwrap();
2018 match stmt {
2019 Statement::UpdateQuery(upd) => {
2020 assert_eq!(upd.source, "User");
2021 assert!(upd.filter.is_some());
2022 assert_eq!(upd.assignments.len(), 1);
2023 }
2024 _ => panic!("expected update"),
2025 }
2026 }
2027
2028 #[test]
2029 fn test_parse_delete() {
2030 let stmt = parse("User filter .age < 18 delete").unwrap();
2031 match stmt {
2032 Statement::DeleteQuery(del) => {
2033 assert_eq!(del.source, "User");
2034 assert!(del.filter.is_some());
2035 }
2036 _ => panic!("expected delete"),
2037 }
2038 }
2039
2040 #[test]
2041 fn test_parse_count() {
2042 let stmt = parse("count(User)").unwrap();
2043 match stmt {
2044 Statement::Query(q) => {
2045 let agg = q.aggregation.unwrap();
2046 assert_eq!(agg.function, AggFunc::Count);
2047 assert!(q.filter.is_none());
2048 }
2049 _ => panic!("expected query with aggregation"),
2050 }
2051 }
2052
2053 #[test]
2054 fn test_parse_count_with_filter() {
2055 let stmt = parse("count(User filter .age > 30)").unwrap();
2058 match stmt {
2059 Statement::Query(q) => {
2060 assert_eq!(q.source, "User");
2061 let agg = q.aggregation.unwrap();
2062 assert_eq!(agg.function, AggFunc::Count);
2063 assert!(q.filter.is_some(), "filter should have been parsed");
2064 }
2065 _ => panic!("expected query with aggregation"),
2066 }
2067 }
2068
2069 #[test]
2070 fn test_parse_count_with_filter_and_limit() {
2071 let stmt = parse("count(User filter .age > 30 limit 100)").unwrap();
2072 match stmt {
2073 Statement::Query(q) => {
2074 assert_eq!(q.source, "User");
2075 assert!(q.filter.is_some());
2076 assert!(q.limit.is_some());
2077 assert_eq!(q.aggregation.unwrap().function, AggFunc::Count);
2078 }
2079 _ => panic!("expected query with aggregation"),
2080 }
2081 }
2082
2083 #[test]
2084 fn test_parse_create_type() {
2085 let stmt = parse("type User { required name: str, age: int }").unwrap();
2086 match stmt {
2087 Statement::CreateType(ct) => {
2088 assert_eq!(ct.name, "User");
2089 assert_eq!(ct.fields.len(), 2);
2090 assert!(ct.fields[0].required);
2091 assert!(!ct.fields[1].required);
2092 }
2093 _ => panic!("expected create type"),
2094 }
2095 }
2096
2097 #[test]
2098 fn test_parse_sum_with_field_projection() {
2099 let stmt = parse("sum(User filter .age > 30 { .age })").unwrap();
2102 match stmt {
2103 Statement::Query(q) => {
2104 let agg = q.aggregation.expect("aggregate");
2105 assert_eq!(agg.function, AggFunc::Sum);
2106 assert_eq!(agg.field.as_deref(), Some("age"));
2107 assert!(
2108 q.projection.is_none(),
2109 "projection should be lifted into agg.field"
2110 );
2111 }
2112 _ => panic!("expected query"),
2113 }
2114 }
2115
2116 #[test]
2117 fn test_parse_avg_min_max_with_field() {
2118 for (src, expected) in [
2119 ("avg(User { .age })", AggFunc::Avg),
2120 ("min(User { .age })", AggFunc::Min),
2121 ("max(User { .age })", AggFunc::Max),
2122 ] {
2123 let stmt = parse(src).unwrap();
2124 match stmt {
2125 Statement::Query(q) => {
2126 let agg = q.aggregation.unwrap();
2127 assert_eq!(agg.function, expected, "func mismatch for {src}");
2128 assert_eq!(
2129 agg.field.as_deref(),
2130 Some("age"),
2131 "field mismatch for {src}"
2132 );
2133 assert!(
2134 q.projection.is_none(),
2135 "projection should be cleared for {src}"
2136 );
2137 }
2138 _ => panic!("expected query for {src}"),
2139 }
2140 }
2141 }
2142
2143 #[test]
2144 fn test_parse_count_leaves_projection_alone() {
2145 let stmt = parse("count(User { .age })").unwrap();
2148 match stmt {
2149 Statement::Query(q) => {
2150 let agg = q.aggregation.unwrap();
2151 assert_eq!(agg.function, AggFunc::Count);
2152 assert!(agg.field.is_none());
2153 assert!(q.projection.is_some(), "count must not eat projection");
2154 }
2155 _ => panic!("expected query"),
2156 }
2157 }
2158
2159 #[test]
2164 fn test_parse_source_alias() {
2165 let stmt = parse("User as u filter u.age > 30").unwrap();
2166 match stmt {
2167 Statement::Query(q) => {
2168 assert_eq!(q.source, "User");
2169 assert_eq!(q.alias.as_deref(), Some("u"));
2170 assert!(q.joins.is_empty());
2171 match q.filter.unwrap() {
2172 Expr::BinaryOp(l, BinOp::Gt, _) => match *l {
2173 Expr::QualifiedField { qualifier, field } => {
2174 assert_eq!(qualifier, "u");
2175 assert_eq!(field, "age");
2176 }
2177 other => panic!("expected qualified field, got {other:?}"),
2178 },
2179 other => panic!("expected >, got {other:?}"),
2180 }
2181 }
2182 _ => panic!("expected query"),
2183 }
2184 }
2185
2186 #[test]
2187 fn test_parse_inner_join_on() {
2188 let stmt = parse("User as u inner join Order as o on u.id = o.user_id").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_eq!(q.joins.len(), 1);
2194 let j = &q.joins[0];
2195 assert_eq!(j.kind, JoinKind::Inner);
2196 assert_eq!(j.source, "Order");
2197 assert_eq!(j.alias.as_deref(), Some("o"));
2198 let on = j.on.as_ref().expect("on clause");
2199 match on {
2200 Expr::BinaryOp(l, BinOp::Eq, r) => {
2201 assert!(matches!(**l, Expr::QualifiedField { .. }));
2202 assert!(matches!(**r, Expr::QualifiedField { .. }));
2203 }
2204 other => panic!("expected eq, got {other:?}"),
2205 }
2206 }
2207 _ => panic!("expected query"),
2208 }
2209 }
2210
2211 #[test]
2212 fn test_parse_bare_join_defaults_to_inner() {
2213 let stmt = parse("User join Order on User.id = Order.user_id").unwrap();
2214 match stmt {
2215 Statement::Query(q) => {
2216 assert_eq!(q.joins.len(), 1);
2217 assert_eq!(q.joins[0].kind, JoinKind::Inner);
2218 }
2219 _ => panic!("expected query"),
2220 }
2221 }
2222
2223 #[test]
2224 fn test_parse_left_outer_join() {
2225 let stmt = parse("User as u left outer join Order as o on u.id = o.user_id").unwrap();
2226 match stmt {
2227 Statement::Query(q) => {
2228 assert_eq!(q.joins.len(), 1);
2229 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2230 }
2231 _ => panic!("expected query"),
2232 }
2233 }
2234
2235 #[test]
2236 fn test_parse_left_join_without_outer_keyword() {
2237 let stmt = parse("User as u left join Order as o on u.id = o.user_id").unwrap();
2239 match stmt {
2240 Statement::Query(q) => {
2241 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2242 }
2243 _ => panic!("expected query"),
2244 }
2245 }
2246
2247 #[test]
2248 fn test_parse_right_join() {
2249 let stmt = parse("User as u right join Order as o on u.id = o.user_id").unwrap();
2250 match stmt {
2251 Statement::Query(q) => {
2252 assert_eq!(q.joins[0].kind, JoinKind::RightOuter);
2253 }
2254 _ => panic!("expected query"),
2255 }
2256 }
2257
2258 #[test]
2259 fn test_parse_cross_join_has_no_on() {
2260 let stmt = parse("User cross join Order").unwrap();
2261 match stmt {
2262 Statement::Query(q) => {
2263 assert_eq!(q.joins[0].kind, JoinKind::Cross);
2264 assert!(q.joins[0].on.is_none());
2265 }
2266 _ => panic!("expected query"),
2267 }
2268 }
2269
2270 #[test]
2271 fn test_parse_multi_join_chain() {
2272 let stmt = parse(
2273 "User as u join Order as o on u.id = o.user_id \
2274 join Product as p on o.product_id = p.id",
2275 )
2276 .unwrap();
2277 match stmt {
2278 Statement::Query(q) => {
2279 assert_eq!(q.joins.len(), 2);
2280 assert_eq!(q.joins[0].source, "Order");
2281 assert_eq!(q.joins[1].source, "Product");
2282 }
2283 _ => panic!("expected query"),
2284 }
2285 }
2286
2287 #[test]
2288 fn test_parse_join_with_filter_tail() {
2289 let stmt = parse(
2291 "User as u join Order as o on u.id = o.user_id \
2292 filter o.total > 100 order .name limit 10",
2293 )
2294 .unwrap();
2295 match stmt {
2296 Statement::Query(q) => {
2297 assert_eq!(q.joins.len(), 1);
2298 assert!(q.filter.is_some());
2299 assert!(q.order.is_some());
2300 assert!(q.limit.is_some());
2301 }
2302 _ => panic!("expected query"),
2303 }
2304 }
2305
2306 #[test]
2307 fn test_parse_join_requires_on_for_inner() {
2308 let err = parse("User join Order").unwrap_err();
2310 assert!(
2311 err.message().contains("on"),
2312 "expected on-clause error, got {:?}",
2313 err.message()
2314 );
2315 }
2316
2317 #[test]
2318 fn test_parse_update_on_joined_query_errors() {
2319 let err =
2322 parse("User as u join Order as o on u.id = o.user_id update { age := 1 }").unwrap_err();
2323 assert!(err.message().contains("update"));
2324 }
2325
2326 #[test]
2327 fn test_parse_delete_on_joined_query_errors() {
2328 let err = parse("User as u join Order as o on u.id = o.user_id delete").unwrap_err();
2329 assert!(err.message().contains("delete"));
2330 }
2331
2332 #[test]
2335 fn test_parse_distinct() {
2336 let stmt = parse("User distinct { .name }").unwrap();
2337 match stmt {
2338 Statement::Query(q) => {
2339 assert!(q.distinct);
2340 assert!(q.projection.is_some());
2341 }
2342 _ => panic!("expected query"),
2343 }
2344 }
2345
2346 #[test]
2347 fn test_parse_in_list() {
2348 let stmt = parse(r#"User filter .name in ("Alice", "Bob")"#).unwrap();
2349 match stmt {
2350 Statement::Query(q) => match q.filter.unwrap() {
2351 Expr::InList {
2352 expr,
2353 list,
2354 negated,
2355 } => {
2356 assert!(!negated);
2357 assert!(matches!(*expr, Expr::Field(f) if f == "name"));
2358 assert_eq!(list.len(), 2);
2359 }
2360 other => panic!("expected InList, got {other:?}"),
2361 },
2362 _ => panic!("expected query"),
2363 }
2364 }
2365
2366 #[test]
2367 fn test_parse_not_in_list() {
2368 let stmt = parse("User filter .age not in (1, 2, 3)").unwrap();
2369 match stmt {
2370 Statement::Query(q) => match q.filter.unwrap() {
2371 Expr::InList { negated, list, .. } => {
2372 assert!(negated);
2373 assert_eq!(list.len(), 3);
2374 }
2375 other => panic!("expected InList, got {other:?}"),
2376 },
2377 _ => panic!("expected query"),
2378 }
2379 }
2380
2381 #[test]
2382 fn test_parse_between() {
2383 let stmt = parse("User filter .age between 10 and 20").unwrap();
2385 match stmt {
2386 Statement::Query(q) => {
2387 match q.filter.unwrap() {
2388 Expr::BinaryOp(_, BinOp::And, _) => {} other => panic!("expected And (desugared between), got {other:?}"),
2390 }
2391 }
2392 _ => panic!("expected query"),
2393 }
2394 }
2395
2396 #[test]
2397 fn test_parse_not_between() {
2398 let stmt = parse("User filter .age not between 10 and 20").unwrap();
2400 match stmt {
2401 Statement::Query(q) => {
2402 match q.filter.unwrap() {
2403 Expr::BinaryOp(_, BinOp::Or, _) => {} other => panic!("expected Or (desugared not between), got {other:?}"),
2405 }
2406 }
2407 _ => panic!("expected query"),
2408 }
2409 }
2410
2411 #[test]
2412 fn test_parse_like() {
2413 let stmt = parse(r#"User filter .name like "A%""#).unwrap();
2414 match stmt {
2415 Statement::Query(q) => match q.filter.unwrap() {
2416 Expr::BinaryOp(l, BinOp::Like, r) => {
2417 assert!(matches!(*l, Expr::Field(f) if f == "name"));
2418 assert!(matches!(*r, Expr::Literal(Literal::String(s)) if s == "A%"));
2419 }
2420 other => panic!("expected Like, got {other:?}"),
2421 },
2422 _ => panic!("expected query"),
2423 }
2424 }
2425
2426 #[test]
2427 fn test_parse_not_like() {
2428 let stmt = parse(r#"User filter .name not like "A%""#).unwrap();
2429 match stmt {
2430 Statement::Query(q) => match q.filter.unwrap() {
2431 Expr::UnaryOp(UnaryOp::Not, inner) => {
2432 assert!(matches!(*inner, Expr::BinaryOp(_, BinOp::Like, _)));
2433 }
2434 other => panic!("expected Not(Like), got {other:?}"),
2435 },
2436 _ => panic!("expected query"),
2437 }
2438 }
2439
2440 #[test]
2443 fn test_parse_group_by_single_key() {
2444 let stmt = parse("User group .status { .status, n: count(.name) }").unwrap();
2445 match stmt {
2446 Statement::Query(q) => {
2447 let gb = q.group_by.unwrap();
2448 assert_eq!(gb.keys, vec!["status"]);
2449 assert!(gb.having.is_none());
2450 let proj = q.projection.unwrap();
2451 assert_eq!(proj.len(), 2);
2452 assert!(matches!(
2453 &proj[1].expr,
2454 Expr::FunctionCall(AggFunc::Count, _)
2455 ));
2456 assert_eq!(proj[1].alias.as_deref(), Some("n"));
2457 }
2458 _ => panic!("expected query"),
2459 }
2460 }
2461
2462 #[test]
2463 fn test_parse_group_by_multi_key() {
2464 let stmt = parse("User group .status, .age { .status, .age }").unwrap();
2465 match stmt {
2466 Statement::Query(q) => {
2467 let gb = q.group_by.unwrap();
2468 assert_eq!(gb.keys, vec!["status", "age"]);
2469 }
2470 _ => panic!("expected query"),
2471 }
2472 }
2473
2474 #[test]
2475 fn test_parse_group_by_having() {
2476 let stmt = parse("User group .status having count(.name) > 1 { .status }").unwrap();
2477 match stmt {
2478 Statement::Query(q) => {
2479 let gb = q.group_by.unwrap();
2480 assert_eq!(gb.keys, vec!["status"]);
2481 assert!(gb.having.is_some());
2482 match gb.having.unwrap() {
2484 Expr::BinaryOp(l, BinOp::Gt, _) => {
2485 assert!(matches!(*l, Expr::FunctionCall(AggFunc::Count, _)));
2486 }
2487 other => panic!("expected BinaryOp, got {other:?}"),
2488 }
2489 }
2490 _ => panic!("expected query"),
2491 }
2492 }
2493
2494 #[test]
2495 fn test_parse_aggregate_in_projection() {
2496 let stmt = parse("User group .status { .status, count(.name), sum(.age) }").unwrap();
2498 match stmt {
2499 Statement::Query(q) => {
2500 let proj = q.projection.unwrap();
2501 assert_eq!(proj.len(), 3);
2502 assert!(matches!(
2503 &proj[1].expr,
2504 Expr::FunctionCall(AggFunc::Count, _)
2505 ));
2506 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Sum, _)));
2507 }
2508 _ => panic!("expected query"),
2509 }
2510 }
2511
2512 #[test]
2513 fn test_parse_aggregate_in_aliased_projection() {
2514 let stmt = parse("User group .status { .status, total: count(.name), average: avg(.age) }")
2515 .unwrap();
2516 match stmt {
2517 Statement::Query(q) => {
2518 let proj = q.projection.unwrap();
2519 assert_eq!(proj[1].alias.as_deref(), Some("total"));
2520 assert!(matches!(
2521 &proj[1].expr,
2522 Expr::FunctionCall(AggFunc::Count, _)
2523 ));
2524 assert_eq!(proj[2].alias.as_deref(), Some("average"));
2525 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Avg, _)));
2526 }
2527 _ => panic!("expected query"),
2528 }
2529 }
2530
2531 #[test]
2534 fn test_parse_is_null() {
2535 let stmt = parse("User filter .age is null").unwrap();
2536 match stmt {
2537 Statement::Query(q) => {
2538 let filter = q.filter.unwrap();
2539 assert_eq!(
2540 filter,
2541 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2542 );
2543 }
2544 _ => panic!("expected query"),
2545 }
2546 }
2547
2548 #[test]
2549 fn test_parse_is_not_null() {
2550 let stmt = parse("User filter .age is not null").unwrap();
2551 match stmt {
2552 Statement::Query(q) => {
2553 let filter = q.filter.unwrap();
2554 assert_eq!(
2555 filter,
2556 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2557 );
2558 }
2559 _ => panic!("expected query"),
2560 }
2561 }
2562
2563 #[test]
2564 fn test_parse_eq_null_desugars_to_is_null() {
2565 let stmt = parse("User filter .age = null").unwrap();
2566 match stmt {
2567 Statement::Query(q) => {
2568 let filter = q.filter.unwrap();
2569 assert_eq!(
2570 filter,
2571 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2572 );
2573 }
2574 _ => panic!("expected query"),
2575 }
2576 }
2577
2578 #[test]
2579 fn test_parse_neq_null_desugars_to_is_not_null() {
2580 let stmt = parse("User filter .age != null").unwrap();
2581 match stmt {
2582 Statement::Query(q) => {
2583 let filter = q.filter.unwrap();
2584 assert_eq!(
2585 filter,
2586 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2587 );
2588 }
2589 _ => panic!("expected query"),
2590 }
2591 }
2592
2593 #[test]
2594 fn test_parse_null_comparisons_parse_ok() {
2595 assert!(parse("User filter .age < null").is_ok());
2599 assert!(parse("User filter .age >= null").is_ok());
2600 }
2601
2602 #[test]
2603 fn test_parse_count_star_expr() {
2604 let stmt = parse("User filter count(*) > 0").unwrap();
2605 match stmt {
2606 Statement::Query(q) => {
2607 let filter = q.filter.unwrap();
2608 match filter {
2609 Expr::BinaryOp(left, BinOp::Gt, _) => {
2610 assert_eq!(
2611 *left,
2612 Expr::FunctionCall(AggFunc::Count, Box::new(Expr::Field("*".into())))
2613 );
2614 }
2615 _ => panic!("expected comparison"),
2616 }
2617 }
2618 _ => panic!("expected query"),
2619 }
2620 }
2621
2622 #[test]
2625 fn test_parse_upper_in_filter() {
2626 let stmt = parse(r#"User filter upper(.name) = "ALICE""#).unwrap();
2627 match stmt {
2628 Statement::Query(q) => {
2629 let f = q.filter.unwrap();
2630 match f {
2631 Expr::BinaryOp(left, BinOp::Eq, _right) => {
2632 assert!(matches!(*left, Expr::ScalarFunc(ScalarFn::Upper, _)));
2633 }
2634 _ => panic!("expected binary op with upper"),
2635 }
2636 }
2637 _ => panic!("expected query"),
2638 }
2639 }
2640
2641 #[test]
2642 fn test_parse_substring() {
2643 let stmt = parse("User { sub: substring(.name, 1, 3) }").unwrap();
2644 match stmt {
2645 Statement::Query(q) => {
2646 let proj = q.projection.unwrap();
2647 match &proj[0].expr {
2648 Expr::ScalarFunc(ScalarFn::Substring, args) => {
2649 assert_eq!(args.len(), 3);
2650 }
2651 other => panic!("expected ScalarFunc Substring, got {other:?}"),
2652 }
2653 }
2654 _ => panic!("expected query"),
2655 }
2656 }
2657
2658 #[test]
2659 fn test_parse_concat() {
2660 let stmt = parse(r#"User { full: concat(.name, " - ", .email) }"#).unwrap();
2661 match stmt {
2662 Statement::Query(q) => {
2663 let proj = q.projection.unwrap();
2664 match &proj[0].expr {
2665 Expr::ScalarFunc(ScalarFn::Concat, args) => {
2666 assert_eq!(args.len(), 3);
2667 }
2668 other => panic!("expected ScalarFunc Concat, got {other:?}"),
2669 }
2670 }
2671 _ => panic!("expected query"),
2672 }
2673 }
2674
2675 #[test]
2678 fn test_parse_case_single_when() {
2679 let stmt = parse(r#"User filter case when .age > 30 then true else false end"#).unwrap();
2680 match stmt {
2681 Statement::Query(q) => {
2682 let filter = q.filter.unwrap();
2683 match filter {
2684 Expr::Case { whens, else_expr } => {
2685 assert_eq!(whens.len(), 1);
2686 assert!(else_expr.is_some());
2687 }
2688 other => panic!("expected Case expr, got {other:?}"),
2689 }
2690 }
2691 _ => panic!("expected query"),
2692 }
2693 }
2694
2695 #[test]
2696 fn test_parse_case_multiple_whens() {
2697 let stmt = parse(
2698 r#"User { label: case when .age > 30 then "senior" when .age > 20 then "adult" else "young" end }"#
2699 ).unwrap();
2700 match stmt {
2701 Statement::Query(q) => {
2702 let proj = q.projection.unwrap();
2703 match &proj[0].expr {
2704 Expr::Case { whens, else_expr } => {
2705 assert_eq!(whens.len(), 2);
2706 assert!(else_expr.is_some());
2707 }
2708 other => panic!("expected Case expr, got {other:?}"),
2709 }
2710 }
2711 _ => panic!("expected query"),
2712 }
2713 }
2714
2715 #[test]
2716 fn test_parse_case_without_else() {
2717 let stmt = parse(r#"User filter case when .age > 30 then true end"#).unwrap();
2718 match stmt {
2719 Statement::Query(q) => {
2720 let filter = q.filter.unwrap();
2721 match filter {
2722 Expr::Case { whens, else_expr } => {
2723 assert_eq!(whens.len(), 1);
2724 assert!(else_expr.is_none());
2725 }
2726 other => panic!("expected Case expr, got {other:?}"),
2727 }
2728 }
2729 _ => panic!("expected query"),
2730 }
2731 }
2732
2733 #[test]
2736 fn test_parse_mul_expr() {
2737 let stmt = parse("User filter .price * .quantity > 100").unwrap();
2738 match stmt {
2739 Statement::Query(q) => {
2740 let filter = q.filter.unwrap();
2741 match filter {
2742 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2743 Expr::BinaryOp(_, BinOp::Mul, _) => {}
2744 other => panic!("expected Mul, got {other:?}"),
2745 },
2746 other => panic!("expected BinaryOp Gt, got {other:?}"),
2747 }
2748 }
2749 _ => panic!("expected query"),
2750 }
2751 }
2752
2753 #[test]
2754 fn test_parse_div_expr() {
2755 let stmt = parse("User { ratio: .total / .count }").unwrap();
2756 match stmt {
2757 Statement::Query(q) => {
2758 let proj = q.projection.unwrap();
2759 assert_eq!(proj[0].alias.as_deref(), Some("ratio"));
2760 match &proj[0].expr {
2761 Expr::BinaryOp(_, BinOp::Div, _) => {}
2762 other => panic!("expected Div, got {other:?}"),
2763 }
2764 }
2765 _ => panic!("expected query"),
2766 }
2767 }
2768
2769 #[test]
2770 fn test_parse_mul_div_precedence() {
2771 let stmt = parse("User filter .a + .b * .c > 0").unwrap();
2773 match stmt {
2774 Statement::Query(q) => {
2775 let filter = q.filter.unwrap();
2776 match filter {
2777 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2778 Expr::BinaryOp(_, BinOp::Add, right) => {
2779 assert!(matches!(*right, Expr::BinaryOp(_, BinOp::Mul, _)));
2780 }
2781 other => panic!("expected Add, got {other:?}"),
2782 },
2783 other => panic!("expected Gt, got {other:?}"),
2784 }
2785 }
2786 _ => panic!("expected query"),
2787 }
2788 }
2789
2790 #[test]
2793 fn test_parse_multi_order() {
2794 let stmt = parse("User order .name asc, .age desc").unwrap();
2795 match stmt {
2796 Statement::Query(q) => {
2797 let order = q.order.unwrap();
2798 assert_eq!(order.keys.len(), 2);
2799 assert_eq!(order.keys[0].field, "name");
2800 assert!(!order.keys[0].descending);
2801 assert_eq!(order.keys[1].field, "age");
2802 assert!(order.keys[1].descending);
2803 }
2804 _ => panic!("expected query"),
2805 }
2806 }
2807
2808 #[test]
2809 fn test_parse_order_default_asc() {
2810 let stmt = parse("User order .name").unwrap();
2811 match stmt {
2812 Statement::Query(q) => {
2813 let order = q.order.unwrap();
2814 assert_eq!(order.keys.len(), 1);
2815 assert!(!order.keys[0].descending);
2816 }
2817 _ => panic!("expected query"),
2818 }
2819 }
2820
2821 #[test]
2824 fn test_parse_alter_add_column() {
2825 let stmt = parse("alter User add column status: str").unwrap();
2826 match stmt {
2827 Statement::AlterTable(at) => {
2828 assert_eq!(at.table, "User");
2829 match at.action {
2830 AlterAction::AddColumn {
2831 name,
2832 type_name,
2833 required,
2834 } => {
2835 assert_eq!(name, "status");
2836 assert_eq!(type_name, "str");
2837 assert!(!required);
2838 }
2839 other => panic!("expected AddColumn, got {other:?}"),
2840 }
2841 }
2842 other => panic!("expected AlterTable, got {other:?}"),
2843 }
2844 }
2845
2846 #[test]
2847 fn test_parse_alter_add_required_column() {
2848 let stmt = parse("alter User add required status: str").unwrap();
2849 match stmt {
2850 Statement::AlterTable(at) => match at.action {
2851 AlterAction::AddColumn { required, .. } => assert!(required),
2852 other => panic!("expected AddColumn, got {other:?}"),
2853 },
2854 other => panic!("expected AlterTable, got {other:?}"),
2855 }
2856 }
2857
2858 #[test]
2859 fn test_parse_alter_drop_column() {
2860 let stmt = parse("alter User drop column status").unwrap();
2861 match stmt {
2862 Statement::AlterTable(at) => {
2863 assert_eq!(at.table, "User");
2864 match at.action {
2865 AlterAction::DropColumn { name } => assert_eq!(name, "status"),
2866 other => panic!("expected DropColumn, got {other:?}"),
2867 }
2868 }
2869 other => panic!("expected AlterTable, got {other:?}"),
2870 }
2871 }
2872
2873 #[test]
2874 fn test_parse_alter_drop_without_column_keyword() {
2875 let stmt = parse("alter User drop status").unwrap();
2876 match stmt {
2877 Statement::AlterTable(at) => match at.action {
2878 AlterAction::DropColumn { name } => assert_eq!(name, "status"),
2879 other => panic!("expected DropColumn, got {other:?}"),
2880 },
2881 other => panic!("expected AlterTable, got {other:?}"),
2882 }
2883 }
2884
2885 #[test]
2886 fn test_parse_drop_table() {
2887 let stmt = parse("drop User").unwrap();
2888 match stmt {
2889 Statement::DropTable(dt) => assert_eq!(dt.table, "User"),
2890 other => panic!("expected DropTable, got {other:?}"),
2891 }
2892 }
2893
2894 #[test]
2897 fn test_parse_in_subquery() {
2898 let stmt = parse("User filter .name in (VIP { .name })").unwrap();
2899 match stmt {
2900 Statement::Query(q) => {
2901 let filter = q.filter.unwrap();
2902 match filter {
2903 Expr::InSubquery {
2904 expr,
2905 subquery,
2906 negated,
2907 } => {
2908 assert!(!negated);
2909 assert!(matches!(*expr, Expr::Field(ref f) if f == "name"));
2910 assert_eq!(subquery.source, "VIP");
2911 }
2912 other => panic!("expected InSubquery, got {other:?}"),
2913 }
2914 }
2915 _ => panic!("expected query"),
2916 }
2917 }
2918
2919 #[test]
2920 fn test_parse_not_in_subquery() {
2921 let stmt = parse("User filter .id not in (Order { .user_id })").unwrap();
2922 match stmt {
2923 Statement::Query(q) => match q.filter.unwrap() {
2924 Expr::InSubquery { negated, .. } => assert!(negated),
2925 other => panic!("expected InSubquery, got {other:?}"),
2926 },
2927 _ => panic!("expected query"),
2928 }
2929 }
2930
2931 #[test]
2932 fn test_parse_in_literal_list_still_works() {
2933 let stmt = parse("User filter .age in (25, 30, 35)").unwrap();
2935 match stmt {
2936 Statement::Query(q) => match q.filter.unwrap() {
2937 Expr::InList { list, negated, .. } => {
2938 assert!(!negated);
2939 assert_eq!(list.len(), 3);
2940 }
2941 other => panic!("expected InList, got {other:?}"),
2942 },
2943 _ => panic!("expected query"),
2944 }
2945 }
2946
2947 #[test]
2950 fn test_parse_create_view() {
2951 let stmt = parse("materialize OldUsers as User filter .age > 28").unwrap();
2952 match stmt {
2953 Statement::CreateView(cv) => {
2954 assert_eq!(cv.name, "OldUsers");
2955 assert_eq!(cv.query.source, "User");
2956 assert!(cv.query.filter.is_some());
2957 assert!(!cv.query_text.is_empty());
2958 }
2959 _ => panic!("expected CreateView"),
2960 }
2961 }
2962
2963 #[test]
2964 fn test_parse_create_view_with_projection() {
2965 let stmt = parse("materialize UserNames as User { .name }").unwrap();
2966 match stmt {
2967 Statement::CreateView(cv) => {
2968 assert_eq!(cv.name, "UserNames");
2969 assert!(cv.query.projection.is_some());
2970 }
2971 _ => panic!("expected CreateView"),
2972 }
2973 }
2974
2975 #[test]
2976 fn test_parse_refresh_view() {
2977 let stmt = parse("refresh OldUsers").unwrap();
2978 match stmt {
2979 Statement::RefreshView(rv) => {
2980 assert_eq!(rv.name, "OldUsers");
2981 }
2982 _ => panic!("expected RefreshView"),
2983 }
2984 }
2985
2986 #[test]
2987 fn test_parse_drop_view() {
2988 let stmt = parse("drop view OldUsers").unwrap();
2989 match stmt {
2990 Statement::DropView(dv) => {
2991 assert_eq!(dv.name, "OldUsers");
2992 }
2993 _ => panic!("expected DropView"),
2994 }
2995 }
2996
2997 #[test]
2998 fn test_parse_drop_table_still_works() {
2999 let stmt = parse("drop Users").unwrap();
3000 match stmt {
3001 Statement::DropTable(dt) => {
3002 assert_eq!(dt.table, "Users");
3003 }
3004 _ => panic!("expected DropTable"),
3005 }
3006 }
3007
3008 #[test]
3009 fn test_parse_union() {
3010 let stmt = parse("User union Order").unwrap();
3011 match stmt {
3012 Statement::Union(u) => {
3013 assert!(!u.all);
3014 match *u.left {
3015 Statement::Query(_) => {}
3016 _ => panic!("expected Query on left"),
3017 }
3018 match *u.right {
3019 Statement::Query(_) => {}
3020 _ => panic!("expected Query on right"),
3021 }
3022 }
3023 _ => panic!("expected Union"),
3024 }
3025 }
3026
3027 #[test]
3028 fn test_parse_union_all() {
3029 let stmt = parse("User union all Order").unwrap();
3030 match stmt {
3031 Statement::Union(u) => {
3032 assert!(u.all, "expected UNION ALL");
3033 match *u.left {
3034 Statement::Query(_) => {}
3035 _ => panic!("expected Query on left"),
3036 }
3037 match *u.right {
3038 Statement::Query(_) => {}
3039 _ => panic!("expected Query on right"),
3040 }
3041 }
3042 _ => panic!("expected Union"),
3043 }
3044 }
3045
3046 #[test]
3047 fn test_parse_union_chain() {
3048 let stmt = parse("User union Order union Product").unwrap();
3050 match stmt {
3051 Statement::Union(outer) => {
3052 assert!(!outer.all);
3053 match *outer.right {
3055 Statement::Query(q) => assert_eq!(q.source, "Product"),
3056 _ => panic!("expected Query(Product) on right"),
3057 }
3058 match *outer.left {
3060 Statement::Union(inner) => {
3061 assert!(!inner.all);
3062 match *inner.left {
3063 Statement::Query(q) => assert_eq!(q.source, "User"),
3064 _ => panic!("expected Query(User)"),
3065 }
3066 match *inner.right {
3067 Statement::Query(q) => assert_eq!(q.source, "Order"),
3068 _ => panic!("expected Query(Order)"),
3069 }
3070 }
3071 _ => panic!("expected inner Union"),
3072 }
3073 }
3074 _ => panic!("expected Union"),
3075 }
3076 }
3077
3078 #[test]
3079 fn test_parse_union_with_filter() {
3080 let stmt = parse("User filter .age > 10 union Order filter .total > 50").unwrap();
3081 match stmt {
3082 Statement::Union(u) => {
3083 assert!(!u.all);
3084 match *u.left {
3086 Statement::Query(q) => {
3087 assert_eq!(q.source, "User");
3088 assert!(q.filter.is_some());
3089 }
3090 _ => panic!("expected Query on left"),
3091 }
3092 match *u.right {
3093 Statement::Query(q) => {
3094 assert_eq!(q.source, "Order");
3095 assert!(q.filter.is_some());
3096 }
3097 _ => panic!("expected Query on right"),
3098 }
3099 }
3100 _ => panic!("expected Union"),
3101 }
3102 }
3103
3104 #[test]
3105 fn test_parse_count_distinct_standalone() {
3106 let stmt = parse("count(distinct User { .name })").unwrap();
3107 match stmt {
3108 Statement::Query(q) => {
3109 let agg = q.aggregation.unwrap();
3110 assert_eq!(agg.function, AggFunc::CountDistinct);
3111 assert_eq!(agg.field.as_deref(), Some("name"));
3112 }
3113 _ => panic!("expected Query"),
3114 }
3115 }
3116
3117 #[test]
3118 fn test_parse_count_distinct_in_projection() {
3119 let stmt = parse("User group .dept { .dept, count(distinct .name) }").unwrap();
3120 match stmt {
3121 Statement::Query(q) => {
3122 let proj = q.projection.unwrap();
3123 assert_eq!(proj.len(), 2);
3124 match &proj[1].expr {
3125 Expr::FunctionCall(func, _) => {
3126 assert_eq!(*func, AggFunc::CountDistinct);
3127 }
3128 _ => panic!("expected FunctionCall"),
3129 }
3130 }
3131 _ => panic!("expected Query"),
3132 }
3133 }
3134
3135 #[test]
3138 fn test_parse_window_row_number_order() {
3139 let stmt = parse("User { .name, rn: row_number() over (order .age) }").unwrap();
3140 match stmt {
3141 Statement::Query(q) => {
3142 let proj = q.projection.unwrap();
3143 assert_eq!(proj.len(), 2);
3144 assert_eq!(proj[1].alias.as_deref(), Some("rn"));
3145 match &proj[1].expr {
3146 Expr::Window {
3147 function,
3148 args,
3149 partition_by,
3150 order_by,
3151 } => {
3152 assert_eq!(*function, WindowFunc::RowNumber);
3153 assert!(args.is_empty());
3154 assert!(partition_by.is_empty());
3155 assert_eq!(order_by.len(), 1);
3156 assert_eq!(order_by[0].field, "age");
3157 assert!(!order_by[0].descending);
3158 }
3159 other => panic!("expected Window, got {other:?}"),
3160 }
3161 }
3162 _ => panic!("expected query"),
3163 }
3164 }
3165
3166 #[test]
3167 fn test_parse_window_sum_partition_order() {
3168 let stmt =
3169 parse("User { .name, s: sum(.salary) over (partition .dept order .salary) }").unwrap();
3170 match stmt {
3171 Statement::Query(q) => {
3172 let proj = q.projection.unwrap();
3173 assert_eq!(proj.len(), 2);
3174 assert_eq!(proj[1].alias.as_deref(), Some("s"));
3175 match &proj[1].expr {
3176 Expr::Window {
3177 function,
3178 args,
3179 partition_by,
3180 order_by,
3181 } => {
3182 assert_eq!(*function, WindowFunc::Sum);
3183 assert_eq!(args.len(), 1);
3184 assert!(matches!(&args[0], Expr::Field(f) if f == "salary"));
3185 assert_eq!(partition_by, &["dept"]);
3186 assert_eq!(order_by.len(), 1);
3187 assert_eq!(order_by[0].field, "salary");
3188 assert!(!order_by[0].descending);
3189 }
3190 other => panic!("expected Window, got {other:?}"),
3191 }
3192 }
3193 _ => panic!("expected query"),
3194 }
3195 }
3196
3197 #[test]
3198 fn test_parse_window_rank_desc() {
3199 let stmt =
3200 parse("User { .dept, .salary, r: rank() over (partition .dept order .salary desc) }")
3201 .unwrap();
3202 match stmt {
3203 Statement::Query(q) => {
3204 let proj = q.projection.unwrap();
3205 assert_eq!(proj.len(), 3);
3206 match &proj[2].expr {
3207 Expr::Window {
3208 function,
3209 partition_by,
3210 order_by,
3211 ..
3212 } => {
3213 assert_eq!(*function, WindowFunc::Rank);
3214 assert_eq!(partition_by, &["dept"]);
3215 assert_eq!(order_by.len(), 1);
3216 assert!(order_by[0].descending);
3217 }
3218 other => panic!("expected Window, got {other:?}"),
3219 }
3220 }
3221 _ => panic!("expected query"),
3222 }
3223 }
3224
3225 #[test]
3226 fn test_parse_window_dense_rank() {
3227 let stmt = parse("User { .name, dr: dense_rank() over (order .score desc) }").unwrap();
3228 match stmt {
3229 Statement::Query(q) => {
3230 let proj = q.projection.unwrap();
3231 assert_eq!(proj.len(), 2);
3232 match &proj[1].expr {
3233 Expr::Window { function, .. } => {
3234 assert_eq!(*function, WindowFunc::DenseRank);
3235 }
3236 other => panic!("expected Window, got {other:?}"),
3237 }
3238 }
3239 _ => panic!("expected query"),
3240 }
3241 }
3242
3243 #[test]
3244 fn test_parse_sum_without_over_is_aggregate() {
3245 let stmt = parse("User group .dept { .dept, total: sum(.salary) }").unwrap();
3247 match stmt {
3248 Statement::Query(q) => {
3249 let proj = q.projection.unwrap();
3250 assert_eq!(proj.len(), 2);
3251 match &proj[1].expr {
3252 Expr::FunctionCall(AggFunc::Sum, _) => {} other => panic!("expected FunctionCall(Sum), got {other:?}"),
3254 }
3255 }
3256 _ => panic!("expected query"),
3257 }
3258 }
3259
3260 #[test]
3261 fn test_nesting_depth_limit() {
3262 let mut query = String::from("User filter ");
3264 for _ in 0..70 {
3265 query.push('(');
3266 }
3267 query.push_str(".age > 1");
3268 for _ in 0..70 {
3269 query.push(')');
3270 }
3271 let result = parse(&query);
3272 assert!(result.is_err());
3273 let err = result.unwrap_err();
3274 assert!(
3275 err.message().contains("nesting depth"),
3276 "expected nesting depth error, got: {}",
3277 err.message()
3278 );
3279 }
3280
3281 #[test]
3282 fn test_moderate_nesting_succeeds() {
3283 let mut query = String::from("User filter ");
3285 for _ in 0..10 {
3286 query.push('(');
3287 }
3288 query.push_str(".age > 1");
3289 for _ in 0..10 {
3290 query.push(')');
3291 }
3292 assert!(parse(&query).is_ok());
3293 }
3294
3295 #[test]
3299 fn test_parse_fuzz_repro_projection_eof() {
3300 let err = parse("nn{").expect_err("unterminated projection must error, not panic");
3301 let _ = err.message();
3302 }
3303
3304 #[test]
3307 fn test_parse_fuzz_repro_short_projection_eof() {
3308 let err = parse("z{").expect_err("unterminated projection must error, not panic");
3309 let _ = err.message();
3310 }
3311
3312 #[test]
3313 fn test_update_at_statement_start_gives_helpful_error() {
3314 let err =
3315 parse(r#"update User filter .name = "Alice" { age := 31 }"#).expect_err("should fail");
3316 let msg = err.message();
3317 assert!(
3318 msg.contains("pipeline syntax"),
3319 "error should mention pipeline syntax, got: {msg}"
3320 );
3321 assert!(
3322 msg.contains("update"),
3323 "error should mention 'update', got: {msg}"
3324 );
3325 }
3326
3327 #[test]
3328 fn test_delete_at_statement_start_gives_helpful_error() {
3329 let err = parse("delete User filter .age < 18").expect_err("should fail");
3330 let msg = err.message();
3331 assert!(
3332 msg.contains("pipeline syntax"),
3333 "error should mention pipeline syntax, got: {msg}"
3334 );
3335 assert!(
3336 msg.contains("delete"),
3337 "error should mention 'delete', got: {msg}"
3338 );
3339 }
3340}