partiql/parser/
clauses.rs

1use nom::branch::alt;
2use nom::bytes::complete::{tag, tag_no_case};
3use nom::character::complete::{multispace0, multispace1};
4use nom::combinator::opt;
5use nom::error::context;
6use nom::multi::separated_list1;
7use nom::sequence::{preceded, tuple};
8use nom::IResult;
9
10pub use crate::parser::elements;
11pub use crate::parser::elements::comma;
12pub use crate::parser::expressions;
13pub use crate::parser::keywords;
14pub use crate::parser::parse_expr;
15pub use crate::parser::parse_value;
16pub use crate::parser::string_allowed_in_field;
17pub use crate::sql::clause;
18use crate::sql::Field;
19use crate::sql::WhereCond;
20
21pub fn select(input: &str) -> IResult<&str, Vec<Field>> {
22    let (input, vec) = context(
23        "select claues",
24        preceded(
25            tag_no_case("SELECT"),
26            preceded(
27                multispace0,
28                separated_list1(comma, expressions::parse_field),
29            ),
30        ),
31    )(input)?;
32    Ok((input, vec))
33}
34
35pub fn from<'a>(input: &'a str) -> IResult<&'a str, Vec<Field>> {
36    let (input, fields) = context(
37        "from clause",
38        preceded(
39            tag_no_case("FROM"),
40            preceded(
41                multispace0,
42                preceded(
43                    multispace0,
44                    separated_list1(comma, expressions::parse_field),
45                ),
46            ),
47        ),
48    )(input)?;
49    Ok((input, fields))
50}
51
52pub fn left_join<'a>(input: &'a str) -> IResult<&'a str, Vec<Field>> {
53    let (input, fields) = context(
54        "left join clause",
55        preceded(
56            tuple((tag_no_case("LEFT"), multispace1, tag_no_case("JOIN"))),
57            preceded(
58                multispace0,
59                preceded(
60                    multispace0,
61                    separated_list1(comma, expressions::parse_field),
62                ),
63            ),
64        ),
65    )(input)?;
66    Ok((input, fields))
67}
68
69pub fn parse_where(input: &str) -> IResult<&str, WhereCond> {
70    preceded(
71        tag_no_case("WHERE"),
72        alt((parse_where_eq, parse_where_like)),
73    )(input)
74}
75
76pub fn parse_where_eq(input: &str) -> IResult<&str, WhereCond> {
77    let (input, (expr, _, right)) = preceded(
78        multispace0,
79        tuple((
80            parse_expr,
81            preceded(multispace0, tag("=")),
82            preceded(multispace0, parse_value),
83        )),
84    )(input)?;
85    let res = WhereCond::Eq { expr, right };
86    Ok((input, res))
87}
88
89pub fn parse_where_like(input: &str) -> IResult<&str, WhereCond> {
90    let (input, (expr, _, s)) = preceded(
91        multispace0,
92        tuple((
93            parse_expr,
94            preceded(multispace0, tag_no_case("LIKE")),
95            preceded(multispace0, elements::string),
96        )),
97    )(input)?;
98    let res = WhereCond::Like {
99        expr,
100        right: s.to_string(),
101    };
102    Ok((input, res))
103}
104
105pub fn orderby(input: &str) -> IResult<&str, clause::OrderBy> {
106    let (input, (_, field_name, opt_asc_or_desc)) = tuple((
107        tag_no_case("ORDER BY"),
108        preceded(multispace0, string_allowed_in_field),
109        preceded(
110            multispace0,
111            opt(alt((tag_no_case("ASC"), tag_no_case("DESC")))),
112        ),
113    ))(input)?;
114
115    let is_asc = opt_asc_or_desc
116        .map(|asc_or_desc| asc_or_desc.to_lowercase() == "asc")
117        .unwrap_or(true);
118    Ok((
119        input,
120        clause::OrderBy {
121            label: field_name,
122            is_asc,
123        },
124    ))
125}
126
127pub fn limit(input: &str) -> IResult<&str, clause::Limit> {
128    let (input, (_, limit, opt_offset)) = tuple((
129        tag_no_case("LIMIT"),
130        preceded(multispace0, elements::integer),
131        opt(preceded(multispace0, offset)),
132    ))(input)?;
133
134    let offset = opt_offset.unwrap_or(0);
135    Ok((input, clause::Limit { limit, offset }))
136}
137
138pub fn offset(input: &str) -> IResult<&str, u64> {
139    preceded(
140        tag_no_case("OFFSET"),
141        preceded(multispace0, elements::integer),
142    )(input)
143}
144
145#[cfg(test)]
146mod tests {
147    use std::str::FromStr;
148
149    use super::from;
150    use crate::sql::Field;
151
152    #[test]
153    fn parse_from() -> anyhow::Result<()> {
154        assert_eq!(from("FROM [1,2,3]")?.1, vec![Field::from_str("[1,2,3]")?],);
155        assert_eq!(
156            from("FROM [1,2,3] AS arr")?.1,
157            vec![Field::from_str("[1,2,3] AS arr")?],
158        );
159        assert_eq!(
160            from("FROM x.y.z AS xyz")?.1,
161            vec![Field::from_str("x.y.z AS xyz")?],
162        );
163
164        Ok(())
165    }
166}