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}