Skip to main content

limbo_sqlite3_parser/to_sql_string/
expr.rs

1use 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: _, // TODO: Ignore database for now
153                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            // Code-generator-only node (see `Expr::Register`): it has no SQL
270            // surface syntax, and no rewritten tree is ever rendered back to
271            // SQL text, so the rendering is empty rather than a made-up token.
272            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
365// Can't impl Display here as it is already implemented for it
366impl 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}