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