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::Subquery(select) => {
270 ret.push('(');
271 ret.push_str(&select.to_sql_string(context));
272 ret.push(')');
273 }
274 Expr::Unary(unary_operator, expr) => {
275 ret.push_str(&unary_operator.to_string());
276 ret.push(' ');
277 ret.push_str(&expr.to_sql_string(context));
278 }
279 Expr::Variable(variable) => {
280 ret.push_str(variable);
281 }
282 };
283 ret
284 }
285}
286
287impl Display for ast::Operator {
288 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289 let value = match self {
290 Self::Add => "+",
291 Self::And => "AND",
292 Self::ArrowRight => "->",
293 Self::ArrowRightShift => "->>",
294 Self::BitwiseAnd => "&",
295 Self::BitwiseNot => "~",
296 Self::BitwiseOr => "|",
297 Self::Concat => "||",
298 Self::Divide => "/",
299 Self::Equals => "=",
300 Self::Greater => ">",
301 Self::GreaterEquals => ">=",
302 Self::Is => "IS",
303 Self::IsNot => "IS NOT",
304 Self::LeftShift => "<<",
305 Self::Less => "<",
306 Self::LessEquals => "<=",
307 Self::Modulus => "%",
308 Self::Multiply => "*",
309 Self::NotEquals => "!=",
310 Self::Or => "OR",
311 Self::RightShift => ">>",
312 Self::Subtract => "-",
313 };
314 write!(f, "{}", value)
315 }
316}
317
318impl ToSqlString for ast::Type {
319 fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
320 let mut ret = self.name.clone();
321 if let Some(size) = &self.size {
322 ret.push(' ');
323 ret.push('(');
324 ret.push_str(&size.to_sql_string(context));
325 ret.push(')');
326 }
327 ret
328 }
329}
330
331impl ToSqlString for ast::TypeSize {
332 fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
333 let mut ret = String::new();
334 match self {
335 Self::MaxSize(e) => {
336 ret.push_str(&e.to_sql_string(context));
337 }
338 Self::TypeSize(lhs, rhs) => {
339 ret.push_str(&lhs.to_sql_string(context));
340 ret.push_str(", ");
341 ret.push_str(&rhs.to_sql_string(context));
342 }
343 };
344 ret
345 }
346}
347
348impl Display for ast::Distinctness {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 write!(
351 f,
352 "{}",
353 match self {
354 Self::All => "ALL",
355 Self::Distinct => "DISTINCT",
356 }
357 )
358 }
359}
360
361impl ToSqlString for ast::QualifiedName {
363 fn to_sql_string<C: super::ToSqlContext>(&self, _context: &C) -> String {
364 let mut ret = String::new();
365 if let Some(db_name) = &self.db_name {
366 ret.push_str(&db_name.0);
367 ret.push('.');
368 }
369 if let Some(alias) = &self.alias {
370 ret.push_str(&alias.0);
371 ret.push('.');
372 }
373 ret.push_str(&self.name.0);
374 ret
375 }
376}
377
378impl Display for ast::LikeOperator {
379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380 self.to_fmt(f)
381 }
382}
383
384impl Display for ast::Literal {
385 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
386 write!(
387 f,
388 "{}",
389 match self {
390 Self::Blob(b) => format!("x'{b}'"),
391 Self::CurrentDate => "CURRENT_DATE".to_string(),
392 Self::CurrentTime => "CURRENT_TIME".to_string(),
393 Self::CurrentTimestamp => "CURRENT_TIMESTAMP".to_string(),
394 Self::Keyword(keyword) => keyword.clone(),
395 Self::Null => "NULL".to_string(),
396 Self::Numeric(num) => num.clone(),
397 Self::String(s) => s.clone(),
398 }
399 )
400 }
401}
402
403impl Display for ast::ResolveType {
404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405 self.to_fmt(f)
406 }
407}
408
409impl Display for ast::UnaryOperator {
410 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411 write!(
412 f,
413 "{}",
414 match self {
415 Self::BitwiseNot => "~",
416 Self::Negative => "-",
417 Self::Not => "NOT",
418 Self::Positive => "+",
419 }
420 )
421 }
422}
423
424impl ToSqlString for ast::Over {
425 fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
426 let mut ret = vec!["OVER".to_string()];
427 match self {
428 Self::Name(name) => {
429 ret.push(name.0.clone());
430 }
431 Self::Window(window) => {
432 ret.push(window.to_sql_string(context));
433 }
434 }
435 ret.join(" ")
436 }
437}
438
439#[cfg(test)]
440mod tests {
441 use crate::to_sql_string_test;
442
443 to_sql_string_test!(
444 test_function_call_distinct,
445 "SELECT COUNT(DISTINCT x) FROM t;"
446 );
447
448 to_sql_string_test!(
449 test_function_call_order_by,
450 "SELECT GROUP_CONCAT(x ORDER BY y) FROM t;"
451 );
452
453 to_sql_string_test!(
454 test_function_call_distinct_and_order_by,
455 "SELECT GROUP_CONCAT(DISTINCT x ORDER BY y) FROM t;"
456 );
457
458 to_sql_string_test!(
459 test_function_call_order_by_multiple_columns_and_direction,
460 "SELECT GROUP_CONCAT(x ORDER BY y ASC, z DESC) FROM t;"
461 );
462}