1use super::super::ast::{BinOp, Expr, ExprSubquery, FieldRef, Span, UnaryOp};
48use super::super::lexer::Token;
49use super::error::ParseError;
50use super::Parser;
51use super::PlaceholderMode;
52use crate::storage::schema::{DataType, Value};
53
54fn is_duration_unit(unit: &str) -> bool {
55 matches!(
56 unit.to_ascii_lowercase().as_str(),
57 "ms" | "msec"
58 | "millisecond"
59 | "milliseconds"
60 | "s"
61 | "sec"
62 | "secs"
63 | "second"
64 | "seconds"
65 | "m"
66 | "min"
67 | "mins"
68 | "minute"
69 | "minutes"
70 | "h"
71 | "hr"
72 | "hrs"
73 | "hour"
74 | "hours"
75 | "d"
76 | "day"
77 | "days"
78 )
79}
80
81fn keyword_function_name(token: &Token) -> Option<&'static str> {
82 match token {
83 Token::Count => Some("COUNT"),
84 Token::Sum => Some("SUM"),
85 Token::Avg => Some("AVG"),
86 Token::Min => Some("MIN"),
87 Token::Max => Some("MAX"),
88 Token::First => Some("FIRST"),
89 Token::Last => Some("LAST"),
90 Token::Left => Some("LEFT"),
91 Token::Right => Some("RIGHT"),
92 Token::Contains => Some("CONTAINS"),
93 Token::Kv => Some("KV"),
94 _ => None,
95 }
96}
97
98impl<'a> Parser<'a> {
99 pub fn parse_expr(&mut self) -> Result<Expr, ParseError> {
102 self.parse_expr_prec(0)
103 }
104
105 pub(crate) fn parse_expr_with_min_precedence(
106 &mut self,
107 min_prec: u8,
108 ) -> Result<Expr, ParseError> {
109 self.parse_expr_prec(min_prec)
110 }
111
112 pub(crate) fn continue_expr(&mut self, left: Expr, min_prec: u8) -> Result<Expr, ParseError> {
115 self.parse_expr_suffix(left, min_prec)
116 }
117
118 fn parse_expr_prec(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
121 self.enter_depth()?;
126 let result = (|| {
127 let left = self.parse_expr_unary()?;
128 self.parse_expr_suffix(left, min_prec)
129 })();
130 self.exit_depth();
131 result
132 }
133
134 fn parse_expr_suffix(&mut self, mut left: Expr, min_prec: u8) -> Result<Expr, ParseError> {
135 loop {
136 let Some((op, prec)) = self.peek_binop() else {
137 if min_prec <= 32 {
139 if let Some(node) = self.try_parse_postfix(&left)? {
140 left = node;
141 continue;
142 }
143 }
144 break;
145 };
146 if prec < min_prec {
147 break;
148 }
149 self.advance()?; let start_span = self.span_start_of(&left);
151 let rhs = self.parse_expr_prec(prec + 1)?;
152 let end_span = self.span_end_of(&rhs);
153 left = Expr::BinaryOp {
154 op,
155 lhs: Box::new(left),
156 rhs: Box::new(rhs),
157 span: Span::new(start_span, end_span),
158 };
159 }
160 Ok(left)
161 }
162
163 fn parse_expr_unary(&mut self) -> Result<Expr, ParseError> {
166 match self.peek() {
167 Token::Not => {
168 let start = self.position();
169 self.advance()?;
170 let operand = self.parse_expr_prec(25)?;
171 let end = self.span_end_of(&operand);
172 Ok(Expr::UnaryOp {
173 op: UnaryOp::Not,
174 operand: Box::new(operand),
175 span: Span::new(start, end),
176 })
177 }
178 Token::Dash => {
179 let start = self.position();
180 self.advance()?;
181 let operand = self.parse_expr_prec(70)?;
182 let end = self.span_end_of(&operand);
183 Ok(Expr::UnaryOp {
184 op: UnaryOp::Neg,
185 operand: Box::new(operand),
186 span: Span::new(start, end),
187 })
188 }
189 Token::Plus => {
190 self.advance()?;
192 self.parse_expr_prec(70)
193 }
194 _ => self.parse_expr_factor(),
195 }
196 }
197
198 fn parse_expr_factor(&mut self) -> Result<Expr, ParseError> {
201 let start = self.position();
202
203 if self.consume(&Token::LParen)? {
205 if self.check(&Token::Select) {
206 let query = self.parse_select_query()?;
207 self.expect(Token::RParen)?;
208 return Ok(Expr::Subquery {
209 query: ExprSubquery {
210 query: Box::new(query),
211 },
212 span: Span::new(start, self.position()),
213 });
214 }
215 let inner = self.parse_expr_prec(0)?;
216 self.expect(Token::RParen)?;
217 return Ok(inner);
218 }
219
220 if self.consume(&Token::True)? {
222 return Ok(Expr::Literal {
223 value: Value::Boolean(true),
224 span: Span::new(start, self.position()),
225 });
226 }
227 if self.consume(&Token::False)? {
228 return Ok(Expr::Literal {
229 value: Value::Boolean(false),
230 span: Span::new(start, self.position()),
231 });
232 }
233 if self.consume(&Token::Null)? {
234 return Ok(Expr::Literal {
235 value: Value::Null,
236 span: Span::new(start, self.position()),
237 });
238 }
239
240 if let Token::Integer(n) = *self.peek() {
244 self.advance()?;
245 if let Token::Ident(ref unit) = *self.peek() {
246 if is_duration_unit(unit) {
247 let duration = format!("{n}{}", unit.to_ascii_lowercase());
248 self.advance()?;
249 return Ok(Expr::Literal {
250 value: Value::text(duration),
251 span: Span::new(start, self.position()),
252 });
253 }
254 }
255 return Ok(Expr::Literal {
256 value: Value::Integer(n),
257 span: Span::new(start, self.position()),
258 });
259 }
260 if let Token::Float(n) = *self.peek() {
261 self.advance()?;
262 return Ok(Expr::Literal {
263 value: Value::Float(n),
264 span: Span::new(start, self.position()),
265 });
266 }
267 if let Token::String(ref s) = *self.peek() {
268 let text = s.clone();
269 self.advance()?;
270 return Ok(Expr::Literal {
271 value: Value::text(text),
272 span: Span::new(start, self.position()),
273 });
274 }
275
276 if matches!(
281 self.peek(),
282 Token::LBrace | Token::LBracket | Token::JsonLiteral(_)
283 ) {
284 let value = self
285 .parse_literal_value()
286 .map_err(|e| ParseError::new(e.message, self.position()))?;
287 return Ok(Expr::Literal {
288 value,
289 span: Span::new(start, self.position()),
290 });
291 }
292
293 if self.check(&Token::Question) {
297 let (index, span) = self.parse_question_param_index()?;
298 return Ok(Expr::Parameter { index, span });
299 }
300
301 if self.consume(&Token::Dollar)? {
302 if let Token::Integer(n) = *self.peek() {
307 if n < 1 {
308 return Err(ParseError::new(
309 "placeholder index must be >= 1".to_string(),
310 self.position(),
311 ));
312 }
313 if self.placeholder_mode == PlaceholderMode::Question {
314 return Err(ParseError::new(
315 "cannot mix `?` and `$N` placeholders in one statement".to_string(),
316 self.position(),
317 ));
318 }
319 self.placeholder_mode = PlaceholderMode::Dollar;
320 self.advance()?;
321 return Ok(Expr::Parameter {
322 index: (n - 1) as usize,
323 span: Span::new(start, self.position()),
324 });
325 }
326 let path = self.parse_dollar_ref_path()?;
327 let path_lc = path.to_ascii_lowercase();
328 let (name, key) = if let Some(rest) = path_lc.strip_prefix("secret.") {
329 ("__SECRET_REF", format!("red.vault/{rest}"))
330 } else if path_lc.starts_with("red.secret.") {
331 let rest = path_lc.trim_start_matches("red.secret.");
332 ("__SECRET_REF", format!("red.vault/{rest}"))
333 } else if let Some(rest) = path_lc.strip_prefix("config.") {
334 ("CONFIG", format!("red.config/{rest}"))
335 } else if path_lc.starts_with("red.config.") {
336 let rest = path_lc.trim_start_matches("red.config.");
337 ("CONFIG", format!("red.config/{rest}"))
338 } else {
339 return Err(ParseError::new(
340 format!(
341 "unknown $ reference `${path}`; expected $secret.*, $red.secret.*, $config.*, or $red.config.*"
342 ),
343 self.position(),
344 ));
345 };
346 return Ok(Expr::FunctionCall {
347 name: name.to_string(),
348 args: vec![Expr::Literal {
349 value: Value::text(key),
350 span: Span::new(start, self.position()),
351 }],
352 span: Span::new(start, self.position()),
353 });
354 }
355
356 if let Some(name) = keyword_function_name(self.peek()) {
357 if matches!(self.peek_next()?, Token::LParen) {
358 self.advance()?; return self.parse_function_call_expr_with_name(start, name.to_string());
360 }
361 }
362
363 if let Token::Ident(ref name) = *self.peek() {
371 let name_upper = name.to_uppercase();
372
373 if name_upper == "CASE" {
383 return self.parse_case_expr(start);
384 }
385
386 let saved_name = name.clone();
387 self.advance()?; if matches!(self.peek(), Token::LParen) {
391 return self.parse_function_call_expr_with_name(start, saved_name);
392 }
393
394 if matches!(self.peek(), Token::Dot) {
396 let mut segments = vec![saved_name];
397 while self.consume(&Token::Dot)? {
398 segments.push(self.expect_ident_or_keyword()?);
399 }
400 let field = FieldRef::TableColumn {
401 table: segments.remove(0),
402 column: segments.join("."),
403 };
404 let end = self.position();
405 return Ok(Expr::Column {
406 field,
407 span: Span::new(start, end),
408 });
409 }
410
411 let field = FieldRef::TableColumn {
413 table: String::new(),
414 column: saved_name,
415 };
416 let end = self.position();
417 return Ok(Expr::Column {
418 field,
419 span: Span::new(start, end),
420 });
421 }
422
423 let field = self.parse_field_ref()?;
428 let end = self.position();
429 Ok(Expr::Column {
430 field,
431 span: Span::new(start, end),
432 })
433 }
434
435 fn parse_dollar_ref_path(&mut self) -> Result<String, ParseError> {
436 let mut path = self.expect_ident_or_keyword()?;
437 while self.consume(&Token::Dot)? {
438 let next = self.expect_ident_or_keyword()?;
439 path = format!("{path}.{next}");
440 }
441 Ok(path)
442 }
443
444 fn parse_function_call_expr_with_name(
445 &mut self,
446 start: crate::storage::query::lexer::Position,
447 function_name: String,
448 ) -> Result<Expr, ParseError> {
449 self.expect(Token::LParen)?;
450
451 if function_name.eq_ignore_ascii_case("CAST") {
452 let inner = self.parse_expr_prec(0)?;
453 self.expect(Token::As)?;
454 let type_name = self.expect_ident_or_keyword()?;
455 self.expect(Token::RParen)?;
456 let end = self.position();
457 let Some(target) = DataType::from_sql_name(&type_name) else {
458 return Err(ParseError::new(
459 format!("unknown type name {type_name:?} in CAST"),
463 self.position(),
464 ));
465 };
466 return Ok(Expr::Cast {
467 inner: Box::new(inner),
468 target,
469 span: Span::new(start, end),
470 });
471 }
472
473 if function_name.eq_ignore_ascii_case("TRIM") {
474 let (name, args) = self.parse_trim_expr_args()?;
475 self.expect(Token::RParen)?;
476 let end = self.position();
477 return Ok(Expr::FunctionCall {
478 name,
479 args,
480 span: Span::new(start, end),
481 });
482 }
483
484 if function_name.eq_ignore_ascii_case("POSITION") {
485 let args = self.parse_position_expr_args()?;
486 self.expect(Token::RParen)?;
487 let end = self.position();
488 return Ok(Expr::FunctionCall {
489 name: function_name,
490 args,
491 span: Span::new(start, end),
492 });
493 }
494
495 if function_name.eq_ignore_ascii_case("SUBSTRING") {
496 let args = self.parse_substring_expr_args()?;
497 self.expect(Token::RParen)?;
498 let end = self.position();
499 return Ok(Expr::FunctionCall {
500 name: function_name,
501 args,
502 span: Span::new(start, end),
503 });
504 }
505
506 if function_name.eq_ignore_ascii_case("COUNT") {
507 if self.consume(&Token::Distinct)? {
508 let arg = self.parse_expr_prec(0)?;
509 self.expect(Token::RParen)?;
510 let end = self.position();
511 return Ok(Expr::FunctionCall {
512 name: "COUNT_DISTINCT".to_string(),
513 args: vec![arg],
514 span: Span::new(start, end),
515 });
516 }
517
518 if self.consume(&Token::Star)? {
519 self.expect(Token::RParen)?;
520 let end = self.position();
521 return Ok(Expr::FunctionCall {
522 name: function_name,
523 args: vec![Expr::Column {
524 field: FieldRef::TableColumn {
525 table: String::new(),
526 column: "*".to_string(),
527 },
528 span: Span::synthetic(),
529 }],
530 span: Span::new(start, end),
531 });
532 }
533 }
534
535 let mut args = Vec::new();
536 if !self.check(&Token::RParen) {
537 loop {
538 args.push(self.parse_expr_prec(0)?);
539 if !self.consume(&Token::Comma)? {
540 break;
541 }
542 }
543 }
544 self.expect(Token::RParen)?;
545 let end = self.position();
546 Ok(Expr::FunctionCall {
547 name: function_name,
548 args,
549 span: Span::new(start, end),
550 })
551 }
552
553 fn parse_case_expr(
556 &mut self,
557 start: crate::storage::query::lexer::Position,
558 ) -> Result<Expr, ParseError> {
559 self.advance()?; let mut branches: Vec<(Expr, Expr)> = Vec::new();
561 loop {
562 if !self.consume_ident_ci("WHEN")? {
563 break;
564 }
565 let cond = self.parse_expr_prec(0)?;
566 if !self.consume_ident_ci("THEN")? {
567 return Err(ParseError::new(
568 "expected THEN after CASE WHEN condition".to_string(),
569 self.position(),
570 ));
571 }
572 let then_val = self.parse_expr_prec(0)?;
573 branches.push((cond, then_val));
574 }
575 if branches.is_empty() {
576 return Err(ParseError::new(
577 "CASE must have at least one WHEN branch".to_string(),
578 self.position(),
579 ));
580 }
581 let else_ = if self.consume_ident_ci("ELSE")? {
582 Some(Box::new(self.parse_expr_prec(0)?))
583 } else {
584 None
585 };
586 if !self.consume_ident_ci("END")? {
587 return Err(ParseError::new(
588 "expected END to close CASE expression".to_string(),
589 self.position(),
590 ));
591 }
592 let end = self.position();
593 Ok(Expr::Case {
594 branches,
595 else_,
596 span: Span::new(start, end),
597 })
598 }
599
600 fn parse_trim_expr_args(&mut self) -> Result<(String, Vec<Expr>), ParseError> {
601 let mut function_name = "TRIM".to_string();
602
603 if self.consume_ident_ci("LEADING")? {
604 function_name = "LTRIM".to_string();
605 } else if self.consume_ident_ci("TRAILING")? {
606 function_name = "RTRIM".to_string();
607 } else if self.consume_ident_ci("BOTH")? {
608 function_name = "TRIM".to_string();
609 }
610
611 if self.consume(&Token::From)? {
612 let source = self.parse_expr_prec(0)?;
613 return Ok((function_name, vec![source]));
614 }
615
616 let first = self.parse_expr_prec(0)?;
617
618 if self.consume(&Token::Comma)? {
619 let second = self.parse_expr_prec(0)?;
620 return Ok((function_name, vec![first, second]));
621 }
622
623 if self.consume(&Token::From)? {
624 let source = self.parse_expr_prec(0)?;
625 return Ok((function_name, vec![source, first]));
626 }
627
628 Ok((function_name, vec![first]))
629 }
630
631 fn parse_position_expr_args(&mut self) -> Result<Vec<Expr>, ParseError> {
635 let needle = self.parse_expr_prec(35)?;
639 if !self.consume(&Token::Comma)? {
640 self.expect(Token::In)?;
641 }
642 let haystack = self.parse_expr_prec(0)?;
643 Ok(vec![needle, haystack])
644 }
645
646 fn parse_substring_expr_args(&mut self) -> Result<Vec<Expr>, ParseError> {
654 let source = self.parse_expr_prec(0)?;
655
656 if self.consume(&Token::Comma)? {
657 let mut args = vec![source];
658 loop {
659 args.push(self.parse_expr_prec(0)?);
660 if !self.consume(&Token::Comma)? {
661 break;
662 }
663 }
664 return Ok(args);
665 }
666
667 if self.consume(&Token::From)? {
668 let start = self.parse_expr_prec(0)?;
669 if self.consume(&Token::For)? {
670 let count = self.parse_expr_prec(0)?;
671 return Ok(vec![source, start, count]);
672 }
673 return Ok(vec![source, start]);
674 }
675
676 if self.consume(&Token::For)? {
677 let count = self.parse_expr_prec(0)?;
678 if self.consume(&Token::From)? {
679 let start = self.parse_expr_prec(0)?;
680 return Ok(vec![source, start, count]);
681 }
682 return Ok(vec![source, Expr::lit(Value::Integer(1)), count]);
683 }
684
685 Ok(vec![source])
686 }
687
688 fn try_parse_postfix(&mut self, left: &Expr) -> Result<Option<Expr>, ParseError> {
698 let start = self.span_start_of(left);
699
700 if self.consume(&Token::Is)? {
702 let negated = self.consume(&Token::Not)?;
703 self.expect(Token::Null)?;
704 let end = self.position();
705 return Ok(Some(Expr::IsNull {
706 operand: Box::new(left.clone()),
707 negated,
708 span: Span::new(start, end),
709 }));
710 }
711
712 let negated = if matches!(self.peek(), Token::Not) {
716 self.advance()?;
717 if !matches!(self.peek(), Token::Between | Token::In) {
718 return Err(ParseError::new(
719 "expected BETWEEN or IN after postfix NOT".to_string(),
720 self.position(),
721 ));
722 }
723 true
724 } else {
725 false
726 };
727
728 if self.consume(&Token::Between)? {
730 let low = self.parse_expr_prec(34)?;
731 self.expect(Token::And)?;
732 let high = self.parse_expr_prec(34)?;
733 let end = self.position();
734 return Ok(Some(Expr::Between {
735 target: Box::new(left.clone()),
736 low: Box::new(low),
737 high: Box::new(high),
738 negated,
739 span: Span::new(start, end),
740 }));
741 }
742
743 if self.consume(&Token::In)? {
745 self.expect(Token::LParen)?;
746 let mut values = Vec::new();
747 if self.check(&Token::Select) {
748 let query = self.parse_select_query()?;
749 values.push(Expr::Subquery {
750 query: ExprSubquery {
751 query: Box::new(query),
752 },
753 span: Span::new(self.span_start_of(left), self.position()),
754 });
755 } else if !self.check(&Token::RParen) {
756 loop {
757 values.push(self.parse_expr_prec(0)?);
758 if !self.consume(&Token::Comma)? {
759 break;
760 }
761 }
762 }
763 self.expect(Token::RParen)?;
764 let end = self.position();
765 return Ok(Some(Expr::InList {
766 target: Box::new(left.clone()),
767 values,
768 negated,
769 span: Span::new(start, end),
770 }));
771 }
772
773 if negated {
774 return Err(ParseError::new(
778 "internal: NOT consumed without BETWEEN/IN follow".to_string(),
779 self.position(),
780 ));
781 }
782 Ok(None)
783 }
784
785 fn peek_binop(&self) -> Option<(BinOp, u8)> {
789 let op = match self.peek() {
790 Token::Or => BinOp::Or,
791 Token::And => BinOp::And,
792 Token::Eq => BinOp::Eq,
793 Token::Ne => BinOp::Ne,
794 Token::Lt => BinOp::Lt,
795 Token::Le => BinOp::Le,
796 Token::Gt => BinOp::Gt,
797 Token::Ge => BinOp::Ge,
798 Token::DoublePipe => BinOp::Concat,
799 Token::Plus => BinOp::Add,
800 Token::Dash => BinOp::Sub,
801 Token::Star => BinOp::Mul,
802 Token::Slash => BinOp::Div,
803 Token::Percent => BinOp::Mod,
804 _ => return None,
805 };
806 Some((op, op.precedence()))
807 }
808
809 fn span_start_of(&self, expr: &Expr) -> crate::storage::query::lexer::Position {
814 let s = expr.span();
815 if s.is_synthetic() {
816 self.position()
817 } else {
818 s.start
819 }
820 }
821
822 fn span_end_of(&self, expr: &Expr) -> crate::storage::query::lexer::Position {
825 let s = expr.span();
826 if s.is_synthetic() {
827 self.position()
828 } else {
829 s.end
830 }
831 }
832}
833
834#[allow(dead_code)]
837fn _expr_module_used(_: Expr) {}
838
839#[cfg(test)]
840mod tests {
841 use super::*;
842 use crate::storage::query::ast::FieldRef;
843
844 fn parse(input: &str) -> Expr {
845 let mut parser = Parser::new(input).expect("lexer init");
846 let expr = parser.parse_expr().expect("parse_expr");
847 expr
848 }
849
850 #[test]
851 fn literal_integer() {
852 let e = parse("42");
853 match e {
854 Expr::Literal {
855 value: Value::Integer(42),
856 ..
857 } => {}
858 other => panic!("expected Integer(42), got {other:?}"),
859 }
860 }
861
862 #[test]
863 fn literal_float() {
864 let e = parse("3.14");
865 match e {
866 Expr::Literal {
867 value: Value::Float(f),
868 ..
869 } => assert!((f - 3.14).abs() < 1e-9),
870 other => panic!("expected float literal, got {other:?}"),
871 }
872 }
873
874 #[test]
875 fn literal_string() {
876 let e = parse("'hello'");
877 match e {
878 Expr::Literal {
879 value: Value::Text(ref s),
880 ..
881 } if s.as_ref() == "hello" => {}
882 other => panic!("expected Text(hello), got {other:?}"),
883 }
884 }
885
886 #[test]
887 fn literal_booleans_and_null() {
888 assert!(matches!(
889 parse("TRUE"),
890 Expr::Literal {
891 value: Value::Boolean(true),
892 ..
893 }
894 ));
895 assert!(matches!(
896 parse("FALSE"),
897 Expr::Literal {
898 value: Value::Boolean(false),
899 ..
900 }
901 ));
902 assert!(matches!(
903 parse("NULL"),
904 Expr::Literal {
905 value: Value::Null,
906 ..
907 }
908 ));
909 }
910
911 #[test]
912 fn bare_column() {
913 let e = parse("user_id");
914 match e {
915 Expr::Column {
916 field: FieldRef::TableColumn { column, .. },
917 ..
918 } => {
919 assert_eq!(column, "user_id");
920 }
921 other => panic!("expected column, got {other:?}"),
922 }
923 }
924
925 #[test]
926 fn arithmetic_precedence_mul_over_add() {
927 let e = parse("a + b * c");
929 let Expr::BinaryOp {
930 op: BinOp::Add,
931 rhs,
932 ..
933 } = e
934 else {
935 panic!("root must be Add");
936 };
937 let Expr::BinaryOp { op: BinOp::Mul, .. } = *rhs else {
938 panic!("rhs must be Mul");
939 };
940 }
941
942 #[test]
943 fn arithmetic_left_associativity() {
944 let e = parse("a - b - c");
946 let Expr::BinaryOp {
947 op: BinOp::Sub,
948 lhs,
949 ..
950 } = e
951 else {
952 panic!("root must be Sub");
953 };
954 let Expr::BinaryOp { op: BinOp::Sub, .. } = *lhs else {
955 panic!("lhs must be Sub (left-assoc)");
956 };
957 }
958
959 #[test]
960 fn parenthesised_override() {
961 let e = parse("(a + b) * c");
963 let Expr::BinaryOp {
964 op: BinOp::Mul,
965 lhs,
966 ..
967 } = e
968 else {
969 panic!("root must be Mul");
970 };
971 let Expr::BinaryOp { op: BinOp::Add, .. } = *lhs else {
972 panic!("lhs must be Add");
973 };
974 }
975
976 #[test]
977 fn comparison_binds_weaker_than_arith() {
978 let e = parse("a + 1 = b - 2");
981 let Expr::BinaryOp {
982 op: BinOp::Eq,
983 lhs,
984 rhs,
985 ..
986 } = e
987 else {
988 panic!("root must be Eq");
989 };
990 assert!(matches!(*lhs, Expr::BinaryOp { op: BinOp::Add, .. }));
991 assert!(matches!(*rhs, Expr::BinaryOp { op: BinOp::Sub, .. }));
992 }
993
994 #[test]
995 fn and_binds_tighter_than_or() {
996 let e = parse("a OR b AND c");
998 let Expr::BinaryOp {
999 op: BinOp::Or, rhs, ..
1000 } = e
1001 else {
1002 panic!("root must be Or");
1003 };
1004 assert!(matches!(*rhs, Expr::BinaryOp { op: BinOp::And, .. }));
1005 }
1006
1007 #[test]
1008 fn unary_negation() {
1009 let e = parse("-a");
1010 let Expr::UnaryOp {
1011 op: UnaryOp::Neg, ..
1012 } = e
1013 else {
1014 panic!("expected unary Neg");
1015 };
1016 }
1017
1018 #[test]
1019 fn unary_not() {
1020 let e = parse("NOT a");
1021 let Expr::UnaryOp {
1022 op: UnaryOp::Not, ..
1023 } = e
1024 else {
1025 panic!("expected unary Not");
1026 };
1027 }
1028
1029 #[test]
1030 fn concat_operator() {
1031 let e = parse("'hello' || name");
1032 let Expr::BinaryOp {
1033 op: BinOp::Concat, ..
1034 } = e
1035 else {
1036 panic!("expected Concat");
1037 };
1038 }
1039
1040 #[test]
1041 fn cast_expr() {
1042 let e = parse("CAST(age AS TEXT)");
1043 let Expr::Cast { target, .. } = e else {
1044 panic!("expected Cast");
1045 };
1046 assert_eq!(target, DataType::Text);
1047 }
1048
1049 #[test]
1050 fn case_expr() {
1051 let e = parse("CASE WHEN a = 1 THEN 'one' WHEN a = 2 THEN 'two' ELSE 'other' END");
1052 let Expr::Case {
1053 branches, else_, ..
1054 } = e
1055 else {
1056 panic!("expected Case");
1057 };
1058 assert_eq!(branches.len(), 2);
1059 assert!(else_.is_some());
1060 }
1061
1062 #[test]
1063 fn is_null_postfix() {
1064 let e = parse("name IS NULL");
1065 assert!(matches!(e, Expr::IsNull { negated: false, .. }));
1066 }
1067
1068 #[test]
1069 fn is_not_null_postfix() {
1070 let e = parse("name IS NOT NULL");
1071 assert!(matches!(e, Expr::IsNull { negated: true, .. }));
1072 }
1073
1074 #[test]
1075 fn between_with_columns() {
1076 let e = parse("temp BETWEEN min_t AND max_t");
1077 let Expr::Between {
1078 target,
1079 low,
1080 high,
1081 negated,
1082 ..
1083 } = e
1084 else {
1085 panic!("expected Between");
1086 };
1087 assert!(!negated);
1088 assert!(matches!(*target, Expr::Column { .. }));
1089 assert!(matches!(*low, Expr::Column { .. }));
1090 assert!(matches!(*high, Expr::Column { .. }));
1091 }
1092
1093 #[test]
1094 fn not_between_negates() {
1095 let e = parse("temp NOT BETWEEN 0 AND 100");
1096 let Expr::Between { negated: true, .. } = e else {
1097 panic!("expected negated Between");
1098 };
1099 }
1100
1101 #[test]
1102 fn in_list_literal() {
1103 let e = parse("status IN (1, 2, 3)");
1104 let Expr::InList {
1105 values, negated, ..
1106 } = e
1107 else {
1108 panic!("expected InList");
1109 };
1110 assert!(!negated);
1111 assert_eq!(values.len(), 3);
1112 }
1113
1114 #[test]
1115 fn not_in_list() {
1116 let e = parse("status NOT IN (1, 2)");
1117 let Expr::InList { negated: true, .. } = e else {
1118 panic!("expected negated InList");
1119 };
1120 }
1121
1122 #[test]
1123 fn function_call_with_args() {
1124 let e = parse("UPPER(name)");
1125 let Expr::FunctionCall { name, args, .. } = e else {
1126 panic!("expected FunctionCall");
1127 };
1128 assert_eq!(name, "UPPER");
1129 assert_eq!(args.len(), 1);
1130 }
1131
1132 #[test]
1133 fn nested_function_call() {
1134 let e = parse("COALESCE(a, UPPER(b))");
1135 let Expr::FunctionCall { name, args, .. } = e else {
1136 panic!("expected FunctionCall");
1137 };
1138 assert_eq!(name, "COALESCE");
1139 assert_eq!(args.len(), 2);
1140 assert!(matches!(&args[1], Expr::FunctionCall { .. }));
1141 }
1142
1143 #[test]
1144 fn duration_literal_parses_as_text() {
1145 let e = parse("time_bucket(5m)");
1146 let Expr::FunctionCall { name, args, .. } = e else {
1147 panic!("expected FunctionCall, got {e:?}");
1148 };
1149 assert_eq!(name.to_uppercase(), "TIME_BUCKET");
1150 assert_eq!(args.len(), 1);
1151 assert!(
1152 matches!(&args[0], Expr::Literal { value: Value::Text(s), .. } if s.as_ref() == "5m"),
1153 "expected Text(\"5m\"), got {:?}",
1154 args[0]
1155 );
1156 }
1157
1158 #[test]
1159 fn placeholder_dollar_one() {
1160 let e = parse("$1");
1161 match e {
1162 Expr::Parameter { index: 0, .. } => {}
1163 other => panic!("expected Parameter(0), got {other:?}"),
1164 }
1165 }
1166
1167 #[test]
1168 fn placeholder_dollar_n() {
1169 let e = parse("$7");
1170 match e {
1171 Expr::Parameter { index: 6, .. } => {}
1172 other => panic!("expected Parameter(6), got {other:?}"),
1173 }
1174 }
1175
1176 #[test]
1177 fn placeholder_in_string_literal_is_text() {
1178 let e = parse("'$1'");
1180 match e {
1181 Expr::Literal {
1182 value: Value::Text(s),
1183 ..
1184 } if s.as_ref() == "$1" => {}
1185 other => panic!("expected text literal '$1', got {other:?}"),
1186 }
1187 }
1188
1189 #[test]
1190 fn placeholder_in_comparison() {
1191 let e = parse("id = $1");
1193 let Expr::BinaryOp {
1194 op: BinOp::Eq, rhs, ..
1195 } = e
1196 else {
1197 panic!("root must be Eq");
1198 };
1199 assert!(matches!(*rhs, Expr::Parameter { index: 0, .. }));
1200 }
1201
1202 #[test]
1203 fn placeholder_zero_rejected() {
1204 let mut parser = Parser::new("$0").expect("lexer");
1205 let err = parser.parse_expr().unwrap_err();
1206 assert!(err.to_string().contains("placeholder"));
1207 }
1208
1209 #[test]
1210 fn placeholder_question_single() {
1211 let e = parse("?");
1213 match e {
1214 Expr::Parameter { index: 0, .. } => {}
1215 other => panic!("expected Parameter(0), got {other:?}"),
1216 }
1217 }
1218
1219 #[test]
1220 fn placeholder_question_numbered() {
1221 let e = parse("?7");
1222 match e {
1223 Expr::Parameter { index: 6, .. } => {}
1224 other => panic!("expected Parameter(6), got {other:?}"),
1225 }
1226 }
1227
1228 #[test]
1229 fn placeholder_question_numbered_zero_rejected() {
1230 let mut parser = Parser::new("?0").expect("lexer");
1231 let err = parser.parse_expr().unwrap_err();
1232 assert!(err.to_string().contains("placeholder"));
1233 }
1234
1235 #[test]
1236 fn placeholder_question_left_to_right() {
1237 let e = parse("id = ? AND name = ?");
1239 let Expr::BinaryOp {
1240 op: BinOp::And,
1241 lhs,
1242 rhs,
1243 ..
1244 } = e
1245 else {
1246 panic!("root must be And");
1247 };
1248 let Expr::BinaryOp {
1249 op: BinOp::Eq,
1250 rhs: r1,
1251 ..
1252 } = *lhs
1253 else {
1254 panic!("lhs must be Eq");
1255 };
1256 assert!(matches!(*r1, Expr::Parameter { index: 0, .. }));
1257 let Expr::BinaryOp {
1258 op: BinOp::Eq,
1259 rhs: r2,
1260 ..
1261 } = *rhs
1262 else {
1263 panic!("rhs must be Eq");
1264 };
1265 assert!(matches!(*r2, Expr::Parameter { index: 1, .. }));
1266 }
1267
1268 #[test]
1269 fn placeholder_question_in_string_literal_is_text() {
1270 let e = parse("'?'");
1271 match e {
1272 Expr::Literal {
1273 value: Value::Text(s),
1274 ..
1275 } if s.as_ref() == "?" => {}
1276 other => panic!("expected text literal '?', got {other:?}"),
1277 }
1278 }
1279
1280 #[test]
1281 fn placeholder_mixing_question_then_dollar_rejected() {
1282 let mut parser = Parser::new("id = ? AND x = $2").expect("lexer");
1283 let err = parser.parse_expr().err().expect("should fail");
1284 assert!(
1285 err.to_string().contains("mix"),
1286 "expected mixing error, got: {err}"
1287 );
1288 }
1289
1290 #[test]
1291 fn placeholder_mixing_dollar_then_question_rejected() {
1292 let mut parser = Parser::new("id = $1 AND x = ?").expect("lexer");
1293 let err = parser.parse_expr().err().expect("should fail");
1294 assert!(
1295 err.to_string().contains("mix"),
1296 "expected mixing error, got: {err}"
1297 );
1298 }
1299
1300 #[test]
1301 fn placeholder_question_in_comment_ignored() {
1302 let mut parser = Parser::new("-- ? ignored\n ?").expect("lexer");
1305 let e = parser.parse_expr().expect("parse_expr");
1306 match e {
1307 Expr::Parameter { index: 0, .. } => {}
1308 other => panic!("expected Parameter(0), got {other:?}"),
1309 }
1310 }
1311
1312 #[test]
1313 fn span_tracks_token_range() {
1314 let mut parser = Parser::new("123 + 456").expect("lexer");
1316 let e = parser.parse_expr().expect("parse_expr");
1317 let span = e.span();
1318 assert!(!span.is_synthetic(), "root span must be real");
1319 assert!(span.start.offset < span.end.offset);
1320 }
1321}