qail_core/parser/grammar/
dml.rs1use super::base::{parse_identifier, parse_value};
2use crate::ast::*;
3use nom::{
4 IResult, Parser,
5 bytes::complete::tag_no_case,
6 character::complete::{char, multispace0, multispace1},
7 multi::separated_list1,
8};
9
10pub fn parse_values_clause(input: &str) -> IResult<&str, Cage> {
12 let (input, _) = tag_no_case("values").parse(input)?;
13 let (input, _) = multispace1(input)?;
14
15 let (input, conditions) = parse_set_assignments(input)?;
16
17 Ok((
18 input,
19 Cage {
20 kind: CageKind::Payload,
21 conditions,
22 logical_op: LogicalOp::And,
23 },
24 ))
25}
26
27pub fn parse_insert_values(input: &str) -> IResult<&str, Cage> {
29 let (input, _) = tag_no_case("values").parse(input)?;
30 let (input, _) = multispace1(input)?;
31
32 let (input, values) =
33 separated_list1((multispace0, char(','), multispace0), parse_value).parse(input)?;
34
35 let conditions: Vec<Condition> = values
36 .into_iter()
37 .enumerate()
38 .map(|(i, val)| {
39 Condition {
40 left: Expr::Named(format!("${}", i + 1)), op: Operator::Eq,
42 value: val,
43 is_array_unnest: false,
44 }
45 })
46 .collect();
47
48 Ok((
49 input,
50 Cage {
51 kind: CageKind::Payload,
52 conditions,
53 logical_op: LogicalOp::And,
54 },
55 ))
56}
57
58pub fn parse_set_assignments(input: &str) -> IResult<&str, Vec<Condition>> {
60 separated_list1((multispace0, char(','), multispace0), parse_assignment).parse(input)
61}
62
63pub fn parse_assignment(input: &str) -> IResult<&str, Condition> {
65 use super::expressions::parse_expression;
66 use nom::branch::alt;
67
68 let (input, column) = parse_identifier(input)?;
69 let (input, _) = multispace0(input)?;
70 let (input, _) = char('=').parse(input)?;
71 let (input, _) = multispace0(input)?;
72
73 let (input, value) = alt((
75 parse_value,
77 parse_subquery_value,
79 nom::combinator::map(parse_expression, |expr| Value::Function(expr.to_string())),
81 ))
82 .parse(input)?;
83
84 Ok((
85 input,
86 Condition {
87 left: Expr::Named(column.to_string()),
88 op: Operator::Eq,
89 value,
90 is_array_unnest: false,
91 },
92 ))
93}
94
95fn parse_subquery_value(input: &str) -> IResult<&str, Value> {
97 let (input, _) = char('(').parse(input)?;
98 let (input, _) = multispace0(input)?;
99 let (input, subquery) = super::parse_root(input)?;
100 let (input, _) = multispace0(input)?;
101 let (input, _) = char(')').parse(input)?;
102 Ok((input, Value::Subquery(Box::new(subquery))))
103}
104
105pub fn parse_on_conflict(input: &str) -> IResult<&str, OnConflict> {
111 use nom::branch::alt;
112
113 let (input, _) = multispace0(input)?;
114 let (input, _) = tag_no_case("conflict").parse(input)?;
115 let (input, _) = multispace0(input)?;
116
117 let (input, _) = char('(').parse(input)?;
118 let (input, _) = multispace0(input)?;
119 let (input, columns) =
120 separated_list1((multispace0, char(','), multispace0), parse_identifier).parse(input)?;
121 let (input, _) = multispace0(input)?;
122 let (input, _) = char(')').parse(input)?;
123 let (input, _) = multispace0(input)?;
124
125 let (input, action) = alt((parse_conflict_nothing, parse_conflict_update)).parse(input)?;
126
127 Ok((
128 input,
129 OnConflict {
130 columns: columns.iter().map(|s| s.to_string()).collect(),
131 action,
132 },
133 ))
134}
135
136fn parse_conflict_nothing(input: &str) -> IResult<&str, ConflictAction> {
138 use nom::combinator::value;
139 value(ConflictAction::DoNothing, tag_no_case("nothing")).parse(input)
140}
141
142fn parse_conflict_update(input: &str) -> IResult<&str, ConflictAction> {
144 let (input, _) = tag_no_case("update").parse(input)?;
145 let (input, _) = multispace1(input)?;
146 let (input, assignments) = parse_conflict_assignments(input)?;
147
148 Ok((input, ConflictAction::DoUpdate { assignments }))
149}
150
151fn parse_conflict_assignments(input: &str) -> IResult<&str, Vec<(String, Expr)>> {
153 separated_list1(
154 (multispace0, char(','), multispace0),
155 parse_conflict_assignment,
156 )
157 .parse(input)
158}
159
160fn parse_conflict_assignment(input: &str) -> IResult<&str, (String, Expr)> {
162 use super::expressions::parse_expression;
163 use nom::branch::alt;
164
165 let (input, column) = parse_identifier(input)?;
166 let (input, _) = multispace0(input)?;
167 let (input, _) = char('=').parse(input)?;
168 let (input, _) = multispace0(input)?;
169
170 let (input, expr) = alt((
173 nom::combinator::map(parse_value, |v| match v {
174 Value::NamedParam(name) => Expr::Named(format!(":{}", name)),
175 Value::Param(n) => Expr::Named(format!("${}", n)),
176 Value::String(s) => Expr::Named(format!("'{}'", s)),
177 Value::Int(n) => Expr::Named(n.to_string()),
178 Value::Float(f) => Expr::Named(f.to_string()),
179 Value::Bool(b) => Expr::Named(b.to_string()),
180 Value::Null => Expr::Named("NULL".to_string()),
181 Value::Array(_) => Expr::Named("ARRAY".to_string()),
182 Value::Function(name) => Expr::Named(name),
183 Value::Subquery(_) => Expr::Named("(SUBQUERY)".to_string()),
184 Value::Column(col) => Expr::Named(col),
185 Value::Uuid(u) => Expr::Named(format!("'{}'", u)),
186 Value::NullUuid => Expr::Named("NULL".to_string()),
187 Value::Interval { amount, unit } => {
188 Expr::Named(format!("INTERVAL '{} {}'", amount, unit))
189 }
190 Value::Timestamp(ts) => Expr::Named(format!("'{}'", ts)),
191 Value::Bytes(bytes) => {
192 let hex: String = bytes.iter().map(|b| format!("{:02x}", b)).collect();
193 Expr::Named(format!("'\\x{}'", hex))
194 }
195 Value::Expr(expr) => (*expr).clone(),
196 }),
197 parse_expression,
199 ))
200 .parse(input)?;
201
202 Ok((input, (column.to_string(), expr)))
203}
204
205pub fn parse_source_query(input: &str) -> IResult<&str, Box<crate::ast::Qail>> {
209 let (input, _) = multispace0(input)?;
210 let (input, _) = tag_no_case("from").parse(input)?;
211 let (input, _) = multispace0(input)?;
212 let (input, _) = char('(').parse(input)?;
213 let (input, _) = multispace0(input)?;
214 let (input, subquery) = super::parse_root(input)?;
215 let (input, _) = multispace0(input)?;
216 let (input, _) = char(')').parse(input)?;
217 Ok((input, Box::new(subquery)))
218}