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