qail_core/parser/grammar/
case_when.rs

1//! CASE WHEN ... THEN ... ELSE ... END expression parsing.
2
3use super::base::{parse_operator, parse_value};
4use super::expressions::{parse_expression, parse_multiplicative_expr};
5use crate::ast::*;
6use nom::{
7    IResult, Parser,
8    bytes::complete::tag_no_case,
9    character::complete::{multispace0, multispace1},
10    combinator::opt,
11    multi::separated_list0,
12    sequence::preceded,
13};
14
15/// Parse CASE WHEN ... END
16pub fn parse_case(input: &str) -> IResult<&str, Expr> {
17    let (input, _) = tag_no_case("case").parse(input)?;
18    let (input, _) = multispace1(input)?;
19
20    let (input, when_clauses) = separated_list0(multispace1, parse_when).parse(input)?;
21
22    let (input, _) = multispace0(input)?;
23
24    // Else - parse as expression (not just value)
25    let (input, else_value) = opt(preceded(
26        (tag_no_case("else"), multispace1),
27        parse_expression,
28    ))
29    .parse(input)?;
30
31    let (input, _) = multispace0(input)?;
32    let (input, _) = tag_no_case("end").parse(input)?;
33
34    Ok((
35        input,
36        Expr::Case {
37            when_clauses,
38            else_value: else_value.map(Box::new),
39            alias: None,
40        },
41    ))
42}
43
44/// Parse a single WHEN condition THEN expression clause
45pub fn parse_when(input: &str) -> IResult<&str, (Condition, Box<Expr>)> {
46    let (input, _) = tag_no_case("when").parse(input)?;
47    let (input, _) = multispace1(input)?;
48
49    let (input, left_expr) = parse_multiplicative_expr(input)?; // Use lower-level to avoid consuming || as OR
50    let (input, _) = multispace0(input)?;
51
52    // Operator
53    let (input, op) = parse_operator(input)?;
54    let (input, _) = multispace0(input)?;
55
56    // For IS NULL / IS NOT NULL, there's no value to parse
57    let (input, val) = if matches!(op, Operator::IsNull | Operator::IsNotNull) {
58        (input, Value::Null)
59    } else {
60        parse_value(input)?
61    };
62
63    // Use multispace0 since IS NULL already consumed trailing space
64    let (input, _) = multispace0(input)?;
65    let (input, _) = tag_no_case("then").parse(input)?;
66    let (input, _) = multispace1(input)?;
67
68    let (input, then_expr) = parse_expression(input)?;
69
70    Ok((
71        input,
72        (
73            Condition {
74                left: left_expr,
75                op,
76                value: val,
77                is_array_unnest: false,
78            },
79            Box::new(then_expr),
80        ),
81    ))
82}