limbo_sqlite3_parser/to_sql_string/
expr.rs1use std::fmt::Display;
2
3use crate::ast::{self, fmt::ToTokens, Expr};
4
5use super::ToSqlString;
6
7impl ToSqlString for Expr {
8 fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
9 let mut ret = String::new();
10 match self {
11 Expr::Between {
12 lhs,
13 not,
14 start,
15 end,
16 } => {
17 ret.push_str(&lhs.to_sql_string(context));
18 ret.push(' ');
19
20 if *not {
21 ret.push_str("NOT ");
22 }
23
24 ret.push_str("BETWEEN ");
25
26 ret.push_str(&start.to_sql_string(context));
27
28 ret.push_str(" AND ");
29
30 ret.push_str(&end.to_sql_string(context));
31 }
32 Expr::Binary(lhs, op, rhs) => {
33 ret.push_str(&lhs.to_sql_string(context));
34 ret.push(' ');
35 ret.push_str(&op.to_string());
36 ret.push(' ');
37 ret.push_str(&rhs.to_sql_string(context));
38 }
39 Expr::Case {
40 base,
41 when_then_pairs,
42 else_expr,
43 } => {
44 ret.push_str("CASE ");
45 if let Some(base) = base {
46 ret.push_str(&base.to_sql_string(context));
47 ret.push(' ');
48 }
49 for (when, then) in when_then_pairs {
50 ret.push_str("WHEN ");
51 ret.push_str(&when.to_sql_string(context));
52 ret.push_str(" THEN ");
53 ret.push_str(&then.to_sql_string(context));
54 }
55 if let Some(else_expr) = else_expr {
56 ret.push_str(" ELSE ");
57 ret.push_str(&else_expr.to_sql_string(context));
58 }
59 ret.push_str(" END");
60 }
61 Expr::Cast { expr, type_name } => {
62 ret.push_str("CAST");
63 ret.push('(');
64 ret.push_str(&expr.to_sql_string(context));
65 if let Some(type_name) = type_name {
66 ret.push_str(" AS ");
67 ret.push_str(&type_name.to_sql_string(context));
68 }
69 ret.push(')');
70 }
71 Expr::Collate(expr, name) => {
72 ret.push_str(&expr.to_sql_string(context));
73 ret.push_str(" COLLATE ");
74 ret.push_str(&name);
75 }
76 Expr::DoublyQualified(name, name1, name2) => {
77 ret.push_str(&name.0);
78 ret.push('.');
79 ret.push_str(&name1.0);
80 ret.push('.');
81 ret.push_str(&name2.0);
82 }
83 Expr::Exists(select) => {
84 ret.push_str("EXISTS (");
85 ret.push_str(&select.to_sql_string(context));
86 ret.push(')');
87 }
88 Expr::FunctionCall {
89 name,
90 distinctness,
91 args,
92 order_by,
93 filter_over,
94 } => {
95 ret.push_str(&name.0);
96 ret.push('(');
97 if let Some(distinctness) = distinctness {
98 ret.push_str(&distinctness.to_string());
99 ret.push(' ');
100 }
101 if let Some(args) = args {
102 let joined_args = args
103 .iter()
104 .map(|arg| arg.to_sql_string(context))
105 .collect::<Vec<_>>()
106 .join(", ");
107 ret.push_str(&joined_args);
108 }
109 if let Some(order_by) = order_by {
110 let joined_order_by = order_by
111 .iter()
112 .map(|sorted_col| sorted_col.to_sql_string(context))
113 .collect::<Vec<_>>()
114 .join(", ");
115 ret.push_str(" ORDER BY ");
116 ret.push_str(&joined_order_by);
117 }
118 ret.push(')');
119 if let Some(filter_over) = filter_over {
120 if let Some(filter) = &filter_over.filter_clause {
121 ret.push_str(&format!(
122 " FILTER (WHERE {})",
123 filter.to_sql_string(context)
124 ));
125 }
126 if let Some(over) = &filter_over.over_clause {
127 ret.push(' ');
128 ret.push_str(&over.to_sql_string(context));
129 }
130 }
131 }
132 Expr::FunctionCallStar { name, filter_over } => {
133 ret.push_str(&name.0);
134 ret.push_str("(*)");
135 if let Some(filter_over) = filter_over {
136 if let Some(filter) = &filter_over.filter_clause {
137 ret.push_str(&format!(
138 " FILTER (WHERE {})",
139 filter.to_sql_string(context)
140 ));
141 }
142 if let Some(over) = &filter_over.over_clause {
143 ret.push(' ');
144 ret.push_str(&over.to_sql_string(context));
145 }
146 }
147 }
148 Expr::Id(id) => {
149 ret.push_str(&id.0);
150 }
151 Expr::Column {
152 database: _, table,
154 column,
155 is_rowid_alias: _,
156 } => {
157 ret.push_str(context.get_table_name(*table));
158 ret.push('.');
159 ret.push_str(context.get_column_name(*table, *column));
160 }
161 Expr::RowId { database: _, table } => {
162 ret.push_str(&format!("{}.rowid", context.get_table_name(*table)))
163 }
164 Expr::InList { lhs, not, rhs } => {
165 ret.push_str(&format!(
166 "{} {}IN ({})",
167 lhs.to_sql_string(context),
168 if *not { "NOT " } else { "" },
169 if let Some(rhs) = rhs {
170 rhs.iter()
171 .map(|expr| expr.to_sql_string(context))
172 .collect::<Vec<_>>()
173 .join(", ")
174 } else {
175 "".to_string()
176 }
177 ));
178 }
179 Expr::InSelect { lhs, not, rhs } => {
180 ret.push_str(&format!(
181 "{} {}IN ({})",
182 lhs.to_sql_string(context),
183 if *not { "NOT " } else { "" },
184 rhs.to_sql_string(context)
185 ));
186 }
187 Expr::InTable {
188 lhs,
189 not,
190 rhs,
191 args,
192 } => {
193 ret.push_str(&lhs.to_sql_string(context));
194 ret.push(' ');
195 if *not {
196 ret.push_str("NOT ");
197 }
198 ret.push_str(&rhs.to_sql_string(context));
199
200 if let Some(args) = args {
201 ret.push('(');
202 let joined_args = args
203 .iter()
204 .map(|expr| expr.to_sql_string(context))
205 .collect::<Vec<_>>()
206 .join(", ");
207 ret.push_str(&joined_args);
208 ret.push(')');
209 }
210 }
211 Expr::IsNull(expr) => {
212 ret.push_str(&expr.to_sql_string(context));
213 ret.push_str(" ISNULL");
214 }
215 Expr::Like {
216 lhs,
217 not,
218 op,
219 rhs,
220 escape,
221 } => {
222 ret.push_str(&lhs.to_sql_string(context));
223 ret.push(' ');
224 if *not {
225 ret.push_str("NOT ");
226 }
227 ret.push_str(&op.to_string());
228 ret.push(' ');
229 ret.push_str(&rhs.to_sql_string(context));
230 if let Some(escape) = escape {
231 ret.push_str(" ESCAPE ");
232 ret.push_str(&escape.to_sql_string(context));
233 }
234 }
235 Expr::Literal(literal) => {
236 ret.push_str(&literal.to_string());
237 }
238 Expr::Name(name) => {
239 ret.push_str(&name.0);
240 }
241 Expr::NotNull(expr) => {
242 ret.push_str(&expr.to_sql_string(context));
243 ret.push_str(" NOT NULL");
244 }
245 Expr::Parenthesized(exprs) => {
246 ret.push('(');
247 let joined_args = exprs
248 .iter()
249 .map(|expr| expr.to_sql_string(context))
250 .collect::<Vec<_>>()
251 .join(", ");
252 ret.push_str(&joined_args);
253 ret.push(')');
254 }
255 Expr::Qualified(name, name1) => {
256 ret.push_str(&name.0);
257 ret.push('.');
258 ret.push_str(&name1.0);
259 }
260 Expr::Raise(resolve_type, expr) => {
261 ret.push_str("RAISE(");
262 ret.push_str(&resolve_type.to_string());
263 if let Some(expr) = expr {
264 ret.push_str(", ");
265 ret.push_str(&expr.to_sql_string(context));
266 }
267 ret.push(')');
268 }
269 Expr::Register(_) => {}
273 Expr::Subquery(select) => {
274 ret.push('(');
275 ret.push_str(&select.to_sql_string(context));
276 ret.push(')');
277 }
278 Expr::Unary(unary_operator, expr) => {
279 ret.push_str(&unary_operator.to_string());
280 ret.push(' ');
281 ret.push_str(&expr.to_sql_string(context));
282 }
283 Expr::Variable(variable) => {
284 ret.push_str(variable);
285 }
286 };
287 ret
288 }
289}
290
291impl Display for ast::Operator {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 let value = match self {
294 Self::Add => "+",
295 Self::And => "AND",
296 Self::ArrowRight => "->",
297 Self::ArrowRightShift => "->>",
298 Self::BitwiseAnd => "&",
299 Self::BitwiseNot => "~",
300 Self::BitwiseOr => "|",
301 Self::Concat => "||",
302 Self::Divide => "/",
303 Self::Equals => "=",
304 Self::Greater => ">",
305 Self::GreaterEquals => ">=",
306 Self::Is => "IS",
307 Self::IsNot => "IS NOT",
308 Self::LeftShift => "<<",
309 Self::Less => "<",
310 Self::LessEquals => "<=",
311 Self::Modulus => "%",
312 Self::Multiply => "*",
313 Self::NotEquals => "!=",
314 Self::Or => "OR",
315 Self::RightShift => ">>",
316 Self::Subtract => "-",
317 };
318 write!(f, "{}", value)
319 }
320}
321
322impl ToSqlString for ast::Type {
323 fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
324 let mut ret = self.name.clone();
325 if let Some(size) = &self.size {
326 ret.push(' ');
327 ret.push('(');
328 ret.push_str(&size.to_sql_string(context));
329 ret.push(')');
330 }
331 ret
332 }
333}
334
335impl ToSqlString for ast::TypeSize {
336 fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
337 let mut ret = String::new();
338 match self {
339 Self::MaxSize(e) => {
340 ret.push_str(&e.to_sql_string(context));
341 }
342 Self::TypeSize(lhs, rhs) => {
343 ret.push_str(&lhs.to_sql_string(context));
344 ret.push_str(", ");
345 ret.push_str(&rhs.to_sql_string(context));
346 }
347 };
348 ret
349 }
350}
351
352impl Display for ast::Distinctness {
353 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
354 write!(
355 f,
356 "{}",
357 match self {
358 Self::All => "ALL",
359 Self::Distinct => "DISTINCT",
360 }
361 )
362 }
363}
364
365impl ToSqlString for ast::QualifiedName {
367 fn to_sql_string<C: super::ToSqlContext>(&self, _context: &C) -> String {
368 let mut ret = String::new();
369 if let Some(db_name) = &self.db_name {
370 ret.push_str(&db_name.0);
371 ret.push('.');
372 }
373 if let Some(alias) = &self.alias {
374 ret.push_str(&alias.0);
375 ret.push('.');
376 }
377 ret.push_str(&self.name.0);
378 ret
379 }
380}
381
382impl Display for ast::LikeOperator {
383 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384 self.to_fmt(f)
385 }
386}
387
388impl Display for ast::Literal {
389 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
390 write!(
391 f,
392 "{}",
393 match self {
394 Self::Blob(b) => format!("x'{b}'"),
395 Self::CurrentDate => "CURRENT_DATE".to_string(),
396 Self::CurrentTime => "CURRENT_TIME".to_string(),
397 Self::CurrentTimestamp => "CURRENT_TIMESTAMP".to_string(),
398 Self::Keyword(keyword) => keyword.clone(),
399 Self::Null => "NULL".to_string(),
400 Self::Numeric(num) => num.clone(),
401 Self::String(s) => s.clone(),
402 }
403 )
404 }
405}
406
407impl Display for ast::ResolveType {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 self.to_fmt(f)
410 }
411}
412
413impl Display for ast::UnaryOperator {
414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415 write!(
416 f,
417 "{}",
418 match self {
419 Self::BitwiseNot => "~",
420 Self::Negative => "-",
421 Self::Not => "NOT",
422 Self::Positive => "+",
423 }
424 )
425 }
426}
427
428impl ToSqlString for ast::Over {
429 fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
430 let mut ret = vec!["OVER".to_string()];
431 match self {
432 Self::Name(name) => {
433 ret.push(name.0.clone());
434 }
435 Self::Window(window) => {
436 ret.push(window.to_sql_string(context));
437 }
438 }
439 ret.join(" ")
440 }
441}
442
443#[cfg(test)]
444mod tests {
445 use crate::to_sql_string_test;
446
447 to_sql_string_test!(
448 test_function_call_distinct,
449 "SELECT COUNT(DISTINCT x) FROM t;"
450 );
451
452 to_sql_string_test!(
453 test_function_call_order_by,
454 "SELECT GROUP_CONCAT(x ORDER BY y) FROM t;"
455 );
456
457 to_sql_string_test!(
458 test_function_call_distinct_and_order_by,
459 "SELECT GROUP_CONCAT(DISTINCT x ORDER BY y) FROM t;"
460 );
461
462 to_sql_string_test!(
463 test_function_call_order_by_multiple_columns_and_direction,
464 "SELECT GROUP_CONCAT(x ORDER BY y ASC, z DESC) FROM t;"
465 );
466}