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 self.depth += 1;
1227 if self.depth > MAX_NESTING_DEPTH {
1228 self.depth -= 1;
1229 return Err(ParseError::NestingDepthExceeded {
1230 max: MAX_NESTING_DEPTH,
1231 });
1232 }
1233 let result = self.parse_primary_inner();
1234 self.depth -= 1;
1235 result
1236 }
1237
1238 fn parse_primary_inner(&mut self) -> Result<Expr, ParseError> {
1239 match self.peek().clone() {
1240 Token::DotIdent(name) => {
1241 self.advance();
1242 Ok(Expr::Field(name))
1243 }
1244 Token::IntLit(v) => {
1245 self.advance();
1246 Ok(Expr::Literal(Literal::Int(v)))
1247 }
1248 Token::FloatLit(v) => {
1249 self.advance();
1250 Ok(Expr::Literal(Literal::Float(v)))
1251 }
1252 Token::StringLit(v) => {
1253 self.advance();
1254 Ok(Expr::Literal(Literal::String(v)))
1255 }
1256 Token::BoolLit(v) => {
1257 self.advance();
1258 Ok(Expr::Literal(Literal::Bool(v)))
1259 }
1260 Token::Null => {
1267 self.advance();
1268 Ok(Expr::Null)
1269 }
1270 Token::Not => {
1271 self.advance();
1272 if *self.peek() == Token::Exists {
1273 self.advance();
1274 if let Some(sub) = self.try_parse_exists_subquery()? {
1278 return Ok(Expr::ExistsSubquery {
1279 subquery: Box::new(sub),
1280 negated: true,
1281 });
1282 }
1283 let expr = self.parse_primary()?;
1284 Ok(Expr::UnaryOp(UnaryOp::NotExists, Box::new(expr)))
1285 } else {
1286 let expr = self.parse_primary()?;
1287 Ok(Expr::UnaryOp(UnaryOp::Not, Box::new(expr)))
1288 }
1289 }
1290 Token::Exists => {
1291 self.advance();
1292 if let Some(sub) = self.try_parse_exists_subquery()? {
1296 return Ok(Expr::ExistsSubquery {
1297 subquery: Box::new(sub),
1298 negated: false,
1299 });
1300 }
1301 let expr = self.parse_primary()?;
1302 Ok(Expr::UnaryOp(UnaryOp::Exists, Box::new(expr)))
1303 }
1304 Token::LParen => {
1305 self.advance();
1306 let expr = self.parse_expr()?;
1307 self.expect(&Token::RParen)?;
1308 Ok(expr)
1309 }
1310 Token::Ident(name) => {
1311 self.advance();
1312 if let Token::DotIdent(field) = self.peek().clone() {
1316 self.advance();
1317 return Ok(Expr::QualifiedField {
1318 qualifier: name,
1319 field,
1320 });
1321 }
1322 Ok(Expr::Field(name))
1323 }
1324 Token::RowNumber | Token::Rank | Token::DenseRank => {
1326 let wfunc = match self.advance() {
1327 Token::RowNumber => WindowFunc::RowNumber,
1328 Token::Rank => WindowFunc::Rank,
1329 Token::DenseRank => WindowFunc::DenseRank,
1330 _ => {
1331 return Err(ParseError::Syntax {
1332 message: "unexpected window function token".into(),
1333 })
1334 }
1335 };
1336 self.expect(&Token::LParen)?;
1337 self.expect(&Token::RParen)?;
1338 let (partition_by, order_by) = self.parse_over_clause()?;
1339 Ok(Expr::Window {
1340 function: wfunc,
1341 args: vec![],
1342 partition_by,
1343 order_by,
1344 })
1345 }
1346 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1350 let mut func = match self.advance() {
1351 Token::Count => AggFunc::Count,
1352 Token::Avg => AggFunc::Avg,
1353 Token::Sum => AggFunc::Sum,
1354 Token::Min => AggFunc::Min,
1355 Token::Max => AggFunc::Max,
1356 _ => {
1357 return Err(ParseError::Syntax {
1358 message: "unexpected aggregate token".into(),
1359 })
1360 }
1361 };
1362 self.expect(&Token::LParen)?;
1363 if func == AggFunc::Count && *self.peek() == Token::Star {
1365 self.advance();
1366 self.expect(&Token::RParen)?;
1367 if *self.peek() == Token::Over {
1369 let (partition_by, order_by) = self.parse_over_clause()?;
1370 return Ok(Expr::Window {
1371 function: WindowFunc::Count,
1372 args: vec![Expr::Field("*".into())],
1373 partition_by,
1374 order_by,
1375 });
1376 }
1377 return Ok(Expr::FunctionCall(
1378 AggFunc::Count,
1379 Box::new(Expr::Field("*".into())),
1380 ));
1381 }
1382 if func == AggFunc::Count && *self.peek() == Token::Distinct {
1384 self.advance();
1385 func = AggFunc::CountDistinct;
1386 }
1387 let inner = self.parse_expr()?;
1388 self.expect(&Token::RParen)?;
1389 if *self.peek() == Token::Over {
1391 let wfunc = match func {
1392 AggFunc::Count => WindowFunc::Count,
1393 AggFunc::Avg => WindowFunc::Avg,
1394 AggFunc::Sum => WindowFunc::Sum,
1395 AggFunc::Min => WindowFunc::Min,
1396 AggFunc::Max => WindowFunc::Max,
1397 _ => {
1398 return Err(ParseError::Unsupported {
1399 feature: "count(distinct ...) over (...) is not supported".into(),
1400 })
1401 }
1402 };
1403 let (partition_by, order_by) = self.parse_over_clause()?;
1404 return Ok(Expr::Window {
1405 function: wfunc,
1406 args: vec![inner],
1407 partition_by,
1408 order_by,
1409 });
1410 }
1411 Ok(Expr::FunctionCall(func, Box::new(inner)))
1412 }
1413 Token::Upper
1414 | Token::Lower
1415 | Token::Length
1416 | Token::Trim
1417 | Token::Substring
1418 | Token::Concat
1419 | Token::Abs
1420 | Token::Round
1421 | Token::Ceil
1422 | Token::Floor
1423 | Token::Sqrt
1424 | Token::Pow
1425 | Token::Now
1426 | Token::Extract
1427 | Token::DateAdd
1428 | Token::DateDiff => {
1429 let tok = self.advance();
1430 let func = token_to_scalar_fn(&tok);
1431 self.expect(&Token::LParen)?;
1432 let mut args = Vec::new();
1433 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1434 args.push(self.parse_expr()?);
1435 if *self.peek() == Token::Comma {
1436 self.advance();
1437 }
1438 }
1439 self.expect(&Token::RParen)?;
1440 Ok(Expr::ScalarFunc(func, args))
1441 }
1442 Token::Cast => {
1443 self.advance();
1444 self.expect(&Token::LParen)?;
1445 let inner = self.parse_expr()?;
1446 self.expect(&Token::Comma)?;
1447 let cast_type = self.parse_cast_type()?;
1448 self.expect(&Token::RParen)?;
1449 Ok(Expr::Cast(Box::new(inner), cast_type))
1450 }
1451 Token::Case => {
1452 self.advance();
1453 let mut whens = Vec::new();
1454 while *self.peek() == Token::When {
1455 self.advance();
1456 let condition = self.parse_expr()?;
1457 self.expect(&Token::Then)?;
1458 let result = self.parse_expr()?;
1459 whens.push((Box::new(condition), Box::new(result)));
1460 }
1461 let else_expr = if *self.peek() == Token::Else {
1462 self.advance();
1463 Some(Box::new(self.parse_expr()?))
1464 } else {
1465 None
1466 };
1467 self.expect(&Token::End)?;
1468 Ok(Expr::Case { whens, else_expr })
1469 }
1470 t => Err(ParseError::Syntax {
1471 message: format!("unexpected token in expression: {}", t.display_name()),
1472 }),
1473 }
1474 }
1475
1476 fn parse_alter_table(&mut self) -> Result<Statement, ParseError> {
1479 self.expect(&Token::Alter)?;
1480 let table = match self.advance() {
1481 Token::Ident(name) => name,
1482 t => {
1483 return Err(ParseError::UnexpectedToken {
1484 expected: "table name after alter".into(),
1485 got: t.display_name(),
1486 })
1487 }
1488 };
1489 match self.peek() {
1490 Token::Add => {
1491 self.advance();
1492 if *self.peek() == Token::Index {
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 index".into(),
1500 got: t.display_name(),
1501 })
1502 }
1503 };
1504 return Ok(Statement::AlterTable(AlterTableExpr {
1505 table,
1506 action: AlterAction::AddIndex { column },
1507 }));
1508 }
1509 if *self.peek() == Token::Unique {
1511 self.advance();
1512 let column = match self.advance() {
1513 Token::DotIdent(n) => n,
1514 t => {
1515 return Err(ParseError::UnexpectedToken {
1516 expected: ".<column> after add unique".into(),
1517 got: t.display_name(),
1518 })
1519 }
1520 };
1521 return Ok(Statement::AlterTable(AlterTableExpr {
1522 table,
1523 action: AlterAction::AddUnique { column },
1524 }));
1525 }
1526 if *self.peek() == Token::Column {
1528 self.advance();
1529 }
1530 let required = if *self.peek() == Token::Required {
1531 self.advance();
1532 true
1533 } else {
1534 false
1535 };
1536 let name = match self.advance() {
1537 Token::Ident(n) => n,
1538 t => {
1539 return Err(ParseError::UnexpectedToken {
1540 expected: "column name".into(),
1541 got: t.display_name(),
1542 })
1543 }
1544 };
1545 self.expect(&Token::Colon)?;
1546 let type_name = match self.advance() {
1547 Token::Ident(n) => n,
1548 t => {
1549 return Err(ParseError::UnexpectedToken {
1550 expected: "type name".into(),
1551 got: t.display_name(),
1552 })
1553 }
1554 };
1555 Ok(Statement::AlterTable(AlterTableExpr {
1556 table,
1557 action: AlterAction::AddColumn {
1558 name,
1559 type_name,
1560 required,
1561 },
1562 }))
1563 }
1564 Token::Drop => {
1565 self.advance();
1566 if *self.peek() == Token::Column {
1568 self.advance();
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 Ok(Statement::AlterTable(AlterTableExpr {
1580 table,
1581 action: AlterAction::DropColumn { name },
1582 }))
1583 }
1584 t => Err(ParseError::UnexpectedToken {
1585 expected: "add or drop after alter <table>".into(),
1586 got: t.display_name(),
1587 }),
1588 }
1589 }
1590
1591 fn parse_drop_or_drop_view(&mut self) -> Result<Statement, ParseError> {
1593 self.expect(&Token::Drop)?;
1594 if *self.peek() == Token::View {
1595 self.advance(); let name = match self.advance() {
1597 Token::Ident(name) => name,
1598 t => {
1599 return Err(ParseError::UnexpectedToken {
1600 expected: "view name after drop view".into(),
1601 got: t.display_name(),
1602 })
1603 }
1604 };
1605 return Ok(Statement::DropView(DropViewExpr { name }));
1606 }
1607 let table = match self.advance() {
1608 Token::Ident(name) => name,
1609 t => {
1610 return Err(ParseError::UnexpectedToken {
1611 expected: "table name after drop".into(),
1612 got: t.display_name(),
1613 })
1614 }
1615 };
1616 Ok(Statement::DropTable(DropTableExpr { table }))
1617 }
1618
1619 fn parse_create_view(&mut self) -> Result<Statement, ParseError> {
1624 self.expect(&Token::Materialized)?;
1625 let name = match self.advance() {
1626 Token::Ident(name) => name,
1627 t => {
1628 return Err(ParseError::UnexpectedToken {
1629 expected: "view name after materialize".into(),
1630 got: t.display_name(),
1631 })
1632 }
1633 };
1634 self.expect(&Token::As)?;
1635 let query_start = self.pos;
1637 let source = match self.advance() {
1638 Token::Ident(s) => s,
1639 t => {
1640 return Err(ParseError::UnexpectedToken {
1641 expected: "source table name".into(),
1642 got: t.display_name(),
1643 })
1644 }
1645 };
1646 let query = self.parse_query_tail(source)?;
1647 let query_text = tokens_to_text(&self.tokens[query_start..self.pos]);
1649 Ok(Statement::CreateView(CreateViewExpr {
1650 name,
1651 query,
1652 query_text,
1653 }))
1654 }
1655
1656 fn maybe_parse_union(&mut self, left: Statement) -> Result<Statement, ParseError> {
1659 if *self.peek() != Token::Union {
1660 return Ok(left);
1661 }
1662 if !matches!(left, Statement::Query(_) | Statement::Union(_)) {
1663 return Err(ParseError::Syntax {
1664 message: "UNION requires a query on the left side".into(),
1665 });
1666 }
1667 self.advance(); let all = if let Token::Ident(s) = self.peek() {
1669 if s == "all" {
1670 self.advance();
1671 true
1672 } else {
1673 false
1674 }
1675 } else {
1676 false
1677 };
1678 let right = self.parse_single_query()?;
1680 let union = Statement::Union(UnionExpr {
1681 left: Box::new(left),
1682 right: Box::new(right),
1683 all,
1684 });
1685 self.maybe_parse_union(union)
1687 }
1688
1689 fn parse_single_query(&mut self) -> Result<Statement, ParseError> {
1691 match self.peek() {
1692 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1693 self.parse_aggregate_query()
1694 }
1695 Token::Ident(_) => self.parse_query_or_mutation(),
1696 _ => Err(ParseError::Syntax {
1697 message: format!(
1698 "expected query after UNION, got {}",
1699 self.peek().display_name()
1700 ),
1701 }),
1702 }
1703 }
1704
1705 fn parse_refresh_view(&mut self) -> Result<Statement, ParseError> {
1707 self.expect(&Token::Refresh)?;
1708 let name = match self.advance() {
1709 Token::Ident(name) => name,
1710 t => {
1711 return Err(ParseError::UnexpectedToken {
1712 expected: "view name after refresh".into(),
1713 got: t.display_name(),
1714 })
1715 }
1716 };
1717 Ok(Statement::RefreshView(RefreshViewExpr { name }))
1718 }
1719
1720 fn parse_create_type(&mut self) -> Result<Statement, ParseError> {
1721 self.expect(&Token::Type)?;
1722 let name = match self.advance() {
1723 Token::Ident(n) => n,
1724 t => {
1725 return Err(ParseError::UnexpectedToken {
1726 expected: "type name".into(),
1727 got: t.display_name(),
1728 })
1729 }
1730 };
1731 self.expect(&Token::LBrace)?;
1732 let mut fields = Vec::new();
1733 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1734 let (mut required, mut unique) = (false, false);
1736 loop {
1737 match self.peek() {
1738 Token::Required => {
1739 self.advance();
1740 required = true;
1741 }
1742 Token::Unique => {
1743 self.advance();
1744 unique = true;
1745 }
1746 _ => break,
1747 }
1748 }
1749 let field_name = match self.advance() {
1750 Token::Ident(n) => n,
1751 t => {
1752 return Err(ParseError::UnexpectedToken {
1753 expected: "field name".into(),
1754 got: t.display_name(),
1755 })
1756 }
1757 };
1758 self.expect(&Token::Colon)?;
1759 let type_name = match self.advance() {
1760 Token::Ident(n) => n,
1761 t => {
1762 return Err(ParseError::UnexpectedToken {
1763 expected: "type name".into(),
1764 got: t.display_name(),
1765 })
1766 }
1767 };
1768 fields.push(FieldDef {
1769 name: field_name,
1770 type_name,
1771 required,
1772 unique,
1773 });
1774 if *self.peek() == Token::Comma {
1775 self.advance();
1776 }
1777 }
1778 self.expect(&Token::RBrace)?;
1779 Ok(Statement::CreateType(CreateTypeExpr { name, fields }))
1780 }
1781}
1782
1783fn tokens_to_text(tokens: &[Token]) -> String {
1787 let mut out = String::with_capacity(64);
1788 for tok in tokens {
1789 if !out.is_empty() && !matches!(tok, Token::Eof) {
1790 out.push(' ');
1791 }
1792 match tok {
1793 Token::Ident(s) => out.push_str(s),
1794 Token::DotIdent(s) => {
1795 out.push('.');
1796 out.push_str(s);
1797 }
1798 Token::IntLit(v) => out.push_str(&v.to_string()),
1799 Token::FloatLit(v) => out.push_str(&v.to_string()),
1800 Token::StringLit(s) => {
1801 out.push('"');
1802 out.push_str(s);
1803 out.push('"');
1804 }
1805 Token::BoolLit(v) => out.push_str(if *v { "true" } else { "false" }),
1806 Token::Param(s) => {
1807 out.push('$');
1808 out.push_str(s);
1809 }
1810 Token::Type => out.push_str("type"),
1811 Token::Filter => out.push_str("filter"),
1812 Token::Order => out.push_str("order"),
1813 Token::Limit => out.push_str("limit"),
1814 Token::Offset => out.push_str("offset"),
1815 Token::Insert => out.push_str("insert"),
1816 Token::Update => out.push_str("update"),
1817 Token::Delete => out.push_str("delete"),
1818 Token::Upsert => out.push_str("upsert"),
1819 Token::Conflict => out.push_str("conflict"),
1820 Token::Select => out.push_str("select"),
1821 Token::Required => out.push_str("required"),
1822 Token::Multi => out.push_str("multi"),
1823 Token::Link => out.push_str("link"),
1824 Token::Index => out.push_str("index"),
1825 Token::Unique => out.push_str("unique"),
1826 Token::On => out.push_str("on"),
1827 Token::Asc => out.push_str("asc"),
1828 Token::Desc => out.push_str("desc"),
1829 Token::And => out.push_str("and"),
1830 Token::Or => out.push_str("or"),
1831 Token::Not => out.push_str("not"),
1832 Token::Exists => out.push_str("exists"),
1833 Token::Let => out.push_str("let"),
1834 Token::As => out.push_str("as"),
1835 Token::Match => out.push_str("match"),
1836 Token::Group => out.push_str("group"),
1837 Token::Join => out.push_str("join"),
1838 Token::Inner => out.push_str("inner"),
1839 Token::LeftKw => out.push_str("left"),
1840 Token::RightKw => out.push_str("right"),
1841 Token::Outer => out.push_str("outer"),
1842 Token::Cross => out.push_str("cross"),
1843 Token::Transaction => out.push_str("transaction"),
1844 Token::Begin => out.push_str("begin"),
1845 Token::Commit => out.push_str("commit"),
1846 Token::Rollback => out.push_str("rollback"),
1847 Token::View => out.push_str("view"),
1848 Token::Materialized => out.push_str("materialized"),
1849 Token::Refresh => out.push_str("refresh"),
1850 Token::Union => out.push_str("union"),
1851 Token::Having => out.push_str("having"),
1852 Token::Distinct => out.push_str("distinct"),
1853 Token::In => out.push_str("in"),
1854 Token::Between => out.push_str("between"),
1855 Token::Like => out.push_str("like"),
1856 Token::Count => out.push_str("count"),
1857 Token::Avg => out.push_str("avg"),
1858 Token::Sum => out.push_str("sum"),
1859 Token::Min => out.push_str("min"),
1860 Token::Max => out.push_str("max"),
1861 Token::Is => out.push_str("is"),
1862 Token::Null => out.push_str("null"),
1863 Token::Upper => out.push_str("upper"),
1864 Token::Lower => out.push_str("lower"),
1865 Token::Length => out.push_str("length"),
1866 Token::Trim => out.push_str("trim"),
1867 Token::Substring => out.push_str("substring"),
1868 Token::Concat => out.push_str("concat"),
1869 Token::Abs => out.push_str("abs"),
1870 Token::Round => out.push_str("round"),
1871 Token::Ceil => out.push_str("ceil"),
1872 Token::Floor => out.push_str("floor"),
1873 Token::Sqrt => out.push_str("sqrt"),
1874 Token::Pow => out.push_str("pow"),
1875 Token::Now => out.push_str("now"),
1876 Token::Extract => out.push_str("extract"),
1877 Token::DateAdd => out.push_str("date_add"),
1878 Token::DateDiff => out.push_str("date_diff"),
1879 Token::Cast => out.push_str("cast"),
1880 Token::Case => out.push_str("case"),
1881 Token::When => out.push_str("when"),
1882 Token::Then => out.push_str("then"),
1883 Token::Else => out.push_str("else"),
1884 Token::End => out.push_str("end"),
1885 Token::Over => out.push_str("over"),
1886 Token::Partition => out.push_str("partition"),
1887 Token::RowNumber => out.push_str("row_number"),
1888 Token::Rank => out.push_str("rank"),
1889 Token::DenseRank => out.push_str("dense_rank"),
1890 Token::Alter => out.push_str("alter"),
1891 Token::Drop => out.push_str("drop"),
1892 Token::Add => out.push_str("add"),
1893 Token::Column => out.push_str("column"),
1894 Token::Eq => out.push('='),
1895 Token::Neq => out.push_str("!="),
1896 Token::Lt => out.push('<'),
1897 Token::Gt => out.push('>'),
1898 Token::Lte => out.push_str("<="),
1899 Token::Gte => out.push_str(">="),
1900 Token::Assign => out.push_str(":="),
1901 Token::Arrow => out.push_str("->"),
1902 Token::Pipe => out.push('|'),
1903 Token::Coalesce => out.push_str("??"),
1904 Token::Plus => out.push('+'),
1905 Token::Minus => out.push('-'),
1906 Token::Star => out.push('*'),
1907 Token::Slash => out.push('/'),
1908 Token::LBrace => out.push('{'),
1909 Token::RBrace => out.push('}'),
1910 Token::LParen => out.push('('),
1911 Token::RParen => out.push(')'),
1912 Token::Comma => out.push(','),
1913 Token::Colon => out.push(':'),
1914 Token::Dot => out.push('.'),
1915 Token::Explain => out.push_str("explain"),
1916 Token::Eof => {}
1917 }
1918 }
1919 out
1920}
1921
1922#[cfg(test)]
1923mod tests {
1924 use super::*;
1925 #[test]
1926 fn test_parse_simple_query() {
1927 let stmt = parse("User").unwrap();
1928 match stmt {
1929 Statement::Query(q) => {
1930 assert_eq!(q.source, "User");
1931 assert!(q.filter.is_none());
1932 assert!(q.projection.is_none());
1933 }
1934 _ => panic!("expected query"),
1935 }
1936 }
1937
1938 #[test]
1939 fn test_parse_filter() {
1940 let stmt = parse("User filter .age > 30").unwrap();
1941 match stmt {
1942 Statement::Query(q) => {
1943 assert_eq!(q.source, "User");
1944 assert!(q.filter.is_some());
1945 }
1946 _ => panic!("expected query"),
1947 }
1948 }
1949
1950 #[test]
1951 fn test_parse_projection() {
1952 let stmt = parse("User { name, email }").unwrap();
1953 match stmt {
1954 Statement::Query(q) => {
1955 let proj = q.projection.unwrap();
1956 assert_eq!(proj.len(), 2);
1957 }
1958 _ => panic!("expected query"),
1959 }
1960 }
1961
1962 #[test]
1963 fn test_parse_filter_order_limit() {
1964 let stmt = parse("User filter .age > 30 order .name desc limit 10").unwrap();
1965 match stmt {
1966 Statement::Query(q) => {
1967 assert!(q.filter.is_some());
1968 let order = q.order.unwrap();
1969 assert_eq!(order.keys.len(), 1);
1970 assert_eq!(order.keys[0].field, "name");
1971 assert!(order.keys[0].descending);
1972 assert!(q.limit.is_some());
1973 }
1974 _ => panic!("expected query"),
1975 }
1976 }
1977
1978 #[test]
1979 fn test_parse_insert() {
1980 let stmt = parse(r#"insert User { name := "Alice", age := 30 }"#).unwrap();
1981 match stmt {
1982 Statement::Insert(ins) => {
1983 assert_eq!(ins.target, "User");
1984 assert_eq!(ins.rows.len(), 1);
1985 assert_eq!(ins.rows[0].len(), 2);
1986 assert_eq!(ins.rows[0][0].field, "name");
1987 assert_eq!(ins.rows[0][1].field, "age");
1988 }
1989 _ => panic!("expected insert"),
1990 }
1991 }
1992
1993 #[test]
1994 fn test_parse_insert_multi_row() {
1995 let stmt =
1996 parse(r#"insert User { name := "Alice", age := 30 }, { name := "Bob", age := 25 }, { name := "Cy" }"#)
1997 .unwrap();
1998 match stmt {
1999 Statement::Insert(ins) => {
2000 assert_eq!(ins.target, "User");
2001 assert_eq!(ins.rows.len(), 3);
2002 assert_eq!(ins.rows[0].len(), 2);
2003 assert_eq!(ins.rows[1][0].field, "name");
2004 assert_eq!(ins.rows[2].len(), 1);
2005 assert_eq!(ins.rows[2][0].field, "name");
2006 }
2007 _ => panic!("expected insert"),
2008 }
2009 }
2010
2011 #[test]
2012 fn test_parse_update() {
2013 let stmt = parse(r#"User filter .email = "alice@ex.com" update { age := 31 }"#).unwrap();
2014 match stmt {
2015 Statement::UpdateQuery(upd) => {
2016 assert_eq!(upd.source, "User");
2017 assert!(upd.filter.is_some());
2018 assert_eq!(upd.assignments.len(), 1);
2019 }
2020 _ => panic!("expected update"),
2021 }
2022 }
2023
2024 #[test]
2025 fn test_parse_delete() {
2026 let stmt = parse("User filter .age < 18 delete").unwrap();
2027 match stmt {
2028 Statement::DeleteQuery(del) => {
2029 assert_eq!(del.source, "User");
2030 assert!(del.filter.is_some());
2031 }
2032 _ => panic!("expected delete"),
2033 }
2034 }
2035
2036 #[test]
2037 fn test_parse_count() {
2038 let stmt = parse("count(User)").unwrap();
2039 match stmt {
2040 Statement::Query(q) => {
2041 let agg = q.aggregation.unwrap();
2042 assert_eq!(agg.function, AggFunc::Count);
2043 assert!(q.filter.is_none());
2044 }
2045 _ => panic!("expected query with aggregation"),
2046 }
2047 }
2048
2049 #[test]
2050 fn test_parse_count_with_filter() {
2051 let stmt = parse("count(User filter .age > 30)").unwrap();
2054 match stmt {
2055 Statement::Query(q) => {
2056 assert_eq!(q.source, "User");
2057 let agg = q.aggregation.unwrap();
2058 assert_eq!(agg.function, AggFunc::Count);
2059 assert!(q.filter.is_some(), "filter should have been parsed");
2060 }
2061 _ => panic!("expected query with aggregation"),
2062 }
2063 }
2064
2065 #[test]
2066 fn test_parse_count_with_filter_and_limit() {
2067 let stmt = parse("count(User filter .age > 30 limit 100)").unwrap();
2068 match stmt {
2069 Statement::Query(q) => {
2070 assert_eq!(q.source, "User");
2071 assert!(q.filter.is_some());
2072 assert!(q.limit.is_some());
2073 assert_eq!(q.aggregation.unwrap().function, AggFunc::Count);
2074 }
2075 _ => panic!("expected query with aggregation"),
2076 }
2077 }
2078
2079 #[test]
2080 fn test_parse_create_type() {
2081 let stmt = parse("type User { required name: str, age: int }").unwrap();
2082 match stmt {
2083 Statement::CreateType(ct) => {
2084 assert_eq!(ct.name, "User");
2085 assert_eq!(ct.fields.len(), 2);
2086 assert!(ct.fields[0].required);
2087 assert!(!ct.fields[1].required);
2088 }
2089 _ => panic!("expected create type"),
2090 }
2091 }
2092
2093 #[test]
2094 fn test_parse_sum_with_field_projection() {
2095 let stmt = parse("sum(User filter .age > 30 { .age })").unwrap();
2098 match stmt {
2099 Statement::Query(q) => {
2100 let agg = q.aggregation.expect("aggregate");
2101 assert_eq!(agg.function, AggFunc::Sum);
2102 assert_eq!(agg.field.as_deref(), Some("age"));
2103 assert!(
2104 q.projection.is_none(),
2105 "projection should be lifted into agg.field"
2106 );
2107 }
2108 _ => panic!("expected query"),
2109 }
2110 }
2111
2112 #[test]
2113 fn test_parse_avg_min_max_with_field() {
2114 for (src, expected) in [
2115 ("avg(User { .age })", AggFunc::Avg),
2116 ("min(User { .age })", AggFunc::Min),
2117 ("max(User { .age })", AggFunc::Max),
2118 ] {
2119 let stmt = parse(src).unwrap();
2120 match stmt {
2121 Statement::Query(q) => {
2122 let agg = q.aggregation.unwrap();
2123 assert_eq!(agg.function, expected, "func mismatch for {src}");
2124 assert_eq!(
2125 agg.field.as_deref(),
2126 Some("age"),
2127 "field mismatch for {src}"
2128 );
2129 assert!(
2130 q.projection.is_none(),
2131 "projection should be cleared for {src}"
2132 );
2133 }
2134 _ => panic!("expected query for {src}"),
2135 }
2136 }
2137 }
2138
2139 #[test]
2140 fn test_parse_count_leaves_projection_alone() {
2141 let stmt = parse("count(User { .age })").unwrap();
2144 match stmt {
2145 Statement::Query(q) => {
2146 let agg = q.aggregation.unwrap();
2147 assert_eq!(agg.function, AggFunc::Count);
2148 assert!(agg.field.is_none());
2149 assert!(q.projection.is_some(), "count must not eat projection");
2150 }
2151 _ => panic!("expected query"),
2152 }
2153 }
2154
2155 #[test]
2160 fn test_parse_source_alias() {
2161 let stmt = parse("User as u filter u.age > 30").unwrap();
2162 match stmt {
2163 Statement::Query(q) => {
2164 assert_eq!(q.source, "User");
2165 assert_eq!(q.alias.as_deref(), Some("u"));
2166 assert!(q.joins.is_empty());
2167 match q.filter.unwrap() {
2168 Expr::BinaryOp(l, BinOp::Gt, _) => match *l {
2169 Expr::QualifiedField { qualifier, field } => {
2170 assert_eq!(qualifier, "u");
2171 assert_eq!(field, "age");
2172 }
2173 other => panic!("expected qualified field, got {other:?}"),
2174 },
2175 other => panic!("expected >, got {other:?}"),
2176 }
2177 }
2178 _ => panic!("expected query"),
2179 }
2180 }
2181
2182 #[test]
2183 fn test_parse_inner_join_on() {
2184 let stmt = parse("User as u inner join Order as o on u.id = o.user_id").unwrap();
2185 match stmt {
2186 Statement::Query(q) => {
2187 assert_eq!(q.source, "User");
2188 assert_eq!(q.alias.as_deref(), Some("u"));
2189 assert_eq!(q.joins.len(), 1);
2190 let j = &q.joins[0];
2191 assert_eq!(j.kind, JoinKind::Inner);
2192 assert_eq!(j.source, "Order");
2193 assert_eq!(j.alias.as_deref(), Some("o"));
2194 let on = j.on.as_ref().expect("on clause");
2195 match on {
2196 Expr::BinaryOp(l, BinOp::Eq, r) => {
2197 assert!(matches!(**l, Expr::QualifiedField { .. }));
2198 assert!(matches!(**r, Expr::QualifiedField { .. }));
2199 }
2200 other => panic!("expected eq, got {other:?}"),
2201 }
2202 }
2203 _ => panic!("expected query"),
2204 }
2205 }
2206
2207 #[test]
2208 fn test_parse_bare_join_defaults_to_inner() {
2209 let stmt = parse("User join Order on User.id = Order.user_id").unwrap();
2210 match stmt {
2211 Statement::Query(q) => {
2212 assert_eq!(q.joins.len(), 1);
2213 assert_eq!(q.joins[0].kind, JoinKind::Inner);
2214 }
2215 _ => panic!("expected query"),
2216 }
2217 }
2218
2219 #[test]
2220 fn test_parse_left_outer_join() {
2221 let stmt = parse("User as u left outer join Order as o on u.id = o.user_id").unwrap();
2222 match stmt {
2223 Statement::Query(q) => {
2224 assert_eq!(q.joins.len(), 1);
2225 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2226 }
2227 _ => panic!("expected query"),
2228 }
2229 }
2230
2231 #[test]
2232 fn test_parse_left_join_without_outer_keyword() {
2233 let stmt = parse("User as u left join Order as o on u.id = o.user_id").unwrap();
2235 match stmt {
2236 Statement::Query(q) => {
2237 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2238 }
2239 _ => panic!("expected query"),
2240 }
2241 }
2242
2243 #[test]
2244 fn test_parse_right_join() {
2245 let stmt = parse("User as u right join Order as o on u.id = o.user_id").unwrap();
2246 match stmt {
2247 Statement::Query(q) => {
2248 assert_eq!(q.joins[0].kind, JoinKind::RightOuter);
2249 }
2250 _ => panic!("expected query"),
2251 }
2252 }
2253
2254 #[test]
2255 fn test_parse_cross_join_has_no_on() {
2256 let stmt = parse("User cross join Order").unwrap();
2257 match stmt {
2258 Statement::Query(q) => {
2259 assert_eq!(q.joins[0].kind, JoinKind::Cross);
2260 assert!(q.joins[0].on.is_none());
2261 }
2262 _ => panic!("expected query"),
2263 }
2264 }
2265
2266 #[test]
2267 fn test_parse_multi_join_chain() {
2268 let stmt = parse(
2269 "User as u join Order as o on u.id = o.user_id \
2270 join Product as p on o.product_id = p.id",
2271 )
2272 .unwrap();
2273 match stmt {
2274 Statement::Query(q) => {
2275 assert_eq!(q.joins.len(), 2);
2276 assert_eq!(q.joins[0].source, "Order");
2277 assert_eq!(q.joins[1].source, "Product");
2278 }
2279 _ => panic!("expected query"),
2280 }
2281 }
2282
2283 #[test]
2284 fn test_parse_join_with_filter_tail() {
2285 let stmt = parse(
2287 "User as u join Order as o on u.id = o.user_id \
2288 filter o.total > 100 order .name limit 10",
2289 )
2290 .unwrap();
2291 match stmt {
2292 Statement::Query(q) => {
2293 assert_eq!(q.joins.len(), 1);
2294 assert!(q.filter.is_some());
2295 assert!(q.order.is_some());
2296 assert!(q.limit.is_some());
2297 }
2298 _ => panic!("expected query"),
2299 }
2300 }
2301
2302 #[test]
2303 fn test_parse_join_requires_on_for_inner() {
2304 let err = parse("User join Order").unwrap_err();
2306 assert!(
2307 err.message().contains("on"),
2308 "expected on-clause error, got {:?}",
2309 err.message()
2310 );
2311 }
2312
2313 #[test]
2314 fn test_parse_update_on_joined_query_errors() {
2315 let err =
2318 parse("User as u join Order as o on u.id = o.user_id update { age := 1 }").unwrap_err();
2319 assert!(err.message().contains("update"));
2320 }
2321
2322 #[test]
2323 fn test_parse_delete_on_joined_query_errors() {
2324 let err = parse("User as u join Order as o on u.id = o.user_id delete").unwrap_err();
2325 assert!(err.message().contains("delete"));
2326 }
2327
2328 #[test]
2331 fn test_parse_distinct() {
2332 let stmt = parse("User distinct { .name }").unwrap();
2333 match stmt {
2334 Statement::Query(q) => {
2335 assert!(q.distinct);
2336 assert!(q.projection.is_some());
2337 }
2338 _ => panic!("expected query"),
2339 }
2340 }
2341
2342 #[test]
2343 fn test_parse_in_list() {
2344 let stmt = parse(r#"User filter .name in ("Alice", "Bob")"#).unwrap();
2345 match stmt {
2346 Statement::Query(q) => match q.filter.unwrap() {
2347 Expr::InList {
2348 expr,
2349 list,
2350 negated,
2351 } => {
2352 assert!(!negated);
2353 assert!(matches!(*expr, Expr::Field(f) if f == "name"));
2354 assert_eq!(list.len(), 2);
2355 }
2356 other => panic!("expected InList, got {other:?}"),
2357 },
2358 _ => panic!("expected query"),
2359 }
2360 }
2361
2362 #[test]
2363 fn test_parse_not_in_list() {
2364 let stmt = parse("User filter .age not in (1, 2, 3)").unwrap();
2365 match stmt {
2366 Statement::Query(q) => match q.filter.unwrap() {
2367 Expr::InList { negated, list, .. } => {
2368 assert!(negated);
2369 assert_eq!(list.len(), 3);
2370 }
2371 other => panic!("expected InList, got {other:?}"),
2372 },
2373 _ => panic!("expected query"),
2374 }
2375 }
2376
2377 #[test]
2378 fn test_parse_between() {
2379 let stmt = parse("User filter .age between 10 and 20").unwrap();
2381 match stmt {
2382 Statement::Query(q) => {
2383 match q.filter.unwrap() {
2384 Expr::BinaryOp(_, BinOp::And, _) => {} other => panic!("expected And (desugared between), got {other:?}"),
2386 }
2387 }
2388 _ => panic!("expected query"),
2389 }
2390 }
2391
2392 #[test]
2393 fn test_parse_not_between() {
2394 let stmt = parse("User filter .age not between 10 and 20").unwrap();
2396 match stmt {
2397 Statement::Query(q) => {
2398 match q.filter.unwrap() {
2399 Expr::BinaryOp(_, BinOp::Or, _) => {} other => panic!("expected Or (desugared not between), got {other:?}"),
2401 }
2402 }
2403 _ => panic!("expected query"),
2404 }
2405 }
2406
2407 #[test]
2408 fn test_parse_like() {
2409 let stmt = parse(r#"User filter .name like "A%""#).unwrap();
2410 match stmt {
2411 Statement::Query(q) => match q.filter.unwrap() {
2412 Expr::BinaryOp(l, BinOp::Like, r) => {
2413 assert!(matches!(*l, Expr::Field(f) if f == "name"));
2414 assert!(matches!(*r, Expr::Literal(Literal::String(s)) if s == "A%"));
2415 }
2416 other => panic!("expected Like, got {other:?}"),
2417 },
2418 _ => panic!("expected query"),
2419 }
2420 }
2421
2422 #[test]
2423 fn test_parse_not_like() {
2424 let stmt = parse(r#"User filter .name not like "A%""#).unwrap();
2425 match stmt {
2426 Statement::Query(q) => match q.filter.unwrap() {
2427 Expr::UnaryOp(UnaryOp::Not, inner) => {
2428 assert!(matches!(*inner, Expr::BinaryOp(_, BinOp::Like, _)));
2429 }
2430 other => panic!("expected Not(Like), got {other:?}"),
2431 },
2432 _ => panic!("expected query"),
2433 }
2434 }
2435
2436 #[test]
2439 fn test_parse_group_by_single_key() {
2440 let stmt = parse("User group .status { .status, n: count(.name) }").unwrap();
2441 match stmt {
2442 Statement::Query(q) => {
2443 let gb = q.group_by.unwrap();
2444 assert_eq!(gb.keys, vec!["status"]);
2445 assert!(gb.having.is_none());
2446 let proj = q.projection.unwrap();
2447 assert_eq!(proj.len(), 2);
2448 assert!(matches!(
2449 &proj[1].expr,
2450 Expr::FunctionCall(AggFunc::Count, _)
2451 ));
2452 assert_eq!(proj[1].alias.as_deref(), Some("n"));
2453 }
2454 _ => panic!("expected query"),
2455 }
2456 }
2457
2458 #[test]
2459 fn test_parse_group_by_multi_key() {
2460 let stmt = parse("User group .status, .age { .status, .age }").unwrap();
2461 match stmt {
2462 Statement::Query(q) => {
2463 let gb = q.group_by.unwrap();
2464 assert_eq!(gb.keys, vec!["status", "age"]);
2465 }
2466 _ => panic!("expected query"),
2467 }
2468 }
2469
2470 #[test]
2471 fn test_parse_group_by_having() {
2472 let stmt = parse("User group .status having count(.name) > 1 { .status }").unwrap();
2473 match stmt {
2474 Statement::Query(q) => {
2475 let gb = q.group_by.unwrap();
2476 assert_eq!(gb.keys, vec!["status"]);
2477 assert!(gb.having.is_some());
2478 match gb.having.unwrap() {
2480 Expr::BinaryOp(l, BinOp::Gt, _) => {
2481 assert!(matches!(*l, Expr::FunctionCall(AggFunc::Count, _)));
2482 }
2483 other => panic!("expected BinaryOp, got {other:?}"),
2484 }
2485 }
2486 _ => panic!("expected query"),
2487 }
2488 }
2489
2490 #[test]
2491 fn test_parse_aggregate_in_projection() {
2492 let stmt = parse("User group .status { .status, count(.name), sum(.age) }").unwrap();
2494 match stmt {
2495 Statement::Query(q) => {
2496 let proj = q.projection.unwrap();
2497 assert_eq!(proj.len(), 3);
2498 assert!(matches!(
2499 &proj[1].expr,
2500 Expr::FunctionCall(AggFunc::Count, _)
2501 ));
2502 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Sum, _)));
2503 }
2504 _ => panic!("expected query"),
2505 }
2506 }
2507
2508 #[test]
2509 fn test_parse_aggregate_in_aliased_projection() {
2510 let stmt = parse("User group .status { .status, total: count(.name), average: avg(.age) }")
2511 .unwrap();
2512 match stmt {
2513 Statement::Query(q) => {
2514 let proj = q.projection.unwrap();
2515 assert_eq!(proj[1].alias.as_deref(), Some("total"));
2516 assert!(matches!(
2517 &proj[1].expr,
2518 Expr::FunctionCall(AggFunc::Count, _)
2519 ));
2520 assert_eq!(proj[2].alias.as_deref(), Some("average"));
2521 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Avg, _)));
2522 }
2523 _ => panic!("expected query"),
2524 }
2525 }
2526
2527 #[test]
2530 fn test_parse_is_null() {
2531 let stmt = parse("User filter .age is null").unwrap();
2532 match stmt {
2533 Statement::Query(q) => {
2534 let filter = q.filter.unwrap();
2535 assert_eq!(
2536 filter,
2537 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2538 );
2539 }
2540 _ => panic!("expected query"),
2541 }
2542 }
2543
2544 #[test]
2545 fn test_parse_is_not_null() {
2546 let stmt = parse("User filter .age is not null").unwrap();
2547 match stmt {
2548 Statement::Query(q) => {
2549 let filter = q.filter.unwrap();
2550 assert_eq!(
2551 filter,
2552 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2553 );
2554 }
2555 _ => panic!("expected query"),
2556 }
2557 }
2558
2559 #[test]
2560 fn test_parse_eq_null_desugars_to_is_null() {
2561 let stmt = parse("User filter .age = null").unwrap();
2562 match stmt {
2563 Statement::Query(q) => {
2564 let filter = q.filter.unwrap();
2565 assert_eq!(
2566 filter,
2567 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2568 );
2569 }
2570 _ => panic!("expected query"),
2571 }
2572 }
2573
2574 #[test]
2575 fn test_parse_neq_null_desugars_to_is_not_null() {
2576 let stmt = parse("User filter .age != null").unwrap();
2577 match stmt {
2578 Statement::Query(q) => {
2579 let filter = q.filter.unwrap();
2580 assert_eq!(
2581 filter,
2582 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2583 );
2584 }
2585 _ => panic!("expected query"),
2586 }
2587 }
2588
2589 #[test]
2590 fn test_parse_null_comparisons_parse_ok() {
2591 assert!(parse("User filter .age < null").is_ok());
2595 assert!(parse("User filter .age >= null").is_ok());
2596 }
2597
2598 #[test]
2599 fn test_parse_count_star_expr() {
2600 let stmt = parse("User filter count(*) > 0").unwrap();
2601 match stmt {
2602 Statement::Query(q) => {
2603 let filter = q.filter.unwrap();
2604 match filter {
2605 Expr::BinaryOp(left, BinOp::Gt, _) => {
2606 assert_eq!(
2607 *left,
2608 Expr::FunctionCall(AggFunc::Count, Box::new(Expr::Field("*".into())))
2609 );
2610 }
2611 _ => panic!("expected comparison"),
2612 }
2613 }
2614 _ => panic!("expected query"),
2615 }
2616 }
2617
2618 #[test]
2621 fn test_parse_upper_in_filter() {
2622 let stmt = parse(r#"User filter upper(.name) = "ALICE""#).unwrap();
2623 match stmt {
2624 Statement::Query(q) => {
2625 let f = q.filter.unwrap();
2626 match f {
2627 Expr::BinaryOp(left, BinOp::Eq, _right) => {
2628 assert!(matches!(*left, Expr::ScalarFunc(ScalarFn::Upper, _)));
2629 }
2630 _ => panic!("expected binary op with upper"),
2631 }
2632 }
2633 _ => panic!("expected query"),
2634 }
2635 }
2636
2637 #[test]
2638 fn test_parse_substring() {
2639 let stmt = parse("User { sub: substring(.name, 1, 3) }").unwrap();
2640 match stmt {
2641 Statement::Query(q) => {
2642 let proj = q.projection.unwrap();
2643 match &proj[0].expr {
2644 Expr::ScalarFunc(ScalarFn::Substring, args) => {
2645 assert_eq!(args.len(), 3);
2646 }
2647 other => panic!("expected ScalarFunc Substring, got {other:?}"),
2648 }
2649 }
2650 _ => panic!("expected query"),
2651 }
2652 }
2653
2654 #[test]
2655 fn test_parse_concat() {
2656 let stmt = parse(r#"User { full: concat(.name, " - ", .email) }"#).unwrap();
2657 match stmt {
2658 Statement::Query(q) => {
2659 let proj = q.projection.unwrap();
2660 match &proj[0].expr {
2661 Expr::ScalarFunc(ScalarFn::Concat, args) => {
2662 assert_eq!(args.len(), 3);
2663 }
2664 other => panic!("expected ScalarFunc Concat, got {other:?}"),
2665 }
2666 }
2667 _ => panic!("expected query"),
2668 }
2669 }
2670
2671 #[test]
2674 fn test_parse_case_single_when() {
2675 let stmt = parse(r#"User filter case when .age > 30 then true else false end"#).unwrap();
2676 match stmt {
2677 Statement::Query(q) => {
2678 let filter = q.filter.unwrap();
2679 match filter {
2680 Expr::Case { whens, else_expr } => {
2681 assert_eq!(whens.len(), 1);
2682 assert!(else_expr.is_some());
2683 }
2684 other => panic!("expected Case expr, got {other:?}"),
2685 }
2686 }
2687 _ => panic!("expected query"),
2688 }
2689 }
2690
2691 #[test]
2692 fn test_parse_case_multiple_whens() {
2693 let stmt = parse(
2694 r#"User { label: case when .age > 30 then "senior" when .age > 20 then "adult" else "young" end }"#
2695 ).unwrap();
2696 match stmt {
2697 Statement::Query(q) => {
2698 let proj = q.projection.unwrap();
2699 match &proj[0].expr {
2700 Expr::Case { whens, else_expr } => {
2701 assert_eq!(whens.len(), 2);
2702 assert!(else_expr.is_some());
2703 }
2704 other => panic!("expected Case expr, got {other:?}"),
2705 }
2706 }
2707 _ => panic!("expected query"),
2708 }
2709 }
2710
2711 #[test]
2712 fn test_parse_case_without_else() {
2713 let stmt = parse(r#"User filter case when .age > 30 then true end"#).unwrap();
2714 match stmt {
2715 Statement::Query(q) => {
2716 let filter = q.filter.unwrap();
2717 match filter {
2718 Expr::Case { whens, else_expr } => {
2719 assert_eq!(whens.len(), 1);
2720 assert!(else_expr.is_none());
2721 }
2722 other => panic!("expected Case expr, got {other:?}"),
2723 }
2724 }
2725 _ => panic!("expected query"),
2726 }
2727 }
2728
2729 #[test]
2732 fn test_parse_mul_expr() {
2733 let stmt = parse("User filter .price * .quantity > 100").unwrap();
2734 match stmt {
2735 Statement::Query(q) => {
2736 let filter = q.filter.unwrap();
2737 match filter {
2738 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2739 Expr::BinaryOp(_, BinOp::Mul, _) => {}
2740 other => panic!("expected Mul, got {other:?}"),
2741 },
2742 other => panic!("expected BinaryOp Gt, got {other:?}"),
2743 }
2744 }
2745 _ => panic!("expected query"),
2746 }
2747 }
2748
2749 #[test]
2750 fn test_parse_div_expr() {
2751 let stmt = parse("User { ratio: .total / .count }").unwrap();
2752 match stmt {
2753 Statement::Query(q) => {
2754 let proj = q.projection.unwrap();
2755 assert_eq!(proj[0].alias.as_deref(), Some("ratio"));
2756 match &proj[0].expr {
2757 Expr::BinaryOp(_, BinOp::Div, _) => {}
2758 other => panic!("expected Div, got {other:?}"),
2759 }
2760 }
2761 _ => panic!("expected query"),
2762 }
2763 }
2764
2765 #[test]
2766 fn test_parse_mul_div_precedence() {
2767 let stmt = parse("User filter .a + .b * .c > 0").unwrap();
2769 match stmt {
2770 Statement::Query(q) => {
2771 let filter = q.filter.unwrap();
2772 match filter {
2773 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2774 Expr::BinaryOp(_, BinOp::Add, right) => {
2775 assert!(matches!(*right, Expr::BinaryOp(_, BinOp::Mul, _)));
2776 }
2777 other => panic!("expected Add, got {other:?}"),
2778 },
2779 other => panic!("expected Gt, got {other:?}"),
2780 }
2781 }
2782 _ => panic!("expected query"),
2783 }
2784 }
2785
2786 #[test]
2789 fn test_parse_multi_order() {
2790 let stmt = parse("User order .name asc, .age desc").unwrap();
2791 match stmt {
2792 Statement::Query(q) => {
2793 let order = q.order.unwrap();
2794 assert_eq!(order.keys.len(), 2);
2795 assert_eq!(order.keys[0].field, "name");
2796 assert!(!order.keys[0].descending);
2797 assert_eq!(order.keys[1].field, "age");
2798 assert!(order.keys[1].descending);
2799 }
2800 _ => panic!("expected query"),
2801 }
2802 }
2803
2804 #[test]
2805 fn test_parse_order_default_asc() {
2806 let stmt = parse("User order .name").unwrap();
2807 match stmt {
2808 Statement::Query(q) => {
2809 let order = q.order.unwrap();
2810 assert_eq!(order.keys.len(), 1);
2811 assert!(!order.keys[0].descending);
2812 }
2813 _ => panic!("expected query"),
2814 }
2815 }
2816
2817 #[test]
2820 fn test_parse_alter_add_column() {
2821 let stmt = parse("alter User add column status: str").unwrap();
2822 match stmt {
2823 Statement::AlterTable(at) => {
2824 assert_eq!(at.table, "User");
2825 match at.action {
2826 AlterAction::AddColumn {
2827 name,
2828 type_name,
2829 required,
2830 } => {
2831 assert_eq!(name, "status");
2832 assert_eq!(type_name, "str");
2833 assert!(!required);
2834 }
2835 other => panic!("expected AddColumn, got {other:?}"),
2836 }
2837 }
2838 other => panic!("expected AlterTable, got {other:?}"),
2839 }
2840 }
2841
2842 #[test]
2843 fn test_parse_alter_add_required_column() {
2844 let stmt = parse("alter User add required status: str").unwrap();
2845 match stmt {
2846 Statement::AlterTable(at) => match at.action {
2847 AlterAction::AddColumn { required, .. } => assert!(required),
2848 other => panic!("expected AddColumn, got {other:?}"),
2849 },
2850 other => panic!("expected AlterTable, got {other:?}"),
2851 }
2852 }
2853
2854 #[test]
2855 fn test_parse_type_with_unique_modifier() {
2856 let stmt = parse("type User { required unique email: str, age: int }").unwrap();
2857 match stmt {
2858 Statement::CreateType(ct) => {
2859 assert!(ct.fields[0].required && ct.fields[0].unique);
2860 assert!(!ct.fields[1].unique);
2861 }
2862 other => panic!("expected CreateType, got {other:?}"),
2863 }
2864 }
2865
2866 #[test]
2867 fn test_parse_type_unique_before_required() {
2868 let stmt = parse("type User { unique required email: str }").unwrap();
2870 match stmt {
2871 Statement::CreateType(ct) => {
2872 assert!(ct.fields[0].required && ct.fields[0].unique);
2873 }
2874 other => panic!("expected CreateType, got {other:?}"),
2875 }
2876 }
2877
2878 #[test]
2879 fn test_parse_alter_add_unique() {
2880 let stmt = parse("alter User add unique .email").unwrap();
2881 match stmt {
2882 Statement::AlterTable(at) => assert!(matches!(
2883 at.action,
2884 AlterAction::AddUnique { ref column } if column == "email"
2885 )),
2886 other => panic!("expected AlterTable, got {other:?}"),
2887 }
2888 }
2889
2890 #[test]
2891 fn test_parse_alter_drop_column() {
2892 let stmt = parse("alter User drop column status").unwrap();
2893 match stmt {
2894 Statement::AlterTable(at) => {
2895 assert_eq!(at.table, "User");
2896 match at.action {
2897 AlterAction::DropColumn { name } => assert_eq!(name, "status"),
2898 other => panic!("expected DropColumn, got {other:?}"),
2899 }
2900 }
2901 other => panic!("expected AlterTable, got {other:?}"),
2902 }
2903 }
2904
2905 #[test]
2906 fn test_parse_alter_drop_without_column_keyword() {
2907 let stmt = parse("alter User drop status").unwrap();
2908 match stmt {
2909 Statement::AlterTable(at) => match at.action {
2910 AlterAction::DropColumn { name } => assert_eq!(name, "status"),
2911 other => panic!("expected DropColumn, got {other:?}"),
2912 },
2913 other => panic!("expected AlterTable, got {other:?}"),
2914 }
2915 }
2916
2917 #[test]
2918 fn test_parse_drop_table() {
2919 let stmt = parse("drop User").unwrap();
2920 match stmt {
2921 Statement::DropTable(dt) => assert_eq!(dt.table, "User"),
2922 other => panic!("expected DropTable, got {other:?}"),
2923 }
2924 }
2925
2926 #[test]
2929 fn test_parse_in_subquery() {
2930 let stmt = parse("User filter .name in (VIP { .name })").unwrap();
2931 match stmt {
2932 Statement::Query(q) => {
2933 let filter = q.filter.unwrap();
2934 match filter {
2935 Expr::InSubquery {
2936 expr,
2937 subquery,
2938 negated,
2939 } => {
2940 assert!(!negated);
2941 assert!(matches!(*expr, Expr::Field(ref f) if f == "name"));
2942 assert_eq!(subquery.source, "VIP");
2943 }
2944 other => panic!("expected InSubquery, got {other:?}"),
2945 }
2946 }
2947 _ => panic!("expected query"),
2948 }
2949 }
2950
2951 #[test]
2952 fn test_parse_not_in_subquery() {
2953 let stmt = parse("User filter .id not in (Order { .user_id })").unwrap();
2954 match stmt {
2955 Statement::Query(q) => match q.filter.unwrap() {
2956 Expr::InSubquery { negated, .. } => assert!(negated),
2957 other => panic!("expected InSubquery, got {other:?}"),
2958 },
2959 _ => panic!("expected query"),
2960 }
2961 }
2962
2963 #[test]
2964 fn test_parse_in_literal_list_still_works() {
2965 let stmt = parse("User filter .age in (25, 30, 35)").unwrap();
2967 match stmt {
2968 Statement::Query(q) => match q.filter.unwrap() {
2969 Expr::InList { list, negated, .. } => {
2970 assert!(!negated);
2971 assert_eq!(list.len(), 3);
2972 }
2973 other => panic!("expected InList, got {other:?}"),
2974 },
2975 _ => panic!("expected query"),
2976 }
2977 }
2978
2979 #[test]
2982 fn test_parse_create_view() {
2983 let stmt = parse("materialize OldUsers as User filter .age > 28").unwrap();
2984 match stmt {
2985 Statement::CreateView(cv) => {
2986 assert_eq!(cv.name, "OldUsers");
2987 assert_eq!(cv.query.source, "User");
2988 assert!(cv.query.filter.is_some());
2989 assert!(!cv.query_text.is_empty());
2990 }
2991 _ => panic!("expected CreateView"),
2992 }
2993 }
2994
2995 #[test]
2996 fn test_parse_create_view_with_projection() {
2997 let stmt = parse("materialize UserNames as User { .name }").unwrap();
2998 match stmt {
2999 Statement::CreateView(cv) => {
3000 assert_eq!(cv.name, "UserNames");
3001 assert!(cv.query.projection.is_some());
3002 }
3003 _ => panic!("expected CreateView"),
3004 }
3005 }
3006
3007 #[test]
3008 fn test_parse_refresh_view() {
3009 let stmt = parse("refresh OldUsers").unwrap();
3010 match stmt {
3011 Statement::RefreshView(rv) => {
3012 assert_eq!(rv.name, "OldUsers");
3013 }
3014 _ => panic!("expected RefreshView"),
3015 }
3016 }
3017
3018 #[test]
3019 fn test_parse_drop_view() {
3020 let stmt = parse("drop view OldUsers").unwrap();
3021 match stmt {
3022 Statement::DropView(dv) => {
3023 assert_eq!(dv.name, "OldUsers");
3024 }
3025 _ => panic!("expected DropView"),
3026 }
3027 }
3028
3029 #[test]
3030 fn test_parse_drop_table_still_works() {
3031 let stmt = parse("drop Users").unwrap();
3032 match stmt {
3033 Statement::DropTable(dt) => {
3034 assert_eq!(dt.table, "Users");
3035 }
3036 _ => panic!("expected DropTable"),
3037 }
3038 }
3039
3040 #[test]
3041 fn test_parse_union() {
3042 let stmt = parse("User union Order").unwrap();
3043 match stmt {
3044 Statement::Union(u) => {
3045 assert!(!u.all);
3046 match *u.left {
3047 Statement::Query(_) => {}
3048 _ => panic!("expected Query on left"),
3049 }
3050 match *u.right {
3051 Statement::Query(_) => {}
3052 _ => panic!("expected Query on right"),
3053 }
3054 }
3055 _ => panic!("expected Union"),
3056 }
3057 }
3058
3059 #[test]
3060 fn test_parse_union_all() {
3061 let stmt = parse("User union all Order").unwrap();
3062 match stmt {
3063 Statement::Union(u) => {
3064 assert!(u.all, "expected UNION ALL");
3065 match *u.left {
3066 Statement::Query(_) => {}
3067 _ => panic!("expected Query on left"),
3068 }
3069 match *u.right {
3070 Statement::Query(_) => {}
3071 _ => panic!("expected Query on right"),
3072 }
3073 }
3074 _ => panic!("expected Union"),
3075 }
3076 }
3077
3078 #[test]
3079 fn test_parse_union_chain() {
3080 let stmt = parse("User union Order union Product").unwrap();
3082 match stmt {
3083 Statement::Union(outer) => {
3084 assert!(!outer.all);
3085 match *outer.right {
3087 Statement::Query(q) => assert_eq!(q.source, "Product"),
3088 _ => panic!("expected Query(Product) on right"),
3089 }
3090 match *outer.left {
3092 Statement::Union(inner) => {
3093 assert!(!inner.all);
3094 match *inner.left {
3095 Statement::Query(q) => assert_eq!(q.source, "User"),
3096 _ => panic!("expected Query(User)"),
3097 }
3098 match *inner.right {
3099 Statement::Query(q) => assert_eq!(q.source, "Order"),
3100 _ => panic!("expected Query(Order)"),
3101 }
3102 }
3103 _ => panic!("expected inner Union"),
3104 }
3105 }
3106 _ => panic!("expected Union"),
3107 }
3108 }
3109
3110 #[test]
3111 fn test_parse_union_with_filter() {
3112 let stmt = parse("User filter .age > 10 union Order filter .total > 50").unwrap();
3113 match stmt {
3114 Statement::Union(u) => {
3115 assert!(!u.all);
3116 match *u.left {
3118 Statement::Query(q) => {
3119 assert_eq!(q.source, "User");
3120 assert!(q.filter.is_some());
3121 }
3122 _ => panic!("expected Query on left"),
3123 }
3124 match *u.right {
3125 Statement::Query(q) => {
3126 assert_eq!(q.source, "Order");
3127 assert!(q.filter.is_some());
3128 }
3129 _ => panic!("expected Query on right"),
3130 }
3131 }
3132 _ => panic!("expected Union"),
3133 }
3134 }
3135
3136 #[test]
3137 fn test_parse_count_distinct_standalone() {
3138 let stmt = parse("count(distinct User { .name })").unwrap();
3139 match stmt {
3140 Statement::Query(q) => {
3141 let agg = q.aggregation.unwrap();
3142 assert_eq!(agg.function, AggFunc::CountDistinct);
3143 assert_eq!(agg.field.as_deref(), Some("name"));
3144 }
3145 _ => panic!("expected Query"),
3146 }
3147 }
3148
3149 #[test]
3150 fn test_parse_count_distinct_in_projection() {
3151 let stmt = parse("User group .dept { .dept, count(distinct .name) }").unwrap();
3152 match stmt {
3153 Statement::Query(q) => {
3154 let proj = q.projection.unwrap();
3155 assert_eq!(proj.len(), 2);
3156 match &proj[1].expr {
3157 Expr::FunctionCall(func, _) => {
3158 assert_eq!(*func, AggFunc::CountDistinct);
3159 }
3160 _ => panic!("expected FunctionCall"),
3161 }
3162 }
3163 _ => panic!("expected Query"),
3164 }
3165 }
3166
3167 #[test]
3170 fn test_parse_window_row_number_order() {
3171 let stmt = parse("User { .name, rn: row_number() over (order .age) }").unwrap();
3172 match stmt {
3173 Statement::Query(q) => {
3174 let proj = q.projection.unwrap();
3175 assert_eq!(proj.len(), 2);
3176 assert_eq!(proj[1].alias.as_deref(), Some("rn"));
3177 match &proj[1].expr {
3178 Expr::Window {
3179 function,
3180 args,
3181 partition_by,
3182 order_by,
3183 } => {
3184 assert_eq!(*function, WindowFunc::RowNumber);
3185 assert!(args.is_empty());
3186 assert!(partition_by.is_empty());
3187 assert_eq!(order_by.len(), 1);
3188 assert_eq!(order_by[0].field, "age");
3189 assert!(!order_by[0].descending);
3190 }
3191 other => panic!("expected Window, got {other:?}"),
3192 }
3193 }
3194 _ => panic!("expected query"),
3195 }
3196 }
3197
3198 #[test]
3199 fn test_parse_window_sum_partition_order() {
3200 let stmt =
3201 parse("User { .name, s: sum(.salary) over (partition .dept order .salary) }").unwrap();
3202 match stmt {
3203 Statement::Query(q) => {
3204 let proj = q.projection.unwrap();
3205 assert_eq!(proj.len(), 2);
3206 assert_eq!(proj[1].alias.as_deref(), Some("s"));
3207 match &proj[1].expr {
3208 Expr::Window {
3209 function,
3210 args,
3211 partition_by,
3212 order_by,
3213 } => {
3214 assert_eq!(*function, WindowFunc::Sum);
3215 assert_eq!(args.len(), 1);
3216 assert!(matches!(&args[0], Expr::Field(f) if f == "salary"));
3217 assert_eq!(partition_by, &["dept"]);
3218 assert_eq!(order_by.len(), 1);
3219 assert_eq!(order_by[0].field, "salary");
3220 assert!(!order_by[0].descending);
3221 }
3222 other => panic!("expected Window, got {other:?}"),
3223 }
3224 }
3225 _ => panic!("expected query"),
3226 }
3227 }
3228
3229 #[test]
3230 fn test_parse_window_rank_desc() {
3231 let stmt =
3232 parse("User { .dept, .salary, r: rank() over (partition .dept order .salary desc) }")
3233 .unwrap();
3234 match stmt {
3235 Statement::Query(q) => {
3236 let proj = q.projection.unwrap();
3237 assert_eq!(proj.len(), 3);
3238 match &proj[2].expr {
3239 Expr::Window {
3240 function,
3241 partition_by,
3242 order_by,
3243 ..
3244 } => {
3245 assert_eq!(*function, WindowFunc::Rank);
3246 assert_eq!(partition_by, &["dept"]);
3247 assert_eq!(order_by.len(), 1);
3248 assert!(order_by[0].descending);
3249 }
3250 other => panic!("expected Window, got {other:?}"),
3251 }
3252 }
3253 _ => panic!("expected query"),
3254 }
3255 }
3256
3257 #[test]
3258 fn test_parse_window_dense_rank() {
3259 let stmt = parse("User { .name, dr: dense_rank() over (order .score desc) }").unwrap();
3260 match stmt {
3261 Statement::Query(q) => {
3262 let proj = q.projection.unwrap();
3263 assert_eq!(proj.len(), 2);
3264 match &proj[1].expr {
3265 Expr::Window { function, .. } => {
3266 assert_eq!(*function, WindowFunc::DenseRank);
3267 }
3268 other => panic!("expected Window, got {other:?}"),
3269 }
3270 }
3271 _ => panic!("expected query"),
3272 }
3273 }
3274
3275 #[test]
3276 fn test_parse_sum_without_over_is_aggregate() {
3277 let stmt = parse("User group .dept { .dept, total: sum(.salary) }").unwrap();
3279 match stmt {
3280 Statement::Query(q) => {
3281 let proj = q.projection.unwrap();
3282 assert_eq!(proj.len(), 2);
3283 match &proj[1].expr {
3284 Expr::FunctionCall(AggFunc::Sum, _) => {} other => panic!("expected FunctionCall(Sum), got {other:?}"),
3286 }
3287 }
3288 _ => panic!("expected query"),
3289 }
3290 }
3291
3292 #[test]
3293 fn test_nesting_depth_limit() {
3294 let mut query = String::from("User filter ");
3296 for _ in 0..70 {
3297 query.push('(');
3298 }
3299 query.push_str(".age > 1");
3300 for _ in 0..70 {
3301 query.push(')');
3302 }
3303 let result = parse(&query);
3304 assert!(result.is_err());
3305 let err = result.unwrap_err();
3306 assert!(
3307 err.message().contains("nesting depth"),
3308 "expected nesting depth error, got: {}",
3309 err.message()
3310 );
3311 }
3312
3313 #[test]
3314 fn test_unary_prefix_nesting_depth_limit() {
3315 let query = String::from("User filter ") + &"not ".repeat(5000) + ".active";
3319 let result = parse(&query);
3320 assert!(result.is_err());
3321 let err = result.unwrap_err();
3322 assert!(
3323 err.message().contains("nesting depth"),
3324 "expected nesting depth error, got: {}",
3325 err.message()
3326 );
3327 }
3328
3329 #[test]
3330 fn test_moderate_nesting_succeeds() {
3331 let mut query = String::from("User filter ");
3333 for _ in 0..10 {
3334 query.push('(');
3335 }
3336 query.push_str(".age > 1");
3337 for _ in 0..10 {
3338 query.push(')');
3339 }
3340 assert!(parse(&query).is_ok());
3341 }
3342
3343 #[test]
3347 fn test_parse_fuzz_repro_projection_eof() {
3348 let err = parse("nn{").expect_err("unterminated projection must error, not panic");
3349 let _ = err.message();
3350 }
3351
3352 #[test]
3355 fn test_parse_fuzz_repro_short_projection_eof() {
3356 let err = parse("z{").expect_err("unterminated projection must error, not panic");
3357 let _ = err.message();
3358 }
3359
3360 #[test]
3361 fn test_update_at_statement_start_gives_helpful_error() {
3362 let err =
3363 parse(r#"update User filter .name = "Alice" { age := 31 }"#).expect_err("should fail");
3364 let msg = err.message();
3365 assert!(
3366 msg.contains("pipeline syntax"),
3367 "error should mention pipeline syntax, got: {msg}"
3368 );
3369 assert!(
3370 msg.contains("update"),
3371 "error should mention 'update', got: {msg}"
3372 );
3373 }
3374
3375 #[test]
3376 fn test_delete_at_statement_start_gives_helpful_error() {
3377 let err = parse("delete User filter .age < 18").expect_err("should fail");
3378 let msg = err.message();
3379 assert!(
3380 msg.contains("pipeline syntax"),
3381 "error should mention pipeline syntax, got: {msg}"
3382 );
3383 assert!(
3384 msg.contains("delete"),
3385 "error should mention 'delete', got: {msg}"
3386 );
3387 }
3388}
3389
3390#[cfg(test)]
3391mod cleanup_parser_dx_tests {
3392 use super::*;
3393
3394 #[test]
3395 fn typoed_statement_keyword_gets_suggestion() {
3396 let err = parse("updat User set age = 1").unwrap_err();
3397 let msg = err.to_string();
3398 assert!(msg.contains("near token"), "{msg}");
3399 assert!(msg.contains("did you mean `update`"), "{msg}");
3400 }
3401}