1use super::error::ParseError;
48use super::Parser;
49use super::PlaceholderMode;
50use crate::ast::{BinOp, Expr, ExprSubquery, FieldRef, Span, UnaryOp};
51use crate::lexer::Token;
52use reddb_types::types::{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
98fn is_window_eligible_function(name: &str) -> bool {
104 matches!(
105 name.to_ascii_uppercase().as_str(),
106 "LAG"
108 | "LEAD"
109 | "ROW_NUMBER"
110 | "RANK"
111 | "DENSE_RANK"
112 | "PERCENT_RANK"
113 | "CUME_DIST"
114 | "NTILE"
115 | "FIRST_VALUE"
116 | "LAST_VALUE"
117 | "NTH_VALUE"
118 | "COUNT"
120 | "SUM"
121 | "AVG"
122 | "MIN"
123 | "MAX"
124 | "STDDEV"
125 | "VARIANCE"
126 | "MEDIAN"
127 | "PERCENTILE"
128 | "GROUP_CONCAT"
129 | "STRING_AGG"
130 | "FIRST"
131 | "LAST"
132 | "ARRAY_AGG"
133 | "COUNT_DISTINCT"
134 )
135}
136
137fn bare_zero_arg_function_name(name: &str) -> Option<&'static str> {
138 match name.to_ascii_uppercase().as_str() {
139 "CURRENT_TIMESTAMP" => Some("CURRENT_TIMESTAMP"),
140 "CURRENT_DATE" => Some("CURRENT_DATE"),
141 "CURRENT_TIME" => Some("CURRENT_TIME"),
142 _ => None,
143 }
144}
145
146impl<'a> Parser<'a> {
147 pub fn parse_expr(&mut self) -> Result<Expr, ParseError> {
150 self.parse_expr_prec(0)
151 }
152
153 pub(crate) fn parse_expr_with_min_precedence(
154 &mut self,
155 min_prec: u8,
156 ) -> Result<Expr, ParseError> {
157 self.parse_expr_prec(min_prec)
158 }
159
160 pub(crate) fn continue_expr(&mut self, left: Expr, min_prec: u8) -> Result<Expr, ParseError> {
163 self.parse_expr_suffix(left, min_prec)
164 }
165
166 fn parse_expr_prec(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
169 self.enter_depth()?;
174 let result = (|| {
175 let left = self.parse_expr_unary()?;
176 self.parse_expr_suffix(left, min_prec)
177 })();
178 self.exit_depth();
179 result
180 }
181
182 fn parse_expr_suffix(&mut self, mut left: Expr, min_prec: u8) -> Result<Expr, ParseError> {
183 let max_infix_chain = self.depth.max_depth.saturating_mul(8).max(1);
184 let mut infix_chain = 0usize;
185 loop {
186 let Some((op, prec)) = self.peek_binop() else {
187 if min_prec <= 32 {
189 if let Some(node) = self.try_parse_postfix(&left)? {
190 left = node;
191 continue;
192 }
193 }
194 break;
195 };
196 if prec < min_prec {
197 break;
198 }
199 infix_chain += 1;
200 if infix_chain > max_infix_chain {
201 return Err(ParseError::token_limit(
202 "max_tokens",
203 self.max_tokens,
204 self.position(),
205 ));
206 }
207 self.advance()?; let start_span = self.span_start_of(&left);
209 let rhs = self.parse_expr_prec(prec + 1)?;
210 let end_span = self.span_end_of(&rhs);
211 left = Expr::BinaryOp {
212 op,
213 lhs: Box::new(left),
214 rhs: Box::new(rhs),
215 span: Span::new(start_span, end_span),
216 };
217 }
218 Ok(left)
219 }
220
221 fn parse_expr_unary(&mut self) -> Result<Expr, ParseError> {
224 match self.peek() {
225 Token::Not => {
226 let start = self.position();
227 self.advance()?;
228 let operand = self.parse_expr_prec(25)?;
229 let end = self.span_end_of(&operand);
230 Ok(Expr::UnaryOp {
231 op: UnaryOp::Not,
232 operand: Box::new(operand),
233 span: Span::new(start, end),
234 })
235 }
236 Token::Dash => {
237 let start = self.position();
238 self.advance()?;
239 let operand = self.parse_expr_prec(70)?;
240 let end = self.span_end_of(&operand);
241 Ok(Expr::UnaryOp {
242 op: UnaryOp::Neg,
243 operand: Box::new(operand),
244 span: Span::new(start, end),
245 })
246 }
247 Token::Plus => {
248 self.advance()?;
250 self.parse_expr_prec(70)
251 }
252 _ => self.parse_expr_factor(),
253 }
254 }
255
256 fn parse_expr_factor(&mut self) -> Result<Expr, ParseError> {
259 let start = self.position();
260
261 if self.consume(&Token::LParen)? {
263 if self.check(&Token::Select) {
264 let query = self.parse_select_query()?;
265 self.expect(Token::RParen)?;
266 return Ok(Expr::Subquery {
267 query: ExprSubquery {
268 query: Box::new(query),
269 },
270 span: Span::new(start, self.position()),
271 });
272 }
273 let inner = self.parse_expr_prec(0)?;
274 self.expect(Token::RParen)?;
275 return Ok(inner);
276 }
277
278 if self.consume(&Token::True)? {
280 return Ok(Expr::Literal {
281 value: Value::Boolean(true),
282 span: Span::new(start, self.position()),
283 });
284 }
285 if self.consume(&Token::False)? {
286 return Ok(Expr::Literal {
287 value: Value::Boolean(false),
288 span: Span::new(start, self.position()),
289 });
290 }
291 if self.consume(&Token::Null)? {
292 return Ok(Expr::Literal {
293 value: Value::Null,
294 span: Span::new(start, self.position()),
295 });
296 }
297
298 if let Token::Integer(n) = *self.peek() {
302 self.advance()?;
303 if let Token::Ident(ref unit) = *self.peek() {
304 if is_duration_unit(unit) {
305 let duration = format!("{n}{}", unit.to_ascii_lowercase());
306 self.advance()?;
307 return Ok(Expr::Literal {
308 value: Value::text(duration),
309 span: Span::new(start, self.position()),
310 });
311 }
312 }
313 return Ok(Expr::Literal {
314 value: Value::Integer(n),
315 span: Span::new(start, self.position()),
316 });
317 }
318 if let Token::Float(n) = *self.peek() {
319 self.advance()?;
320 return Ok(Expr::Literal {
321 value: Value::Float(n),
322 span: Span::new(start, self.position()),
323 });
324 }
325 if let Token::String(ref s) = *self.peek() {
326 let text = s.clone();
327 self.advance()?;
328 return Ok(Expr::Literal {
329 value: Value::text(text),
330 span: Span::new(start, self.position()),
331 });
332 }
333
334 if matches!(
339 self.peek(),
340 Token::LBrace | Token::LBracket | Token::JsonLiteral(_)
341 ) {
342 let value = self
343 .parse_literal_value()
344 .map_err(|e| ParseError::new(e.message, self.position()))?;
345 return Ok(Expr::Literal {
346 value,
347 span: Span::new(start, self.position()),
348 });
349 }
350
351 if self.check(&Token::Question) {
355 let (index, span) = self.parse_question_param_index()?;
356 return Ok(Expr::Parameter { index, span });
357 }
358
359 if self.consume(&Token::Dollar)? {
360 if let Token::Integer(n) = *self.peek() {
365 if n < 1 {
366 return Err(ParseError::new(
367 "placeholder index must be >= 1".to_string(),
368 self.position(),
369 ));
370 }
371 if self.placeholder_mode == PlaceholderMode::Question {
372 return Err(ParseError::new(
373 "cannot mix `?` and `$N` placeholders in one statement".to_string(),
374 self.position(),
375 ));
376 }
377 self.placeholder_mode = PlaceholderMode::Dollar;
378 self.advance()?;
379 return Ok(Expr::Parameter {
380 index: (n - 1) as usize,
381 span: Span::new(start, self.position()),
382 });
383 }
384 let path = self.parse_dollar_ref_path()?;
385 let path_lc = path.to_ascii_lowercase();
386 let (name, key) = if let Some(rest) = path_lc.strip_prefix("secret.") {
387 ("__SECRET_REF", format!("red.vault/{rest}"))
388 } else if let Some(rest) = path_lc
389 .strip_prefix("red.secret.")
390 .or_else(|| path_lc.strip_prefix("red.secrets."))
391 {
392 ("__SECRET_REF", format!("red.vault/{rest}"))
393 } else if let Some(rest) = path_lc.strip_prefix("config.") {
394 ("CONFIG", format!("red.config/{rest}"))
395 } else if path_lc.starts_with("red.config.") {
396 let rest = path_lc.trim_start_matches("red.config.");
397 ("CONFIG", format!("red.config/{rest}"))
398 } else {
399 return Err(ParseError::new(
400 format!(
401 "unknown $ reference `${path}`; expected $secret.*, $red.secret.*, $red.secrets.*, $config.*, or $red.config.*"
402 ),
403 self.position(),
404 ));
405 };
406 return Ok(Expr::FunctionCall {
407 name: name.to_string(),
408 args: vec![Expr::Literal {
409 value: Value::text(key),
410 span: Span::new(start, self.position()),
411 }],
412 span: Span::new(start, self.position()),
413 });
414 }
415
416 if let Some(name) = keyword_function_name(self.peek()) {
417 if matches!(self.peek_next()?, Token::LParen) {
418 self.advance()?; return self.parse_function_call_expr_with_name(start, name.to_string());
420 }
421 }
422
423 if let Token::Ident(ref name) = *self.peek() {
431 let name_upper = name.to_uppercase();
432
433 if name_upper == "CASE" {
443 return self.parse_case_expr(start);
444 }
445
446 let saved_name = name.clone();
447 self.advance()?; if matches!(self.peek(), Token::LParen) {
451 return self.parse_function_call_expr_with_name(start, saved_name);
452 }
453
454 if let Some(function_name) = bare_zero_arg_function_name(&saved_name) {
455 let end = self.position();
456 return Ok(Expr::FunctionCall {
457 name: function_name.to_string(),
458 args: Vec::new(),
459 span: Span::new(start, end),
460 });
461 }
462
463 if matches!(self.peek(), Token::Dot) {
465 let mut segments = vec![saved_name];
466 while self.consume(&Token::Dot)? {
467 segments.push(self.expect_ident_or_keyword()?);
468 }
469 if matches!(self.peek(), Token::LParen) {
470 return self.parse_function_call_expr_with_name(start, segments.join("."));
471 }
472 let field = FieldRef::TableColumn {
473 table: segments.remove(0),
474 column: segments.join("."),
475 };
476 let end = self.position();
477 return Ok(Expr::Column {
478 field,
479 span: Span::new(start, end),
480 });
481 }
482
483 let field = FieldRef::TableColumn {
485 table: String::new(),
486 column: saved_name,
487 };
488 let end = self.position();
489 return Ok(Expr::Column {
490 field,
491 span: Span::new(start, end),
492 });
493 }
494
495 let field = self.parse_field_ref()?;
500 let end = self.position();
501 Ok(Expr::Column {
502 field,
503 span: Span::new(start, end),
504 })
505 }
506
507 fn parse_dollar_ref_path(&mut self) -> Result<String, ParseError> {
508 let mut path = self.expect_ident_or_keyword()?;
509 while self.consume(&Token::Dot)? {
510 let next = self.expect_ident_or_keyword()?;
511 path = format!("{path}.{next}");
512 }
513 Ok(path)
514 }
515
516 fn parse_function_call_expr_with_name(
517 &mut self,
518 start: crate::lexer::Position,
519 function_name: String,
520 ) -> Result<Expr, ParseError> {
521 let call = self.parse_function_call_expr_with_name_inner(start, function_name)?;
522 if matches!(self.peek(), Token::Over) {
528 return self.lift_to_window_call(start, call);
529 }
530 Ok(call)
531 }
532
533 fn parse_function_call_expr_with_name_inner(
534 &mut self,
535 start: crate::lexer::Position,
536 function_name: String,
537 ) -> Result<Expr, ParseError> {
538 self.expect(Token::LParen)?;
539
540 if function_name.eq_ignore_ascii_case("CAST") {
541 let inner = self.parse_expr_prec(0)?;
542 self.expect(Token::As)?;
543 let type_name = self.expect_ident_or_keyword()?;
544 self.expect(Token::RParen)?;
545 let end = self.position();
546 let Some(target) = DataType::from_sql_name(&type_name) else {
547 return Err(ParseError::new(
548 format!("unknown type name {type_name:?} in CAST"),
552 self.position(),
553 ));
554 };
555 return Ok(Expr::Cast {
556 inner: Box::new(inner),
557 target,
558 span: Span::new(start, end),
559 });
560 }
561
562 if function_name.eq_ignore_ascii_case("TRIM") {
563 let (name, args) = self.parse_trim_expr_args()?;
564 self.expect(Token::RParen)?;
565 let end = self.position();
566 return Ok(Expr::FunctionCall {
567 name,
568 args,
569 span: Span::new(start, end),
570 });
571 }
572
573 if function_name.eq_ignore_ascii_case("POSITION") {
574 let args = self.parse_position_expr_args()?;
575 self.expect(Token::RParen)?;
576 let end = self.position();
577 return Ok(Expr::FunctionCall {
578 name: function_name,
579 args,
580 span: Span::new(start, end),
581 });
582 }
583
584 if function_name.eq_ignore_ascii_case("SUBSTRING") {
585 let args = self.parse_substring_expr_args()?;
586 self.expect(Token::RParen)?;
587 let end = self.position();
588 return Ok(Expr::FunctionCall {
589 name: function_name,
590 args,
591 span: Span::new(start, end),
592 });
593 }
594
595 if function_name.eq_ignore_ascii_case("COUNT") {
596 if self.consume(&Token::Distinct)? {
597 let arg = self.parse_expr_prec(0)?;
598 self.expect(Token::RParen)?;
599 let end = self.position();
600 return Ok(Expr::FunctionCall {
601 name: "COUNT_DISTINCT".to_string(),
602 args: vec![arg],
603 span: Span::new(start, end),
604 });
605 }
606
607 if self.consume(&Token::Star)? {
608 self.expect(Token::RParen)?;
609 let end = self.position();
610 return Ok(Expr::FunctionCall {
611 name: function_name,
612 args: vec![Expr::Column {
613 field: FieldRef::TableColumn {
614 table: String::new(),
615 column: "*".to_string(),
616 },
617 span: Span::synthetic(),
618 }],
619 span: Span::new(start, end),
620 });
621 }
622 }
623
624 if function_name.eq_ignore_ascii_case("CONFIG") || function_name.eq_ignore_ascii_case("KV")
635 {
636 let mut args = Vec::new();
637 if !self.check(&Token::RParen) {
638 loop {
639 args.push(self.parse_config_kv_arg(start)?);
640 if !self.consume(&Token::Comma)? {
641 break;
642 }
643 }
644 }
645 self.expect(Token::RParen)?;
646 let end = self.position();
647 return Ok(Expr::FunctionCall {
648 name: function_name,
649 args,
650 span: Span::new(start, end),
651 });
652 }
653
654 let mut args = Vec::new();
655 if !self.check(&Token::RParen) {
656 loop {
657 args.push(self.parse_expr_prec(0)?);
658 if !self.consume(&Token::Comma)? {
659 break;
660 }
661 }
662 }
663 self.expect(Token::RParen)?;
664 let end = self.position();
665 Ok(Expr::FunctionCall {
666 name: function_name,
667 args,
668 span: Span::new(start, end),
669 })
670 }
671
672 fn parse_config_kv_arg(&mut self, start: crate::lexer::Position) -> Result<Expr, ParseError> {
679 let mut is_expression_start = matches!(
684 self.peek(),
685 Token::String(_)
686 | Token::Integer(_)
687 | Token::Float(_)
688 | Token::Dollar
689 | Token::Question
690 | Token::LParen
691 );
692 if matches!(self.peek(), Token::Ident(_)) && matches!(self.peek_next()?, Token::LParen) {
695 is_expression_start = true;
696 }
697 if !is_expression_start && !self.check(&Token::RParen) {
698 let mut path = self.expect_ident_or_keyword()?;
699 while self.consume(&Token::Dot)? {
700 let next = self.expect_ident_or_keyword()?;
701 path = format!("{path}.{next}");
702 }
703 let end = self.position();
704 return Ok(Expr::Literal {
705 value: Value::text(path.to_ascii_lowercase()),
706 span: Span::new(start, end),
707 });
708 }
709 self.parse_expr_prec(0)
710 }
711
712 fn lift_to_window_call(
719 &mut self,
720 start: crate::lexer::Position,
721 call: Expr,
722 ) -> Result<Expr, ParseError> {
723 let (name, args) = match call {
724 Expr::FunctionCall { name, args, .. } => (name, args),
725 other => {
726 return Err(ParseError::new(
727 format!(
728 "OVER may only follow a function call, got {:?}",
729 std::mem::discriminant(&other)
730 ),
731 self.position(),
732 ));
733 }
734 };
735 if !is_window_eligible_function(&name) {
736 return Err(ParseError::new(
737 format!(
738 "function `{}` cannot be used with an OVER clause; \
739 expected a window function (LAG, LEAD, ROW_NUMBER, \
740 RANK, DENSE_RANK) or an aggregate",
741 name.to_uppercase()
742 ),
743 self.position(),
744 ));
745 }
746 let window = self.parse_over_clause()?;
747 let end = self.position();
748 Ok(Expr::WindowFunctionCall {
749 name,
750 args,
751 window,
752 span: Span::new(start, end),
753 })
754 }
755
756 fn parse_over_clause(&mut self) -> Result<crate::ast::WindowSpec, ParseError> {
759 self.expect(Token::Over)?;
760 self.expect(Token::LParen)?;
761
762 let mut spec = crate::ast::WindowSpec::default();
763
764 if self.consume(&Token::Partition)? {
765 self.expect(Token::By)?;
766 loop {
767 spec.partition_by.push(self.parse_expr_prec(0)?);
768 if !self.consume(&Token::Comma)? {
769 break;
770 }
771 }
772 }
773
774 if self.consume(&Token::Order)? {
775 self.expect(Token::By)?;
776 loop {
777 let expr = self.parse_expr_prec(0)?;
778 let ascending = if self.consume(&Token::Desc)? {
779 false
780 } else {
781 self.consume(&Token::Asc)?;
782 true
783 };
784 let mut nulls_first = !ascending;
787 if self.consume(&Token::Nulls)? {
788 if self.consume(&Token::First)? {
789 nulls_first = true;
790 } else if self.consume(&Token::Last)? {
791 nulls_first = false;
792 } else {
793 return Err(ParseError::new(
794 "expected FIRST or LAST after NULLS".to_string(),
795 self.position(),
796 ));
797 }
798 }
799 spec.order_by.push(crate::ast::WindowOrderItem {
800 expr,
801 ascending,
802 nulls_first,
803 });
804 if !self.consume(&Token::Comma)? {
805 break;
806 }
807 }
808 }
809
810 if matches!(self.peek(), Token::Rows | Token::Range) {
811 spec.frame = Some(self.parse_window_frame()?);
812 }
813
814 self.expect(Token::RParen)?;
815 Ok(spec)
816 }
817
818 fn parse_window_frame(&mut self) -> Result<crate::ast::WindowFrame, ParseError> {
819 let unit = if self.consume(&Token::Rows)? {
820 crate::ast::WindowFrameUnit::Rows
821 } else if self.consume(&Token::Range)? {
822 crate::ast::WindowFrameUnit::Range
823 } else {
824 return Err(ParseError::new(
825 "expected ROWS or RANGE in window frame".to_string(),
826 self.position(),
827 ));
828 };
829
830 if self.consume(&Token::Between)? {
831 let start = self.parse_window_frame_bound()?;
832 self.expect(Token::And)?;
833 let end = self.parse_window_frame_bound()?;
834 Ok(crate::ast::WindowFrame {
835 unit,
836 start,
837 end: Some(end),
838 })
839 } else {
840 let start = self.parse_window_frame_bound()?;
841 Ok(crate::ast::WindowFrame {
842 unit,
843 start,
844 end: None,
845 })
846 }
847 }
848
849 fn parse_window_frame_bound(&mut self) -> Result<crate::ast::WindowFrameBound, ParseError> {
850 use crate::ast::WindowFrameBound;
851 if self.consume(&Token::Unbounded)? {
852 if self.consume(&Token::Preceding)? {
853 return Ok(WindowFrameBound::UnboundedPreceding);
854 }
855 if self.consume(&Token::Following)? {
856 return Ok(WindowFrameBound::UnboundedFollowing);
857 }
858 return Err(ParseError::new(
859 "expected PRECEDING or FOLLOWING after UNBOUNDED".to_string(),
860 self.position(),
861 ));
862 }
863 if self.consume(&Token::Current)? {
864 self.expect(Token::Row)?;
865 return Ok(WindowFrameBound::CurrentRow);
866 }
867 let offset = self.parse_expr_prec(0)?;
869 if self.consume(&Token::Preceding)? {
870 return Ok(WindowFrameBound::Preceding(Box::new(offset)));
871 }
872 if self.consume(&Token::Following)? {
873 return Ok(WindowFrameBound::Following(Box::new(offset)));
874 }
875 Err(ParseError::new(
876 "expected PRECEDING or FOLLOWING after frame offset".to_string(),
877 self.position(),
878 ))
879 }
880
881 fn parse_case_expr(&mut self, start: crate::lexer::Position) -> Result<Expr, ParseError> {
893 self.advance()?; let selector = if matches!(self.peek(), Token::Ident(id) if id.eq_ignore_ascii_case("WHEN"))
896 {
897 None
898 } else {
899 Some(self.parse_expr_prec(0)?)
900 };
901 let mut branches: Vec<(Expr, Expr)> = Vec::new();
902 loop {
903 if !self.consume_ident_ci("WHEN")? {
904 break;
905 }
906 let when_val = self.parse_expr_prec(0)?;
907 let cond = match &selector {
910 None => when_val,
911 Some(sel) => {
912 let span = Span::new(sel.span().start, when_val.span().end);
913 Expr::BinaryOp {
914 op: BinOp::Eq,
915 lhs: Box::new(sel.clone()),
916 rhs: Box::new(when_val),
917 span,
918 }
919 }
920 };
921 if !self.consume_ident_ci("THEN")? {
922 return Err(ParseError::new(
923 "expected THEN after CASE WHEN condition".to_string(),
924 self.position(),
925 ));
926 }
927 let then_val = self.parse_expr_prec(0)?;
928 branches.push((cond, then_val));
929 }
930 if branches.is_empty() {
931 return Err(ParseError::new(
932 "CASE must have at least one WHEN branch".to_string(),
933 self.position(),
934 ));
935 }
936 let else_ = if self.consume_ident_ci("ELSE")? {
937 Some(Box::new(self.parse_expr_prec(0)?))
938 } else {
939 None
940 };
941 if !self.consume_ident_ci("END")? {
942 return Err(ParseError::new(
943 "expected END to close CASE expression".to_string(),
944 self.position(),
945 ));
946 }
947 let end = self.position();
948 Ok(Expr::Case {
949 branches,
950 else_,
951 span: Span::new(start, end),
952 })
953 }
954
955 fn parse_trim_expr_args(&mut self) -> Result<(String, Vec<Expr>), ParseError> {
956 let mut function_name = "TRIM".to_string();
957
958 if self.consume_ident_ci("LEADING")? {
959 function_name = "LTRIM".to_string();
960 } else if self.consume_ident_ci("TRAILING")? {
961 function_name = "RTRIM".to_string();
962 } else if self.consume_ident_ci("BOTH")? {
963 function_name = "TRIM".to_string();
964 }
965
966 if self.consume(&Token::From)? {
967 let source = self.parse_expr_prec(0)?;
968 return Ok((function_name, vec![source]));
969 }
970
971 let first = self.parse_expr_prec(0)?;
972
973 if self.consume(&Token::Comma)? {
974 let second = self.parse_expr_prec(0)?;
975 return Ok((function_name, vec![first, second]));
976 }
977
978 if self.consume(&Token::From)? {
979 let source = self.parse_expr_prec(0)?;
980 return Ok((function_name, vec![source, first]));
981 }
982
983 Ok((function_name, vec![first]))
984 }
985
986 fn parse_position_expr_args(&mut self) -> Result<Vec<Expr>, ParseError> {
990 let needle = self.parse_expr_prec(35)?;
994 if !self.consume(&Token::Comma)? {
995 self.expect(Token::In)?;
996 }
997 let haystack = self.parse_expr_prec(0)?;
998 Ok(vec![needle, haystack])
999 }
1000
1001 fn parse_substring_expr_args(&mut self) -> Result<Vec<Expr>, ParseError> {
1009 let source = self.parse_expr_prec(0)?;
1010
1011 if self.consume(&Token::Comma)? {
1012 let mut args = vec![source];
1013 loop {
1014 args.push(self.parse_expr_prec(0)?);
1015 if !self.consume(&Token::Comma)? {
1016 break;
1017 }
1018 }
1019 return Ok(args);
1020 }
1021
1022 if self.consume(&Token::From)? {
1023 let start = self.parse_expr_prec(0)?;
1024 if self.consume(&Token::For)? {
1025 let count = self.parse_expr_prec(0)?;
1026 return Ok(vec![source, start, count]);
1027 }
1028 return Ok(vec![source, start]);
1029 }
1030
1031 if self.consume(&Token::For)? {
1032 let count = self.parse_expr_prec(0)?;
1033 if self.consume(&Token::From)? {
1034 let start = self.parse_expr_prec(0)?;
1035 return Ok(vec![source, start, count]);
1036 }
1037 return Ok(vec![source, Expr::lit(Value::Integer(1)), count]);
1038 }
1039
1040 Ok(vec![source])
1041 }
1042
1043 fn try_parse_postfix(&mut self, left: &Expr) -> Result<Option<Expr>, ParseError> {
1053 let start = self.span_start_of(left);
1054
1055 if self.consume(&Token::Is)? {
1057 let negated = self.consume(&Token::Not)?;
1058 self.expect(Token::Null)?;
1059 let end = self.position();
1060 return Ok(Some(Expr::IsNull {
1061 operand: Box::new(left.clone()),
1062 negated,
1063 span: Span::new(start, end),
1064 }));
1065 }
1066
1067 let negated = if matches!(self.peek(), Token::Not) {
1071 self.advance()?;
1072 if !matches!(self.peek(), Token::Between | Token::In) {
1073 return Err(ParseError::new(
1074 "expected BETWEEN or IN after postfix NOT".to_string(),
1075 self.position(),
1076 ));
1077 }
1078 true
1079 } else {
1080 false
1081 };
1082
1083 if self.consume(&Token::Between)? {
1085 let low = self.parse_expr_prec(34)?;
1086 self.expect(Token::And)?;
1087 let high = self.parse_expr_prec(34)?;
1088 let end = self.position();
1089 return Ok(Some(Expr::Between {
1090 target: Box::new(left.clone()),
1091 low: Box::new(low),
1092 high: Box::new(high),
1093 negated,
1094 span: Span::new(start, end),
1095 }));
1096 }
1097
1098 if self.consume(&Token::In)? {
1100 self.expect(Token::LParen)?;
1101 let mut values = Vec::new();
1102 if self.check(&Token::Select) {
1103 let query = self.parse_select_query()?;
1104 values.push(Expr::Subquery {
1105 query: ExprSubquery {
1106 query: Box::new(query),
1107 },
1108 span: Span::new(self.span_start_of(left), self.position()),
1109 });
1110 } else if !self.check(&Token::RParen) {
1111 loop {
1112 values.push(self.parse_expr_prec(0)?);
1113 if !self.consume(&Token::Comma)? {
1114 break;
1115 }
1116 }
1117 }
1118 self.expect(Token::RParen)?;
1119 let end = self.position();
1120 return Ok(Some(Expr::InList {
1121 target: Box::new(left.clone()),
1122 values,
1123 negated,
1124 span: Span::new(start, end),
1125 }));
1126 }
1127
1128 if negated {
1129 return Err(ParseError::new(
1133 "internal: NOT consumed without BETWEEN/IN follow".to_string(),
1134 self.position(),
1135 ));
1136 }
1137 Ok(None)
1138 }
1139
1140 fn peek_binop(&self) -> Option<(BinOp, u8)> {
1144 let op = match self.peek() {
1145 Token::Or => BinOp::Or,
1146 Token::And => BinOp::And,
1147 Token::Eq => BinOp::Eq,
1148 Token::Ne => BinOp::Ne,
1149 Token::Lt => BinOp::Lt,
1150 Token::Le => BinOp::Le,
1151 Token::Gt => BinOp::Gt,
1152 Token::Ge => BinOp::Ge,
1153 Token::DoublePipe => BinOp::Concat,
1154 Token::Plus => BinOp::Add,
1155 Token::Dash => BinOp::Sub,
1156 Token::Star => BinOp::Mul,
1157 Token::Slash => BinOp::Div,
1158 Token::Percent => BinOp::Mod,
1159 _ => return None,
1160 };
1161 Some((op, op.precedence()))
1162 }
1163
1164 fn span_start_of(&self, expr: &Expr) -> crate::lexer::Position {
1169 let s = expr.span();
1170 if s.is_synthetic() {
1171 self.position()
1172 } else {
1173 s.start
1174 }
1175 }
1176
1177 fn span_end_of(&self, expr: &Expr) -> crate::lexer::Position {
1180 let s = expr.span();
1181 if s.is_synthetic() {
1182 self.position()
1183 } else {
1184 s.end
1185 }
1186 }
1187}
1188
1189#[allow(dead_code)]
1192fn _expr_module_used(_: Expr) {}
1193
1194#[cfg(test)]
1195mod tests {
1196 use super::*;
1197 use crate::ast::FieldRef;
1198
1199 fn parse(input: &str) -> Expr {
1200 let mut parser = Parser::new(input).expect("lexer init");
1201 let expr = parser.parse_expr().expect("parse_expr");
1202 expr
1203 }
1204
1205 #[test]
1206 fn literal_integer() {
1207 let e = parse("42");
1208 match e {
1209 Expr::Literal {
1210 value: Value::Integer(42),
1211 ..
1212 } => {}
1213 other => panic!("expected Integer(42), got {other:?}"),
1214 }
1215 }
1216
1217 #[test]
1218 fn literal_float() {
1219 let e = parse("3.14");
1220 match e {
1221 Expr::Literal {
1222 value: Value::Float(f),
1223 ..
1224 } => assert!((f - 3.14).abs() < 1e-9),
1225 other => panic!("expected float literal, got {other:?}"),
1226 }
1227 }
1228
1229 #[test]
1230 fn literal_string() {
1231 let e = parse("'hello'");
1232 match e {
1233 Expr::Literal {
1234 value: Value::Text(ref s),
1235 ..
1236 } if s.as_ref() == "hello" => {}
1237 other => panic!("expected Text(hello), got {other:?}"),
1238 }
1239 }
1240
1241 #[test]
1242 fn literal_booleans_and_null() {
1243 assert!(matches!(
1244 parse("TRUE"),
1245 Expr::Literal {
1246 value: Value::Boolean(true),
1247 ..
1248 }
1249 ));
1250 assert!(matches!(
1251 parse("FALSE"),
1252 Expr::Literal {
1253 value: Value::Boolean(false),
1254 ..
1255 }
1256 ));
1257 assert!(matches!(
1258 parse("NULL"),
1259 Expr::Literal {
1260 value: Value::Null,
1261 ..
1262 }
1263 ));
1264 }
1265
1266 #[test]
1267 fn bare_column() {
1268 let e = parse("user_id");
1269 match e {
1270 Expr::Column {
1271 field: FieldRef::TableColumn { column, .. },
1272 ..
1273 } => {
1274 assert_eq!(column, "user_id");
1275 }
1276 other => panic!("expected column, got {other:?}"),
1277 }
1278 }
1279
1280 #[test]
1281 fn arithmetic_precedence_mul_over_add() {
1282 let e = parse("a + b * c");
1284 let Expr::BinaryOp {
1285 op: BinOp::Add,
1286 rhs,
1287 ..
1288 } = e
1289 else {
1290 panic!("root must be Add");
1291 };
1292 let Expr::BinaryOp { op: BinOp::Mul, .. } = *rhs else {
1293 panic!("rhs must be Mul");
1294 };
1295 }
1296
1297 #[test]
1298 fn arithmetic_left_associativity() {
1299 let e = parse("a - b - c");
1301 let Expr::BinaryOp {
1302 op: BinOp::Sub,
1303 lhs,
1304 ..
1305 } = e
1306 else {
1307 panic!("root must be Sub");
1308 };
1309 let Expr::BinaryOp { op: BinOp::Sub, .. } = *lhs else {
1310 panic!("lhs must be Sub (left-assoc)");
1311 };
1312 }
1313
1314 #[test]
1315 fn parenthesised_override() {
1316 let e = parse("(a + b) * c");
1318 let Expr::BinaryOp {
1319 op: BinOp::Mul,
1320 lhs,
1321 ..
1322 } = e
1323 else {
1324 panic!("root must be Mul");
1325 };
1326 let Expr::BinaryOp { op: BinOp::Add, .. } = *lhs else {
1327 panic!("lhs must be Add");
1328 };
1329 }
1330
1331 #[test]
1332 fn comparison_binds_weaker_than_arith() {
1333 let e = parse("a + 1 = b - 2");
1336 let Expr::BinaryOp {
1337 op: BinOp::Eq,
1338 lhs,
1339 rhs,
1340 ..
1341 } = e
1342 else {
1343 panic!("root must be Eq");
1344 };
1345 assert!(matches!(*lhs, Expr::BinaryOp { op: BinOp::Add, .. }));
1346 assert!(matches!(*rhs, Expr::BinaryOp { op: BinOp::Sub, .. }));
1347 }
1348
1349 #[test]
1350 fn and_binds_tighter_than_or() {
1351 let e = parse("a OR b AND c");
1353 let Expr::BinaryOp {
1354 op: BinOp::Or, rhs, ..
1355 } = e
1356 else {
1357 panic!("root must be Or");
1358 };
1359 assert!(matches!(*rhs, Expr::BinaryOp { op: BinOp::And, .. }));
1360 }
1361
1362 #[test]
1363 fn unary_negation() {
1364 let e = parse("-a");
1365 let Expr::UnaryOp {
1366 op: UnaryOp::Neg, ..
1367 } = e
1368 else {
1369 panic!("expected unary Neg");
1370 };
1371 }
1372
1373 #[test]
1374 fn unary_not() {
1375 let e = parse("NOT a");
1376 let Expr::UnaryOp {
1377 op: UnaryOp::Not, ..
1378 } = e
1379 else {
1380 panic!("expected unary Not");
1381 };
1382 }
1383
1384 #[test]
1385 fn concat_operator() {
1386 let e = parse("'hello' || name");
1387 let Expr::BinaryOp {
1388 op: BinOp::Concat, ..
1389 } = e
1390 else {
1391 panic!("expected Concat");
1392 };
1393 }
1394
1395 #[test]
1396 fn cast_expr() {
1397 let e = parse("CAST(age AS TEXT)");
1398 let Expr::Cast { target, .. } = e else {
1399 panic!("expected Cast");
1400 };
1401 assert_eq!(target, DataType::Text);
1402 }
1403
1404 #[test]
1405 fn case_expr() {
1406 let e = parse("CASE WHEN a = 1 THEN 'one' WHEN a = 2 THEN 'two' ELSE 'other' END");
1407 let Expr::Case {
1408 branches, else_, ..
1409 } = e
1410 else {
1411 panic!("expected Case");
1412 };
1413 assert_eq!(branches.len(), 2);
1414 assert!(else_.is_some());
1415 }
1416
1417 #[test]
1418 fn simple_case_desugars_to_equality() {
1419 let e = parse("CASE id WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'many' END");
1420 let Expr::Case {
1421 branches, else_, ..
1422 } = e
1423 else {
1424 panic!("expected Case");
1425 };
1426 assert_eq!(branches.len(), 2);
1427 assert!(else_.is_some());
1428 for (cond, _) in &branches {
1430 let Expr::BinaryOp { op, lhs, .. } = cond else {
1431 panic!("expected desugared equality condition");
1432 };
1433 assert_eq!(*op, BinOp::Eq);
1434 assert!(matches!(**lhs, Expr::Column { .. }));
1435 }
1436 }
1437
1438 #[test]
1439 fn is_null_postfix() {
1440 let e = parse("name IS NULL");
1441 assert!(matches!(e, Expr::IsNull { negated: false, .. }));
1442 }
1443
1444 #[test]
1445 fn is_not_null_postfix() {
1446 let e = parse("name IS NOT NULL");
1447 assert!(matches!(e, Expr::IsNull { negated: true, .. }));
1448 }
1449
1450 #[test]
1451 fn between_with_columns() {
1452 let e = parse("temp BETWEEN min_t AND max_t");
1453 let Expr::Between {
1454 target,
1455 low,
1456 high,
1457 negated,
1458 ..
1459 } = e
1460 else {
1461 panic!("expected Between");
1462 };
1463 assert!(!negated);
1464 assert!(matches!(*target, Expr::Column { .. }));
1465 assert!(matches!(*low, Expr::Column { .. }));
1466 assert!(matches!(*high, Expr::Column { .. }));
1467 }
1468
1469 #[test]
1470 fn not_between_negates() {
1471 let e = parse("temp NOT BETWEEN 0 AND 100");
1472 let Expr::Between { negated: true, .. } = e else {
1473 panic!("expected negated Between");
1474 };
1475 }
1476
1477 #[test]
1478 fn in_list_literal() {
1479 let e = parse("status IN (1, 2, 3)");
1480 let Expr::InList {
1481 values, negated, ..
1482 } = e
1483 else {
1484 panic!("expected InList");
1485 };
1486 assert!(!negated);
1487 assert_eq!(values.len(), 3);
1488 }
1489
1490 #[test]
1491 fn not_in_list() {
1492 let e = parse("status NOT IN (1, 2)");
1493 let Expr::InList { negated: true, .. } = e else {
1494 panic!("expected negated InList");
1495 };
1496 }
1497
1498 #[test]
1499 fn function_call_with_args() {
1500 let e = parse("UPPER(name)");
1501 let Expr::FunctionCall { name, args, .. } = e else {
1502 panic!("expected FunctionCall");
1503 };
1504 assert_eq!(name, "UPPER");
1505 assert_eq!(args.len(), 1);
1506 }
1507
1508 #[test]
1509 fn nested_function_call() {
1510 let e = parse("COALESCE(a, UPPER(b))");
1511 let Expr::FunctionCall { name, args, .. } = e else {
1512 panic!("expected FunctionCall");
1513 };
1514 assert_eq!(name, "COALESCE");
1515 assert_eq!(args.len(), 2);
1516 assert!(matches!(&args[1], Expr::FunctionCall { .. }));
1517 }
1518
1519 #[test]
1520 fn duration_literal_parses_as_text() {
1521 let e = parse("time_bucket(5m)");
1522 let Expr::FunctionCall { name, args, .. } = e else {
1523 panic!("expected FunctionCall, got {e:?}");
1524 };
1525 assert_eq!(name.to_uppercase(), "TIME_BUCKET");
1526 assert_eq!(args.len(), 1);
1527 assert!(
1528 matches!(&args[0], Expr::Literal { value: Value::Text(s), .. } if s.as_ref() == "5m"),
1529 "expected Text(\"5m\"), got {:?}",
1530 args[0]
1531 );
1532 }
1533
1534 #[test]
1535 fn placeholder_dollar_one() {
1536 let e = parse("$1");
1537 match e {
1538 Expr::Parameter { index: 0, .. } => {}
1539 other => panic!("expected Parameter(0), got {other:?}"),
1540 }
1541 }
1542
1543 #[test]
1544 fn placeholder_dollar_n() {
1545 let e = parse("$7");
1546 match e {
1547 Expr::Parameter { index: 6, .. } => {}
1548 other => panic!("expected Parameter(6), got {other:?}"),
1549 }
1550 }
1551
1552 #[test]
1553 fn placeholder_in_string_literal_is_text() {
1554 let e = parse("'$1'");
1556 match e {
1557 Expr::Literal {
1558 value: Value::Text(s),
1559 ..
1560 } if s.as_ref() == "$1" => {}
1561 other => panic!("expected text literal '$1', got {other:?}"),
1562 }
1563 }
1564
1565 #[test]
1566 fn placeholder_in_comparison() {
1567 let e = parse("id = $1");
1569 let Expr::BinaryOp {
1570 op: BinOp::Eq, rhs, ..
1571 } = e
1572 else {
1573 panic!("root must be Eq");
1574 };
1575 assert!(matches!(*rhs, Expr::Parameter { index: 0, .. }));
1576 }
1577
1578 #[test]
1579 fn placeholder_zero_rejected() {
1580 let mut parser = Parser::new("$0").expect("lexer");
1581 let err = parser.parse_expr().unwrap_err();
1582 assert!(err.to_string().contains("placeholder"));
1583 }
1584
1585 #[test]
1586 fn placeholder_question_single() {
1587 let e = parse("?");
1589 match e {
1590 Expr::Parameter { index: 0, .. } => {}
1591 other => panic!("expected Parameter(0), got {other:?}"),
1592 }
1593 }
1594
1595 #[test]
1596 fn placeholder_question_numbered() {
1597 let e = parse("?7");
1598 match e {
1599 Expr::Parameter { index: 6, .. } => {}
1600 other => panic!("expected Parameter(6), got {other:?}"),
1601 }
1602 }
1603
1604 #[test]
1605 fn placeholder_question_numbered_zero_rejected() {
1606 let mut parser = Parser::new("?0").expect("lexer");
1607 let err = parser.parse_expr().unwrap_err();
1608 assert!(err.to_string().contains("placeholder"));
1609 }
1610
1611 #[test]
1612 fn placeholder_question_left_to_right() {
1613 let e = parse("id = ? AND name = ?");
1615 let Expr::BinaryOp {
1616 op: BinOp::And,
1617 lhs,
1618 rhs,
1619 ..
1620 } = e
1621 else {
1622 panic!("root must be And");
1623 };
1624 let Expr::BinaryOp {
1625 op: BinOp::Eq,
1626 rhs: r1,
1627 ..
1628 } = *lhs
1629 else {
1630 panic!("lhs must be Eq");
1631 };
1632 assert!(matches!(*r1, Expr::Parameter { index: 0, .. }));
1633 let Expr::BinaryOp {
1634 op: BinOp::Eq,
1635 rhs: r2,
1636 ..
1637 } = *rhs
1638 else {
1639 panic!("rhs must be Eq");
1640 };
1641 assert!(matches!(*r2, Expr::Parameter { index: 1, .. }));
1642 }
1643
1644 #[test]
1645 fn placeholder_question_in_string_literal_is_text() {
1646 let e = parse("'?'");
1647 match e {
1648 Expr::Literal {
1649 value: Value::Text(s),
1650 ..
1651 } if s.as_ref() == "?" => {}
1652 other => panic!("expected text literal '?', got {other:?}"),
1653 }
1654 }
1655
1656 #[test]
1657 fn placeholder_mixing_question_then_dollar_rejected() {
1658 let mut parser = Parser::new("id = ? AND x = $2").expect("lexer");
1659 let err = parser.parse_expr().err().expect("should fail");
1660 assert!(
1661 err.to_string().contains("mix"),
1662 "expected mixing error, got: {err}"
1663 );
1664 }
1665
1666 #[test]
1667 fn placeholder_mixing_dollar_then_question_rejected() {
1668 let mut parser = Parser::new("id = $1 AND x = ?").expect("lexer");
1669 let err = parser.parse_expr().err().expect("should fail");
1670 assert!(
1671 err.to_string().contains("mix"),
1672 "expected mixing error, got: {err}"
1673 );
1674 }
1675
1676 #[test]
1677 fn placeholder_question_in_comment_ignored() {
1678 let mut parser = Parser::new("-- ? ignored\n ?").expect("lexer");
1681 let e = parser.parse_expr().expect("parse_expr");
1682 match e {
1683 Expr::Parameter { index: 0, .. } => {}
1684 other => panic!("expected Parameter(0), got {other:?}"),
1685 }
1686 }
1687
1688 #[test]
1689 fn unary_plus_is_noop() {
1690 let e = parse("+42");
1691 assert!(matches!(
1692 e,
1693 Expr::Literal {
1694 value: Value::Integer(42),
1695 ..
1696 }
1697 ));
1698 }
1699
1700 #[test]
1701 fn parenthesised_select_becomes_subquery_expr() {
1702 let e = parse("(SELECT 1)");
1703 assert!(matches!(e, Expr::Subquery { .. }));
1704 }
1705
1706 #[test]
1707 fn bare_zero_arg_current_functions_parse_as_calls() {
1708 for (input, expected) in [
1709 ("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP"),
1710 ("CURRENT_DATE", "CURRENT_DATE"),
1711 ("CURRENT_TIME", "CURRENT_TIME"),
1712 ] {
1713 let e = parse(input);
1714 let Expr::FunctionCall { name, args, .. } = e else {
1715 panic!("expected FunctionCall for {input}");
1716 };
1717 assert_eq!(name, expected);
1718 assert!(args.is_empty());
1719 }
1720 }
1721
1722 #[test]
1723 fn keyword_function_names_parse_as_calls() {
1724 for (input, expected_len) in [
1725 ("COUNT(*)", 1),
1726 ("SUM(amount)", 1),
1727 ("LEFT(name, 2)", 2),
1728 ("RIGHT(name, 2)", 2),
1729 ("CONTAINS(body, 'red')", 2),
1730 ("KV(cfg, path)", 2),
1731 ] {
1732 let e = parse(input);
1733 let Expr::FunctionCall { args, .. } = e else {
1734 panic!("expected FunctionCall for {input}");
1735 };
1736 assert_eq!(args.len(), expected_len, "{input}");
1737 }
1738 }
1739
1740 #[test]
1741 fn count_distinct_lowers_to_count_distinct_function() {
1742 let e = parse("COUNT(DISTINCT user_id)");
1743 let Expr::FunctionCall { name, args, .. } = e else {
1744 panic!("expected FunctionCall");
1745 };
1746 assert_eq!(name, "COUNT_DISTINCT");
1747 assert_eq!(args.len(), 1);
1748 }
1749
1750 #[test]
1751 fn dollar_secret_and_config_refs_become_function_calls() {
1752 for (input, expected_name, expected_key) in [
1753 ("$secret.api_key", "__SECRET_REF", "red.vault/api_key"),
1754 ("$red.secret.api_key", "__SECRET_REF", "red.vault/api_key"),
1755 ("$red.secrets.api_key", "__SECRET_REF", "red.vault/api_key"),
1756 ("$config.ai.provider", "CONFIG", "red.config/ai.provider"),
1757 (
1758 "$red.config.ai.provider",
1759 "CONFIG",
1760 "red.config/ai.provider",
1761 ),
1762 ] {
1763 let e = parse(input);
1764 let Expr::FunctionCall { name, args, .. } = e else {
1765 panic!("expected FunctionCall for {input}");
1766 };
1767 assert_eq!(name, expected_name);
1768 assert!(matches!(
1769 &args[..],
1770 [Expr::Literal { value: Value::Text(key), .. }] if key.as_ref() == expected_key
1771 ));
1772 }
1773 }
1774
1775 #[test]
1776 fn dollar_ref_rejects_unknown_namespace() {
1777 let mut parser = Parser::new("$tenant.id").expect("lexer");
1778 let err = parser
1779 .parse_expr()
1780 .expect_err("unknown namespace should fail");
1781 assert!(err.to_string().contains("unknown $ reference"));
1782 }
1783
1784 #[test]
1785 fn config_and_kv_bare_path_args_lowercase_to_text() {
1786 let e = parse("CONFIG(Red.AI.Default.Provider, 'openai')");
1787 let Expr::FunctionCall { name, args, .. } = e else {
1788 panic!("expected FunctionCall");
1789 };
1790 assert_eq!(name, "CONFIG");
1791 assert_eq!(args.len(), 2);
1792 assert!(matches!(
1793 &args[0],
1794 Expr::Literal { value: Value::Text(path), .. }
1795 if path.as_ref() == "red.ai.default.provider"
1796 ));
1797 assert!(matches!(
1798 &args[1],
1799 Expr::Literal { value: Value::Text(provider), .. } if provider.as_ref() == "openai"
1800 ));
1801
1802 let e = parse("KV(cfg, default.role, LOWER(name))");
1803 let Expr::FunctionCall { name, args, .. } = e else {
1804 panic!("expected FunctionCall");
1805 };
1806 assert_eq!(name, "KV");
1807 assert!(matches!(
1808 &args[0],
1809 Expr::Literal { value: Value::Text(path), .. } if path.as_ref() == "cfg"
1810 ));
1811 assert!(matches!(
1812 &args[1],
1813 Expr::Literal { value: Value::Text(path), .. } if path.as_ref() == "default.role"
1814 ));
1815 assert!(matches!(&args[2], Expr::FunctionCall { name, .. } if name == "LOWER"));
1816 }
1817
1818 #[test]
1819 fn cast_rejects_unknown_type_name() {
1820 let mut parser = Parser::new("CAST(age AS BOGUS_TYPE)").expect("lexer");
1821 let err = parser
1822 .parse_expr()
1823 .expect_err("unknown cast target should fail");
1824 assert!(err.to_string().contains("unknown type name"));
1825 }
1826
1827 #[test]
1828 fn trim_position_and_substring_sql_forms_lower_to_function_args() {
1829 let e = parse("TRIM(LEADING 'x' FROM name)");
1830 let Expr::FunctionCall { name, args, .. } = e else {
1831 panic!("expected trim function");
1832 };
1833 assert_eq!(name, "LTRIM");
1834 assert_eq!(args.len(), 2);
1835
1836 let e = parse("TRIM(TRAILING FROM name)");
1837 let Expr::FunctionCall { name, args, .. } = e else {
1838 panic!("expected trim function");
1839 };
1840 assert_eq!(name, "RTRIM");
1841 assert_eq!(args.len(), 1);
1842
1843 let e = parse("POSITION('x' IN name)");
1844 let Expr::FunctionCall { name, args, .. } = e else {
1845 panic!("expected position function");
1846 };
1847 assert_eq!(name, "POSITION");
1848 assert_eq!(args.len(), 2);
1849
1850 let e = parse("POSITION('x', name)");
1851 let Expr::FunctionCall { args, .. } = e else {
1852 panic!("expected position function");
1853 };
1854 assert_eq!(args.len(), 2);
1855
1856 let e = parse("SUBSTRING(name FROM 2 FOR 3)");
1857 let Expr::FunctionCall { name, args, .. } = e else {
1858 panic!("expected substring function");
1859 };
1860 assert_eq!(name, "SUBSTRING");
1861 assert_eq!(args.len(), 3);
1862
1863 let e = parse("SUBSTRING(name FOR 3)");
1864 let Expr::FunctionCall { args, .. } = e else {
1865 panic!("expected substring function");
1866 };
1867 assert_eq!(args.len(), 3);
1868 assert!(matches!(
1869 args[1],
1870 Expr::Literal {
1871 value: Value::Integer(1),
1872 ..
1873 }
1874 ));
1875 }
1876
1877 #[test]
1878 fn postfix_in_accepts_subquery_and_empty_list() {
1879 let e = parse("id IN (SELECT user_id FROM users)");
1880 let Expr::InList { values, .. } = e else {
1881 panic!("expected InList");
1882 };
1883 assert!(matches!(&values[..], [Expr::Subquery { .. }]));
1884
1885 let e = parse("id IN ()");
1886 let Expr::InList { values, .. } = e else {
1887 panic!("expected InList");
1888 };
1889 assert!(values.is_empty());
1890 }
1891
1892 #[test]
1893 fn postfix_not_requires_between_or_in() {
1894 let mut parser = Parser::new("status NOT NULL").expect("lexer");
1895 let err = parser.parse_expr().expect_err("postfix NOT should fail");
1896 assert!(err.to_string().contains("BETWEEN or IN"));
1897 }
1898
1899 #[test]
1900 fn case_reports_missing_then_end_and_empty_branch() {
1901 for input in [
1902 "CASE END",
1903 "CASE WHEN a = 1 'one' END",
1904 "CASE WHEN a = 1 THEN 'one'",
1905 ] {
1906 let mut parser = Parser::new(input).expect("lexer");
1907 assert!(
1908 parser.parse_expr().is_err(),
1909 "expected CASE parse failure for {input}"
1910 );
1911 }
1912 }
1913
1914 #[test]
1915 fn span_tracks_token_range() {
1916 let mut parser = Parser::new("123 + 456").expect("lexer");
1918 let e = parser.parse_expr().expect("parse_expr");
1919 let span = e.span();
1920 assert!(!span.is_synthetic(), "root span must be real");
1921 assert!(span.start.offset < span.end.offset);
1922 }
1923
1924 fn try_parse(input: &str) -> Result<Expr, ParseError> {
1929 let mut parser = Parser::new(input).expect("lexer init");
1930 parser.parse_expr()
1931 }
1932
1933 #[test]
1934 fn window_lag_partition_and_order() {
1935 let e = parse("LAG(ts) OVER (PARTITION BY user_id ORDER BY ts)");
1936 let Expr::WindowFunctionCall {
1937 name, args, window, ..
1938 } = e
1939 else {
1940 panic!("expected WindowFunctionCall");
1941 };
1942 assert_eq!(name.to_uppercase(), "LAG");
1943 assert_eq!(args.len(), 1);
1944 assert_eq!(window.partition_by.len(), 1);
1945 assert_eq!(window.order_by.len(), 1);
1946 assert!(window.order_by[0].ascending);
1947 assert!(window.frame.is_none());
1948 }
1949
1950 #[test]
1951 fn window_row_number_empty_over() {
1952 let e = parse("ROW_NUMBER() OVER ()");
1953 let Expr::WindowFunctionCall {
1954 name, args, window, ..
1955 } = e
1956 else {
1957 panic!("expected WindowFunctionCall");
1958 };
1959 assert_eq!(name.to_uppercase(), "ROW_NUMBER");
1960 assert!(args.is_empty());
1961 assert!(window.partition_by.is_empty());
1962 assert!(window.order_by.is_empty());
1963 assert!(window.frame.is_none());
1964 }
1965
1966 #[test]
1967 fn window_sum_with_frame_rows_between() {
1968 let e = parse(
1969 "SUM(amount) OVER (PARTITION BY user_id ORDER BY ts \
1970 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)",
1971 );
1972 let Expr::WindowFunctionCall { name, window, .. } = e else {
1973 panic!("expected WindowFunctionCall");
1974 };
1975 assert_eq!(name.to_uppercase(), "SUM");
1976 let frame = window.frame.expect("frame present");
1977 assert!(matches!(frame.unit, crate::ast::WindowFrameUnit::Rows));
1978 assert!(matches!(
1979 frame.start,
1980 crate::ast::WindowFrameBound::Preceding(_)
1981 ));
1982 assert!(matches!(
1983 frame.end,
1984 Some(crate::ast::WindowFrameBound::CurrentRow)
1985 ));
1986 }
1987
1988 #[test]
1989 fn window_rank_order_desc_multiple_keys() {
1990 let e = parse("RANK() OVER (ORDER BY score DESC, ts)");
1991 let Expr::WindowFunctionCall { window, .. } = e else {
1992 panic!("expected WindowFunctionCall");
1993 };
1994 assert_eq!(window.order_by.len(), 2);
1995 assert!(!window.order_by[0].ascending);
1996 assert!(window.order_by[1].ascending);
1997 }
1998
1999 #[test]
2000 fn window_unbounded_preceding_following_frame() {
2001 let e = parse(
2002 "AVG(x) OVER (ORDER BY t \
2003 RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)",
2004 );
2005 let Expr::WindowFunctionCall { window, .. } = e else {
2006 panic!("expected WindowFunctionCall");
2007 };
2008 let frame = window.frame.expect("frame present");
2009 assert!(matches!(frame.unit, crate::ast::WindowFrameUnit::Range));
2010 assert!(matches!(
2011 frame.start,
2012 crate::ast::WindowFrameBound::UnboundedPreceding
2013 ));
2014 assert!(matches!(
2015 frame.end,
2016 Some(crate::ast::WindowFrameBound::UnboundedFollowing)
2017 ));
2018 }
2019
2020 #[test]
2021 fn window_rejects_non_window_function() {
2022 let err = try_parse("UPPER(name) OVER (PARTITION BY id)")
2024 .err()
2025 .expect("should reject scalar OVER");
2026 let msg = err.to_string();
2027 assert!(
2028 msg.contains("UPPER") || msg.contains("upper"),
2029 "error should mention function name, got: {msg}"
2030 );
2031 assert!(msg.to_ascii_uppercase().contains("OVER") || msg.contains("window"));
2032 }
2033
2034 #[test]
2035 fn window_rejects_missing_open_paren() {
2036 let err = try_parse("LAG(ts) OVER PARTITION BY user_id")
2037 .err()
2038 .expect("should reject");
2039 let msg = err.to_string();
2040 assert!(
2041 msg.contains("(") || msg.to_ascii_uppercase().contains("EXPECTED"),
2042 "got: {msg}"
2043 );
2044 }
2045
2046 #[test]
2047 fn window_rejects_invalid_frame_syntax() {
2048 let err = try_parse("LAG(ts) OVER (ORDER BY ts ROWS CURRENT)")
2050 .err()
2051 .expect("should reject");
2052 let msg = err.to_string();
2053 assert!(
2054 !msg.is_empty(),
2055 "expected non-empty error for malformed frame"
2056 );
2057 }
2058
2059 #[test]
2060 fn window_first_value_with_partition_only() {
2061 let e = parse("FIRST_VALUE(price) OVER (PARTITION BY symbol)");
2062 let Expr::WindowFunctionCall {
2063 name, window, args, ..
2064 } = e
2065 else {
2066 panic!("expected WindowFunctionCall");
2067 };
2068 assert_eq!(name.to_uppercase(), "FIRST_VALUE");
2069 assert_eq!(args.len(), 1);
2070 assert_eq!(window.partition_by.len(), 1);
2071 assert!(window.order_by.is_empty());
2072 }
2073
2074 #[test]
2075 fn window_order_nulls_first_and_last() {
2076 let e = parse("SUM(x) OVER (ORDER BY score ASC NULLS FIRST, ts DESC NULLS LAST)");
2077 let Expr::WindowFunctionCall { window, .. } = e else {
2078 panic!("expected WindowFunctionCall");
2079 };
2080 assert_eq!(window.order_by.len(), 2);
2081 assert!(window.order_by[0].ascending);
2082 assert!(window.order_by[0].nulls_first);
2083 assert!(!window.order_by[1].ascending);
2084 assert!(!window.order_by[1].nulls_first);
2085 }
2086
2087 #[test]
2088 fn window_single_bound_frames() {
2089 let e = parse("SUM(x) OVER (ORDER BY ts ROWS 3 PRECEDING)");
2090 let Expr::WindowFunctionCall { window, .. } = e else {
2091 panic!("expected WindowFunctionCall");
2092 };
2093 let frame = window.frame.expect("frame");
2094 assert!(matches!(
2095 frame.start,
2096 crate::ast::WindowFrameBound::Preceding(_)
2097 ));
2098 assert!(frame.end.is_none());
2099
2100 let e = parse("SUM(x) OVER (ORDER BY ts RANGE 1 FOLLOWING)");
2101 let Expr::WindowFunctionCall { window, .. } = e else {
2102 panic!("expected WindowFunctionCall");
2103 };
2104 let frame = window.frame.expect("frame");
2105 assert!(matches!(
2106 frame.start,
2107 crate::ast::WindowFrameBound::Following(_)
2108 ));
2109 assert!(frame.end.is_none());
2110 }
2111
2112 #[test]
2113 fn window_reports_nulls_and_frame_bound_errors() {
2114 for input in [
2115 "SUM(x) OVER (ORDER BY score NULLS MIDDLE)",
2116 "SUM(x) OVER (ORDER BY score ROWS UNBOUNDED)",
2117 "SUM(x) OVER (ORDER BY score ROWS 3)",
2118 ] {
2119 let err = try_parse(input).expect_err("window syntax should fail");
2120 assert!(!err.to_string().is_empty(), "{input}");
2121 }
2122 }
2123}