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 expect_named_ident(&mut self, context: &str) -> Result<String, ParseError> {
298 match self.advance() {
299 Token::Ident(n) => Ok(n),
300 t => Err(self.named_ident_error(context, &t)),
301 }
302 }
303
304 fn named_ident_error(&self, context: &str, got: &Token) -> ParseError {
308 if let Some(kw) = got.keyword_str() {
309 ParseError::Syntax {
310 message: format!(
314 "syntax error: '{kw}' is a reserved word and cannot be used as a {context}; \
315 rename it or quote it as `{kw}`"
316 ),
317 }
318 } else {
319 ParseError::UnexpectedToken {
320 expected: context.into(),
321 got: got.display_name(),
322 }
323 }
324 }
325
326 fn parse_optional_if_not_exists(&mut self) -> bool {
329 if matches!(self.peek(), Token::Ident(w) if w == "if")
330 && matches!(self.tokens.get(self.pos + 1), Some(Token::Not))
331 && matches!(self.tokens.get(self.pos + 2), Some(Token::Exists))
332 {
333 self.pos += 3;
334 true
335 } else {
336 false
337 }
338 }
339
340 fn parse_optional_if_exists(&mut self) -> bool {
342 if matches!(self.peek(), Token::Ident(w) if w == "if")
343 && matches!(self.tokens.get(self.pos + 1), Some(Token::Exists))
344 {
345 self.pos += 2;
346 true
347 } else {
348 false
349 }
350 }
351
352 fn parse_statement(&mut self) -> Result<Statement, ParseError> {
353 self.depth += 1;
354 if self.depth > MAX_NESTING_DEPTH {
355 self.depth -= 1;
356 return Err(ParseError::NestingDepthExceeded {
357 max: MAX_NESTING_DEPTH,
358 });
359 }
360 if matches!(self.peek(), Token::Explain) {
361 self.advance();
362 let inner = self.parse_statement()?;
363 self.depth -= 1;
364 return Ok(Statement::Explain(Box::new(inner)));
365 }
366 let stmt = match self.peek() {
367 Token::Insert => self.parse_insert(),
368 Token::Upsert => self.parse_upsert(),
369 Token::Type => self.parse_create_type(),
370 Token::Alter => self.parse_alter_table(),
371 Token::Drop => self.parse_drop_or_drop_view(),
372 Token::Materialized => self.parse_create_view(),
373 Token::Refresh => self.parse_refresh_view(),
374 Token::Begin => {
375 self.advance();
376 if *self.peek() == Token::Transaction {
378 self.advance();
379 }
380 return Ok(Statement::Begin);
381 }
382 Token::Commit => {
383 self.advance();
384 return Ok(Statement::Commit);
385 }
386 Token::Rollback => {
387 self.advance();
388 return Ok(Statement::Rollback);
389 }
390 Token::Schema => self.parse_schema(),
391 Token::Describe => self.parse_describe(),
392 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
393 self.parse_aggregate_query()
394 }
395 Token::Ident(_) => self.parse_query_or_mutation(),
396 Token::Update => Err(ParseError::Syntax {
397 message: "'update' cannot start a statement — in PowQL, use pipeline syntax: \
398 TableName filter ... update { ... }"
399 .into(),
400 }),
401 Token::Delete => Err(ParseError::Syntax {
402 message: "'delete' cannot start a statement — in PowQL, use pipeline syntax: \
403 TableName filter ... delete"
404 .into(),
405 }),
406 _ => Err(self.unexpected("statement", self.peek())),
407 }?;
408 let result = self.maybe_parse_union(stmt);
410 self.depth -= 1;
411 result
412 }
413
414 fn parse_query_or_mutation(&mut self) -> Result<Statement, ParseError> {
415 let source = match self.advance() {
416 Token::Ident(name) => name,
417 t => {
418 return Err(ParseError::UnexpectedToken {
419 expected: "type name".into(),
420 got: t.display_name(),
421 })
422 }
423 };
424 let alias = self.try_parse_alias();
425 let joins = self.parse_joins()?;
426
427 let mut filter = None;
431 let mut order = None;
432 let mut limit = None;
433 let mut offset = None;
434 let mut projection = None;
435 let mut distinct = false;
436 let mut group_by = None;
437
438 loop {
439 match self.peek() {
440 Token::Distinct => {
441 self.advance();
442 distinct = true;
443 }
444 Token::Group => {
445 self.advance();
446 group_by = Some(self.parse_group_by()?);
447 }
448 Token::Filter => {
449 self.advance();
450 filter = Some(self.parse_expr()?);
451 }
452 Token::Order => {
453 self.advance();
454 order = Some(self.parse_order()?);
455 }
456 Token::Limit => {
457 self.advance();
458 limit = Some(self.parse_expr()?);
459 }
460 Token::Offset => {
461 self.advance();
462 offset = Some(self.parse_expr()?);
463 }
464 Token::LBrace => {
465 projection = Some(self.parse_projection()?);
466 }
467 Token::Having => {
468 self.advance();
470 let having_expr = self.parse_expr()?;
471 let group = group_by.as_mut().ok_or_else(|| ParseError::Syntax {
472 message: "having without group by".into(),
473 })?;
474 let rewritten = match projection.as_ref() {
475 Some(fields) => substitute_projection_aliases(having_expr, fields),
476 None => having_expr,
477 };
478 group.having = Some(match group.having.take() {
479 Some(existing) => {
480 Expr::BinaryOp(Box::new(existing), BinOp::And, Box::new(rewritten))
481 }
482 None => rewritten,
483 });
484 }
485 Token::Update => {
486 if !joins.is_empty() {
487 return Err(ParseError::Unsupported {
488 feature: "update on a joined query is not supported".into(),
489 });
490 }
491 self.advance();
492 let assignments = self.parse_assignments()?;
493 let returning = *self.peek() == Token::Returning;
495 if returning {
496 self.advance();
497 }
498 return Ok(Statement::UpdateQuery(UpdateExpr {
499 source,
500 filter,
501 assignments,
502 returning,
503 }));
504 }
505 Token::Delete => {
506 if !joins.is_empty() {
507 return Err(ParseError::Unsupported {
508 feature: "delete on a joined query is not supported".into(),
509 });
510 }
511 self.advance();
512 let returning = *self.peek() == Token::Returning;
514 if returning {
515 self.advance();
516 }
517 return Ok(Statement::DeleteQuery(DeleteExpr {
518 source,
519 filter,
520 returning,
521 }));
522 }
523 _ => break,
524 }
525 }
526
527 Ok(Statement::Query(QueryExpr {
528 source,
529 alias,
530 joins,
531 filter,
532 order,
533 limit,
534 offset,
535 projection,
536 aggregation: None,
537 distinct,
538 group_by,
539 }))
540 }
541
542 fn parse_query_tail(&mut self, source: String) -> Result<QueryExpr, ParseError> {
548 let alias = self.try_parse_alias();
549 let joins = self.parse_joins()?;
550 let mut filter = None;
551 let mut order = None;
552 let mut limit = None;
553 let mut offset = None;
554 let mut projection = None;
555 let mut distinct = false;
556 let mut group_by = None;
557
558 loop {
559 match self.peek() {
560 Token::Distinct => {
561 self.advance();
562 distinct = true;
563 }
564 Token::Group => {
565 self.advance();
566 group_by = Some(self.parse_group_by()?);
567 }
568 Token::Filter => {
569 self.advance();
570 filter = Some(self.parse_expr()?);
571 }
572 Token::Order => {
573 self.advance();
574 order = Some(self.parse_order()?);
575 }
576 Token::Limit => {
577 self.advance();
578 limit = Some(self.parse_expr()?);
579 }
580 Token::Offset => {
581 self.advance();
582 offset = Some(self.parse_expr()?);
583 }
584 Token::LBrace => {
585 projection = Some(self.parse_projection()?);
586 }
587 Token::Having => {
588 self.advance();
594 let having_expr = self.parse_expr()?;
595 let group = group_by.as_mut().ok_or_else(|| ParseError::Syntax {
596 message: "having without group by".into(),
597 })?;
598 let rewritten = match projection.as_ref() {
599 Some(fields) => substitute_projection_aliases(having_expr, fields),
600 None => having_expr,
601 };
602 group.having = Some(match group.having.take() {
603 Some(existing) => {
604 Expr::BinaryOp(Box::new(existing), BinOp::And, Box::new(rewritten))
605 }
606 None => rewritten,
607 });
608 }
609 _ => break,
610 }
611 }
612
613 Ok(QueryExpr {
614 source,
615 alias,
616 joins,
617 filter,
618 order,
619 limit,
620 offset,
621 projection,
622 aggregation: None,
623 distinct,
624 group_by,
625 })
626 }
627
628 fn try_parse_alias(&mut self) -> Option<String> {
632 if *self.peek() == Token::As {
633 self.advance();
634 if let Token::Ident(name) = self.peek().clone() {
635 self.advance();
636 return Some(name);
637 }
638 }
639 None
640 }
641
642 fn parse_joins(&mut self) -> Result<Vec<JoinClause>, ParseError> {
649 let mut joins = Vec::new();
650 loop {
651 let kind = match self.peek() {
652 Token::Join => {
653 self.advance();
654 JoinKind::Inner
655 }
656 Token::Inner => {
657 self.advance();
658 self.expect(&Token::Join)?;
659 JoinKind::Inner
660 }
661 Token::LeftKw => {
662 self.advance();
663 if *self.peek() == Token::Outer {
664 self.advance();
665 }
666 self.expect(&Token::Join)?;
667 JoinKind::LeftOuter
668 }
669 Token::RightKw => {
670 self.advance();
671 if *self.peek() == Token::Outer {
672 self.advance();
673 }
674 self.expect(&Token::Join)?;
675 JoinKind::RightOuter
676 }
677 Token::Cross => {
678 self.advance();
679 self.expect(&Token::Join)?;
680 JoinKind::Cross
681 }
682 _ => break,
683 };
684
685 let source = match self.advance() {
686 Token::Ident(name) => name,
687 t => {
688 return Err(ParseError::UnexpectedToken {
689 expected: "type name after join".into(),
690 got: t.display_name(),
691 });
692 }
693 };
694 let alias = self.try_parse_alias();
695 let on = if kind == JoinKind::Cross {
696 None
697 } else if *self.peek() == Token::On {
698 self.advance();
699 Some(self.parse_expr()?)
700 } else {
701 return Err(ParseError::Syntax {
702 message: format!("expected `on <expr>` after join {source}"),
703 });
704 };
705
706 joins.push(JoinClause {
707 kind,
708 source,
709 alias,
710 on,
711 });
712 }
713 Ok(joins)
714 }
715
716 fn parse_insert(&mut self) -> Result<Statement, ParseError> {
717 self.expect(&Token::Insert)?;
718 let target = match self.advance() {
719 Token::Ident(name) => name,
720 t => {
721 return Err(ParseError::UnexpectedToken {
722 expected: "type name".into(),
723 got: t.display_name(),
724 })
725 }
726 };
727 let mut rows = vec![self.parse_assignments()?];
731 while *self.peek() == Token::Comma {
732 self.advance(); rows.push(self.parse_assignments()?);
734 }
735 let returning = *self.peek() == Token::Returning;
737 if returning {
738 self.advance();
739 }
740 Ok(Statement::Insert(InsertExpr {
741 target,
742 rows,
743 returning,
744 }))
745 }
746
747 fn parse_upsert(&mut self) -> Result<Statement, ParseError> {
749 self.expect(&Token::Upsert)?;
750 let target = match self.advance() {
751 Token::Ident(name) => name,
752 t => {
753 return Err(ParseError::UnexpectedToken {
754 expected: "type name".into(),
755 got: t.display_name(),
756 })
757 }
758 };
759 self.expect(&Token::On)?;
760 let key_column = match self.advance() {
761 Token::DotIdent(name) => name,
762 t => {
763 return Err(ParseError::UnexpectedToken {
764 expected: ".key_column".into(),
765 got: t.display_name(),
766 })
767 }
768 };
769 let assignments = self.parse_assignments()?;
770 let on_conflict = if *self.peek() == Token::On {
771 self.advance(); self.expect(&Token::Conflict)?;
773 self.parse_assignments()?
774 } else {
775 Vec::new()
776 };
777 Ok(Statement::Upsert(UpsertExpr {
778 target,
779 key_column,
780 assignments,
781 on_conflict,
782 }))
783 }
784
785 fn parse_assignments(&mut self) -> Result<Vec<Assignment>, ParseError> {
786 self.expect(&Token::LBrace)?;
787 let mut assignments = Vec::new();
788 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
789 let field = self.expect_named_ident("field name")?;
790 self.expect(&Token::Assign)?;
791 let value = self.parse_expr()?;
792 assignments.push(Assignment { field, value });
793 if *self.peek() == Token::Comma {
794 self.advance();
795 }
796 }
797 self.expect(&Token::RBrace)?;
798 Ok(assignments)
799 }
800
801 fn parse_projection(&mut self) -> Result<Vec<ProjectionField>, ParseError> {
802 self.expect(&Token::LBrace)?;
803 let mut fields = Vec::new();
804 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
805 if matches!(self.peek(), Token::Ident(_))
813 && matches!(self.tokens.get(self.pos + 1), Some(Token::Colon))
814 {
815 let alias = match self.advance() {
816 Token::Ident(name) => name,
817 _ => unreachable!("guarded by the matches! above"),
818 };
819 self.advance(); let expr = self.parse_expr()?;
821 fields.push(ProjectionField {
822 alias: Some(alias),
823 expr,
824 });
825 } else {
826 let expr = self.parse_expr()?;
827 fields.push(ProjectionField { alias: None, expr });
828 }
829 if *self.peek() == Token::Comma {
830 self.advance();
831 }
832 }
833 self.expect(&Token::RBrace)?;
834 Ok(fields)
835 }
836
837 fn parse_over_clause(&mut self) -> Result<(Vec<String>, Vec<OrderKey>), ParseError> {
840 self.expect(&Token::Over)?;
841 self.expect(&Token::LParen)?;
842 let mut partition_by = Vec::new();
843 let mut order_by = Vec::new();
844 if *self.peek() == Token::Partition {
845 self.advance();
846 while let Token::DotIdent(name) = self.peek() {
847 let name = name.clone();
848 self.advance();
849 partition_by.push(name);
850 if *self.peek() == Token::Comma {
851 if matches!(self.tokens.get(self.pos + 1), Some(Token::DotIdent(_))) {
855 self.advance();
856 } else {
857 break;
858 }
859 } else {
860 break;
861 }
862 }
863 }
864 if *self.peek() == Token::Order {
865 self.advance();
866 while let Token::DotIdent(name) = self.peek() {
867 let field = name.clone();
868 self.advance();
869 let descending = match self.peek() {
870 Token::Desc => {
871 self.advance();
872 true
873 }
874 Token::Asc => {
875 self.advance();
876 false
877 }
878 _ => false,
879 };
880 order_by.push(OrderKey { field, descending });
881 if *self.peek() == Token::Comma {
882 self.advance();
883 } else {
884 break;
885 }
886 }
887 }
888 self.expect(&Token::RParen)?;
889 Ok((partition_by, order_by))
890 }
891
892 fn parse_cast_type(&mut self) -> Result<CastType, ParseError> {
894 match self.advance() {
895 Token::StringLit(s) => match s.as_str() {
896 "int" | "Int" | "INT" => Ok(CastType::Int),
897 "float" | "Float" | "FLOAT" => Ok(CastType::Float),
898 "str" | "Str" | "STR" | "string" | "String" => Ok(CastType::Str),
899 "bool" | "Bool" | "BOOL" | "boolean" => Ok(CastType::Bool),
900 "datetime" | "DateTime" | "DATETIME" => Ok(CastType::DateTime),
901 "uuid" | "Uuid" | "UUID" => Ok(CastType::Uuid),
902 "bytes" | "Bytes" | "BYTES" | "bytea" => Ok(CastType::Bytes),
903 other => Err(ParseError::Syntax {
904 message: format!("invalid cast type: \"{other}\""),
905 }),
906 },
907 t => Err(ParseError::UnexpectedToken {
908 expected: "string literal for cast type".into(),
909 got: t.display_name(),
910 }),
911 }
912 }
913
914 fn parse_order(&mut self) -> Result<OrderClause, ParseError> {
915 let mut keys = Vec::new();
916 loop {
917 let field = match self.advance() {
918 Token::DotIdent(name) => name,
919 t => {
920 return Err(ParseError::UnexpectedToken {
921 expected: ".field after order".into(),
922 got: t.display_name(),
923 })
924 }
925 };
926 let descending = match self.peek() {
927 Token::Desc => {
928 self.advance();
929 true
930 }
931 Token::Asc => {
932 self.advance();
933 false
934 }
935 _ => false,
936 };
937 keys.push(OrderKey { field, descending });
938 if *self.peek() == Token::Comma {
939 self.advance();
940 } else {
941 break;
942 }
943 }
944 Ok(OrderClause { keys })
945 }
946
947 fn parse_aggregate_query(&mut self) -> Result<Statement, ParseError> {
948 let mut func = match self.advance() {
949 Token::Count => AggFunc::Count,
950 Token::Avg => AggFunc::Avg,
951 Token::Sum => AggFunc::Sum,
952 Token::Min => AggFunc::Min,
953 Token::Max => AggFunc::Max,
954 t => {
955 return Err(ParseError::UnexpectedToken {
956 expected: "aggregate function".into(),
957 got: t.display_name(),
958 })
959 }
960 };
961 self.expect(&Token::LParen)?;
962 if func == AggFunc::Count && *self.peek() == Token::Distinct {
964 self.advance();
965 func = AggFunc::CountDistinct;
966 }
967 let source = match self.advance() {
968 Token::Ident(name) => name,
969 t => {
970 return Err(ParseError::UnexpectedToken {
971 expected: "type name".into(),
972 got: t.display_name(),
973 })
974 }
975 };
976 let mut query = self.parse_query_tail(source)?;
980 self.expect(&Token::RParen)?;
981
982 let mut agg_field: Option<String> = None;
989 if func != AggFunc::Count {
990 if let Some(proj) = &query.projection {
991 if proj.len() == 1 && proj[0].alias.is_none() {
992 if let Expr::Field(name) = &proj[0].expr {
993 agg_field = Some(name.clone());
994 }
995 }
996 }
997 if agg_field.is_some() {
998 query.projection = None;
999 }
1000 }
1001 query.aggregation = Some(AggregateExpr {
1002 function: func,
1003 field: agg_field,
1004 });
1005 Ok(Statement::Query(query))
1006 }
1007
1008 fn parse_expr(&mut self) -> Result<Expr, ParseError> {
1009 self.depth += 1;
1010 if self.depth > MAX_NESTING_DEPTH {
1011 self.depth -= 1;
1012 return Err(ParseError::NestingDepthExceeded {
1013 max: MAX_NESTING_DEPTH,
1014 });
1015 }
1016 let result = self.parse_or_expr();
1017 self.depth -= 1;
1018 result
1019 }
1020
1021 fn parse_or_expr(&mut self) -> Result<Expr, ParseError> {
1022 let mut left = self.parse_and_expr()?;
1023 while *self.peek() == Token::Or {
1024 self.advance();
1025 let right = self.parse_and_expr()?;
1026 left = Expr::BinaryOp(Box::new(left), BinOp::Or, Box::new(right));
1027 }
1028 Ok(left)
1029 }
1030
1031 fn parse_and_expr(&mut self) -> Result<Expr, ParseError> {
1032 let mut left = self.parse_comparison()?;
1033 while *self.peek() == Token::And {
1034 self.advance();
1035 let right = self.parse_comparison()?;
1036 left = Expr::BinaryOp(Box::new(left), BinOp::And, Box::new(right));
1037 }
1038 Ok(left)
1039 }
1040
1041 fn parse_comparison(&mut self) -> Result<Expr, ParseError> {
1042 let left = self.parse_additive()?;
1043
1044 if *self.peek() == Token::Is {
1046 self.advance();
1047 if *self.peek() == Token::Not {
1048 self.advance();
1049 self.expect(&Token::Null)?;
1050 return Ok(Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(left)));
1051 } else {
1052 self.expect(&Token::Null)?;
1053 return Ok(Expr::UnaryOp(UnaryOp::IsNull, Box::new(left)));
1054 }
1055 }
1056
1057 match self.peek() {
1060 Token::In => {
1061 self.advance();
1062 return self.parse_in_list(left, false);
1063 }
1064 Token::Like => {
1065 self.advance();
1066 let pattern = self.parse_additive()?;
1067 return Ok(Expr::BinaryOp(
1068 Box::new(left),
1069 BinOp::Like,
1070 Box::new(pattern),
1071 ));
1072 }
1073 Token::Between => {
1074 self.advance();
1075 return self.parse_between(left, false);
1076 }
1077 Token::Not => {
1078 let next = self.tokens.get(self.pos + 1);
1082 match next {
1083 Some(Token::In) => {
1084 self.advance(); self.advance(); return self.parse_in_list(left, true);
1087 }
1088 Some(Token::Like) => {
1089 self.advance(); self.advance(); let pattern = self.parse_additive()?;
1092 let like = Expr::BinaryOp(Box::new(left), BinOp::Like, Box::new(pattern));
1093 return Ok(Expr::UnaryOp(UnaryOp::Not, Box::new(like)));
1094 }
1095 Some(Token::Between) => {
1096 self.advance(); self.advance(); return self.parse_between(left, true);
1099 }
1100 _ => {}
1101 }
1102 }
1103 _ => {}
1104 }
1105
1106 let op = match self.peek() {
1107 Token::Eq => BinOp::Eq,
1108 Token::Neq => BinOp::Neq,
1109 Token::Lt => BinOp::Lt,
1110 Token::Gt => BinOp::Gt,
1111 Token::Lte => BinOp::Lte,
1112 Token::Gte => BinOp::Gte,
1113 _ => return Ok(left),
1114 };
1115 self.advance();
1116 if *self.peek() == Token::Null {
1120 match op {
1121 BinOp::Eq => {
1122 self.advance();
1123 return Ok(Expr::UnaryOp(UnaryOp::IsNull, Box::new(left)));
1124 }
1125 BinOp::Neq => {
1126 self.advance();
1127 return Ok(Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(left)));
1128 }
1129 _ => {}
1130 }
1131 }
1132 let right = self.parse_additive()?;
1133 Ok(Expr::BinaryOp(Box::new(left), op, Box::new(right)))
1134 }
1135
1136 fn parse_in_list(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
1141 self.expect(&Token::LParen)?;
1142 if let Token::Ident(_) = self.peek() {
1144 let after = self.tokens.get(self.pos + 1);
1147 let is_subquery = !matches!(after, Some(Token::Comma) | Some(Token::RParen));
1148 if is_subquery {
1149 let source = match self.advance() {
1150 Token::Ident(name) => name,
1151 _ => unreachable!(),
1152 };
1153 let subquery = self.parse_query_tail(source)?;
1154 self.expect(&Token::RParen)?;
1155 return Ok(Expr::InSubquery {
1156 expr: Box::new(expr),
1157 subquery: Box::new(subquery),
1158 negated,
1159 });
1160 }
1161 }
1162 let mut list = Vec::new();
1163 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1164 list.push(self.parse_expr()?);
1165 if *self.peek() == Token::Comma {
1166 self.advance();
1167 }
1168 }
1169 self.expect(&Token::RParen)?;
1170 Ok(Expr::InList {
1171 expr: Box::new(expr),
1172 list,
1173 negated,
1174 })
1175 }
1176
1177 fn try_parse_exists_subquery(&mut self) -> Result<Option<QueryExpr>, ParseError> {
1185 if *self.peek() != Token::LParen {
1186 return Ok(None);
1187 }
1188 let after_lparen = self.tokens.get(self.pos + 1);
1192 if !matches!(after_lparen, Some(Token::Ident(_))) {
1193 return Ok(None);
1194 }
1195 self.expect(&Token::LParen)?;
1196 let source = match self.advance() {
1197 Token::Ident(name) => name,
1198 _ => unreachable!(),
1199 };
1200 let subquery = self.parse_query_tail(source)?;
1201 self.expect(&Token::RParen)?;
1202 Ok(Some(subquery))
1203 }
1204
1205 fn parse_between(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
1209 let low = self.parse_additive()?;
1210 self.expect(&Token::And)?;
1211 let high = self.parse_additive()?;
1212 if negated {
1213 Ok(Expr::BinaryOp(
1215 Box::new(Expr::BinaryOp(
1216 Box::new(expr.clone()),
1217 BinOp::Lt,
1218 Box::new(low),
1219 )),
1220 BinOp::Or,
1221 Box::new(Expr::BinaryOp(Box::new(expr), BinOp::Gt, Box::new(high))),
1222 ))
1223 } else {
1224 Ok(Expr::BinaryOp(
1226 Box::new(Expr::BinaryOp(
1227 Box::new(expr.clone()),
1228 BinOp::Gte,
1229 Box::new(low),
1230 )),
1231 BinOp::And,
1232 Box::new(Expr::BinaryOp(Box::new(expr), BinOp::Lte, Box::new(high))),
1233 ))
1234 }
1235 }
1236
1237 fn parse_group_by(&mut self) -> Result<GroupByClause, ParseError> {
1244 let mut keys = Vec::new();
1245 loop {
1246 match self.peek().clone() {
1247 Token::DotIdent(field) => {
1248 self.advance();
1249 keys.push(GroupKey::Unqualified(field));
1250 }
1251 Token::Ident(alias) => {
1252 self.advance();
1253 match self.peek().clone() {
1254 Token::DotIdent(field) => {
1255 self.advance();
1256 keys.push(GroupKey::Qualified { alias, field });
1257 }
1258 _ => {
1259 return Err(ParseError::Syntax {
1260 message: "expected .field after qualifier in group key".into(),
1261 });
1262 }
1263 }
1264 }
1265 _ => break,
1266 }
1267 if *self.peek() == Token::Comma {
1268 self.advance();
1269 } else {
1270 break;
1271 }
1272 }
1273 if keys.is_empty() {
1274 return Err(ParseError::Syntax {
1275 message: "expected at least one group key after group".into(),
1276 });
1277 }
1278 let having = if *self.peek() == Token::Having {
1279 self.advance();
1280 Some(self.parse_expr()?)
1281 } else {
1282 None
1283 };
1284 Ok(GroupByClause { keys, having })
1285 }
1286
1287 fn parse_additive(&mut self) -> Result<Expr, ParseError> {
1288 let mut left = self.parse_multiplicative()?;
1289 loop {
1290 let op = match self.peek() {
1291 Token::Plus => BinOp::Add,
1292 Token::Minus => BinOp::Sub,
1293 Token::Coalesce => {
1294 self.advance();
1295 let right = self.parse_multiplicative()?;
1296 left = Expr::Coalesce(Box::new(left), Box::new(right));
1297 continue;
1298 }
1299 _ => break,
1300 };
1301 self.advance();
1302 let right = self.parse_multiplicative()?;
1303 left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
1304 }
1305 Ok(left)
1306 }
1307
1308 fn parse_multiplicative(&mut self) -> Result<Expr, ParseError> {
1309 let mut left = self.parse_primary()?;
1310 loop {
1311 let op = match self.peek() {
1312 Token::Star => BinOp::Mul,
1313 Token::Slash => BinOp::Div,
1314 _ => break,
1315 };
1316 self.advance();
1317 let right = self.parse_primary()?;
1318 left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
1319 }
1320 Ok(left)
1321 }
1322
1323 fn parse_primary(&mut self) -> Result<Expr, ParseError> {
1324 self.depth += 1;
1329 if self.depth > MAX_NESTING_DEPTH {
1330 self.depth -= 1;
1331 return Err(ParseError::NestingDepthExceeded {
1332 max: MAX_NESTING_DEPTH,
1333 });
1334 }
1335 let result = self.parse_primary_inner();
1336 self.depth -= 1;
1337 result
1338 }
1339
1340 fn parse_primary_inner(&mut self) -> Result<Expr, ParseError> {
1341 match self.peek().clone() {
1342 Token::DotIdent(name) => {
1343 self.advance();
1344 Ok(Expr::Field(name))
1345 }
1346 Token::IntLit(v) => {
1347 self.advance();
1348 Ok(Expr::Literal(Literal::Int(v)))
1349 }
1350 Token::FloatLit(v) => {
1351 self.advance();
1352 Ok(Expr::Literal(Literal::Float(v)))
1353 }
1354 Token::StringLit(v) => {
1355 self.advance();
1356 Ok(Expr::Literal(Literal::String(v)))
1357 }
1358 Token::BoolLit(v) => {
1359 self.advance();
1360 Ok(Expr::Literal(Literal::Bool(v)))
1361 }
1362 Token::Null => {
1369 self.advance();
1370 Ok(Expr::Null)
1371 }
1372 Token::Not => {
1373 self.advance();
1374 if *self.peek() == Token::Exists {
1375 self.advance();
1376 if let Some(sub) = self.try_parse_exists_subquery()? {
1380 return Ok(Expr::ExistsSubquery {
1381 subquery: Box::new(sub),
1382 negated: true,
1383 });
1384 }
1385 let expr = self.parse_primary()?;
1386 Ok(Expr::UnaryOp(UnaryOp::NotExists, Box::new(expr)))
1387 } else {
1388 let expr = self.parse_primary()?;
1389 Ok(Expr::UnaryOp(UnaryOp::Not, Box::new(expr)))
1390 }
1391 }
1392 Token::Exists => {
1393 self.advance();
1394 if let Some(sub) = self.try_parse_exists_subquery()? {
1398 return Ok(Expr::ExistsSubquery {
1399 subquery: Box::new(sub),
1400 negated: false,
1401 });
1402 }
1403 let expr = self.parse_primary()?;
1404 Ok(Expr::UnaryOp(UnaryOp::Exists, Box::new(expr)))
1405 }
1406 Token::LParen => {
1407 self.advance();
1408 let expr = self.parse_expr()?;
1409 self.expect(&Token::RParen)?;
1410 Ok(expr)
1411 }
1412 Token::Ident(name) => {
1413 self.advance();
1414 if *self.peek() == Token::LParen {
1419 let cast_type = match name.as_str() {
1420 "uuid" => Some(CastType::Uuid),
1421 "bytes" => Some(CastType::Bytes),
1422 _ => None,
1423 };
1424 if let Some(cast_type) = cast_type {
1425 self.advance(); let inner = self.parse_expr()?;
1427 self.expect(&Token::RParen)?;
1428 return Ok(Expr::Cast(Box::new(inner), cast_type));
1429 }
1430 }
1431 if let Token::DotIdent(field) = self.peek().clone() {
1435 self.advance();
1436 return Ok(Expr::QualifiedField {
1437 qualifier: name,
1438 field,
1439 });
1440 }
1441 Ok(Expr::Field(name))
1442 }
1443 Token::RowNumber | Token::Rank | Token::DenseRank => {
1445 let wfunc = match self.advance() {
1446 Token::RowNumber => WindowFunc::RowNumber,
1447 Token::Rank => WindowFunc::Rank,
1448 Token::DenseRank => WindowFunc::DenseRank,
1449 _ => {
1450 return Err(ParseError::Syntax {
1451 message: "unexpected window function token".into(),
1452 })
1453 }
1454 };
1455 self.expect(&Token::LParen)?;
1456 self.expect(&Token::RParen)?;
1457 let (partition_by, order_by) = self.parse_over_clause()?;
1458 Ok(Expr::Window {
1459 function: wfunc,
1460 args: vec![],
1461 partition_by,
1462 order_by,
1463 })
1464 }
1465 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1469 let mut func = match self.advance() {
1470 Token::Count => AggFunc::Count,
1471 Token::Avg => AggFunc::Avg,
1472 Token::Sum => AggFunc::Sum,
1473 Token::Min => AggFunc::Min,
1474 Token::Max => AggFunc::Max,
1475 _ => {
1476 return Err(ParseError::Syntax {
1477 message: "unexpected aggregate token".into(),
1478 })
1479 }
1480 };
1481 self.expect(&Token::LParen)?;
1482 if func == AggFunc::Count && *self.peek() == Token::Star {
1484 self.advance();
1485 self.expect(&Token::RParen)?;
1486 if *self.peek() == Token::Over {
1488 let (partition_by, order_by) = self.parse_over_clause()?;
1489 return Ok(Expr::Window {
1490 function: WindowFunc::Count,
1491 args: vec![Expr::Field("*".into())],
1492 partition_by,
1493 order_by,
1494 });
1495 }
1496 return Ok(Expr::FunctionCall(
1497 AggFunc::Count,
1498 Box::new(Expr::Field("*".into())),
1499 ));
1500 }
1501 if func == AggFunc::Count && *self.peek() == Token::Distinct {
1503 self.advance();
1504 func = AggFunc::CountDistinct;
1505 }
1506 let inner = self.parse_expr()?;
1507 self.expect(&Token::RParen)?;
1508 if *self.peek() == Token::Over {
1510 let wfunc = match func {
1511 AggFunc::Count => WindowFunc::Count,
1512 AggFunc::Avg => WindowFunc::Avg,
1513 AggFunc::Sum => WindowFunc::Sum,
1514 AggFunc::Min => WindowFunc::Min,
1515 AggFunc::Max => WindowFunc::Max,
1516 _ => {
1517 return Err(ParseError::Unsupported {
1518 feature: "count(distinct ...) over (...) is not supported".into(),
1519 })
1520 }
1521 };
1522 let (partition_by, order_by) = self.parse_over_clause()?;
1523 return Ok(Expr::Window {
1524 function: wfunc,
1525 args: vec![inner],
1526 partition_by,
1527 order_by,
1528 });
1529 }
1530 Ok(Expr::FunctionCall(func, Box::new(inner)))
1531 }
1532 Token::Upper
1533 | Token::Lower
1534 | Token::Length
1535 | Token::Trim
1536 | Token::Substring
1537 | Token::Concat
1538 | Token::Abs
1539 | Token::Round
1540 | Token::Ceil
1541 | Token::Floor
1542 | Token::Sqrt
1543 | Token::Pow
1544 | Token::Now
1545 | Token::Extract
1546 | Token::DateAdd
1547 | Token::DateDiff => {
1548 let tok = self.advance();
1549 let func = token_to_scalar_fn(&tok);
1550 self.expect(&Token::LParen)?;
1551 let mut args = Vec::new();
1552 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1553 args.push(self.parse_expr()?);
1554 if *self.peek() == Token::Comma {
1555 self.advance();
1556 }
1557 }
1558 self.expect(&Token::RParen)?;
1559 Ok(Expr::ScalarFunc(func, args))
1560 }
1561 Token::Cast => {
1562 self.advance();
1563 self.expect(&Token::LParen)?;
1564 let inner = self.parse_expr()?;
1565 self.expect(&Token::Comma)?;
1566 let cast_type = self.parse_cast_type()?;
1567 self.expect(&Token::RParen)?;
1568 Ok(Expr::Cast(Box::new(inner), cast_type))
1569 }
1570 Token::Case => {
1571 self.advance();
1572 let mut whens = Vec::new();
1573 while *self.peek() == Token::When {
1574 self.advance();
1575 let condition = self.parse_expr()?;
1576 self.expect(&Token::Then)?;
1577 let result = self.parse_expr()?;
1578 whens.push((Box::new(condition), Box::new(result)));
1579 }
1580 let else_expr = if *self.peek() == Token::Else {
1581 self.advance();
1582 Some(Box::new(self.parse_expr()?))
1583 } else {
1584 None
1585 };
1586 self.expect(&Token::End)?;
1587 Ok(Expr::Case { whens, else_expr })
1588 }
1589 t => Err(ParseError::Syntax {
1590 message: format!("unexpected token in expression: {}", t.display_name()),
1591 }),
1592 }
1593 }
1594
1595 fn parse_alter_table(&mut self) -> Result<Statement, ParseError> {
1598 self.expect(&Token::Alter)?;
1599 let table = match self.advance() {
1600 Token::Ident(name) => name,
1601 t => {
1602 return Err(ParseError::UnexpectedToken {
1603 expected: "table name after alter".into(),
1604 got: t.display_name(),
1605 })
1606 }
1607 };
1608 match self.peek() {
1609 Token::Add => {
1610 self.advance();
1611 if *self.peek() == Token::Index {
1613 self.advance();
1614 let if_not_exists = self.parse_optional_if_not_exists();
1615 let column = match self.advance() {
1616 Token::DotIdent(n) => n,
1617 t => {
1618 return Err(ParseError::UnexpectedToken {
1619 expected: ".<column> after add index".into(),
1620 got: t.display_name(),
1621 })
1622 }
1623 };
1624 return Ok(Statement::AlterTable(AlterTableExpr {
1625 table,
1626 action: AlterAction::AddIndex {
1627 column,
1628 if_not_exists,
1629 },
1630 }));
1631 }
1632 if *self.peek() == Token::Unique {
1634 self.advance();
1635 let if_not_exists = self.parse_optional_if_not_exists();
1636 let column = match self.advance() {
1637 Token::DotIdent(n) => n,
1638 t => {
1639 return Err(ParseError::UnexpectedToken {
1640 expected: ".<column> after add unique".into(),
1641 got: t.display_name(),
1642 })
1643 }
1644 };
1645 return Ok(Statement::AlterTable(AlterTableExpr {
1646 table,
1647 action: AlterAction::AddUnique {
1648 column,
1649 if_not_exists,
1650 },
1651 }));
1652 }
1653 if *self.peek() == Token::Column {
1655 self.advance();
1656 }
1657 let required = if *self.peek() == Token::Required {
1658 self.advance();
1659 true
1660 } else {
1661 false
1662 };
1663 let name = self.expect_named_ident("column name")?;
1664 self.expect(&Token::Colon)?;
1665 let type_name = match self.advance() {
1666 Token::Ident(n) => n,
1667 t => {
1668 return Err(ParseError::UnexpectedToken {
1669 expected: "type name".into(),
1670 got: t.display_name(),
1671 })
1672 }
1673 };
1674 Ok(Statement::AlterTable(AlterTableExpr {
1675 table,
1676 action: AlterAction::AddColumn {
1677 name,
1678 type_name,
1679 required,
1680 },
1681 }))
1682 }
1683 Token::Drop => {
1684 self.advance();
1685 if *self.peek() == Token::Column {
1687 self.advance();
1688 }
1689 let if_exists = self.parse_optional_if_exists();
1690 let name = self.expect_named_ident("column name")?;
1691 Ok(Statement::AlterTable(AlterTableExpr {
1692 table,
1693 action: AlterAction::DropColumn { name, if_exists },
1694 }))
1695 }
1696 t => Err(ParseError::UnexpectedToken {
1697 expected: "add or drop after alter <table>".into(),
1698 got: t.display_name(),
1699 }),
1700 }
1701 }
1702
1703 fn parse_drop_or_drop_view(&mut self) -> Result<Statement, ParseError> {
1705 self.expect(&Token::Drop)?;
1706 if *self.peek() == Token::View {
1707 self.advance(); let if_exists = self.parse_optional_if_exists();
1709 let name = match self.advance() {
1710 Token::Ident(name) => name,
1711 t => {
1712 return Err(ParseError::UnexpectedToken {
1713 expected: "view name after drop view".into(),
1714 got: t.display_name(),
1715 })
1716 }
1717 };
1718 return Ok(Statement::DropView(DropViewExpr { name, if_exists }));
1719 }
1720 let if_exists = self.parse_optional_if_exists();
1721 let table = match self.advance() {
1722 Token::Ident(name) => name,
1723 t => {
1724 return Err(ParseError::UnexpectedToken {
1725 expected: "table name after drop".into(),
1726 got: t.display_name(),
1727 })
1728 }
1729 };
1730 Ok(Statement::DropTable(DropTableExpr { table, if_exists }))
1731 }
1732
1733 fn parse_create_view(&mut self) -> Result<Statement, ParseError> {
1738 self.expect(&Token::Materialized)?;
1739 let name = match self.advance() {
1740 Token::Ident(name) => name,
1741 t => {
1742 return Err(ParseError::UnexpectedToken {
1743 expected: "view name after materialize".into(),
1744 got: t.display_name(),
1745 })
1746 }
1747 };
1748 self.expect(&Token::As)?;
1749 let query_start = self.pos;
1751 let source = match self.advance() {
1752 Token::Ident(s) => s,
1753 t => {
1754 return Err(ParseError::UnexpectedToken {
1755 expected: "source table name".into(),
1756 got: t.display_name(),
1757 })
1758 }
1759 };
1760 let query = self.parse_query_tail(source)?;
1761 let query_text = tokens_to_text(&self.tokens[query_start..self.pos]);
1763 Ok(Statement::CreateView(CreateViewExpr {
1764 name,
1765 query,
1766 query_text,
1767 }))
1768 }
1769
1770 fn maybe_parse_union(&mut self, left: Statement) -> Result<Statement, ParseError> {
1773 if *self.peek() != Token::Union {
1774 return Ok(left);
1775 }
1776 if !matches!(left, Statement::Query(_) | Statement::Union(_)) {
1777 return Err(ParseError::Syntax {
1778 message: "UNION requires a query on the left side".into(),
1779 });
1780 }
1781 self.advance(); let all = if let Token::Ident(s) = self.peek() {
1783 if s == "all" {
1784 self.advance();
1785 true
1786 } else {
1787 false
1788 }
1789 } else {
1790 false
1791 };
1792 let right = self.parse_single_query()?;
1794 let union = Statement::Union(UnionExpr {
1795 left: Box::new(left),
1796 right: Box::new(right),
1797 all,
1798 });
1799 self.maybe_parse_union(union)
1801 }
1802
1803 fn parse_single_query(&mut self) -> Result<Statement, ParseError> {
1805 match self.peek() {
1806 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1807 self.parse_aggregate_query()
1808 }
1809 Token::Ident(_) => self.parse_query_or_mutation(),
1810 _ => Err(ParseError::Syntax {
1811 message: format!(
1812 "expected query after UNION, got {}",
1813 self.peek().display_name()
1814 ),
1815 }),
1816 }
1817 }
1818
1819 fn parse_refresh_view(&mut self) -> Result<Statement, ParseError> {
1821 self.expect(&Token::Refresh)?;
1822 let name = match self.advance() {
1823 Token::Ident(name) => name,
1824 t => {
1825 return Err(ParseError::UnexpectedToken {
1826 expected: "view name after refresh".into(),
1827 got: t.display_name(),
1828 })
1829 }
1830 };
1831 Ok(Statement::RefreshView(RefreshViewExpr { name }))
1832 }
1833
1834 fn parse_create_type(&mut self) -> Result<Statement, ParseError> {
1835 self.expect(&Token::Type)?;
1836 let name = self.expect_named_ident("type name")?;
1837 let if_not_exists = self.parse_optional_if_not_exists();
1838 self.expect(&Token::LBrace)?;
1839 let mut fields = Vec::new();
1840 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1841 let (mut required, mut unique, mut auto) = (false, false, false);
1846 loop {
1847 let is_modifier =
1848 matches!(self.peek(), Token::Required | Token::Unique | Token::Auto)
1849 && !matches!(self.tokens.get(self.pos + 1), Some(Token::Colon));
1850 if !is_modifier {
1851 break;
1852 }
1853 match self.advance() {
1854 Token::Required => required = true,
1855 Token::Unique => unique = true,
1856 Token::Auto => auto = true,
1857 _ => unreachable!("guarded by is_modifier"),
1858 }
1859 }
1860 let field_name = self.expect_named_ident("field name")?;
1861 self.expect(&Token::Colon)?;
1862 let type_name = match self.advance() {
1863 Token::Ident(n) => n,
1864 t => {
1865 return Err(ParseError::UnexpectedToken {
1866 expected: "type name".into(),
1867 got: t.display_name(),
1868 })
1869 }
1870 };
1871 let default = if *self.peek() == Token::Default {
1874 self.advance();
1875 Some(self.parse_default_literal()?)
1876 } else {
1877 None
1878 };
1879 fields.push(FieldDef {
1880 name: field_name,
1881 type_name,
1882 required,
1883 unique,
1884 default,
1885 auto,
1886 });
1887 if *self.peek() == Token::Comma {
1888 self.advance();
1889 }
1890 }
1891 self.expect(&Token::RBrace)?;
1892 Ok(Statement::CreateType(CreateTypeExpr {
1893 name,
1894 fields,
1895 if_not_exists,
1896 }))
1897 }
1898
1899 fn parse_schema(&mut self) -> Result<Statement, ParseError> {
1902 self.expect(&Token::Schema)?;
1903 if let Token::Ident(_) = self.peek() {
1904 let table = self.expect_named_ident("type name")?;
1905 return Ok(Statement::Describe(table));
1906 }
1907 Ok(Statement::ListTypes)
1908 }
1909
1910 fn parse_describe(&mut self) -> Result<Statement, ParseError> {
1912 self.expect(&Token::Describe)?;
1913 let table = self.expect_named_ident("type name")?;
1914 Ok(Statement::Describe(table))
1915 }
1916
1917 fn parse_default_literal(&mut self) -> Result<Literal, ParseError> {
1921 match self.advance() {
1922 Token::IntLit(v) => Ok(Literal::Int(v)),
1923 Token::FloatLit(v) => Ok(Literal::Float(v)),
1924 Token::StringLit(v) => Ok(Literal::String(v)),
1925 Token::BoolLit(v) => Ok(Literal::Bool(v)),
1926 t => Err(ParseError::UnexpectedToken {
1927 expected: "literal default value".into(),
1928 got: t.display_name(),
1929 }),
1930 }
1931 }
1932}
1933
1934fn tokens_to_text(tokens: &[Token]) -> String {
1938 let mut out = String::with_capacity(64);
1939 for tok in tokens {
1940 if !out.is_empty() && !matches!(tok, Token::Eof) {
1941 out.push(' ');
1942 }
1943 match tok {
1944 Token::Ident(s) => out.push_str(s),
1945 Token::DotIdent(s) => {
1946 out.push('.');
1947 out.push_str(s);
1948 }
1949 Token::IntLit(v) => out.push_str(&v.to_string()),
1950 Token::FloatLit(v) => out.push_str(&v.to_string()),
1951 Token::StringLit(s) => {
1952 out.push('"');
1953 out.push_str(s);
1954 out.push('"');
1955 }
1956 Token::BoolLit(v) => out.push_str(if *v { "true" } else { "false" }),
1957 Token::Param(s) => {
1958 out.push('$');
1959 out.push_str(s);
1960 }
1961 Token::Type => out.push_str("type"),
1962 Token::Filter => out.push_str("filter"),
1963 Token::Order => out.push_str("order"),
1964 Token::Limit => out.push_str("limit"),
1965 Token::Offset => out.push_str("offset"),
1966 Token::Insert => out.push_str("insert"),
1967 Token::Update => out.push_str("update"),
1968 Token::Delete => out.push_str("delete"),
1969 Token::Upsert => out.push_str("upsert"),
1970 Token::Returning => out.push_str("returning"),
1971 Token::Conflict => out.push_str("conflict"),
1972 Token::Select => out.push_str("select"),
1973 Token::Required => out.push_str("required"),
1974 Token::Default => out.push_str("default"),
1975 Token::Auto => out.push_str("auto"),
1976 Token::Multi => out.push_str("multi"),
1977 Token::Link => out.push_str("link"),
1978 Token::Index => out.push_str("index"),
1979 Token::Unique => out.push_str("unique"),
1980 Token::On => out.push_str("on"),
1981 Token::Asc => out.push_str("asc"),
1982 Token::Desc => out.push_str("desc"),
1983 Token::And => out.push_str("and"),
1984 Token::Or => out.push_str("or"),
1985 Token::Not => out.push_str("not"),
1986 Token::Exists => out.push_str("exists"),
1987 Token::Let => out.push_str("let"),
1988 Token::As => out.push_str("as"),
1989 Token::Match => out.push_str("match"),
1990 Token::Group => out.push_str("group"),
1991 Token::Join => out.push_str("join"),
1992 Token::Inner => out.push_str("inner"),
1993 Token::LeftKw => out.push_str("left"),
1994 Token::RightKw => out.push_str("right"),
1995 Token::Outer => out.push_str("outer"),
1996 Token::Cross => out.push_str("cross"),
1997 Token::Transaction => out.push_str("transaction"),
1998 Token::Begin => out.push_str("begin"),
1999 Token::Commit => out.push_str("commit"),
2000 Token::Rollback => out.push_str("rollback"),
2001 Token::View => out.push_str("view"),
2002 Token::Materialized => out.push_str("materialized"),
2003 Token::Refresh => out.push_str("refresh"),
2004 Token::Union => out.push_str("union"),
2005 Token::Having => out.push_str("having"),
2006 Token::Distinct => out.push_str("distinct"),
2007 Token::In => out.push_str("in"),
2008 Token::Between => out.push_str("between"),
2009 Token::Like => out.push_str("like"),
2010 Token::Count => out.push_str("count"),
2011 Token::Avg => out.push_str("avg"),
2012 Token::Sum => out.push_str("sum"),
2013 Token::Min => out.push_str("min"),
2014 Token::Max => out.push_str("max"),
2015 Token::Is => out.push_str("is"),
2016 Token::Null => out.push_str("null"),
2017 Token::Upper => out.push_str("upper"),
2018 Token::Lower => out.push_str("lower"),
2019 Token::Length => out.push_str("length"),
2020 Token::Trim => out.push_str("trim"),
2021 Token::Substring => out.push_str("substring"),
2022 Token::Concat => out.push_str("concat"),
2023 Token::Abs => out.push_str("abs"),
2024 Token::Round => out.push_str("round"),
2025 Token::Ceil => out.push_str("ceil"),
2026 Token::Floor => out.push_str("floor"),
2027 Token::Sqrt => out.push_str("sqrt"),
2028 Token::Pow => out.push_str("pow"),
2029 Token::Now => out.push_str("now"),
2030 Token::Extract => out.push_str("extract"),
2031 Token::DateAdd => out.push_str("date_add"),
2032 Token::DateDiff => out.push_str("date_diff"),
2033 Token::Cast => out.push_str("cast"),
2034 Token::Case => out.push_str("case"),
2035 Token::When => out.push_str("when"),
2036 Token::Then => out.push_str("then"),
2037 Token::Else => out.push_str("else"),
2038 Token::End => out.push_str("end"),
2039 Token::Over => out.push_str("over"),
2040 Token::Partition => out.push_str("partition"),
2041 Token::RowNumber => out.push_str("row_number"),
2042 Token::Rank => out.push_str("rank"),
2043 Token::DenseRank => out.push_str("dense_rank"),
2044 Token::Alter => out.push_str("alter"),
2045 Token::Drop => out.push_str("drop"),
2046 Token::Add => out.push_str("add"),
2047 Token::Column => out.push_str("column"),
2048 Token::Eq => out.push('='),
2049 Token::Neq => out.push_str("!="),
2050 Token::Lt => out.push('<'),
2051 Token::Gt => out.push('>'),
2052 Token::Lte => out.push_str("<="),
2053 Token::Gte => out.push_str(">="),
2054 Token::Assign => out.push_str(":="),
2055 Token::Arrow => out.push_str("->"),
2056 Token::Pipe => out.push('|'),
2057 Token::Coalesce => out.push_str("??"),
2058 Token::Plus => out.push('+'),
2059 Token::Minus => out.push('-'),
2060 Token::Star => out.push('*'),
2061 Token::Slash => out.push('/'),
2062 Token::LBrace => out.push('{'),
2063 Token::RBrace => out.push('}'),
2064 Token::LParen => out.push('('),
2065 Token::RParen => out.push(')'),
2066 Token::Comma => out.push(','),
2067 Token::Colon => out.push(':'),
2068 Token::Dot => out.push('.'),
2069 Token::Explain => out.push_str("explain"),
2070 Token::Schema => out.push_str("schema"),
2071 Token::Describe => out.push_str("describe"),
2072 Token::Eof => {}
2073 }
2074 }
2075 out
2076}
2077
2078#[cfg(test)]
2079mod tests {
2080 use super::*;
2081 #[test]
2082 fn test_parse_simple_query() {
2083 let stmt = parse("User").unwrap();
2084 match stmt {
2085 Statement::Query(q) => {
2086 assert_eq!(q.source, "User");
2087 assert!(q.filter.is_none());
2088 assert!(q.projection.is_none());
2089 }
2090 _ => panic!("expected query"),
2091 }
2092 }
2093
2094 #[test]
2095 fn test_parse_filter() {
2096 let stmt = parse("User filter .age > 30").unwrap();
2097 match stmt {
2098 Statement::Query(q) => {
2099 assert_eq!(q.source, "User");
2100 assert!(q.filter.is_some());
2101 }
2102 _ => panic!("expected query"),
2103 }
2104 }
2105
2106 #[test]
2107 fn test_parse_projection() {
2108 let stmt = parse("User { name, email }").unwrap();
2109 match stmt {
2110 Statement::Query(q) => {
2111 let proj = q.projection.unwrap();
2112 assert_eq!(proj.len(), 2);
2113 }
2114 _ => panic!("expected query"),
2115 }
2116 }
2117
2118 #[test]
2119 fn test_parse_filter_order_limit() {
2120 let stmt = parse("User filter .age > 30 order .name desc limit 10").unwrap();
2121 match stmt {
2122 Statement::Query(q) => {
2123 assert!(q.filter.is_some());
2124 let order = q.order.unwrap();
2125 assert_eq!(order.keys.len(), 1);
2126 assert_eq!(order.keys[0].field, "name");
2127 assert!(order.keys[0].descending);
2128 assert!(q.limit.is_some());
2129 }
2130 _ => panic!("expected query"),
2131 }
2132 }
2133
2134 #[test]
2135 fn test_parse_insert() {
2136 let stmt = parse(r#"insert User { name := "Alice", age := 30 }"#).unwrap();
2137 match stmt {
2138 Statement::Insert(ins) => {
2139 assert_eq!(ins.target, "User");
2140 assert_eq!(ins.rows.len(), 1);
2141 assert_eq!(ins.rows[0].len(), 2);
2142 assert_eq!(ins.rows[0][0].field, "name");
2143 assert_eq!(ins.rows[0][1].field, "age");
2144 }
2145 _ => panic!("expected insert"),
2146 }
2147 }
2148
2149 #[test]
2150 fn test_parse_insert_multi_row() {
2151 let stmt =
2152 parse(r#"insert User { name := "Alice", age := 30 }, { name := "Bob", age := 25 }, { name := "Cy" }"#)
2153 .unwrap();
2154 match stmt {
2155 Statement::Insert(ins) => {
2156 assert_eq!(ins.target, "User");
2157 assert_eq!(ins.rows.len(), 3);
2158 assert_eq!(ins.rows[0].len(), 2);
2159 assert_eq!(ins.rows[1][0].field, "name");
2160 assert_eq!(ins.rows[2].len(), 1);
2161 assert_eq!(ins.rows[2][0].field, "name");
2162 }
2163 _ => panic!("expected insert"),
2164 }
2165 }
2166
2167 #[test]
2168 fn test_parse_update() {
2169 let stmt = parse(r#"User filter .email = "alice@ex.com" update { age := 31 }"#).unwrap();
2170 match stmt {
2171 Statement::UpdateQuery(upd) => {
2172 assert_eq!(upd.source, "User");
2173 assert!(upd.filter.is_some());
2174 assert_eq!(upd.assignments.len(), 1);
2175 assert!(!upd.returning);
2176 }
2177 _ => panic!("expected update"),
2178 }
2179 }
2180
2181 #[test]
2182 fn test_parse_update_returning() {
2183 let stmt = parse(r#"User filter .name = "Alice" update { age := 31 } returning"#).unwrap();
2184 match stmt {
2185 Statement::UpdateQuery(upd) => assert!(upd.returning),
2186 _ => panic!("expected update"),
2187 }
2188 }
2189
2190 #[test]
2191 fn test_parse_delete() {
2192 let stmt = parse("User filter .age < 18 delete").unwrap();
2193 match stmt {
2194 Statement::DeleteQuery(del) => {
2195 assert_eq!(del.source, "User");
2196 assert!(del.filter.is_some());
2197 assert!(!del.returning);
2198 }
2199 _ => panic!("expected delete"),
2200 }
2201 }
2202
2203 #[test]
2204 fn test_parse_delete_returning() {
2205 let stmt = parse("User filter .age < 18 delete returning").unwrap();
2206 match stmt {
2207 Statement::DeleteQuery(del) => assert!(del.returning),
2208 _ => panic!("expected delete"),
2209 }
2210 }
2211
2212 #[test]
2213 fn test_parse_count() {
2214 let stmt = parse("count(User)").unwrap();
2215 match stmt {
2216 Statement::Query(q) => {
2217 let agg = q.aggregation.unwrap();
2218 assert_eq!(agg.function, AggFunc::Count);
2219 assert!(q.filter.is_none());
2220 }
2221 _ => panic!("expected query with aggregation"),
2222 }
2223 }
2224
2225 #[test]
2226 fn test_parse_count_with_filter() {
2227 let stmt = parse("count(User filter .age > 30)").unwrap();
2230 match stmt {
2231 Statement::Query(q) => {
2232 assert_eq!(q.source, "User");
2233 let agg = q.aggregation.unwrap();
2234 assert_eq!(agg.function, AggFunc::Count);
2235 assert!(q.filter.is_some(), "filter should have been parsed");
2236 }
2237 _ => panic!("expected query with aggregation"),
2238 }
2239 }
2240
2241 #[test]
2242 fn test_parse_count_with_filter_and_limit() {
2243 let stmt = parse("count(User filter .age > 30 limit 100)").unwrap();
2244 match stmt {
2245 Statement::Query(q) => {
2246 assert_eq!(q.source, "User");
2247 assert!(q.filter.is_some());
2248 assert!(q.limit.is_some());
2249 assert_eq!(q.aggregation.unwrap().function, AggFunc::Count);
2250 }
2251 _ => panic!("expected query with aggregation"),
2252 }
2253 }
2254
2255 #[test]
2256 fn test_parse_create_type() {
2257 let stmt = parse("type User { required name: str, age: int }").unwrap();
2258 match stmt {
2259 Statement::CreateType(ct) => {
2260 assert_eq!(ct.name, "User");
2261 assert_eq!(ct.fields.len(), 2);
2262 assert!(ct.fields[0].required);
2263 assert!(!ct.fields[1].required);
2264 }
2265 _ => panic!("expected create type"),
2266 }
2267 }
2268
2269 #[test]
2270 fn test_parse_sum_with_field_projection() {
2271 let stmt = parse("sum(User filter .age > 30 { .age })").unwrap();
2274 match stmt {
2275 Statement::Query(q) => {
2276 let agg = q.aggregation.expect("aggregate");
2277 assert_eq!(agg.function, AggFunc::Sum);
2278 assert_eq!(agg.field.as_deref(), Some("age"));
2279 assert!(
2280 q.projection.is_none(),
2281 "projection should be lifted into agg.field"
2282 );
2283 }
2284 _ => panic!("expected query"),
2285 }
2286 }
2287
2288 #[test]
2289 fn test_parse_avg_min_max_with_field() {
2290 for (src, expected) in [
2291 ("avg(User { .age })", AggFunc::Avg),
2292 ("min(User { .age })", AggFunc::Min),
2293 ("max(User { .age })", AggFunc::Max),
2294 ] {
2295 let stmt = parse(src).unwrap();
2296 match stmt {
2297 Statement::Query(q) => {
2298 let agg = q.aggregation.unwrap();
2299 assert_eq!(agg.function, expected, "func mismatch for {src}");
2300 assert_eq!(
2301 agg.field.as_deref(),
2302 Some("age"),
2303 "field mismatch for {src}"
2304 );
2305 assert!(
2306 q.projection.is_none(),
2307 "projection should be cleared for {src}"
2308 );
2309 }
2310 _ => panic!("expected query for {src}"),
2311 }
2312 }
2313 }
2314
2315 #[test]
2316 fn test_parse_count_leaves_projection_alone() {
2317 let stmt = parse("count(User { .age })").unwrap();
2320 match stmt {
2321 Statement::Query(q) => {
2322 let agg = q.aggregation.unwrap();
2323 assert_eq!(agg.function, AggFunc::Count);
2324 assert!(agg.field.is_none());
2325 assert!(q.projection.is_some(), "count must not eat projection");
2326 }
2327 _ => panic!("expected query"),
2328 }
2329 }
2330
2331 #[test]
2336 fn test_parse_source_alias() {
2337 let stmt = parse("User as u filter u.age > 30").unwrap();
2338 match stmt {
2339 Statement::Query(q) => {
2340 assert_eq!(q.source, "User");
2341 assert_eq!(q.alias.as_deref(), Some("u"));
2342 assert!(q.joins.is_empty());
2343 match q.filter.unwrap() {
2344 Expr::BinaryOp(l, BinOp::Gt, _) => match *l {
2345 Expr::QualifiedField { qualifier, field } => {
2346 assert_eq!(qualifier, "u");
2347 assert_eq!(field, "age");
2348 }
2349 other => panic!("expected qualified field, got {other:?}"),
2350 },
2351 other => panic!("expected >, got {other:?}"),
2352 }
2353 }
2354 _ => panic!("expected query"),
2355 }
2356 }
2357
2358 #[test]
2359 fn test_parse_inner_join_on() {
2360 let stmt = parse("User as u inner join Order as o on u.id = o.user_id").unwrap();
2361 match stmt {
2362 Statement::Query(q) => {
2363 assert_eq!(q.source, "User");
2364 assert_eq!(q.alias.as_deref(), Some("u"));
2365 assert_eq!(q.joins.len(), 1);
2366 let j = &q.joins[0];
2367 assert_eq!(j.kind, JoinKind::Inner);
2368 assert_eq!(j.source, "Order");
2369 assert_eq!(j.alias.as_deref(), Some("o"));
2370 let on = j.on.as_ref().expect("on clause");
2371 match on {
2372 Expr::BinaryOp(l, BinOp::Eq, r) => {
2373 assert!(matches!(**l, Expr::QualifiedField { .. }));
2374 assert!(matches!(**r, Expr::QualifiedField { .. }));
2375 }
2376 other => panic!("expected eq, got {other:?}"),
2377 }
2378 }
2379 _ => panic!("expected query"),
2380 }
2381 }
2382
2383 #[test]
2384 fn test_parse_bare_join_defaults_to_inner() {
2385 let stmt = parse("User join Order on User.id = Order.user_id").unwrap();
2386 match stmt {
2387 Statement::Query(q) => {
2388 assert_eq!(q.joins.len(), 1);
2389 assert_eq!(q.joins[0].kind, JoinKind::Inner);
2390 }
2391 _ => panic!("expected query"),
2392 }
2393 }
2394
2395 #[test]
2396 fn test_parse_left_outer_join() {
2397 let stmt = parse("User as u left outer join Order as o on u.id = o.user_id").unwrap();
2398 match stmt {
2399 Statement::Query(q) => {
2400 assert_eq!(q.joins.len(), 1);
2401 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2402 }
2403 _ => panic!("expected query"),
2404 }
2405 }
2406
2407 #[test]
2408 fn test_parse_left_join_without_outer_keyword() {
2409 let stmt = parse("User as u left join Order as o on u.id = o.user_id").unwrap();
2411 match stmt {
2412 Statement::Query(q) => {
2413 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2414 }
2415 _ => panic!("expected query"),
2416 }
2417 }
2418
2419 #[test]
2420 fn test_parse_right_join() {
2421 let stmt = parse("User as u right join Order as o on u.id = o.user_id").unwrap();
2422 match stmt {
2423 Statement::Query(q) => {
2424 assert_eq!(q.joins[0].kind, JoinKind::RightOuter);
2425 }
2426 _ => panic!("expected query"),
2427 }
2428 }
2429
2430 #[test]
2431 fn test_parse_cross_join_has_no_on() {
2432 let stmt = parse("User cross join Order").unwrap();
2433 match stmt {
2434 Statement::Query(q) => {
2435 assert_eq!(q.joins[0].kind, JoinKind::Cross);
2436 assert!(q.joins[0].on.is_none());
2437 }
2438 _ => panic!("expected query"),
2439 }
2440 }
2441
2442 #[test]
2443 fn test_parse_multi_join_chain() {
2444 let stmt = parse(
2445 "User as u join Order as o on u.id = o.user_id \
2446 join Product as p on o.product_id = p.id",
2447 )
2448 .unwrap();
2449 match stmt {
2450 Statement::Query(q) => {
2451 assert_eq!(q.joins.len(), 2);
2452 assert_eq!(q.joins[0].source, "Order");
2453 assert_eq!(q.joins[1].source, "Product");
2454 }
2455 _ => panic!("expected query"),
2456 }
2457 }
2458
2459 #[test]
2460 fn test_parse_join_with_filter_tail() {
2461 let stmt = parse(
2463 "User as u join Order as o on u.id = o.user_id \
2464 filter o.total > 100 order .name limit 10",
2465 )
2466 .unwrap();
2467 match stmt {
2468 Statement::Query(q) => {
2469 assert_eq!(q.joins.len(), 1);
2470 assert!(q.filter.is_some());
2471 assert!(q.order.is_some());
2472 assert!(q.limit.is_some());
2473 }
2474 _ => panic!("expected query"),
2475 }
2476 }
2477
2478 #[test]
2479 fn test_parse_join_requires_on_for_inner() {
2480 let err = parse("User join Order").unwrap_err();
2482 assert!(
2483 err.message().contains("on"),
2484 "expected on-clause error, got {:?}",
2485 err.message()
2486 );
2487 }
2488
2489 #[test]
2490 fn test_parse_update_on_joined_query_errors() {
2491 let err =
2494 parse("User as u join Order as o on u.id = o.user_id update { age := 1 }").unwrap_err();
2495 assert!(err.message().contains("update"));
2496 }
2497
2498 #[test]
2499 fn test_parse_delete_on_joined_query_errors() {
2500 let err = parse("User as u join Order as o on u.id = o.user_id delete").unwrap_err();
2501 assert!(err.message().contains("delete"));
2502 }
2503
2504 #[test]
2507 fn test_parse_distinct() {
2508 let stmt = parse("User distinct { .name }").unwrap();
2509 match stmt {
2510 Statement::Query(q) => {
2511 assert!(q.distinct);
2512 assert!(q.projection.is_some());
2513 }
2514 _ => panic!("expected query"),
2515 }
2516 }
2517
2518 #[test]
2519 fn test_parse_in_list() {
2520 let stmt = parse(r#"User filter .name in ("Alice", "Bob")"#).unwrap();
2521 match stmt {
2522 Statement::Query(q) => match q.filter.unwrap() {
2523 Expr::InList {
2524 expr,
2525 list,
2526 negated,
2527 } => {
2528 assert!(!negated);
2529 assert!(matches!(*expr, Expr::Field(f) if f == "name"));
2530 assert_eq!(list.len(), 2);
2531 }
2532 other => panic!("expected InList, got {other:?}"),
2533 },
2534 _ => panic!("expected query"),
2535 }
2536 }
2537
2538 #[test]
2539 fn test_parse_not_in_list() {
2540 let stmt = parse("User filter .age not in (1, 2, 3)").unwrap();
2541 match stmt {
2542 Statement::Query(q) => match q.filter.unwrap() {
2543 Expr::InList { negated, list, .. } => {
2544 assert!(negated);
2545 assert_eq!(list.len(), 3);
2546 }
2547 other => panic!("expected InList, got {other:?}"),
2548 },
2549 _ => panic!("expected query"),
2550 }
2551 }
2552
2553 #[test]
2554 fn test_parse_between() {
2555 let stmt = parse("User filter .age between 10 and 20").unwrap();
2557 match stmt {
2558 Statement::Query(q) => {
2559 match q.filter.unwrap() {
2560 Expr::BinaryOp(_, BinOp::And, _) => {} other => panic!("expected And (desugared between), got {other:?}"),
2562 }
2563 }
2564 _ => panic!("expected query"),
2565 }
2566 }
2567
2568 #[test]
2569 fn test_parse_not_between() {
2570 let stmt = parse("User filter .age not between 10 and 20").unwrap();
2572 match stmt {
2573 Statement::Query(q) => {
2574 match q.filter.unwrap() {
2575 Expr::BinaryOp(_, BinOp::Or, _) => {} other => panic!("expected Or (desugared not between), got {other:?}"),
2577 }
2578 }
2579 _ => panic!("expected query"),
2580 }
2581 }
2582
2583 #[test]
2584 fn test_parse_like() {
2585 let stmt = parse(r#"User filter .name like "A%""#).unwrap();
2586 match stmt {
2587 Statement::Query(q) => match q.filter.unwrap() {
2588 Expr::BinaryOp(l, BinOp::Like, r) => {
2589 assert!(matches!(*l, Expr::Field(f) if f == "name"));
2590 assert!(matches!(*r, Expr::Literal(Literal::String(s)) if s == "A%"));
2591 }
2592 other => panic!("expected Like, got {other:?}"),
2593 },
2594 _ => panic!("expected query"),
2595 }
2596 }
2597
2598 #[test]
2599 fn test_parse_not_like() {
2600 let stmt = parse(r#"User filter .name not like "A%""#).unwrap();
2601 match stmt {
2602 Statement::Query(q) => match q.filter.unwrap() {
2603 Expr::UnaryOp(UnaryOp::Not, inner) => {
2604 assert!(matches!(*inner, Expr::BinaryOp(_, BinOp::Like, _)));
2605 }
2606 other => panic!("expected Not(Like), got {other:?}"),
2607 },
2608 _ => panic!("expected query"),
2609 }
2610 }
2611
2612 #[test]
2615 fn test_parse_group_by_single_key() {
2616 let stmt = parse("User group .status { .status, n: count(.name) }").unwrap();
2617 match stmt {
2618 Statement::Query(q) => {
2619 let gb = q.group_by.unwrap();
2620 assert_eq!(gb.keys, vec![GroupKey::Unqualified("status".into())]);
2621 assert!(gb.having.is_none());
2622 let proj = q.projection.unwrap();
2623 assert_eq!(proj.len(), 2);
2624 assert!(matches!(
2625 &proj[1].expr,
2626 Expr::FunctionCall(AggFunc::Count, _)
2627 ));
2628 assert_eq!(proj[1].alias.as_deref(), Some("n"));
2629 }
2630 _ => panic!("expected query"),
2631 }
2632 }
2633
2634 #[test]
2635 fn test_parse_group_by_multi_key() {
2636 let stmt = parse("User group .status, .age { .status, .age }").unwrap();
2637 match stmt {
2638 Statement::Query(q) => {
2639 let gb = q.group_by.unwrap();
2640 assert_eq!(
2641 gb.keys,
2642 vec![
2643 GroupKey::Unqualified("status".into()),
2644 GroupKey::Unqualified("age".into())
2645 ]
2646 );
2647 }
2648 _ => panic!("expected query"),
2649 }
2650 }
2651
2652 #[test]
2653 fn test_parse_group_by_having() {
2654 let stmt = parse("User group .status having count(.name) > 1 { .status }").unwrap();
2655 match stmt {
2656 Statement::Query(q) => {
2657 let gb = q.group_by.unwrap();
2658 assert_eq!(gb.keys, vec![GroupKey::Unqualified("status".into())]);
2659 assert!(gb.having.is_some());
2660 match gb.having.unwrap() {
2662 Expr::BinaryOp(l, BinOp::Gt, _) => {
2663 assert!(matches!(*l, Expr::FunctionCall(AggFunc::Count, _)));
2664 }
2665 other => panic!("expected BinaryOp, got {other:?}"),
2666 }
2667 }
2668 _ => panic!("expected query"),
2669 }
2670 }
2671
2672 #[test]
2673 fn test_parse_aggregate_in_projection() {
2674 let stmt = parse("User group .status { .status, count(.name), sum(.age) }").unwrap();
2676 match stmt {
2677 Statement::Query(q) => {
2678 let proj = q.projection.unwrap();
2679 assert_eq!(proj.len(), 3);
2680 assert!(matches!(
2681 &proj[1].expr,
2682 Expr::FunctionCall(AggFunc::Count, _)
2683 ));
2684 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Sum, _)));
2685 }
2686 _ => panic!("expected query"),
2687 }
2688 }
2689
2690 #[test]
2691 fn test_parse_aggregate_in_aliased_projection() {
2692 let stmt = parse("User group .status { .status, total: count(.name), average: avg(.age) }")
2693 .unwrap();
2694 match stmt {
2695 Statement::Query(q) => {
2696 let proj = q.projection.unwrap();
2697 assert_eq!(proj[1].alias.as_deref(), Some("total"));
2698 assert!(matches!(
2699 &proj[1].expr,
2700 Expr::FunctionCall(AggFunc::Count, _)
2701 ));
2702 assert_eq!(proj[2].alias.as_deref(), Some("average"));
2703 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Avg, _)));
2704 }
2705 _ => panic!("expected query"),
2706 }
2707 }
2708
2709 #[test]
2712 fn test_parse_is_null() {
2713 let stmt = parse("User filter .age is null").unwrap();
2714 match stmt {
2715 Statement::Query(q) => {
2716 let filter = q.filter.unwrap();
2717 assert_eq!(
2718 filter,
2719 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2720 );
2721 }
2722 _ => panic!("expected query"),
2723 }
2724 }
2725
2726 #[test]
2727 fn test_parse_is_not_null() {
2728 let stmt = parse("User filter .age is not null").unwrap();
2729 match stmt {
2730 Statement::Query(q) => {
2731 let filter = q.filter.unwrap();
2732 assert_eq!(
2733 filter,
2734 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2735 );
2736 }
2737 _ => panic!("expected query"),
2738 }
2739 }
2740
2741 #[test]
2742 fn test_parse_eq_null_desugars_to_is_null() {
2743 let stmt = parse("User filter .age = null").unwrap();
2744 match stmt {
2745 Statement::Query(q) => {
2746 let filter = q.filter.unwrap();
2747 assert_eq!(
2748 filter,
2749 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2750 );
2751 }
2752 _ => panic!("expected query"),
2753 }
2754 }
2755
2756 #[test]
2757 fn test_parse_neq_null_desugars_to_is_not_null() {
2758 let stmt = parse("User filter .age != null").unwrap();
2759 match stmt {
2760 Statement::Query(q) => {
2761 let filter = q.filter.unwrap();
2762 assert_eq!(
2763 filter,
2764 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2765 );
2766 }
2767 _ => panic!("expected query"),
2768 }
2769 }
2770
2771 #[test]
2772 fn test_parse_null_comparisons_parse_ok() {
2773 assert!(parse("User filter .age < null").is_ok());
2777 assert!(parse("User filter .age >= null").is_ok());
2778 }
2779
2780 #[test]
2781 fn test_parse_count_star_expr() {
2782 let stmt = parse("User filter count(*) > 0").unwrap();
2783 match stmt {
2784 Statement::Query(q) => {
2785 let filter = q.filter.unwrap();
2786 match filter {
2787 Expr::BinaryOp(left, BinOp::Gt, _) => {
2788 assert_eq!(
2789 *left,
2790 Expr::FunctionCall(AggFunc::Count, Box::new(Expr::Field("*".into())))
2791 );
2792 }
2793 _ => panic!("expected comparison"),
2794 }
2795 }
2796 _ => panic!("expected query"),
2797 }
2798 }
2799
2800 #[test]
2803 fn test_parse_upper_in_filter() {
2804 let stmt = parse(r#"User filter upper(.name) = "ALICE""#).unwrap();
2805 match stmt {
2806 Statement::Query(q) => {
2807 let f = q.filter.unwrap();
2808 match f {
2809 Expr::BinaryOp(left, BinOp::Eq, _right) => {
2810 assert!(matches!(*left, Expr::ScalarFunc(ScalarFn::Upper, _)));
2811 }
2812 _ => panic!("expected binary op with upper"),
2813 }
2814 }
2815 _ => panic!("expected query"),
2816 }
2817 }
2818
2819 #[test]
2820 fn test_parse_substring() {
2821 let stmt = parse("User { sub: substring(.name, 1, 3) }").unwrap();
2822 match stmt {
2823 Statement::Query(q) => {
2824 let proj = q.projection.unwrap();
2825 match &proj[0].expr {
2826 Expr::ScalarFunc(ScalarFn::Substring, args) => {
2827 assert_eq!(args.len(), 3);
2828 }
2829 other => panic!("expected ScalarFunc Substring, got {other:?}"),
2830 }
2831 }
2832 _ => panic!("expected query"),
2833 }
2834 }
2835
2836 #[test]
2837 fn test_parse_concat() {
2838 let stmt = parse(r#"User { full: concat(.name, " - ", .email) }"#).unwrap();
2839 match stmt {
2840 Statement::Query(q) => {
2841 let proj = q.projection.unwrap();
2842 match &proj[0].expr {
2843 Expr::ScalarFunc(ScalarFn::Concat, args) => {
2844 assert_eq!(args.len(), 3);
2845 }
2846 other => panic!("expected ScalarFunc Concat, got {other:?}"),
2847 }
2848 }
2849 _ => panic!("expected query"),
2850 }
2851 }
2852
2853 #[test]
2856 fn test_parse_case_single_when() {
2857 let stmt = parse(r#"User filter case when .age > 30 then true else false end"#).unwrap();
2858 match stmt {
2859 Statement::Query(q) => {
2860 let filter = q.filter.unwrap();
2861 match filter {
2862 Expr::Case { whens, else_expr } => {
2863 assert_eq!(whens.len(), 1);
2864 assert!(else_expr.is_some());
2865 }
2866 other => panic!("expected Case expr, got {other:?}"),
2867 }
2868 }
2869 _ => panic!("expected query"),
2870 }
2871 }
2872
2873 #[test]
2874 fn test_parse_case_multiple_whens() {
2875 let stmt = parse(
2876 r#"User { label: case when .age > 30 then "senior" when .age > 20 then "adult" else "young" end }"#
2877 ).unwrap();
2878 match stmt {
2879 Statement::Query(q) => {
2880 let proj = q.projection.unwrap();
2881 match &proj[0].expr {
2882 Expr::Case { whens, else_expr } => {
2883 assert_eq!(whens.len(), 2);
2884 assert!(else_expr.is_some());
2885 }
2886 other => panic!("expected Case expr, got {other:?}"),
2887 }
2888 }
2889 _ => panic!("expected query"),
2890 }
2891 }
2892
2893 #[test]
2894 fn test_parse_case_without_else() {
2895 let stmt = parse(r#"User filter case when .age > 30 then true end"#).unwrap();
2896 match stmt {
2897 Statement::Query(q) => {
2898 let filter = q.filter.unwrap();
2899 match filter {
2900 Expr::Case { whens, else_expr } => {
2901 assert_eq!(whens.len(), 1);
2902 assert!(else_expr.is_none());
2903 }
2904 other => panic!("expected Case expr, got {other:?}"),
2905 }
2906 }
2907 _ => panic!("expected query"),
2908 }
2909 }
2910
2911 #[test]
2914 fn test_parse_mul_expr() {
2915 let stmt = parse("User filter .price * .quantity > 100").unwrap();
2916 match stmt {
2917 Statement::Query(q) => {
2918 let filter = q.filter.unwrap();
2919 match filter {
2920 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2921 Expr::BinaryOp(_, BinOp::Mul, _) => {}
2922 other => panic!("expected Mul, got {other:?}"),
2923 },
2924 other => panic!("expected BinaryOp Gt, got {other:?}"),
2925 }
2926 }
2927 _ => panic!("expected query"),
2928 }
2929 }
2930
2931 #[test]
2932 fn test_parse_div_expr() {
2933 let stmt = parse("User { ratio: .total / .count }").unwrap();
2934 match stmt {
2935 Statement::Query(q) => {
2936 let proj = q.projection.unwrap();
2937 assert_eq!(proj[0].alias.as_deref(), Some("ratio"));
2938 match &proj[0].expr {
2939 Expr::BinaryOp(_, BinOp::Div, _) => {}
2940 other => panic!("expected Div, got {other:?}"),
2941 }
2942 }
2943 _ => panic!("expected query"),
2944 }
2945 }
2946
2947 #[test]
2948 fn test_parse_mul_div_precedence() {
2949 let stmt = parse("User filter .a + .b * .c > 0").unwrap();
2951 match stmt {
2952 Statement::Query(q) => {
2953 let filter = q.filter.unwrap();
2954 match filter {
2955 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2956 Expr::BinaryOp(_, BinOp::Add, right) => {
2957 assert!(matches!(*right, Expr::BinaryOp(_, BinOp::Mul, _)));
2958 }
2959 other => panic!("expected Add, got {other:?}"),
2960 },
2961 other => panic!("expected Gt, got {other:?}"),
2962 }
2963 }
2964 _ => panic!("expected query"),
2965 }
2966 }
2967
2968 #[test]
2971 fn test_parse_multi_order() {
2972 let stmt = parse("User order .name asc, .age desc").unwrap();
2973 match stmt {
2974 Statement::Query(q) => {
2975 let order = q.order.unwrap();
2976 assert_eq!(order.keys.len(), 2);
2977 assert_eq!(order.keys[0].field, "name");
2978 assert!(!order.keys[0].descending);
2979 assert_eq!(order.keys[1].field, "age");
2980 assert!(order.keys[1].descending);
2981 }
2982 _ => panic!("expected query"),
2983 }
2984 }
2985
2986 #[test]
2987 fn test_parse_order_default_asc() {
2988 let stmt = parse("User order .name").unwrap();
2989 match stmt {
2990 Statement::Query(q) => {
2991 let order = q.order.unwrap();
2992 assert_eq!(order.keys.len(), 1);
2993 assert!(!order.keys[0].descending);
2994 }
2995 _ => panic!("expected query"),
2996 }
2997 }
2998
2999 #[test]
3002 fn test_parse_alter_add_column() {
3003 let stmt = parse("alter User add column status: str").unwrap();
3004 match stmt {
3005 Statement::AlterTable(at) => {
3006 assert_eq!(at.table, "User");
3007 match at.action {
3008 AlterAction::AddColumn {
3009 name,
3010 type_name,
3011 required,
3012 } => {
3013 assert_eq!(name, "status");
3014 assert_eq!(type_name, "str");
3015 assert!(!required);
3016 }
3017 other => panic!("expected AddColumn, got {other:?}"),
3018 }
3019 }
3020 other => panic!("expected AlterTable, got {other:?}"),
3021 }
3022 }
3023
3024 #[test]
3025 fn test_parse_alter_add_required_column() {
3026 let stmt = parse("alter User add required status: str").unwrap();
3027 match stmt {
3028 Statement::AlterTable(at) => match at.action {
3029 AlterAction::AddColumn { required, .. } => assert!(required),
3030 other => panic!("expected AddColumn, got {other:?}"),
3031 },
3032 other => panic!("expected AlterTable, got {other:?}"),
3033 }
3034 }
3035
3036 #[test]
3037 fn test_parse_type_with_unique_modifier() {
3038 let stmt = parse("type User { required unique email: str, age: int }").unwrap();
3039 match stmt {
3040 Statement::CreateType(ct) => {
3041 assert!(ct.fields[0].required && ct.fields[0].unique);
3042 assert!(!ct.fields[1].unique);
3043 }
3044 other => panic!("expected CreateType, got {other:?}"),
3045 }
3046 }
3047
3048 #[test]
3049 fn test_parse_type_unique_before_required() {
3050 let stmt = parse("type User { unique required email: str }").unwrap();
3052 match stmt {
3053 Statement::CreateType(ct) => {
3054 assert!(ct.fields[0].required && ct.fields[0].unique);
3055 }
3056 other => panic!("expected CreateType, got {other:?}"),
3057 }
3058 }
3059
3060 #[test]
3061 fn test_parse_alter_add_unique() {
3062 let stmt = parse("alter User add unique .email").unwrap();
3063 match stmt {
3064 Statement::AlterTable(at) => assert!(matches!(
3065 at.action,
3066 AlterAction::AddUnique { ref column, .. } if column == "email"
3067 )),
3068 other => panic!("expected AlterTable, got {other:?}"),
3069 }
3070 }
3071
3072 #[test]
3073 fn test_parse_alter_drop_column() {
3074 let stmt = parse("alter User drop column status").unwrap();
3075 match stmt {
3076 Statement::AlterTable(at) => {
3077 assert_eq!(at.table, "User");
3078 match at.action {
3079 AlterAction::DropColumn { name, .. } => assert_eq!(name, "status"),
3080 other => panic!("expected DropColumn, got {other:?}"),
3081 }
3082 }
3083 other => panic!("expected AlterTable, got {other:?}"),
3084 }
3085 }
3086
3087 #[test]
3088 fn test_parse_alter_drop_without_column_keyword() {
3089 let stmt = parse("alter User drop status").unwrap();
3090 match stmt {
3091 Statement::AlterTable(at) => match at.action {
3092 AlterAction::DropColumn { name, .. } => assert_eq!(name, "status"),
3093 other => panic!("expected DropColumn, got {other:?}"),
3094 },
3095 other => panic!("expected AlterTable, got {other:?}"),
3096 }
3097 }
3098
3099 #[test]
3100 fn test_parse_drop_table() {
3101 let stmt = parse("drop User").unwrap();
3102 match stmt {
3103 Statement::DropTable(dt) => assert_eq!(dt.table, "User"),
3104 other => panic!("expected DropTable, got {other:?}"),
3105 }
3106 }
3107
3108 #[test]
3111 fn test_parse_in_subquery() {
3112 let stmt = parse("User filter .name in (VIP { .name })").unwrap();
3113 match stmt {
3114 Statement::Query(q) => {
3115 let filter = q.filter.unwrap();
3116 match filter {
3117 Expr::InSubquery {
3118 expr,
3119 subquery,
3120 negated,
3121 } => {
3122 assert!(!negated);
3123 assert!(matches!(*expr, Expr::Field(ref f) if f == "name"));
3124 assert_eq!(subquery.source, "VIP");
3125 }
3126 other => panic!("expected InSubquery, got {other:?}"),
3127 }
3128 }
3129 _ => panic!("expected query"),
3130 }
3131 }
3132
3133 #[test]
3134 fn test_parse_not_in_subquery() {
3135 let stmt = parse("User filter .id not in (Order { .user_id })").unwrap();
3136 match stmt {
3137 Statement::Query(q) => match q.filter.unwrap() {
3138 Expr::InSubquery { negated, .. } => assert!(negated),
3139 other => panic!("expected InSubquery, got {other:?}"),
3140 },
3141 _ => panic!("expected query"),
3142 }
3143 }
3144
3145 #[test]
3146 fn test_parse_in_literal_list_still_works() {
3147 let stmt = parse("User filter .age in (25, 30, 35)").unwrap();
3149 match stmt {
3150 Statement::Query(q) => match q.filter.unwrap() {
3151 Expr::InList { list, negated, .. } => {
3152 assert!(!negated);
3153 assert_eq!(list.len(), 3);
3154 }
3155 other => panic!("expected InList, got {other:?}"),
3156 },
3157 _ => panic!("expected query"),
3158 }
3159 }
3160
3161 #[test]
3164 fn test_parse_create_view() {
3165 let stmt = parse("materialize OldUsers as User filter .age > 28").unwrap();
3166 match stmt {
3167 Statement::CreateView(cv) => {
3168 assert_eq!(cv.name, "OldUsers");
3169 assert_eq!(cv.query.source, "User");
3170 assert!(cv.query.filter.is_some());
3171 assert!(!cv.query_text.is_empty());
3172 }
3173 _ => panic!("expected CreateView"),
3174 }
3175 }
3176
3177 #[test]
3178 fn test_parse_create_view_with_projection() {
3179 let stmt = parse("materialize UserNames as User { .name }").unwrap();
3180 match stmt {
3181 Statement::CreateView(cv) => {
3182 assert_eq!(cv.name, "UserNames");
3183 assert!(cv.query.projection.is_some());
3184 }
3185 _ => panic!("expected CreateView"),
3186 }
3187 }
3188
3189 #[test]
3190 fn test_parse_refresh_view() {
3191 let stmt = parse("refresh OldUsers").unwrap();
3192 match stmt {
3193 Statement::RefreshView(rv) => {
3194 assert_eq!(rv.name, "OldUsers");
3195 }
3196 _ => panic!("expected RefreshView"),
3197 }
3198 }
3199
3200 #[test]
3201 fn test_parse_drop_view() {
3202 let stmt = parse("drop view OldUsers").unwrap();
3203 match stmt {
3204 Statement::DropView(dv) => {
3205 assert_eq!(dv.name, "OldUsers");
3206 }
3207 _ => panic!("expected DropView"),
3208 }
3209 }
3210
3211 #[test]
3212 fn test_parse_drop_table_still_works() {
3213 let stmt = parse("drop Users").unwrap();
3214 match stmt {
3215 Statement::DropTable(dt) => {
3216 assert_eq!(dt.table, "Users");
3217 }
3218 _ => panic!("expected DropTable"),
3219 }
3220 }
3221
3222 #[test]
3223 fn test_parse_union() {
3224 let stmt = parse("User union Order").unwrap();
3225 match stmt {
3226 Statement::Union(u) => {
3227 assert!(!u.all);
3228 match *u.left {
3229 Statement::Query(_) => {}
3230 _ => panic!("expected Query on left"),
3231 }
3232 match *u.right {
3233 Statement::Query(_) => {}
3234 _ => panic!("expected Query on right"),
3235 }
3236 }
3237 _ => panic!("expected Union"),
3238 }
3239 }
3240
3241 #[test]
3242 fn test_parse_union_all() {
3243 let stmt = parse("User union all Order").unwrap();
3244 match stmt {
3245 Statement::Union(u) => {
3246 assert!(u.all, "expected UNION ALL");
3247 match *u.left {
3248 Statement::Query(_) => {}
3249 _ => panic!("expected Query on left"),
3250 }
3251 match *u.right {
3252 Statement::Query(_) => {}
3253 _ => panic!("expected Query on right"),
3254 }
3255 }
3256 _ => panic!("expected Union"),
3257 }
3258 }
3259
3260 #[test]
3261 fn test_parse_union_chain() {
3262 let stmt = parse("User union Order union Product").unwrap();
3264 match stmt {
3265 Statement::Union(outer) => {
3266 assert!(!outer.all);
3267 match *outer.right {
3269 Statement::Query(q) => assert_eq!(q.source, "Product"),
3270 _ => panic!("expected Query(Product) on right"),
3271 }
3272 match *outer.left {
3274 Statement::Union(inner) => {
3275 assert!(!inner.all);
3276 match *inner.left {
3277 Statement::Query(q) => assert_eq!(q.source, "User"),
3278 _ => panic!("expected Query(User)"),
3279 }
3280 match *inner.right {
3281 Statement::Query(q) => assert_eq!(q.source, "Order"),
3282 _ => panic!("expected Query(Order)"),
3283 }
3284 }
3285 _ => panic!("expected inner Union"),
3286 }
3287 }
3288 _ => panic!("expected Union"),
3289 }
3290 }
3291
3292 #[test]
3293 fn test_parse_union_with_filter() {
3294 let stmt = parse("User filter .age > 10 union Order filter .total > 50").unwrap();
3295 match stmt {
3296 Statement::Union(u) => {
3297 assert!(!u.all);
3298 match *u.left {
3300 Statement::Query(q) => {
3301 assert_eq!(q.source, "User");
3302 assert!(q.filter.is_some());
3303 }
3304 _ => panic!("expected Query on left"),
3305 }
3306 match *u.right {
3307 Statement::Query(q) => {
3308 assert_eq!(q.source, "Order");
3309 assert!(q.filter.is_some());
3310 }
3311 _ => panic!("expected Query on right"),
3312 }
3313 }
3314 _ => panic!("expected Union"),
3315 }
3316 }
3317
3318 #[test]
3319 fn test_parse_count_distinct_standalone() {
3320 let stmt = parse("count(distinct User { .name })").unwrap();
3321 match stmt {
3322 Statement::Query(q) => {
3323 let agg = q.aggregation.unwrap();
3324 assert_eq!(agg.function, AggFunc::CountDistinct);
3325 assert_eq!(agg.field.as_deref(), Some("name"));
3326 }
3327 _ => panic!("expected Query"),
3328 }
3329 }
3330
3331 #[test]
3332 fn test_parse_count_distinct_in_projection() {
3333 let stmt = parse("User group .dept { .dept, count(distinct .name) }").unwrap();
3334 match stmt {
3335 Statement::Query(q) => {
3336 let proj = q.projection.unwrap();
3337 assert_eq!(proj.len(), 2);
3338 match &proj[1].expr {
3339 Expr::FunctionCall(func, _) => {
3340 assert_eq!(*func, AggFunc::CountDistinct);
3341 }
3342 _ => panic!("expected FunctionCall"),
3343 }
3344 }
3345 _ => panic!("expected Query"),
3346 }
3347 }
3348
3349 #[test]
3352 fn test_parse_window_row_number_order() {
3353 let stmt = parse("User { .name, rn: row_number() over (order .age) }").unwrap();
3354 match stmt {
3355 Statement::Query(q) => {
3356 let proj = q.projection.unwrap();
3357 assert_eq!(proj.len(), 2);
3358 assert_eq!(proj[1].alias.as_deref(), Some("rn"));
3359 match &proj[1].expr {
3360 Expr::Window {
3361 function,
3362 args,
3363 partition_by,
3364 order_by,
3365 } => {
3366 assert_eq!(*function, WindowFunc::RowNumber);
3367 assert!(args.is_empty());
3368 assert!(partition_by.is_empty());
3369 assert_eq!(order_by.len(), 1);
3370 assert_eq!(order_by[0].field, "age");
3371 assert!(!order_by[0].descending);
3372 }
3373 other => panic!("expected Window, got {other:?}"),
3374 }
3375 }
3376 _ => panic!("expected query"),
3377 }
3378 }
3379
3380 #[test]
3381 fn test_parse_window_sum_partition_order() {
3382 let stmt =
3383 parse("User { .name, s: sum(.salary) over (partition .dept order .salary) }").unwrap();
3384 match stmt {
3385 Statement::Query(q) => {
3386 let proj = q.projection.unwrap();
3387 assert_eq!(proj.len(), 2);
3388 assert_eq!(proj[1].alias.as_deref(), Some("s"));
3389 match &proj[1].expr {
3390 Expr::Window {
3391 function,
3392 args,
3393 partition_by,
3394 order_by,
3395 } => {
3396 assert_eq!(*function, WindowFunc::Sum);
3397 assert_eq!(args.len(), 1);
3398 assert!(matches!(&args[0], Expr::Field(f) if f == "salary"));
3399 assert_eq!(partition_by, &["dept"]);
3400 assert_eq!(order_by.len(), 1);
3401 assert_eq!(order_by[0].field, "salary");
3402 assert!(!order_by[0].descending);
3403 }
3404 other => panic!("expected Window, got {other:?}"),
3405 }
3406 }
3407 _ => panic!("expected query"),
3408 }
3409 }
3410
3411 #[test]
3412 fn test_parse_window_rank_desc() {
3413 let stmt =
3414 parse("User { .dept, .salary, r: rank() over (partition .dept order .salary desc) }")
3415 .unwrap();
3416 match stmt {
3417 Statement::Query(q) => {
3418 let proj = q.projection.unwrap();
3419 assert_eq!(proj.len(), 3);
3420 match &proj[2].expr {
3421 Expr::Window {
3422 function,
3423 partition_by,
3424 order_by,
3425 ..
3426 } => {
3427 assert_eq!(*function, WindowFunc::Rank);
3428 assert_eq!(partition_by, &["dept"]);
3429 assert_eq!(order_by.len(), 1);
3430 assert!(order_by[0].descending);
3431 }
3432 other => panic!("expected Window, got {other:?}"),
3433 }
3434 }
3435 _ => panic!("expected query"),
3436 }
3437 }
3438
3439 #[test]
3440 fn test_parse_window_dense_rank() {
3441 let stmt = parse("User { .name, dr: dense_rank() over (order .score desc) }").unwrap();
3442 match stmt {
3443 Statement::Query(q) => {
3444 let proj = q.projection.unwrap();
3445 assert_eq!(proj.len(), 2);
3446 match &proj[1].expr {
3447 Expr::Window { function, .. } => {
3448 assert_eq!(*function, WindowFunc::DenseRank);
3449 }
3450 other => panic!("expected Window, got {other:?}"),
3451 }
3452 }
3453 _ => panic!("expected query"),
3454 }
3455 }
3456
3457 #[test]
3458 fn test_parse_sum_without_over_is_aggregate() {
3459 let stmt = parse("User group .dept { .dept, total: sum(.salary) }").unwrap();
3461 match stmt {
3462 Statement::Query(q) => {
3463 let proj = q.projection.unwrap();
3464 assert_eq!(proj.len(), 2);
3465 match &proj[1].expr {
3466 Expr::FunctionCall(AggFunc::Sum, _) => {} other => panic!("expected FunctionCall(Sum), got {other:?}"),
3468 }
3469 }
3470 _ => panic!("expected query"),
3471 }
3472 }
3473
3474 #[test]
3475 fn test_nesting_depth_limit() {
3476 let mut query = String::from("User filter ");
3478 for _ in 0..70 {
3479 query.push('(');
3480 }
3481 query.push_str(".age > 1");
3482 for _ in 0..70 {
3483 query.push(')');
3484 }
3485 let result = parse(&query);
3486 assert!(result.is_err());
3487 let err = result.unwrap_err();
3488 assert!(
3489 err.message().contains("nesting depth"),
3490 "expected nesting depth error, got: {}",
3491 err.message()
3492 );
3493 }
3494
3495 #[test]
3496 fn test_unary_prefix_nesting_depth_limit() {
3497 let query = String::from("User filter ") + &"not ".repeat(5000) + ".active";
3501 let result = parse(&query);
3502 assert!(result.is_err());
3503 let err = result.unwrap_err();
3504 assert!(
3505 err.message().contains("nesting depth"),
3506 "expected nesting depth error, got: {}",
3507 err.message()
3508 );
3509 }
3510
3511 #[test]
3512 fn test_moderate_nesting_succeeds() {
3513 let mut query = String::from("User filter ");
3515 for _ in 0..10 {
3516 query.push('(');
3517 }
3518 query.push_str(".age > 1");
3519 for _ in 0..10 {
3520 query.push(')');
3521 }
3522 assert!(parse(&query).is_ok());
3523 }
3524
3525 #[test]
3529 fn test_parse_fuzz_repro_projection_eof() {
3530 let err = parse("nn{").expect_err("unterminated projection must error, not panic");
3531 let _ = err.message();
3532 }
3533
3534 #[test]
3537 fn test_parse_fuzz_repro_short_projection_eof() {
3538 let err = parse("z{").expect_err("unterminated projection must error, not panic");
3539 let _ = err.message();
3540 }
3541
3542 #[test]
3543 fn test_update_at_statement_start_gives_helpful_error() {
3544 let err =
3545 parse(r#"update User filter .name = "Alice" { age := 31 }"#).expect_err("should fail");
3546 let msg = err.message();
3547 assert!(
3548 msg.contains("pipeline syntax"),
3549 "error should mention pipeline syntax, got: {msg}"
3550 );
3551 assert!(
3552 msg.contains("update"),
3553 "error should mention 'update', got: {msg}"
3554 );
3555 }
3556
3557 #[test]
3558 fn test_delete_at_statement_start_gives_helpful_error() {
3559 let err = parse("delete User filter .age < 18").expect_err("should fail");
3560 let msg = err.message();
3561 assert!(
3562 msg.contains("pipeline syntax"),
3563 "error should mention pipeline syntax, got: {msg}"
3564 );
3565 assert!(
3566 msg.contains("delete"),
3567 "error should mention 'delete', got: {msg}"
3568 );
3569 }
3570}
3571
3572#[cfg(test)]
3573mod cleanup_parser_dx_tests {
3574 use super::*;
3575
3576 #[test]
3577 fn typoed_statement_keyword_gets_suggestion() {
3578 let err = parse("updat User set age = 1").unwrap_err();
3579 let msg = err.to_string();
3580 assert!(msg.contains("near token"), "{msg}");
3581 assert!(msg.contains("did you mean `update`"), "{msg}");
3582 }
3583}
3584
3585#[cfg(test)]
3586mod dogfood_dx_tests {
3587 use super::*;
3588
3589 #[test]
3592 fn reserved_word_field_name_gives_actionable_error() {
3593 let err = parse("type Post { type: str }").unwrap_err();
3594 let msg = err.to_string();
3595 assert!(
3596 msg.contains("'type' is a reserved word")
3597 && msg.contains("field name")
3598 && msg.contains("quote it as `type`"),
3599 "unhelpful message: {msg}"
3600 );
3601 }
3602
3603 #[test]
3604 fn reserved_modifier_word_as_field_name_gives_actionable_error() {
3605 let err = parse("type Post { required: bool }").unwrap_err();
3609 let msg = err.to_string();
3610 assert!(
3611 msg.contains("'required' is a reserved word") && msg.contains("quote it as `required`"),
3612 "unhelpful message: {msg}"
3613 );
3614 }
3615
3616 #[test]
3617 fn reserved_word_in_insert_assignment_gives_actionable_error() {
3618 let err = parse(r#"insert Post { type := "x" }"#).unwrap_err();
3619 let msg = err.to_string();
3620 assert!(msg.contains("'type' is a reserved word"), "{msg}");
3621 }
3622
3623 #[test]
3624 fn reserved_word_in_alter_column_gives_actionable_error() {
3625 let err = parse("alter Post add column order: int").unwrap_err();
3626 let msg = err.to_string();
3627 assert!(msg.contains("'order' is a reserved word"), "{msg}");
3628 }
3629
3630 #[test]
3631 fn backtick_field_name_parses_as_identifier() {
3632 let stmt = parse("type Post { `type`: str, `order`: int }").unwrap();
3633 match stmt {
3634 Statement::CreateType(ct) => {
3635 assert_eq!(ct.fields[0].name, "type");
3636 assert_eq!(ct.fields[1].name, "order");
3637 }
3638 other => panic!("expected CreateType, got {other:?}"),
3639 }
3640 }
3641
3642 #[test]
3643 fn backtick_field_still_honors_modifiers() {
3644 let stmt = parse("type Post { required `type`: str }").unwrap();
3645 match stmt {
3646 Statement::CreateType(ct) => {
3647 assert_eq!(ct.fields[0].name, "type");
3648 assert!(ct.fields[0].required);
3649 }
3650 other => panic!("expected CreateType, got {other:?}"),
3651 }
3652 }
3653
3654 #[test]
3657 fn create_type_if_not_exists_parses() {
3658 let stmt = parse("type Post if not exists { id: int }").unwrap();
3659 match stmt {
3660 Statement::CreateType(ct) => assert!(ct.if_not_exists),
3661 other => panic!("expected CreateType, got {other:?}"),
3662 }
3663 }
3664
3665 #[test]
3666 fn create_type_without_clause_defaults_false() {
3667 let stmt = parse("type Post { id: int }").unwrap();
3668 match stmt {
3669 Statement::CreateType(ct) => assert!(!ct.if_not_exists),
3670 other => panic!("expected CreateType, got {other:?}"),
3671 }
3672 }
3673
3674 #[test]
3675 fn drop_if_exists_parses() {
3676 match parse("drop if exists Post").unwrap() {
3677 Statement::DropTable(dt) => assert!(dt.if_exists),
3678 other => panic!("expected DropTable, got {other:?}"),
3679 }
3680 match parse("drop Post").unwrap() {
3681 Statement::DropTable(dt) => assert!(!dt.if_exists),
3682 other => panic!("expected DropTable, got {other:?}"),
3683 }
3684 }
3685
3686 #[test]
3687 fn drop_view_if_exists_parses() {
3688 match parse("drop view if exists ActiveUsers").unwrap() {
3689 Statement::DropView(dv) => {
3690 assert!(dv.if_exists);
3691 assert_eq!(dv.name, "ActiveUsers");
3692 }
3693 other => panic!("expected DropView, got {other:?}"),
3694 }
3695 }
3696
3697 #[test]
3698 fn add_index_and_unique_if_not_exists_parse() {
3699 match parse("alter Post add index if not exists .slug").unwrap() {
3700 Statement::AlterTable(at) => {
3701 assert!(matches!(
3702 at.action,
3703 AlterAction::AddIndex {
3704 if_not_exists: true,
3705 ..
3706 }
3707 ));
3708 }
3709 other => panic!("expected AlterTable, got {other:?}"),
3710 }
3711 match parse("alter Post add unique if not exists .slug").unwrap() {
3712 Statement::AlterTable(at) => {
3713 assert!(matches!(
3714 at.action,
3715 AlterAction::AddUnique {
3716 if_not_exists: true,
3717 ..
3718 }
3719 ));
3720 }
3721 other => panic!("expected AlterTable, got {other:?}"),
3722 }
3723 }
3724
3725 #[test]
3726 fn alter_drop_column_if_exists_parses() {
3727 match parse("alter Post drop column if exists status").unwrap() {
3728 Statement::AlterTable(at) => {
3729 assert!(matches!(
3730 at.action,
3731 AlterAction::DropColumn {
3732 if_exists: true,
3733 ..
3734 }
3735 ));
3736 }
3737 other => panic!("expected AlterTable, got {other:?}"),
3738 }
3739 }
3740
3741 #[test]
3744 fn schema_parses_to_list_types() {
3745 assert_eq!(parse("schema").unwrap(), Statement::ListTypes);
3746 }
3747
3748 #[test]
3749 fn describe_parses_to_describe() {
3750 assert_eq!(
3751 parse("describe Post").unwrap(),
3752 Statement::Describe("Post".to_string())
3753 );
3754 }
3755
3756 #[test]
3757 fn schema_with_type_aliases_describe() {
3758 assert_eq!(
3759 parse("schema Post").unwrap(),
3760 Statement::Describe("Post".to_string())
3761 );
3762 }
3763}