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            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
361// Can't impl Display here as it is already implemented for it
362impl 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}