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> {
1239 let mut keys = Vec::new();
1240 while let Token::DotIdent(name) = self.peek() {
1241 let name = name.clone();
1242 self.advance();
1243 keys.push(name);
1244 if *self.peek() == Token::Comma {
1245 self.advance();
1246 } else {
1247 break;
1248 }
1249 }
1250 if keys.is_empty() {
1251 return Err(ParseError::Syntax {
1252 message: "expected at least one .field after group".into(),
1253 });
1254 }
1255 let having = if *self.peek() == Token::Having {
1256 self.advance();
1257 Some(self.parse_expr()?)
1258 } else {
1259 None
1260 };
1261 Ok(GroupByClause { keys, having })
1262 }
1263
1264 fn parse_additive(&mut self) -> Result<Expr, ParseError> {
1265 let mut left = self.parse_multiplicative()?;
1266 loop {
1267 let op = match self.peek() {
1268 Token::Plus => BinOp::Add,
1269 Token::Minus => BinOp::Sub,
1270 Token::Coalesce => {
1271 self.advance();
1272 let right = self.parse_multiplicative()?;
1273 left = Expr::Coalesce(Box::new(left), Box::new(right));
1274 continue;
1275 }
1276 _ => break,
1277 };
1278 self.advance();
1279 let right = self.parse_multiplicative()?;
1280 left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
1281 }
1282 Ok(left)
1283 }
1284
1285 fn parse_multiplicative(&mut self) -> Result<Expr, ParseError> {
1286 let mut left = self.parse_primary()?;
1287 loop {
1288 let op = match self.peek() {
1289 Token::Star => BinOp::Mul,
1290 Token::Slash => BinOp::Div,
1291 _ => break,
1292 };
1293 self.advance();
1294 let right = self.parse_primary()?;
1295 left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
1296 }
1297 Ok(left)
1298 }
1299
1300 fn parse_primary(&mut self) -> Result<Expr, ParseError> {
1301 self.depth += 1;
1306 if self.depth > MAX_NESTING_DEPTH {
1307 self.depth -= 1;
1308 return Err(ParseError::NestingDepthExceeded {
1309 max: MAX_NESTING_DEPTH,
1310 });
1311 }
1312 let result = self.parse_primary_inner();
1313 self.depth -= 1;
1314 result
1315 }
1316
1317 fn parse_primary_inner(&mut self) -> Result<Expr, ParseError> {
1318 match self.peek().clone() {
1319 Token::DotIdent(name) => {
1320 self.advance();
1321 Ok(Expr::Field(name))
1322 }
1323 Token::IntLit(v) => {
1324 self.advance();
1325 Ok(Expr::Literal(Literal::Int(v)))
1326 }
1327 Token::FloatLit(v) => {
1328 self.advance();
1329 Ok(Expr::Literal(Literal::Float(v)))
1330 }
1331 Token::StringLit(v) => {
1332 self.advance();
1333 Ok(Expr::Literal(Literal::String(v)))
1334 }
1335 Token::BoolLit(v) => {
1336 self.advance();
1337 Ok(Expr::Literal(Literal::Bool(v)))
1338 }
1339 Token::Null => {
1346 self.advance();
1347 Ok(Expr::Null)
1348 }
1349 Token::Not => {
1350 self.advance();
1351 if *self.peek() == Token::Exists {
1352 self.advance();
1353 if let Some(sub) = self.try_parse_exists_subquery()? {
1357 return Ok(Expr::ExistsSubquery {
1358 subquery: Box::new(sub),
1359 negated: true,
1360 });
1361 }
1362 let expr = self.parse_primary()?;
1363 Ok(Expr::UnaryOp(UnaryOp::NotExists, Box::new(expr)))
1364 } else {
1365 let expr = self.parse_primary()?;
1366 Ok(Expr::UnaryOp(UnaryOp::Not, Box::new(expr)))
1367 }
1368 }
1369 Token::Exists => {
1370 self.advance();
1371 if let Some(sub) = self.try_parse_exists_subquery()? {
1375 return Ok(Expr::ExistsSubquery {
1376 subquery: Box::new(sub),
1377 negated: false,
1378 });
1379 }
1380 let expr = self.parse_primary()?;
1381 Ok(Expr::UnaryOp(UnaryOp::Exists, Box::new(expr)))
1382 }
1383 Token::LParen => {
1384 self.advance();
1385 let expr = self.parse_expr()?;
1386 self.expect(&Token::RParen)?;
1387 Ok(expr)
1388 }
1389 Token::Ident(name) => {
1390 self.advance();
1391 if *self.peek() == Token::LParen {
1396 let cast_type = match name.as_str() {
1397 "uuid" => Some(CastType::Uuid),
1398 "bytes" => Some(CastType::Bytes),
1399 _ => None,
1400 };
1401 if let Some(cast_type) = cast_type {
1402 self.advance(); let inner = self.parse_expr()?;
1404 self.expect(&Token::RParen)?;
1405 return Ok(Expr::Cast(Box::new(inner), cast_type));
1406 }
1407 }
1408 if let Token::DotIdent(field) = self.peek().clone() {
1412 self.advance();
1413 return Ok(Expr::QualifiedField {
1414 qualifier: name,
1415 field,
1416 });
1417 }
1418 Ok(Expr::Field(name))
1419 }
1420 Token::RowNumber | Token::Rank | Token::DenseRank => {
1422 let wfunc = match self.advance() {
1423 Token::RowNumber => WindowFunc::RowNumber,
1424 Token::Rank => WindowFunc::Rank,
1425 Token::DenseRank => WindowFunc::DenseRank,
1426 _ => {
1427 return Err(ParseError::Syntax {
1428 message: "unexpected window function token".into(),
1429 })
1430 }
1431 };
1432 self.expect(&Token::LParen)?;
1433 self.expect(&Token::RParen)?;
1434 let (partition_by, order_by) = self.parse_over_clause()?;
1435 Ok(Expr::Window {
1436 function: wfunc,
1437 args: vec![],
1438 partition_by,
1439 order_by,
1440 })
1441 }
1442 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1446 let mut func = match self.advance() {
1447 Token::Count => AggFunc::Count,
1448 Token::Avg => AggFunc::Avg,
1449 Token::Sum => AggFunc::Sum,
1450 Token::Min => AggFunc::Min,
1451 Token::Max => AggFunc::Max,
1452 _ => {
1453 return Err(ParseError::Syntax {
1454 message: "unexpected aggregate token".into(),
1455 })
1456 }
1457 };
1458 self.expect(&Token::LParen)?;
1459 if func == AggFunc::Count && *self.peek() == Token::Star {
1461 self.advance();
1462 self.expect(&Token::RParen)?;
1463 if *self.peek() == Token::Over {
1465 let (partition_by, order_by) = self.parse_over_clause()?;
1466 return Ok(Expr::Window {
1467 function: WindowFunc::Count,
1468 args: vec![Expr::Field("*".into())],
1469 partition_by,
1470 order_by,
1471 });
1472 }
1473 return Ok(Expr::FunctionCall(
1474 AggFunc::Count,
1475 Box::new(Expr::Field("*".into())),
1476 ));
1477 }
1478 if func == AggFunc::Count && *self.peek() == Token::Distinct {
1480 self.advance();
1481 func = AggFunc::CountDistinct;
1482 }
1483 let inner = self.parse_expr()?;
1484 self.expect(&Token::RParen)?;
1485 if *self.peek() == Token::Over {
1487 let wfunc = match func {
1488 AggFunc::Count => WindowFunc::Count,
1489 AggFunc::Avg => WindowFunc::Avg,
1490 AggFunc::Sum => WindowFunc::Sum,
1491 AggFunc::Min => WindowFunc::Min,
1492 AggFunc::Max => WindowFunc::Max,
1493 _ => {
1494 return Err(ParseError::Unsupported {
1495 feature: "count(distinct ...) over (...) is not supported".into(),
1496 })
1497 }
1498 };
1499 let (partition_by, order_by) = self.parse_over_clause()?;
1500 return Ok(Expr::Window {
1501 function: wfunc,
1502 args: vec![inner],
1503 partition_by,
1504 order_by,
1505 });
1506 }
1507 Ok(Expr::FunctionCall(func, Box::new(inner)))
1508 }
1509 Token::Upper
1510 | Token::Lower
1511 | Token::Length
1512 | Token::Trim
1513 | Token::Substring
1514 | Token::Concat
1515 | Token::Abs
1516 | Token::Round
1517 | Token::Ceil
1518 | Token::Floor
1519 | Token::Sqrt
1520 | Token::Pow
1521 | Token::Now
1522 | Token::Extract
1523 | Token::DateAdd
1524 | Token::DateDiff => {
1525 let tok = self.advance();
1526 let func = token_to_scalar_fn(&tok);
1527 self.expect(&Token::LParen)?;
1528 let mut args = Vec::new();
1529 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1530 args.push(self.parse_expr()?);
1531 if *self.peek() == Token::Comma {
1532 self.advance();
1533 }
1534 }
1535 self.expect(&Token::RParen)?;
1536 Ok(Expr::ScalarFunc(func, args))
1537 }
1538 Token::Cast => {
1539 self.advance();
1540 self.expect(&Token::LParen)?;
1541 let inner = self.parse_expr()?;
1542 self.expect(&Token::Comma)?;
1543 let cast_type = self.parse_cast_type()?;
1544 self.expect(&Token::RParen)?;
1545 Ok(Expr::Cast(Box::new(inner), cast_type))
1546 }
1547 Token::Case => {
1548 self.advance();
1549 let mut whens = Vec::new();
1550 while *self.peek() == Token::When {
1551 self.advance();
1552 let condition = self.parse_expr()?;
1553 self.expect(&Token::Then)?;
1554 let result = self.parse_expr()?;
1555 whens.push((Box::new(condition), Box::new(result)));
1556 }
1557 let else_expr = if *self.peek() == Token::Else {
1558 self.advance();
1559 Some(Box::new(self.parse_expr()?))
1560 } else {
1561 None
1562 };
1563 self.expect(&Token::End)?;
1564 Ok(Expr::Case { whens, else_expr })
1565 }
1566 t => Err(ParseError::Syntax {
1567 message: format!("unexpected token in expression: {}", t.display_name()),
1568 }),
1569 }
1570 }
1571
1572 fn parse_alter_table(&mut self) -> Result<Statement, ParseError> {
1575 self.expect(&Token::Alter)?;
1576 let table = match self.advance() {
1577 Token::Ident(name) => name,
1578 t => {
1579 return Err(ParseError::UnexpectedToken {
1580 expected: "table name after alter".into(),
1581 got: t.display_name(),
1582 })
1583 }
1584 };
1585 match self.peek() {
1586 Token::Add => {
1587 self.advance();
1588 if *self.peek() == Token::Index {
1590 self.advance();
1591 let if_not_exists = self.parse_optional_if_not_exists();
1592 let column = match self.advance() {
1593 Token::DotIdent(n) => n,
1594 t => {
1595 return Err(ParseError::UnexpectedToken {
1596 expected: ".<column> after add index".into(),
1597 got: t.display_name(),
1598 })
1599 }
1600 };
1601 return Ok(Statement::AlterTable(AlterTableExpr {
1602 table,
1603 action: AlterAction::AddIndex {
1604 column,
1605 if_not_exists,
1606 },
1607 }));
1608 }
1609 if *self.peek() == Token::Unique {
1611 self.advance();
1612 let if_not_exists = self.parse_optional_if_not_exists();
1613 let column = match self.advance() {
1614 Token::DotIdent(n) => n,
1615 t => {
1616 return Err(ParseError::UnexpectedToken {
1617 expected: ".<column> after add unique".into(),
1618 got: t.display_name(),
1619 })
1620 }
1621 };
1622 return Ok(Statement::AlterTable(AlterTableExpr {
1623 table,
1624 action: AlterAction::AddUnique {
1625 column,
1626 if_not_exists,
1627 },
1628 }));
1629 }
1630 if *self.peek() == Token::Column {
1632 self.advance();
1633 }
1634 let required = if *self.peek() == Token::Required {
1635 self.advance();
1636 true
1637 } else {
1638 false
1639 };
1640 let name = self.expect_named_ident("column name")?;
1641 self.expect(&Token::Colon)?;
1642 let type_name = match self.advance() {
1643 Token::Ident(n) => n,
1644 t => {
1645 return Err(ParseError::UnexpectedToken {
1646 expected: "type name".into(),
1647 got: t.display_name(),
1648 })
1649 }
1650 };
1651 Ok(Statement::AlterTable(AlterTableExpr {
1652 table,
1653 action: AlterAction::AddColumn {
1654 name,
1655 type_name,
1656 required,
1657 },
1658 }))
1659 }
1660 Token::Drop => {
1661 self.advance();
1662 if *self.peek() == Token::Column {
1664 self.advance();
1665 }
1666 let if_exists = self.parse_optional_if_exists();
1667 let name = self.expect_named_ident("column name")?;
1668 Ok(Statement::AlterTable(AlterTableExpr {
1669 table,
1670 action: AlterAction::DropColumn { name, if_exists },
1671 }))
1672 }
1673 t => Err(ParseError::UnexpectedToken {
1674 expected: "add or drop after alter <table>".into(),
1675 got: t.display_name(),
1676 }),
1677 }
1678 }
1679
1680 fn parse_drop_or_drop_view(&mut self) -> Result<Statement, ParseError> {
1682 self.expect(&Token::Drop)?;
1683 if *self.peek() == Token::View {
1684 self.advance(); let if_exists = self.parse_optional_if_exists();
1686 let name = match self.advance() {
1687 Token::Ident(name) => name,
1688 t => {
1689 return Err(ParseError::UnexpectedToken {
1690 expected: "view name after drop view".into(),
1691 got: t.display_name(),
1692 })
1693 }
1694 };
1695 return Ok(Statement::DropView(DropViewExpr { name, if_exists }));
1696 }
1697 let if_exists = self.parse_optional_if_exists();
1698 let table = match self.advance() {
1699 Token::Ident(name) => name,
1700 t => {
1701 return Err(ParseError::UnexpectedToken {
1702 expected: "table name after drop".into(),
1703 got: t.display_name(),
1704 })
1705 }
1706 };
1707 Ok(Statement::DropTable(DropTableExpr { table, if_exists }))
1708 }
1709
1710 fn parse_create_view(&mut self) -> Result<Statement, ParseError> {
1715 self.expect(&Token::Materialized)?;
1716 let name = match self.advance() {
1717 Token::Ident(name) => name,
1718 t => {
1719 return Err(ParseError::UnexpectedToken {
1720 expected: "view name after materialize".into(),
1721 got: t.display_name(),
1722 })
1723 }
1724 };
1725 self.expect(&Token::As)?;
1726 let query_start = self.pos;
1728 let source = match self.advance() {
1729 Token::Ident(s) => s,
1730 t => {
1731 return Err(ParseError::UnexpectedToken {
1732 expected: "source table name".into(),
1733 got: t.display_name(),
1734 })
1735 }
1736 };
1737 let query = self.parse_query_tail(source)?;
1738 let query_text = tokens_to_text(&self.tokens[query_start..self.pos]);
1740 Ok(Statement::CreateView(CreateViewExpr {
1741 name,
1742 query,
1743 query_text,
1744 }))
1745 }
1746
1747 fn maybe_parse_union(&mut self, left: Statement) -> Result<Statement, ParseError> {
1750 if *self.peek() != Token::Union {
1751 return Ok(left);
1752 }
1753 if !matches!(left, Statement::Query(_) | Statement::Union(_)) {
1754 return Err(ParseError::Syntax {
1755 message: "UNION requires a query on the left side".into(),
1756 });
1757 }
1758 self.advance(); let all = if let Token::Ident(s) = self.peek() {
1760 if s == "all" {
1761 self.advance();
1762 true
1763 } else {
1764 false
1765 }
1766 } else {
1767 false
1768 };
1769 let right = self.parse_single_query()?;
1771 let union = Statement::Union(UnionExpr {
1772 left: Box::new(left),
1773 right: Box::new(right),
1774 all,
1775 });
1776 self.maybe_parse_union(union)
1778 }
1779
1780 fn parse_single_query(&mut self) -> Result<Statement, ParseError> {
1782 match self.peek() {
1783 Token::Count | Token::Avg | Token::Sum | Token::Min | Token::Max => {
1784 self.parse_aggregate_query()
1785 }
1786 Token::Ident(_) => self.parse_query_or_mutation(),
1787 _ => Err(ParseError::Syntax {
1788 message: format!(
1789 "expected query after UNION, got {}",
1790 self.peek().display_name()
1791 ),
1792 }),
1793 }
1794 }
1795
1796 fn parse_refresh_view(&mut self) -> Result<Statement, ParseError> {
1798 self.expect(&Token::Refresh)?;
1799 let name = match self.advance() {
1800 Token::Ident(name) => name,
1801 t => {
1802 return Err(ParseError::UnexpectedToken {
1803 expected: "view name after refresh".into(),
1804 got: t.display_name(),
1805 })
1806 }
1807 };
1808 Ok(Statement::RefreshView(RefreshViewExpr { name }))
1809 }
1810
1811 fn parse_create_type(&mut self) -> Result<Statement, ParseError> {
1812 self.expect(&Token::Type)?;
1813 let name = self.expect_named_ident("type name")?;
1814 let if_not_exists = self.parse_optional_if_not_exists();
1815 self.expect(&Token::LBrace)?;
1816 let mut fields = Vec::new();
1817 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1818 let (mut required, mut unique, mut auto) = (false, false, false);
1823 loop {
1824 let is_modifier =
1825 matches!(self.peek(), Token::Required | Token::Unique | Token::Auto)
1826 && !matches!(self.tokens.get(self.pos + 1), Some(Token::Colon));
1827 if !is_modifier {
1828 break;
1829 }
1830 match self.advance() {
1831 Token::Required => required = true,
1832 Token::Unique => unique = true,
1833 Token::Auto => auto = true,
1834 _ => unreachable!("guarded by is_modifier"),
1835 }
1836 }
1837 let field_name = self.expect_named_ident("field name")?;
1838 self.expect(&Token::Colon)?;
1839 let type_name = match self.advance() {
1840 Token::Ident(n) => n,
1841 t => {
1842 return Err(ParseError::UnexpectedToken {
1843 expected: "type name".into(),
1844 got: t.display_name(),
1845 })
1846 }
1847 };
1848 let default = if *self.peek() == Token::Default {
1851 self.advance();
1852 Some(self.parse_default_literal()?)
1853 } else {
1854 None
1855 };
1856 fields.push(FieldDef {
1857 name: field_name,
1858 type_name,
1859 required,
1860 unique,
1861 default,
1862 auto,
1863 });
1864 if *self.peek() == Token::Comma {
1865 self.advance();
1866 }
1867 }
1868 self.expect(&Token::RBrace)?;
1869 Ok(Statement::CreateType(CreateTypeExpr {
1870 name,
1871 fields,
1872 if_not_exists,
1873 }))
1874 }
1875
1876 fn parse_schema(&mut self) -> Result<Statement, ParseError> {
1879 self.expect(&Token::Schema)?;
1880 if let Token::Ident(_) = self.peek() {
1881 let table = self.expect_named_ident("type name")?;
1882 return Ok(Statement::Describe(table));
1883 }
1884 Ok(Statement::ListTypes)
1885 }
1886
1887 fn parse_describe(&mut self) -> Result<Statement, ParseError> {
1889 self.expect(&Token::Describe)?;
1890 let table = self.expect_named_ident("type name")?;
1891 Ok(Statement::Describe(table))
1892 }
1893
1894 fn parse_default_literal(&mut self) -> Result<Literal, ParseError> {
1898 match self.advance() {
1899 Token::IntLit(v) => Ok(Literal::Int(v)),
1900 Token::FloatLit(v) => Ok(Literal::Float(v)),
1901 Token::StringLit(v) => Ok(Literal::String(v)),
1902 Token::BoolLit(v) => Ok(Literal::Bool(v)),
1903 t => Err(ParseError::UnexpectedToken {
1904 expected: "literal default value".into(),
1905 got: t.display_name(),
1906 }),
1907 }
1908 }
1909}
1910
1911fn tokens_to_text(tokens: &[Token]) -> String {
1915 let mut out = String::with_capacity(64);
1916 for tok in tokens {
1917 if !out.is_empty() && !matches!(tok, Token::Eof) {
1918 out.push(' ');
1919 }
1920 match tok {
1921 Token::Ident(s) => out.push_str(s),
1922 Token::DotIdent(s) => {
1923 out.push('.');
1924 out.push_str(s);
1925 }
1926 Token::IntLit(v) => out.push_str(&v.to_string()),
1927 Token::FloatLit(v) => out.push_str(&v.to_string()),
1928 Token::StringLit(s) => {
1929 out.push('"');
1930 out.push_str(s);
1931 out.push('"');
1932 }
1933 Token::BoolLit(v) => out.push_str(if *v { "true" } else { "false" }),
1934 Token::Param(s) => {
1935 out.push('$');
1936 out.push_str(s);
1937 }
1938 Token::Type => out.push_str("type"),
1939 Token::Filter => out.push_str("filter"),
1940 Token::Order => out.push_str("order"),
1941 Token::Limit => out.push_str("limit"),
1942 Token::Offset => out.push_str("offset"),
1943 Token::Insert => out.push_str("insert"),
1944 Token::Update => out.push_str("update"),
1945 Token::Delete => out.push_str("delete"),
1946 Token::Upsert => out.push_str("upsert"),
1947 Token::Returning => out.push_str("returning"),
1948 Token::Conflict => out.push_str("conflict"),
1949 Token::Select => out.push_str("select"),
1950 Token::Required => out.push_str("required"),
1951 Token::Default => out.push_str("default"),
1952 Token::Auto => out.push_str("auto"),
1953 Token::Multi => out.push_str("multi"),
1954 Token::Link => out.push_str("link"),
1955 Token::Index => out.push_str("index"),
1956 Token::Unique => out.push_str("unique"),
1957 Token::On => out.push_str("on"),
1958 Token::Asc => out.push_str("asc"),
1959 Token::Desc => out.push_str("desc"),
1960 Token::And => out.push_str("and"),
1961 Token::Or => out.push_str("or"),
1962 Token::Not => out.push_str("not"),
1963 Token::Exists => out.push_str("exists"),
1964 Token::Let => out.push_str("let"),
1965 Token::As => out.push_str("as"),
1966 Token::Match => out.push_str("match"),
1967 Token::Group => out.push_str("group"),
1968 Token::Join => out.push_str("join"),
1969 Token::Inner => out.push_str("inner"),
1970 Token::LeftKw => out.push_str("left"),
1971 Token::RightKw => out.push_str("right"),
1972 Token::Outer => out.push_str("outer"),
1973 Token::Cross => out.push_str("cross"),
1974 Token::Transaction => out.push_str("transaction"),
1975 Token::Begin => out.push_str("begin"),
1976 Token::Commit => out.push_str("commit"),
1977 Token::Rollback => out.push_str("rollback"),
1978 Token::View => out.push_str("view"),
1979 Token::Materialized => out.push_str("materialized"),
1980 Token::Refresh => out.push_str("refresh"),
1981 Token::Union => out.push_str("union"),
1982 Token::Having => out.push_str("having"),
1983 Token::Distinct => out.push_str("distinct"),
1984 Token::In => out.push_str("in"),
1985 Token::Between => out.push_str("between"),
1986 Token::Like => out.push_str("like"),
1987 Token::Count => out.push_str("count"),
1988 Token::Avg => out.push_str("avg"),
1989 Token::Sum => out.push_str("sum"),
1990 Token::Min => out.push_str("min"),
1991 Token::Max => out.push_str("max"),
1992 Token::Is => out.push_str("is"),
1993 Token::Null => out.push_str("null"),
1994 Token::Upper => out.push_str("upper"),
1995 Token::Lower => out.push_str("lower"),
1996 Token::Length => out.push_str("length"),
1997 Token::Trim => out.push_str("trim"),
1998 Token::Substring => out.push_str("substring"),
1999 Token::Concat => out.push_str("concat"),
2000 Token::Abs => out.push_str("abs"),
2001 Token::Round => out.push_str("round"),
2002 Token::Ceil => out.push_str("ceil"),
2003 Token::Floor => out.push_str("floor"),
2004 Token::Sqrt => out.push_str("sqrt"),
2005 Token::Pow => out.push_str("pow"),
2006 Token::Now => out.push_str("now"),
2007 Token::Extract => out.push_str("extract"),
2008 Token::DateAdd => out.push_str("date_add"),
2009 Token::DateDiff => out.push_str("date_diff"),
2010 Token::Cast => out.push_str("cast"),
2011 Token::Case => out.push_str("case"),
2012 Token::When => out.push_str("when"),
2013 Token::Then => out.push_str("then"),
2014 Token::Else => out.push_str("else"),
2015 Token::End => out.push_str("end"),
2016 Token::Over => out.push_str("over"),
2017 Token::Partition => out.push_str("partition"),
2018 Token::RowNumber => out.push_str("row_number"),
2019 Token::Rank => out.push_str("rank"),
2020 Token::DenseRank => out.push_str("dense_rank"),
2021 Token::Alter => out.push_str("alter"),
2022 Token::Drop => out.push_str("drop"),
2023 Token::Add => out.push_str("add"),
2024 Token::Column => out.push_str("column"),
2025 Token::Eq => out.push('='),
2026 Token::Neq => out.push_str("!="),
2027 Token::Lt => out.push('<'),
2028 Token::Gt => out.push('>'),
2029 Token::Lte => out.push_str("<="),
2030 Token::Gte => out.push_str(">="),
2031 Token::Assign => out.push_str(":="),
2032 Token::Arrow => out.push_str("->"),
2033 Token::Pipe => out.push('|'),
2034 Token::Coalesce => out.push_str("??"),
2035 Token::Plus => out.push('+'),
2036 Token::Minus => out.push('-'),
2037 Token::Star => out.push('*'),
2038 Token::Slash => out.push('/'),
2039 Token::LBrace => out.push('{'),
2040 Token::RBrace => out.push('}'),
2041 Token::LParen => out.push('('),
2042 Token::RParen => out.push(')'),
2043 Token::Comma => out.push(','),
2044 Token::Colon => out.push(':'),
2045 Token::Dot => out.push('.'),
2046 Token::Explain => out.push_str("explain"),
2047 Token::Schema => out.push_str("schema"),
2048 Token::Describe => out.push_str("describe"),
2049 Token::Eof => {}
2050 }
2051 }
2052 out
2053}
2054
2055#[cfg(test)]
2056mod tests {
2057 use super::*;
2058 #[test]
2059 fn test_parse_simple_query() {
2060 let stmt = parse("User").unwrap();
2061 match stmt {
2062 Statement::Query(q) => {
2063 assert_eq!(q.source, "User");
2064 assert!(q.filter.is_none());
2065 assert!(q.projection.is_none());
2066 }
2067 _ => panic!("expected query"),
2068 }
2069 }
2070
2071 #[test]
2072 fn test_parse_filter() {
2073 let stmt = parse("User filter .age > 30").unwrap();
2074 match stmt {
2075 Statement::Query(q) => {
2076 assert_eq!(q.source, "User");
2077 assert!(q.filter.is_some());
2078 }
2079 _ => panic!("expected query"),
2080 }
2081 }
2082
2083 #[test]
2084 fn test_parse_projection() {
2085 let stmt = parse("User { name, email }").unwrap();
2086 match stmt {
2087 Statement::Query(q) => {
2088 let proj = q.projection.unwrap();
2089 assert_eq!(proj.len(), 2);
2090 }
2091 _ => panic!("expected query"),
2092 }
2093 }
2094
2095 #[test]
2096 fn test_parse_filter_order_limit() {
2097 let stmt = parse("User filter .age > 30 order .name desc limit 10").unwrap();
2098 match stmt {
2099 Statement::Query(q) => {
2100 assert!(q.filter.is_some());
2101 let order = q.order.unwrap();
2102 assert_eq!(order.keys.len(), 1);
2103 assert_eq!(order.keys[0].field, "name");
2104 assert!(order.keys[0].descending);
2105 assert!(q.limit.is_some());
2106 }
2107 _ => panic!("expected query"),
2108 }
2109 }
2110
2111 #[test]
2112 fn test_parse_insert() {
2113 let stmt = parse(r#"insert User { name := "Alice", age := 30 }"#).unwrap();
2114 match stmt {
2115 Statement::Insert(ins) => {
2116 assert_eq!(ins.target, "User");
2117 assert_eq!(ins.rows.len(), 1);
2118 assert_eq!(ins.rows[0].len(), 2);
2119 assert_eq!(ins.rows[0][0].field, "name");
2120 assert_eq!(ins.rows[0][1].field, "age");
2121 }
2122 _ => panic!("expected insert"),
2123 }
2124 }
2125
2126 #[test]
2127 fn test_parse_insert_multi_row() {
2128 let stmt =
2129 parse(r#"insert User { name := "Alice", age := 30 }, { name := "Bob", age := 25 }, { name := "Cy" }"#)
2130 .unwrap();
2131 match stmt {
2132 Statement::Insert(ins) => {
2133 assert_eq!(ins.target, "User");
2134 assert_eq!(ins.rows.len(), 3);
2135 assert_eq!(ins.rows[0].len(), 2);
2136 assert_eq!(ins.rows[1][0].field, "name");
2137 assert_eq!(ins.rows[2].len(), 1);
2138 assert_eq!(ins.rows[2][0].field, "name");
2139 }
2140 _ => panic!("expected insert"),
2141 }
2142 }
2143
2144 #[test]
2145 fn test_parse_update() {
2146 let stmt = parse(r#"User filter .email = "alice@ex.com" update { age := 31 }"#).unwrap();
2147 match stmt {
2148 Statement::UpdateQuery(upd) => {
2149 assert_eq!(upd.source, "User");
2150 assert!(upd.filter.is_some());
2151 assert_eq!(upd.assignments.len(), 1);
2152 assert!(!upd.returning);
2153 }
2154 _ => panic!("expected update"),
2155 }
2156 }
2157
2158 #[test]
2159 fn test_parse_update_returning() {
2160 let stmt = parse(r#"User filter .name = "Alice" update { age := 31 } returning"#).unwrap();
2161 match stmt {
2162 Statement::UpdateQuery(upd) => assert!(upd.returning),
2163 _ => panic!("expected update"),
2164 }
2165 }
2166
2167 #[test]
2168 fn test_parse_delete() {
2169 let stmt = parse("User filter .age < 18 delete").unwrap();
2170 match stmt {
2171 Statement::DeleteQuery(del) => {
2172 assert_eq!(del.source, "User");
2173 assert!(del.filter.is_some());
2174 assert!(!del.returning);
2175 }
2176 _ => panic!("expected delete"),
2177 }
2178 }
2179
2180 #[test]
2181 fn test_parse_delete_returning() {
2182 let stmt = parse("User filter .age < 18 delete returning").unwrap();
2183 match stmt {
2184 Statement::DeleteQuery(del) => assert!(del.returning),
2185 _ => panic!("expected delete"),
2186 }
2187 }
2188
2189 #[test]
2190 fn test_parse_count() {
2191 let stmt = parse("count(User)").unwrap();
2192 match stmt {
2193 Statement::Query(q) => {
2194 let agg = q.aggregation.unwrap();
2195 assert_eq!(agg.function, AggFunc::Count);
2196 assert!(q.filter.is_none());
2197 }
2198 _ => panic!("expected query with aggregation"),
2199 }
2200 }
2201
2202 #[test]
2203 fn test_parse_count_with_filter() {
2204 let stmt = parse("count(User filter .age > 30)").unwrap();
2207 match stmt {
2208 Statement::Query(q) => {
2209 assert_eq!(q.source, "User");
2210 let agg = q.aggregation.unwrap();
2211 assert_eq!(agg.function, AggFunc::Count);
2212 assert!(q.filter.is_some(), "filter should have been parsed");
2213 }
2214 _ => panic!("expected query with aggregation"),
2215 }
2216 }
2217
2218 #[test]
2219 fn test_parse_count_with_filter_and_limit() {
2220 let stmt = parse("count(User filter .age > 30 limit 100)").unwrap();
2221 match stmt {
2222 Statement::Query(q) => {
2223 assert_eq!(q.source, "User");
2224 assert!(q.filter.is_some());
2225 assert!(q.limit.is_some());
2226 assert_eq!(q.aggregation.unwrap().function, AggFunc::Count);
2227 }
2228 _ => panic!("expected query with aggregation"),
2229 }
2230 }
2231
2232 #[test]
2233 fn test_parse_create_type() {
2234 let stmt = parse("type User { required name: str, age: int }").unwrap();
2235 match stmt {
2236 Statement::CreateType(ct) => {
2237 assert_eq!(ct.name, "User");
2238 assert_eq!(ct.fields.len(), 2);
2239 assert!(ct.fields[0].required);
2240 assert!(!ct.fields[1].required);
2241 }
2242 _ => panic!("expected create type"),
2243 }
2244 }
2245
2246 #[test]
2247 fn test_parse_sum_with_field_projection() {
2248 let stmt = parse("sum(User filter .age > 30 { .age })").unwrap();
2251 match stmt {
2252 Statement::Query(q) => {
2253 let agg = q.aggregation.expect("aggregate");
2254 assert_eq!(agg.function, AggFunc::Sum);
2255 assert_eq!(agg.field.as_deref(), Some("age"));
2256 assert!(
2257 q.projection.is_none(),
2258 "projection should be lifted into agg.field"
2259 );
2260 }
2261 _ => panic!("expected query"),
2262 }
2263 }
2264
2265 #[test]
2266 fn test_parse_avg_min_max_with_field() {
2267 for (src, expected) in [
2268 ("avg(User { .age })", AggFunc::Avg),
2269 ("min(User { .age })", AggFunc::Min),
2270 ("max(User { .age })", AggFunc::Max),
2271 ] {
2272 let stmt = parse(src).unwrap();
2273 match stmt {
2274 Statement::Query(q) => {
2275 let agg = q.aggregation.unwrap();
2276 assert_eq!(agg.function, expected, "func mismatch for {src}");
2277 assert_eq!(
2278 agg.field.as_deref(),
2279 Some("age"),
2280 "field mismatch for {src}"
2281 );
2282 assert!(
2283 q.projection.is_none(),
2284 "projection should be cleared for {src}"
2285 );
2286 }
2287 _ => panic!("expected query for {src}"),
2288 }
2289 }
2290 }
2291
2292 #[test]
2293 fn test_parse_count_leaves_projection_alone() {
2294 let stmt = parse("count(User { .age })").unwrap();
2297 match stmt {
2298 Statement::Query(q) => {
2299 let agg = q.aggregation.unwrap();
2300 assert_eq!(agg.function, AggFunc::Count);
2301 assert!(agg.field.is_none());
2302 assert!(q.projection.is_some(), "count must not eat projection");
2303 }
2304 _ => panic!("expected query"),
2305 }
2306 }
2307
2308 #[test]
2313 fn test_parse_source_alias() {
2314 let stmt = parse("User as u filter u.age > 30").unwrap();
2315 match stmt {
2316 Statement::Query(q) => {
2317 assert_eq!(q.source, "User");
2318 assert_eq!(q.alias.as_deref(), Some("u"));
2319 assert!(q.joins.is_empty());
2320 match q.filter.unwrap() {
2321 Expr::BinaryOp(l, BinOp::Gt, _) => match *l {
2322 Expr::QualifiedField { qualifier, field } => {
2323 assert_eq!(qualifier, "u");
2324 assert_eq!(field, "age");
2325 }
2326 other => panic!("expected qualified field, got {other:?}"),
2327 },
2328 other => panic!("expected >, got {other:?}"),
2329 }
2330 }
2331 _ => panic!("expected query"),
2332 }
2333 }
2334
2335 #[test]
2336 fn test_parse_inner_join_on() {
2337 let stmt = parse("User as u inner join Order as o on u.id = o.user_id").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_eq!(q.joins.len(), 1);
2343 let j = &q.joins[0];
2344 assert_eq!(j.kind, JoinKind::Inner);
2345 assert_eq!(j.source, "Order");
2346 assert_eq!(j.alias.as_deref(), Some("o"));
2347 let on = j.on.as_ref().expect("on clause");
2348 match on {
2349 Expr::BinaryOp(l, BinOp::Eq, r) => {
2350 assert!(matches!(**l, Expr::QualifiedField { .. }));
2351 assert!(matches!(**r, Expr::QualifiedField { .. }));
2352 }
2353 other => panic!("expected eq, got {other:?}"),
2354 }
2355 }
2356 _ => panic!("expected query"),
2357 }
2358 }
2359
2360 #[test]
2361 fn test_parse_bare_join_defaults_to_inner() {
2362 let stmt = parse("User join Order on User.id = Order.user_id").unwrap();
2363 match stmt {
2364 Statement::Query(q) => {
2365 assert_eq!(q.joins.len(), 1);
2366 assert_eq!(q.joins[0].kind, JoinKind::Inner);
2367 }
2368 _ => panic!("expected query"),
2369 }
2370 }
2371
2372 #[test]
2373 fn test_parse_left_outer_join() {
2374 let stmt = parse("User as u left outer join Order as o on u.id = o.user_id").unwrap();
2375 match stmt {
2376 Statement::Query(q) => {
2377 assert_eq!(q.joins.len(), 1);
2378 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2379 }
2380 _ => panic!("expected query"),
2381 }
2382 }
2383
2384 #[test]
2385 fn test_parse_left_join_without_outer_keyword() {
2386 let stmt = parse("User as u left join Order as o on u.id = o.user_id").unwrap();
2388 match stmt {
2389 Statement::Query(q) => {
2390 assert_eq!(q.joins[0].kind, JoinKind::LeftOuter);
2391 }
2392 _ => panic!("expected query"),
2393 }
2394 }
2395
2396 #[test]
2397 fn test_parse_right_join() {
2398 let stmt = parse("User as u right join Order as o on u.id = o.user_id").unwrap();
2399 match stmt {
2400 Statement::Query(q) => {
2401 assert_eq!(q.joins[0].kind, JoinKind::RightOuter);
2402 }
2403 _ => panic!("expected query"),
2404 }
2405 }
2406
2407 #[test]
2408 fn test_parse_cross_join_has_no_on() {
2409 let stmt = parse("User cross join Order").unwrap();
2410 match stmt {
2411 Statement::Query(q) => {
2412 assert_eq!(q.joins[0].kind, JoinKind::Cross);
2413 assert!(q.joins[0].on.is_none());
2414 }
2415 _ => panic!("expected query"),
2416 }
2417 }
2418
2419 #[test]
2420 fn test_parse_multi_join_chain() {
2421 let stmt = parse(
2422 "User as u join Order as o on u.id = o.user_id \
2423 join Product as p on o.product_id = p.id",
2424 )
2425 .unwrap();
2426 match stmt {
2427 Statement::Query(q) => {
2428 assert_eq!(q.joins.len(), 2);
2429 assert_eq!(q.joins[0].source, "Order");
2430 assert_eq!(q.joins[1].source, "Product");
2431 }
2432 _ => panic!("expected query"),
2433 }
2434 }
2435
2436 #[test]
2437 fn test_parse_join_with_filter_tail() {
2438 let stmt = parse(
2440 "User as u join Order as o on u.id = o.user_id \
2441 filter o.total > 100 order .name limit 10",
2442 )
2443 .unwrap();
2444 match stmt {
2445 Statement::Query(q) => {
2446 assert_eq!(q.joins.len(), 1);
2447 assert!(q.filter.is_some());
2448 assert!(q.order.is_some());
2449 assert!(q.limit.is_some());
2450 }
2451 _ => panic!("expected query"),
2452 }
2453 }
2454
2455 #[test]
2456 fn test_parse_join_requires_on_for_inner() {
2457 let err = parse("User join Order").unwrap_err();
2459 assert!(
2460 err.message().contains("on"),
2461 "expected on-clause error, got {:?}",
2462 err.message()
2463 );
2464 }
2465
2466 #[test]
2467 fn test_parse_update_on_joined_query_errors() {
2468 let err =
2471 parse("User as u join Order as o on u.id = o.user_id update { age := 1 }").unwrap_err();
2472 assert!(err.message().contains("update"));
2473 }
2474
2475 #[test]
2476 fn test_parse_delete_on_joined_query_errors() {
2477 let err = parse("User as u join Order as o on u.id = o.user_id delete").unwrap_err();
2478 assert!(err.message().contains("delete"));
2479 }
2480
2481 #[test]
2484 fn test_parse_distinct() {
2485 let stmt = parse("User distinct { .name }").unwrap();
2486 match stmt {
2487 Statement::Query(q) => {
2488 assert!(q.distinct);
2489 assert!(q.projection.is_some());
2490 }
2491 _ => panic!("expected query"),
2492 }
2493 }
2494
2495 #[test]
2496 fn test_parse_in_list() {
2497 let stmt = parse(r#"User filter .name in ("Alice", "Bob")"#).unwrap();
2498 match stmt {
2499 Statement::Query(q) => match q.filter.unwrap() {
2500 Expr::InList {
2501 expr,
2502 list,
2503 negated,
2504 } => {
2505 assert!(!negated);
2506 assert!(matches!(*expr, Expr::Field(f) if f == "name"));
2507 assert_eq!(list.len(), 2);
2508 }
2509 other => panic!("expected InList, got {other:?}"),
2510 },
2511 _ => panic!("expected query"),
2512 }
2513 }
2514
2515 #[test]
2516 fn test_parse_not_in_list() {
2517 let stmt = parse("User filter .age not in (1, 2, 3)").unwrap();
2518 match stmt {
2519 Statement::Query(q) => match q.filter.unwrap() {
2520 Expr::InList { negated, list, .. } => {
2521 assert!(negated);
2522 assert_eq!(list.len(), 3);
2523 }
2524 other => panic!("expected InList, got {other:?}"),
2525 },
2526 _ => panic!("expected query"),
2527 }
2528 }
2529
2530 #[test]
2531 fn test_parse_between() {
2532 let stmt = parse("User filter .age between 10 and 20").unwrap();
2534 match stmt {
2535 Statement::Query(q) => {
2536 match q.filter.unwrap() {
2537 Expr::BinaryOp(_, BinOp::And, _) => {} other => panic!("expected And (desugared between), got {other:?}"),
2539 }
2540 }
2541 _ => panic!("expected query"),
2542 }
2543 }
2544
2545 #[test]
2546 fn test_parse_not_between() {
2547 let stmt = parse("User filter .age not between 10 and 20").unwrap();
2549 match stmt {
2550 Statement::Query(q) => {
2551 match q.filter.unwrap() {
2552 Expr::BinaryOp(_, BinOp::Or, _) => {} other => panic!("expected Or (desugared not between), got {other:?}"),
2554 }
2555 }
2556 _ => panic!("expected query"),
2557 }
2558 }
2559
2560 #[test]
2561 fn test_parse_like() {
2562 let stmt = parse(r#"User filter .name like "A%""#).unwrap();
2563 match stmt {
2564 Statement::Query(q) => match q.filter.unwrap() {
2565 Expr::BinaryOp(l, BinOp::Like, r) => {
2566 assert!(matches!(*l, Expr::Field(f) if f == "name"));
2567 assert!(matches!(*r, Expr::Literal(Literal::String(s)) if s == "A%"));
2568 }
2569 other => panic!("expected Like, got {other:?}"),
2570 },
2571 _ => panic!("expected query"),
2572 }
2573 }
2574
2575 #[test]
2576 fn test_parse_not_like() {
2577 let stmt = parse(r#"User filter .name not like "A%""#).unwrap();
2578 match stmt {
2579 Statement::Query(q) => match q.filter.unwrap() {
2580 Expr::UnaryOp(UnaryOp::Not, inner) => {
2581 assert!(matches!(*inner, Expr::BinaryOp(_, BinOp::Like, _)));
2582 }
2583 other => panic!("expected Not(Like), got {other:?}"),
2584 },
2585 _ => panic!("expected query"),
2586 }
2587 }
2588
2589 #[test]
2592 fn test_parse_group_by_single_key() {
2593 let stmt = parse("User group .status { .status, n: count(.name) }").unwrap();
2594 match stmt {
2595 Statement::Query(q) => {
2596 let gb = q.group_by.unwrap();
2597 assert_eq!(gb.keys, vec!["status"]);
2598 assert!(gb.having.is_none());
2599 let proj = q.projection.unwrap();
2600 assert_eq!(proj.len(), 2);
2601 assert!(matches!(
2602 &proj[1].expr,
2603 Expr::FunctionCall(AggFunc::Count, _)
2604 ));
2605 assert_eq!(proj[1].alias.as_deref(), Some("n"));
2606 }
2607 _ => panic!("expected query"),
2608 }
2609 }
2610
2611 #[test]
2612 fn test_parse_group_by_multi_key() {
2613 let stmt = parse("User group .status, .age { .status, .age }").unwrap();
2614 match stmt {
2615 Statement::Query(q) => {
2616 let gb = q.group_by.unwrap();
2617 assert_eq!(gb.keys, vec!["status", "age"]);
2618 }
2619 _ => panic!("expected query"),
2620 }
2621 }
2622
2623 #[test]
2624 fn test_parse_group_by_having() {
2625 let stmt = parse("User group .status having count(.name) > 1 { .status }").unwrap();
2626 match stmt {
2627 Statement::Query(q) => {
2628 let gb = q.group_by.unwrap();
2629 assert_eq!(gb.keys, vec!["status"]);
2630 assert!(gb.having.is_some());
2631 match gb.having.unwrap() {
2633 Expr::BinaryOp(l, BinOp::Gt, _) => {
2634 assert!(matches!(*l, Expr::FunctionCall(AggFunc::Count, _)));
2635 }
2636 other => panic!("expected BinaryOp, got {other:?}"),
2637 }
2638 }
2639 _ => panic!("expected query"),
2640 }
2641 }
2642
2643 #[test]
2644 fn test_parse_aggregate_in_projection() {
2645 let stmt = parse("User group .status { .status, count(.name), sum(.age) }").unwrap();
2647 match stmt {
2648 Statement::Query(q) => {
2649 let proj = q.projection.unwrap();
2650 assert_eq!(proj.len(), 3);
2651 assert!(matches!(
2652 &proj[1].expr,
2653 Expr::FunctionCall(AggFunc::Count, _)
2654 ));
2655 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Sum, _)));
2656 }
2657 _ => panic!("expected query"),
2658 }
2659 }
2660
2661 #[test]
2662 fn test_parse_aggregate_in_aliased_projection() {
2663 let stmt = parse("User group .status { .status, total: count(.name), average: avg(.age) }")
2664 .unwrap();
2665 match stmt {
2666 Statement::Query(q) => {
2667 let proj = q.projection.unwrap();
2668 assert_eq!(proj[1].alias.as_deref(), Some("total"));
2669 assert!(matches!(
2670 &proj[1].expr,
2671 Expr::FunctionCall(AggFunc::Count, _)
2672 ));
2673 assert_eq!(proj[2].alias.as_deref(), Some("average"));
2674 assert!(matches!(&proj[2].expr, Expr::FunctionCall(AggFunc::Avg, _)));
2675 }
2676 _ => panic!("expected query"),
2677 }
2678 }
2679
2680 #[test]
2683 fn test_parse_is_null() {
2684 let stmt = parse("User filter .age is null").unwrap();
2685 match stmt {
2686 Statement::Query(q) => {
2687 let filter = q.filter.unwrap();
2688 assert_eq!(
2689 filter,
2690 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2691 );
2692 }
2693 _ => panic!("expected query"),
2694 }
2695 }
2696
2697 #[test]
2698 fn test_parse_is_not_null() {
2699 let stmt = parse("User filter .age is not null").unwrap();
2700 match stmt {
2701 Statement::Query(q) => {
2702 let filter = q.filter.unwrap();
2703 assert_eq!(
2704 filter,
2705 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2706 );
2707 }
2708 _ => panic!("expected query"),
2709 }
2710 }
2711
2712 #[test]
2713 fn test_parse_eq_null_desugars_to_is_null() {
2714 let stmt = parse("User filter .age = null").unwrap();
2715 match stmt {
2716 Statement::Query(q) => {
2717 let filter = q.filter.unwrap();
2718 assert_eq!(
2719 filter,
2720 Expr::UnaryOp(UnaryOp::IsNull, Box::new(Expr::Field("age".into())))
2721 );
2722 }
2723 _ => panic!("expected query"),
2724 }
2725 }
2726
2727 #[test]
2728 fn test_parse_neq_null_desugars_to_is_not_null() {
2729 let stmt = parse("User filter .age != null").unwrap();
2730 match stmt {
2731 Statement::Query(q) => {
2732 let filter = q.filter.unwrap();
2733 assert_eq!(
2734 filter,
2735 Expr::UnaryOp(UnaryOp::IsNotNull, Box::new(Expr::Field("age".into())))
2736 );
2737 }
2738 _ => panic!("expected query"),
2739 }
2740 }
2741
2742 #[test]
2743 fn test_parse_null_comparisons_parse_ok() {
2744 assert!(parse("User filter .age < null").is_ok());
2748 assert!(parse("User filter .age >= null").is_ok());
2749 }
2750
2751 #[test]
2752 fn test_parse_count_star_expr() {
2753 let stmt = parse("User filter count(*) > 0").unwrap();
2754 match stmt {
2755 Statement::Query(q) => {
2756 let filter = q.filter.unwrap();
2757 match filter {
2758 Expr::BinaryOp(left, BinOp::Gt, _) => {
2759 assert_eq!(
2760 *left,
2761 Expr::FunctionCall(AggFunc::Count, Box::new(Expr::Field("*".into())))
2762 );
2763 }
2764 _ => panic!("expected comparison"),
2765 }
2766 }
2767 _ => panic!("expected query"),
2768 }
2769 }
2770
2771 #[test]
2774 fn test_parse_upper_in_filter() {
2775 let stmt = parse(r#"User filter upper(.name) = "ALICE""#).unwrap();
2776 match stmt {
2777 Statement::Query(q) => {
2778 let f = q.filter.unwrap();
2779 match f {
2780 Expr::BinaryOp(left, BinOp::Eq, _right) => {
2781 assert!(matches!(*left, Expr::ScalarFunc(ScalarFn::Upper, _)));
2782 }
2783 _ => panic!("expected binary op with upper"),
2784 }
2785 }
2786 _ => panic!("expected query"),
2787 }
2788 }
2789
2790 #[test]
2791 fn test_parse_substring() {
2792 let stmt = parse("User { sub: substring(.name, 1, 3) }").unwrap();
2793 match stmt {
2794 Statement::Query(q) => {
2795 let proj = q.projection.unwrap();
2796 match &proj[0].expr {
2797 Expr::ScalarFunc(ScalarFn::Substring, args) => {
2798 assert_eq!(args.len(), 3);
2799 }
2800 other => panic!("expected ScalarFunc Substring, got {other:?}"),
2801 }
2802 }
2803 _ => panic!("expected query"),
2804 }
2805 }
2806
2807 #[test]
2808 fn test_parse_concat() {
2809 let stmt = parse(r#"User { full: concat(.name, " - ", .email) }"#).unwrap();
2810 match stmt {
2811 Statement::Query(q) => {
2812 let proj = q.projection.unwrap();
2813 match &proj[0].expr {
2814 Expr::ScalarFunc(ScalarFn::Concat, args) => {
2815 assert_eq!(args.len(), 3);
2816 }
2817 other => panic!("expected ScalarFunc Concat, got {other:?}"),
2818 }
2819 }
2820 _ => panic!("expected query"),
2821 }
2822 }
2823
2824 #[test]
2827 fn test_parse_case_single_when() {
2828 let stmt = parse(r#"User filter case when .age > 30 then true else false end"#).unwrap();
2829 match stmt {
2830 Statement::Query(q) => {
2831 let filter = q.filter.unwrap();
2832 match filter {
2833 Expr::Case { whens, else_expr } => {
2834 assert_eq!(whens.len(), 1);
2835 assert!(else_expr.is_some());
2836 }
2837 other => panic!("expected Case expr, got {other:?}"),
2838 }
2839 }
2840 _ => panic!("expected query"),
2841 }
2842 }
2843
2844 #[test]
2845 fn test_parse_case_multiple_whens() {
2846 let stmt = parse(
2847 r#"User { label: case when .age > 30 then "senior" when .age > 20 then "adult" else "young" end }"#
2848 ).unwrap();
2849 match stmt {
2850 Statement::Query(q) => {
2851 let proj = q.projection.unwrap();
2852 match &proj[0].expr {
2853 Expr::Case { whens, else_expr } => {
2854 assert_eq!(whens.len(), 2);
2855 assert!(else_expr.is_some());
2856 }
2857 other => panic!("expected Case expr, got {other:?}"),
2858 }
2859 }
2860 _ => panic!("expected query"),
2861 }
2862 }
2863
2864 #[test]
2865 fn test_parse_case_without_else() {
2866 let stmt = parse(r#"User filter case when .age > 30 then true end"#).unwrap();
2867 match stmt {
2868 Statement::Query(q) => {
2869 let filter = q.filter.unwrap();
2870 match filter {
2871 Expr::Case { whens, else_expr } => {
2872 assert_eq!(whens.len(), 1);
2873 assert!(else_expr.is_none());
2874 }
2875 other => panic!("expected Case expr, got {other:?}"),
2876 }
2877 }
2878 _ => panic!("expected query"),
2879 }
2880 }
2881
2882 #[test]
2885 fn test_parse_mul_expr() {
2886 let stmt = parse("User filter .price * .quantity > 100").unwrap();
2887 match stmt {
2888 Statement::Query(q) => {
2889 let filter = q.filter.unwrap();
2890 match filter {
2891 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2892 Expr::BinaryOp(_, BinOp::Mul, _) => {}
2893 other => panic!("expected Mul, got {other:?}"),
2894 },
2895 other => panic!("expected BinaryOp Gt, got {other:?}"),
2896 }
2897 }
2898 _ => panic!("expected query"),
2899 }
2900 }
2901
2902 #[test]
2903 fn test_parse_div_expr() {
2904 let stmt = parse("User { ratio: .total / .count }").unwrap();
2905 match stmt {
2906 Statement::Query(q) => {
2907 let proj = q.projection.unwrap();
2908 assert_eq!(proj[0].alias.as_deref(), Some("ratio"));
2909 match &proj[0].expr {
2910 Expr::BinaryOp(_, BinOp::Div, _) => {}
2911 other => panic!("expected Div, got {other:?}"),
2912 }
2913 }
2914 _ => panic!("expected query"),
2915 }
2916 }
2917
2918 #[test]
2919 fn test_parse_mul_div_precedence() {
2920 let stmt = parse("User filter .a + .b * .c > 0").unwrap();
2922 match stmt {
2923 Statement::Query(q) => {
2924 let filter = q.filter.unwrap();
2925 match filter {
2926 Expr::BinaryOp(left, BinOp::Gt, _) => match *left {
2927 Expr::BinaryOp(_, BinOp::Add, right) => {
2928 assert!(matches!(*right, Expr::BinaryOp(_, BinOp::Mul, _)));
2929 }
2930 other => panic!("expected Add, got {other:?}"),
2931 },
2932 other => panic!("expected Gt, got {other:?}"),
2933 }
2934 }
2935 _ => panic!("expected query"),
2936 }
2937 }
2938
2939 #[test]
2942 fn test_parse_multi_order() {
2943 let stmt = parse("User order .name asc, .age desc").unwrap();
2944 match stmt {
2945 Statement::Query(q) => {
2946 let order = q.order.unwrap();
2947 assert_eq!(order.keys.len(), 2);
2948 assert_eq!(order.keys[0].field, "name");
2949 assert!(!order.keys[0].descending);
2950 assert_eq!(order.keys[1].field, "age");
2951 assert!(order.keys[1].descending);
2952 }
2953 _ => panic!("expected query"),
2954 }
2955 }
2956
2957 #[test]
2958 fn test_parse_order_default_asc() {
2959 let stmt = parse("User order .name").unwrap();
2960 match stmt {
2961 Statement::Query(q) => {
2962 let order = q.order.unwrap();
2963 assert_eq!(order.keys.len(), 1);
2964 assert!(!order.keys[0].descending);
2965 }
2966 _ => panic!("expected query"),
2967 }
2968 }
2969
2970 #[test]
2973 fn test_parse_alter_add_column() {
2974 let stmt = parse("alter User add column status: str").unwrap();
2975 match stmt {
2976 Statement::AlterTable(at) => {
2977 assert_eq!(at.table, "User");
2978 match at.action {
2979 AlterAction::AddColumn {
2980 name,
2981 type_name,
2982 required,
2983 } => {
2984 assert_eq!(name, "status");
2985 assert_eq!(type_name, "str");
2986 assert!(!required);
2987 }
2988 other => panic!("expected AddColumn, got {other:?}"),
2989 }
2990 }
2991 other => panic!("expected AlterTable, got {other:?}"),
2992 }
2993 }
2994
2995 #[test]
2996 fn test_parse_alter_add_required_column() {
2997 let stmt = parse("alter User add required status: str").unwrap();
2998 match stmt {
2999 Statement::AlterTable(at) => match at.action {
3000 AlterAction::AddColumn { required, .. } => assert!(required),
3001 other => panic!("expected AddColumn, got {other:?}"),
3002 },
3003 other => panic!("expected AlterTable, got {other:?}"),
3004 }
3005 }
3006
3007 #[test]
3008 fn test_parse_type_with_unique_modifier() {
3009 let stmt = parse("type User { required unique email: str, age: int }").unwrap();
3010 match stmt {
3011 Statement::CreateType(ct) => {
3012 assert!(ct.fields[0].required && ct.fields[0].unique);
3013 assert!(!ct.fields[1].unique);
3014 }
3015 other => panic!("expected CreateType, got {other:?}"),
3016 }
3017 }
3018
3019 #[test]
3020 fn test_parse_type_unique_before_required() {
3021 let stmt = parse("type User { unique required email: str }").unwrap();
3023 match stmt {
3024 Statement::CreateType(ct) => {
3025 assert!(ct.fields[0].required && ct.fields[0].unique);
3026 }
3027 other => panic!("expected CreateType, got {other:?}"),
3028 }
3029 }
3030
3031 #[test]
3032 fn test_parse_alter_add_unique() {
3033 let stmt = parse("alter User add unique .email").unwrap();
3034 match stmt {
3035 Statement::AlterTable(at) => assert!(matches!(
3036 at.action,
3037 AlterAction::AddUnique { ref column, .. } if column == "email"
3038 )),
3039 other => panic!("expected AlterTable, got {other:?}"),
3040 }
3041 }
3042
3043 #[test]
3044 fn test_parse_alter_drop_column() {
3045 let stmt = parse("alter User drop column status").unwrap();
3046 match stmt {
3047 Statement::AlterTable(at) => {
3048 assert_eq!(at.table, "User");
3049 match at.action {
3050 AlterAction::DropColumn { name, .. } => assert_eq!(name, "status"),
3051 other => panic!("expected DropColumn, got {other:?}"),
3052 }
3053 }
3054 other => panic!("expected AlterTable, got {other:?}"),
3055 }
3056 }
3057
3058 #[test]
3059 fn test_parse_alter_drop_without_column_keyword() {
3060 let stmt = parse("alter User drop status").unwrap();
3061 match stmt {
3062 Statement::AlterTable(at) => match at.action {
3063 AlterAction::DropColumn { name, .. } => assert_eq!(name, "status"),
3064 other => panic!("expected DropColumn, got {other:?}"),
3065 },
3066 other => panic!("expected AlterTable, got {other:?}"),
3067 }
3068 }
3069
3070 #[test]
3071 fn test_parse_drop_table() {
3072 let stmt = parse("drop User").unwrap();
3073 match stmt {
3074 Statement::DropTable(dt) => assert_eq!(dt.table, "User"),
3075 other => panic!("expected DropTable, got {other:?}"),
3076 }
3077 }
3078
3079 #[test]
3082 fn test_parse_in_subquery() {
3083 let stmt = parse("User filter .name in (VIP { .name })").unwrap();
3084 match stmt {
3085 Statement::Query(q) => {
3086 let filter = q.filter.unwrap();
3087 match filter {
3088 Expr::InSubquery {
3089 expr,
3090 subquery,
3091 negated,
3092 } => {
3093 assert!(!negated);
3094 assert!(matches!(*expr, Expr::Field(ref f) if f == "name"));
3095 assert_eq!(subquery.source, "VIP");
3096 }
3097 other => panic!("expected InSubquery, got {other:?}"),
3098 }
3099 }
3100 _ => panic!("expected query"),
3101 }
3102 }
3103
3104 #[test]
3105 fn test_parse_not_in_subquery() {
3106 let stmt = parse("User filter .id not in (Order { .user_id })").unwrap();
3107 match stmt {
3108 Statement::Query(q) => match q.filter.unwrap() {
3109 Expr::InSubquery { negated, .. } => assert!(negated),
3110 other => panic!("expected InSubquery, got {other:?}"),
3111 },
3112 _ => panic!("expected query"),
3113 }
3114 }
3115
3116 #[test]
3117 fn test_parse_in_literal_list_still_works() {
3118 let stmt = parse("User filter .age in (25, 30, 35)").unwrap();
3120 match stmt {
3121 Statement::Query(q) => match q.filter.unwrap() {
3122 Expr::InList { list, negated, .. } => {
3123 assert!(!negated);
3124 assert_eq!(list.len(), 3);
3125 }
3126 other => panic!("expected InList, got {other:?}"),
3127 },
3128 _ => panic!("expected query"),
3129 }
3130 }
3131
3132 #[test]
3135 fn test_parse_create_view() {
3136 let stmt = parse("materialize OldUsers as User filter .age > 28").unwrap();
3137 match stmt {
3138 Statement::CreateView(cv) => {
3139 assert_eq!(cv.name, "OldUsers");
3140 assert_eq!(cv.query.source, "User");
3141 assert!(cv.query.filter.is_some());
3142 assert!(!cv.query_text.is_empty());
3143 }
3144 _ => panic!("expected CreateView"),
3145 }
3146 }
3147
3148 #[test]
3149 fn test_parse_create_view_with_projection() {
3150 let stmt = parse("materialize UserNames as User { .name }").unwrap();
3151 match stmt {
3152 Statement::CreateView(cv) => {
3153 assert_eq!(cv.name, "UserNames");
3154 assert!(cv.query.projection.is_some());
3155 }
3156 _ => panic!("expected CreateView"),
3157 }
3158 }
3159
3160 #[test]
3161 fn test_parse_refresh_view() {
3162 let stmt = parse("refresh OldUsers").unwrap();
3163 match stmt {
3164 Statement::RefreshView(rv) => {
3165 assert_eq!(rv.name, "OldUsers");
3166 }
3167 _ => panic!("expected RefreshView"),
3168 }
3169 }
3170
3171 #[test]
3172 fn test_parse_drop_view() {
3173 let stmt = parse("drop view OldUsers").unwrap();
3174 match stmt {
3175 Statement::DropView(dv) => {
3176 assert_eq!(dv.name, "OldUsers");
3177 }
3178 _ => panic!("expected DropView"),
3179 }
3180 }
3181
3182 #[test]
3183 fn test_parse_drop_table_still_works() {
3184 let stmt = parse("drop Users").unwrap();
3185 match stmt {
3186 Statement::DropTable(dt) => {
3187 assert_eq!(dt.table, "Users");
3188 }
3189 _ => panic!("expected DropTable"),
3190 }
3191 }
3192
3193 #[test]
3194 fn test_parse_union() {
3195 let stmt = parse("User union Order").unwrap();
3196 match stmt {
3197 Statement::Union(u) => {
3198 assert!(!u.all);
3199 match *u.left {
3200 Statement::Query(_) => {}
3201 _ => panic!("expected Query on left"),
3202 }
3203 match *u.right {
3204 Statement::Query(_) => {}
3205 _ => panic!("expected Query on right"),
3206 }
3207 }
3208 _ => panic!("expected Union"),
3209 }
3210 }
3211
3212 #[test]
3213 fn test_parse_union_all() {
3214 let stmt = parse("User union all Order").unwrap();
3215 match stmt {
3216 Statement::Union(u) => {
3217 assert!(u.all, "expected UNION ALL");
3218 match *u.left {
3219 Statement::Query(_) => {}
3220 _ => panic!("expected Query on left"),
3221 }
3222 match *u.right {
3223 Statement::Query(_) => {}
3224 _ => panic!("expected Query on right"),
3225 }
3226 }
3227 _ => panic!("expected Union"),
3228 }
3229 }
3230
3231 #[test]
3232 fn test_parse_union_chain() {
3233 let stmt = parse("User union Order union Product").unwrap();
3235 match stmt {
3236 Statement::Union(outer) => {
3237 assert!(!outer.all);
3238 match *outer.right {
3240 Statement::Query(q) => assert_eq!(q.source, "Product"),
3241 _ => panic!("expected Query(Product) on right"),
3242 }
3243 match *outer.left {
3245 Statement::Union(inner) => {
3246 assert!(!inner.all);
3247 match *inner.left {
3248 Statement::Query(q) => assert_eq!(q.source, "User"),
3249 _ => panic!("expected Query(User)"),
3250 }
3251 match *inner.right {
3252 Statement::Query(q) => assert_eq!(q.source, "Order"),
3253 _ => panic!("expected Query(Order)"),
3254 }
3255 }
3256 _ => panic!("expected inner Union"),
3257 }
3258 }
3259 _ => panic!("expected Union"),
3260 }
3261 }
3262
3263 #[test]
3264 fn test_parse_union_with_filter() {
3265 let stmt = parse("User filter .age > 10 union Order filter .total > 50").unwrap();
3266 match stmt {
3267 Statement::Union(u) => {
3268 assert!(!u.all);
3269 match *u.left {
3271 Statement::Query(q) => {
3272 assert_eq!(q.source, "User");
3273 assert!(q.filter.is_some());
3274 }
3275 _ => panic!("expected Query on left"),
3276 }
3277 match *u.right {
3278 Statement::Query(q) => {
3279 assert_eq!(q.source, "Order");
3280 assert!(q.filter.is_some());
3281 }
3282 _ => panic!("expected Query on right"),
3283 }
3284 }
3285 _ => panic!("expected Union"),
3286 }
3287 }
3288
3289 #[test]
3290 fn test_parse_count_distinct_standalone() {
3291 let stmt = parse("count(distinct User { .name })").unwrap();
3292 match stmt {
3293 Statement::Query(q) => {
3294 let agg = q.aggregation.unwrap();
3295 assert_eq!(agg.function, AggFunc::CountDistinct);
3296 assert_eq!(agg.field.as_deref(), Some("name"));
3297 }
3298 _ => panic!("expected Query"),
3299 }
3300 }
3301
3302 #[test]
3303 fn test_parse_count_distinct_in_projection() {
3304 let stmt = parse("User group .dept { .dept, count(distinct .name) }").unwrap();
3305 match stmt {
3306 Statement::Query(q) => {
3307 let proj = q.projection.unwrap();
3308 assert_eq!(proj.len(), 2);
3309 match &proj[1].expr {
3310 Expr::FunctionCall(func, _) => {
3311 assert_eq!(*func, AggFunc::CountDistinct);
3312 }
3313 _ => panic!("expected FunctionCall"),
3314 }
3315 }
3316 _ => panic!("expected Query"),
3317 }
3318 }
3319
3320 #[test]
3323 fn test_parse_window_row_number_order() {
3324 let stmt = parse("User { .name, rn: row_number() over (order .age) }").unwrap();
3325 match stmt {
3326 Statement::Query(q) => {
3327 let proj = q.projection.unwrap();
3328 assert_eq!(proj.len(), 2);
3329 assert_eq!(proj[1].alias.as_deref(), Some("rn"));
3330 match &proj[1].expr {
3331 Expr::Window {
3332 function,
3333 args,
3334 partition_by,
3335 order_by,
3336 } => {
3337 assert_eq!(*function, WindowFunc::RowNumber);
3338 assert!(args.is_empty());
3339 assert!(partition_by.is_empty());
3340 assert_eq!(order_by.len(), 1);
3341 assert_eq!(order_by[0].field, "age");
3342 assert!(!order_by[0].descending);
3343 }
3344 other => panic!("expected Window, got {other:?}"),
3345 }
3346 }
3347 _ => panic!("expected query"),
3348 }
3349 }
3350
3351 #[test]
3352 fn test_parse_window_sum_partition_order() {
3353 let stmt =
3354 parse("User { .name, s: sum(.salary) over (partition .dept order .salary) }").unwrap();
3355 match stmt {
3356 Statement::Query(q) => {
3357 let proj = q.projection.unwrap();
3358 assert_eq!(proj.len(), 2);
3359 assert_eq!(proj[1].alias.as_deref(), Some("s"));
3360 match &proj[1].expr {
3361 Expr::Window {
3362 function,
3363 args,
3364 partition_by,
3365 order_by,
3366 } => {
3367 assert_eq!(*function, WindowFunc::Sum);
3368 assert_eq!(args.len(), 1);
3369 assert!(matches!(&args[0], Expr::Field(f) if f == "salary"));
3370 assert_eq!(partition_by, &["dept"]);
3371 assert_eq!(order_by.len(), 1);
3372 assert_eq!(order_by[0].field, "salary");
3373 assert!(!order_by[0].descending);
3374 }
3375 other => panic!("expected Window, got {other:?}"),
3376 }
3377 }
3378 _ => panic!("expected query"),
3379 }
3380 }
3381
3382 #[test]
3383 fn test_parse_window_rank_desc() {
3384 let stmt =
3385 parse("User { .dept, .salary, r: rank() over (partition .dept order .salary desc) }")
3386 .unwrap();
3387 match stmt {
3388 Statement::Query(q) => {
3389 let proj = q.projection.unwrap();
3390 assert_eq!(proj.len(), 3);
3391 match &proj[2].expr {
3392 Expr::Window {
3393 function,
3394 partition_by,
3395 order_by,
3396 ..
3397 } => {
3398 assert_eq!(*function, WindowFunc::Rank);
3399 assert_eq!(partition_by, &["dept"]);
3400 assert_eq!(order_by.len(), 1);
3401 assert!(order_by[0].descending);
3402 }
3403 other => panic!("expected Window, got {other:?}"),
3404 }
3405 }
3406 _ => panic!("expected query"),
3407 }
3408 }
3409
3410 #[test]
3411 fn test_parse_window_dense_rank() {
3412 let stmt = parse("User { .name, dr: dense_rank() over (order .score desc) }").unwrap();
3413 match stmt {
3414 Statement::Query(q) => {
3415 let proj = q.projection.unwrap();
3416 assert_eq!(proj.len(), 2);
3417 match &proj[1].expr {
3418 Expr::Window { function, .. } => {
3419 assert_eq!(*function, WindowFunc::DenseRank);
3420 }
3421 other => panic!("expected Window, got {other:?}"),
3422 }
3423 }
3424 _ => panic!("expected query"),
3425 }
3426 }
3427
3428 #[test]
3429 fn test_parse_sum_without_over_is_aggregate() {
3430 let stmt = parse("User group .dept { .dept, total: sum(.salary) }").unwrap();
3432 match stmt {
3433 Statement::Query(q) => {
3434 let proj = q.projection.unwrap();
3435 assert_eq!(proj.len(), 2);
3436 match &proj[1].expr {
3437 Expr::FunctionCall(AggFunc::Sum, _) => {} other => panic!("expected FunctionCall(Sum), got {other:?}"),
3439 }
3440 }
3441 _ => panic!("expected query"),
3442 }
3443 }
3444
3445 #[test]
3446 fn test_nesting_depth_limit() {
3447 let mut query = String::from("User filter ");
3449 for _ in 0..70 {
3450 query.push('(');
3451 }
3452 query.push_str(".age > 1");
3453 for _ in 0..70 {
3454 query.push(')');
3455 }
3456 let result = parse(&query);
3457 assert!(result.is_err());
3458 let err = result.unwrap_err();
3459 assert!(
3460 err.message().contains("nesting depth"),
3461 "expected nesting depth error, got: {}",
3462 err.message()
3463 );
3464 }
3465
3466 #[test]
3467 fn test_unary_prefix_nesting_depth_limit() {
3468 let query = String::from("User filter ") + &"not ".repeat(5000) + ".active";
3472 let result = parse(&query);
3473 assert!(result.is_err());
3474 let err = result.unwrap_err();
3475 assert!(
3476 err.message().contains("nesting depth"),
3477 "expected nesting depth error, got: {}",
3478 err.message()
3479 );
3480 }
3481
3482 #[test]
3483 fn test_moderate_nesting_succeeds() {
3484 let mut query = String::from("User filter ");
3486 for _ in 0..10 {
3487 query.push('(');
3488 }
3489 query.push_str(".age > 1");
3490 for _ in 0..10 {
3491 query.push(')');
3492 }
3493 assert!(parse(&query).is_ok());
3494 }
3495
3496 #[test]
3500 fn test_parse_fuzz_repro_projection_eof() {
3501 let err = parse("nn{").expect_err("unterminated projection must error, not panic");
3502 let _ = err.message();
3503 }
3504
3505 #[test]
3508 fn test_parse_fuzz_repro_short_projection_eof() {
3509 let err = parse("z{").expect_err("unterminated projection must error, not panic");
3510 let _ = err.message();
3511 }
3512
3513 #[test]
3514 fn test_update_at_statement_start_gives_helpful_error() {
3515 let err =
3516 parse(r#"update User filter .name = "Alice" { age := 31 }"#).expect_err("should fail");
3517 let msg = err.message();
3518 assert!(
3519 msg.contains("pipeline syntax"),
3520 "error should mention pipeline syntax, got: {msg}"
3521 );
3522 assert!(
3523 msg.contains("update"),
3524 "error should mention 'update', got: {msg}"
3525 );
3526 }
3527
3528 #[test]
3529 fn test_delete_at_statement_start_gives_helpful_error() {
3530 let err = parse("delete User filter .age < 18").expect_err("should fail");
3531 let msg = err.message();
3532 assert!(
3533 msg.contains("pipeline syntax"),
3534 "error should mention pipeline syntax, got: {msg}"
3535 );
3536 assert!(
3537 msg.contains("delete"),
3538 "error should mention 'delete', got: {msg}"
3539 );
3540 }
3541}
3542
3543#[cfg(test)]
3544mod cleanup_parser_dx_tests {
3545 use super::*;
3546
3547 #[test]
3548 fn typoed_statement_keyword_gets_suggestion() {
3549 let err = parse("updat User set age = 1").unwrap_err();
3550 let msg = err.to_string();
3551 assert!(msg.contains("near token"), "{msg}");
3552 assert!(msg.contains("did you mean `update`"), "{msg}");
3553 }
3554}
3555
3556#[cfg(test)]
3557mod capa_dx_tests {
3558 use super::*;
3559
3560 #[test]
3563 fn reserved_word_field_name_gives_actionable_error() {
3564 let err = parse("type Post { type: str }").unwrap_err();
3565 let msg = err.to_string();
3566 assert!(
3567 msg.contains("'type' is a reserved word")
3568 && msg.contains("field name")
3569 && msg.contains("quote it as `type`"),
3570 "unhelpful message: {msg}"
3571 );
3572 }
3573
3574 #[test]
3575 fn reserved_modifier_word_as_field_name_gives_actionable_error() {
3576 let err = parse("type Post { required: bool }").unwrap_err();
3580 let msg = err.to_string();
3581 assert!(
3582 msg.contains("'required' is a reserved word") && msg.contains("quote it as `required`"),
3583 "unhelpful message: {msg}"
3584 );
3585 }
3586
3587 #[test]
3588 fn reserved_word_in_insert_assignment_gives_actionable_error() {
3589 let err = parse(r#"insert Post { type := "x" }"#).unwrap_err();
3590 let msg = err.to_string();
3591 assert!(msg.contains("'type' is a reserved word"), "{msg}");
3592 }
3593
3594 #[test]
3595 fn reserved_word_in_alter_column_gives_actionable_error() {
3596 let err = parse("alter Post add column order: int").unwrap_err();
3597 let msg = err.to_string();
3598 assert!(msg.contains("'order' is a reserved word"), "{msg}");
3599 }
3600
3601 #[test]
3602 fn backtick_field_name_parses_as_identifier() {
3603 let stmt = parse("type Post { `type`: str, `order`: int }").unwrap();
3604 match stmt {
3605 Statement::CreateType(ct) => {
3606 assert_eq!(ct.fields[0].name, "type");
3607 assert_eq!(ct.fields[1].name, "order");
3608 }
3609 other => panic!("expected CreateType, got {other:?}"),
3610 }
3611 }
3612
3613 #[test]
3614 fn backtick_field_still_honors_modifiers() {
3615 let stmt = parse("type Post { required `type`: str }").unwrap();
3616 match stmt {
3617 Statement::CreateType(ct) => {
3618 assert_eq!(ct.fields[0].name, "type");
3619 assert!(ct.fields[0].required);
3620 }
3621 other => panic!("expected CreateType, got {other:?}"),
3622 }
3623 }
3624
3625 #[test]
3628 fn create_type_if_not_exists_parses() {
3629 let stmt = parse("type Post if not exists { id: int }").unwrap();
3630 match stmt {
3631 Statement::CreateType(ct) => assert!(ct.if_not_exists),
3632 other => panic!("expected CreateType, got {other:?}"),
3633 }
3634 }
3635
3636 #[test]
3637 fn create_type_without_clause_defaults_false() {
3638 let stmt = parse("type Post { id: int }").unwrap();
3639 match stmt {
3640 Statement::CreateType(ct) => assert!(!ct.if_not_exists),
3641 other => panic!("expected CreateType, got {other:?}"),
3642 }
3643 }
3644
3645 #[test]
3646 fn drop_if_exists_parses() {
3647 match parse("drop if exists Post").unwrap() {
3648 Statement::DropTable(dt) => assert!(dt.if_exists),
3649 other => panic!("expected DropTable, got {other:?}"),
3650 }
3651 match parse("drop Post").unwrap() {
3652 Statement::DropTable(dt) => assert!(!dt.if_exists),
3653 other => panic!("expected DropTable, got {other:?}"),
3654 }
3655 }
3656
3657 #[test]
3658 fn drop_view_if_exists_parses() {
3659 match parse("drop view if exists ActiveUsers").unwrap() {
3660 Statement::DropView(dv) => {
3661 assert!(dv.if_exists);
3662 assert_eq!(dv.name, "ActiveUsers");
3663 }
3664 other => panic!("expected DropView, got {other:?}"),
3665 }
3666 }
3667
3668 #[test]
3669 fn add_index_and_unique_if_not_exists_parse() {
3670 match parse("alter Post add index if not exists .slug").unwrap() {
3671 Statement::AlterTable(at) => {
3672 assert!(matches!(
3673 at.action,
3674 AlterAction::AddIndex {
3675 if_not_exists: true,
3676 ..
3677 }
3678 ));
3679 }
3680 other => panic!("expected AlterTable, got {other:?}"),
3681 }
3682 match parse("alter Post add unique if not exists .slug").unwrap() {
3683 Statement::AlterTable(at) => {
3684 assert!(matches!(
3685 at.action,
3686 AlterAction::AddUnique {
3687 if_not_exists: true,
3688 ..
3689 }
3690 ));
3691 }
3692 other => panic!("expected AlterTable, got {other:?}"),
3693 }
3694 }
3695
3696 #[test]
3697 fn alter_drop_column_if_exists_parses() {
3698 match parse("alter Post drop column if exists status").unwrap() {
3699 Statement::AlterTable(at) => {
3700 assert!(matches!(
3701 at.action,
3702 AlterAction::DropColumn {
3703 if_exists: true,
3704 ..
3705 }
3706 ));
3707 }
3708 other => panic!("expected AlterTable, got {other:?}"),
3709 }
3710 }
3711
3712 #[test]
3715 fn schema_parses_to_list_types() {
3716 assert_eq!(parse("schema").unwrap(), Statement::ListTypes);
3717 }
3718
3719 #[test]
3720 fn describe_parses_to_describe() {
3721 assert_eq!(
3722 parse("describe Post").unwrap(),
3723 Statement::Describe("Post".to_string())
3724 );
3725 }
3726
3727 #[test]
3728 fn schema_with_type_aliases_describe() {
3729 assert_eq!(
3730 parse("schema Post").unwrap(),
3731 Statement::Describe("Post".to_string())
3732 );
3733 }
3734}