qail_core/parser/grammar/
dml.rs1use nom::{
2 bytes::complete::{tag_no_case},
3 character::complete::{char, multispace0, multispace1},
4 multi::separated_list1,
5 Parser,
6 IResult,
7};
8use crate::ast::*;
9use super::base::{parse_identifier, parse_value};
10
11pub fn parse_values_clause(input: &str) -> IResult<&str, Cage> {
13 let (input, _) = tag_no_case("values").parse(input)?;
14 let (input, _) = multispace1(input)?;
15
16 let (input, conditions) = parse_set_assignments(input)?;
17
18 Ok((input, Cage {
19 kind: CageKind::Payload,
20 conditions,
21 logical_op: LogicalOp::And,
22 }))
23}
24
25pub fn parse_insert_values(input: &str) -> IResult<&str, Cage> {
27 let (input, _) = tag_no_case("values").parse(input)?;
28 let (input, _) = multispace1(input)?;
29
30 let (input, values) = separated_list1(
32 (multispace0, char(','), multispace0),
33 parse_value
34 ).parse(input)?;
35
36 let conditions: Vec<Condition> = values.into_iter().enumerate().map(|(i, val)| {
38 Condition {
39 left: Expr::Named(format!("${}", i + 1)), op: Operator::Eq,
41 value: val,
42 is_array_unnest: false,
43 }
44 }).collect();
45
46 Ok((input, Cage {
47 kind: CageKind::Payload,
48 conditions,
49 logical_op: LogicalOp::And,
50 }))
51}
52
53pub fn parse_set_assignments(input: &str) -> IResult<&str, Vec<Condition>> {
55 separated_list1(
56 (multispace0, char(','), multispace0),
57 parse_assignment
58 ).parse(input)
59}
60
61pub fn parse_assignment(input: &str) -> IResult<&str, Condition> {
63 use nom::branch::alt;
64 use super::expressions::parse_expression;
65
66 let (input, column) = parse_identifier(input)?;
67 let (input, _) = multispace0(input)?;
68 let (input, _) = char('=').parse(input)?;
69 let (input, _) = multispace0(input)?;
70
71 let (input, value) = alt((
73 parse_value,
75 parse_subquery_value,
77 nom::combinator::map(parse_expression, |expr| {
79 Value::Function(expr.to_string())
80 }),
81 )).parse(input)?;
82
83 Ok((input, Condition {
84 left: Expr::Named(column.to_string()),
85 op: Operator::Eq,
86 value,
87 is_array_unnest: false,
88 }))
89}
90
91fn parse_subquery_value(input: &str) -> IResult<&str, Value> {
93 let (input, _) = char('(').parse(input)?;
94 let (input, _) = multispace0(input)?;
95 let (input, subquery) = super::parse_root(input)?;
96 let (input, _) = multispace0(input)?;
97 let (input, _) = char(')').parse(input)?;
98 Ok((input, Value::Subquery(Box::new(subquery))))
99}
100
101pub fn parse_on_conflict(input: &str) -> IResult<&str, OnConflict> {
107 use nom::branch::alt;
108
109 let (input, _) = multispace0(input)?;
110 let (input, _) = tag_no_case("conflict").parse(input)?;
111 let (input, _) = multispace0(input)?;
112
113 let (input, _) = char('(').parse(input)?;
115 let (input, _) = multispace0(input)?;
116 let (input, columns) = separated_list1(
117 (multispace0, char(','), multispace0),
118 parse_identifier
119 ).parse(input)?;
120 let (input, _) = multispace0(input)?;
121 let (input, _) = char(')').parse(input)?;
122 let (input, _) = multispace0(input)?;
123
124 let (input, action) = alt((
126 parse_conflict_nothing,
127 parse_conflict_update,
128 )).parse(input)?;
129
130 Ok((input, OnConflict {
131 columns: columns.iter().map(|s| s.to_string()).collect(),
132 action,
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 ).parse(input)
157}
158
159fn parse_conflict_assignment(input: &str) -> IResult<&str, (String, Expr)> {
161 use nom::branch::alt;
162 use super::expressions::parse_expression;
163
164 let (input, column) = parse_identifier(input)?;
165 let (input, _) = multispace0(input)?;
166 let (input, _) = char('=').parse(input)?;
167 let (input, _) = multispace0(input)?;
168
169 let (input, expr) = alt((
172 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 } => Expr::Named(format!("INTERVAL '{} {}'", amount, unit)),
188 Value::Timestamp(ts) => Expr::Named(format!("'{}'", ts)),
189 }),
190 parse_expression,
192 )).parse(input)?;
193
194 Ok((input, (column.to_string(), expr)))
195}
196
197pub fn parse_source_query(input: &str) -> IResult<&str, Box<crate::ast::QailCmd>> {
201 let (input, _) = multispace0(input)?;
202 let (input, _) = tag_no_case("from").parse(input)?;
203 let (input, _) = multispace0(input)?;
204 let (input, _) = char('(').parse(input)?;
205 let (input, _) = multispace0(input)?;
206 let (input, subquery) = super::parse_root(input)?;
207 let (input, _) = multispace0(input)?;
208 let (input, _) = char(')').parse(input)?;
209 Ok((input, Box::new(subquery)))
210}