qail_core/parser/grammar/
mod.rs1pub mod base;
2pub mod binary_ops;
3pub mod case_when;
4pub mod clauses;
5pub mod cte;
6pub mod ddl;
7pub mod dml;
8pub mod expressions;
9pub mod functions;
10pub mod joins;
11pub mod special_funcs;
12
13use crate::ast::*;
14use nom::{
15 IResult, Parser,
16 bytes::complete::tag_no_case,
17 character::complete::{multispace0, multispace1},
18 combinator::opt,
19 multi::many0,
20};
21use self::base::*;
22use self::clauses::*;
23use self::ddl::*;
24use self::dml::*;
25use self::joins::*;
26pub fn parse(input: &str) -> Result<Qail, String> {
31 let cleaned = strip_sql_comments(input);
32 match parse_root(&cleaned) {
33 Ok((_, cmd)) => Ok(cmd),
34 Err(e) => Err(format!("Parse error: {:?}", e)),
35 }
36}
37
38pub fn parse_root(input: &str) -> IResult<&str, Qail> {
41 let input = input.trim();
42
43 if let Ok((remaining, cmd)) = parse_txn_command(input) {
45 return Ok((remaining, cmd));
46 }
47
48 if let Ok((remaining, cmd)) = parse_create_index(input) {
50 return Ok((remaining, cmd));
51 }
52
53 let lower_input = input.to_lowercase();
55 let (input, ctes) = if lower_input.starts_with("with")
56 && lower_input
57 .chars()
58 .nth(4)
59 .map(|c| c.is_whitespace())
60 .unwrap_or(false)
61 {
62 let (remaining, (cte_defs, _is_recursive)) = cte::parse_with_clause(input)?;
63 let (remaining, _) = multispace0(remaining)?;
64 (remaining, cte_defs)
65 } else {
66 (input, vec![])
67 };
68
69 let (input, (action, distinct)) = parse_action(input)?;
70 let (input, _) = multispace1(input)?;
72
73 let (input, distinct_on) = if distinct {
75 if let Ok((remaining, _)) = tag_no_case::<_, _, nom::error::Error<&str>>("on").parse(input)
77 {
78 let (remaining, _) = multispace0(remaining)?;
79 let (remaining, exprs) = nom::sequence::delimited(
80 nom::character::complete::char('('),
81 nom::multi::separated_list1(
82 (
83 multispace0,
84 nom::character::complete::char(','),
85 multispace0,
86 ),
87 expressions::parse_expression,
88 ),
89 nom::character::complete::char(')'),
90 )
91 .parse(remaining)?;
92 let (remaining, _) = multispace1(remaining)?;
93 (remaining, exprs)
94 } else {
95 (input, vec![])
96 }
97 } else {
98 (input, vec![])
99 };
100
101 let (input, table) = parse_identifier(input)?;
103 let (input, _) = multispace0(input)?;
104
105 if matches!(action, Action::Make) {
107 return parse_create_table(input, table);
108 }
109
110 let (input, joins) = many0(parse_join_clause).parse(input)?;
111 let (input, _) = multispace0(input)?;
112
113 let (input, set_cages) = if matches!(action, Action::Set) {
115 opt(parse_values_clause).parse(input)?
116 } else {
117 (input, None)
118 };
119 let (input, _) = multispace0(input)?;
120
121 let (input, columns) = opt(parse_fields_clause).parse(input)?;
122 let (input, _) = multispace0(input)?;
123
124 let (input, source_query) = if matches!(action, Action::Add) {
126 opt(dml::parse_source_query).parse(input)?
127 } else {
128 (input, None)
129 };
130 let (input, _) = multispace0(input)?;
131
132 let (input, add_cages) = if source_query.is_none() && matches!(action, Action::Add) {
134 opt(dml::parse_insert_values).parse(input)?
135 } else {
136 (input, None)
137 };
138 let (input, _) = multispace0(input)?;
139
140 let (input, where_cages) = opt(parse_where_clause).parse(input)?;
141 let (input, _) = multispace0(input)?;
142
143 let (input, having) = opt(parse_having_clause).parse(input)?;
144 let (input, _) = multispace0(input)?;
145
146 let (input, on_conflict) = if matches!(action, Action::Add) {
147 opt(dml::parse_on_conflict).parse(input)?
148 } else {
149 (input, None)
150 };
151 let (input, _) = multispace0(input)?;
152
153 let (input, order_cages) = opt(parse_order_by_clause).parse(input)?;
154 let (input, _) = multispace0(input)?;
155 let (input, limit_cage) = opt(parse_limit_clause).parse(input)?;
156 let (input, _) = multispace0(input)?;
157 let (input, offset_cage) = opt(parse_offset_clause).parse(input)?;
158
159 let mut cages = Vec::new();
160
161 if let Some(sc) = set_cages {
163 cages.push(sc);
164 }
165
166 if let Some(ac) = add_cages {
168 cages.push(ac);
169 }
170
171 if let Some(wc) = where_cages {
172 cages.extend(wc);
173 }
174 if let Some(oc) = order_cages {
175 cages.extend(oc);
176 }
177 if let Some(lc) = limit_cage {
178 cages.push(lc);
179 }
180 if let Some(oc) = offset_cage {
181 cages.push(oc);
182 }
183
184 Ok((
185 input,
186 Qail {
187 action,
188 table: table.to_string(),
189 columns: columns.unwrap_or_else(|| vec![Expr::Star]),
190 joins,
191 cages,
192 distinct,
193 distinct_on,
194 index_def: None,
195 table_constraints: vec![],
196 set_ops: vec![],
197 having: having.unwrap_or_default(),
198 group_by_mode: GroupByMode::default(),
199 returning: None,
200 ctes,
201 on_conflict,
202 source_query,
203 channel: None,
204 payload: None,
205 savepoint_name: None,
206 from_tables: vec![],
207 using_tables: vec![],
208 lock_mode: None,
209 fetch: None,
210 default_values: false,
211 overriding: None,
212 sample: None,
213 only_table: false,
214 vector: None,
215 score_threshold: None,
216 vector_name: None,
217 with_vector: false,
218 vector_size: None,
219 distance: None,
220 on_disk: None,
221 function_def: None,
222 trigger_def: None,
223 raw_value: None,
224 redis_ttl: None,
225 redis_set_condition: None,
226 },
227 ))
228}
229
230fn strip_sql_comments(input: &str) -> String {
232 let mut result = String::with_capacity(input.len());
233 let mut chars = input.chars().peekable();
234
235 while let Some(c) = chars.next() {
236 if c == '-' && chars.peek() == Some(&'-') {
237 chars.next(); while let Some(&nc) = chars.peek() {
240 if nc == '\n' {
241 result.push('\n'); chars.next();
243 break;
244 }
245 chars.next();
246 }
247 } else if c == '/' && chars.peek() == Some(&'*') {
248 chars.next(); while let Some(nc) = chars.next() {
251 if nc == '*' && chars.peek() == Some(&'/') {
252 chars.next(); result.push(' '); break;
255 }
256 }
257 } else {
258 result.push(c);
259 }
260 }
261
262 result
263}