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