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                // TODO: pretty sure there should be no ORDER_BY nor DISTINCT
97                ret.push('(');
98                if let Some(args) = args {
99                    let joined_args = args
100                        .iter()
101                        .map(|arg| arg.to_sql_string(context))
102                        .collect::<Vec<_>>()
103                        .join(", ");
104                    ret.push_str(&joined_args);
105                }
106                ret.push(')');
107                if let Some(filter_over) = filter_over {
108                    if let Some(filter) = &filter_over.filter_clause {
109                        ret.push_str(&format!(
110                            " FILTER (WHERE {})",
111                            filter.to_sql_string(context)
112                        ));
113                    }
114                    if let Some(over) = &filter_over.over_clause {
115                        ret.push(' ');
116                        ret.push_str(&over.to_sql_string(context));
117                    }
118                }
119            }
120            Expr::FunctionCallStar { name, filter_over } => {
121                ret.push_str(&name.0);
122                ret.push_str("(*)");
123                if let Some(filter_over) = filter_over {
124                    if let Some(filter) = &filter_over.filter_clause {
125                        ret.push_str(&format!(
126                            " FILTER (WHERE {})",
127                            filter.to_sql_string(context)
128                        ));
129                    }
130                    if let Some(over) = &filter_over.over_clause {
131                        ret.push(' ');
132                        ret.push_str(&over.to_sql_string(context));
133                    }
134                }
135            }
136            Expr::Id(id) => {
137                ret.push_str(&id.0);
138            }
139            Expr::Column {
140                database: _, // TODO: Ignore database for now
141                table,
142                column,
143                is_rowid_alias: _,
144            } => {
145                ret.push_str(context.get_table_name(*table));
146                ret.push('.');
147                ret.push_str(context.get_column_name(*table, *column));
148            }
149            Expr::RowId { database: _, table } => {
150                ret.push_str(&format!("{}.rowid", context.get_table_name(*table)))
151            }
152            Expr::InList { lhs, not, rhs } => {
153                ret.push_str(&format!(
154                    "{} {}IN ({})",
155                    lhs.to_sql_string(context),
156                    if *not { "NOT " } else { "" },
157                    if let Some(rhs) = rhs {
158                        rhs.iter()
159                            .map(|expr| expr.to_sql_string(context))
160                            .collect::<Vec<_>>()
161                            .join(", ")
162                    } else {
163                        "".to_string()
164                    }
165                ));
166            }
167            Expr::InSelect { lhs, not, rhs } => {
168                ret.push_str(&format!(
169                    "{} {}IN ({})",
170                    lhs.to_sql_string(context),
171                    if *not { "NOT " } else { "" },
172                    rhs.to_sql_string(context)
173                ));
174            }
175            Expr::InTable {
176                lhs,
177                not,
178                rhs,
179                args,
180            } => {
181                ret.push_str(&lhs.to_sql_string(context));
182                ret.push(' ');
183                if *not {
184                    ret.push_str("NOT ");
185                }
186                ret.push_str(&rhs.to_sql_string(context));
187
188                if let Some(args) = args {
189                    ret.push('(');
190                    let joined_args = args
191                        .iter()
192                        .map(|expr| expr.to_sql_string(context))
193                        .collect::<Vec<_>>()
194                        .join(", ");
195                    ret.push_str(&joined_args);
196                    ret.push(')');
197                }
198            }
199            Expr::IsNull(expr) => {
200                ret.push_str(&expr.to_sql_string(context));
201                ret.push_str(" ISNULL");
202            }
203            Expr::Like {
204                lhs,
205                not,
206                op,
207                rhs,
208                escape,
209            } => {
210                ret.push_str(&lhs.to_sql_string(context));
211                ret.push(' ');
212                if *not {
213                    ret.push_str("NOT ");
214                }
215                ret.push_str(&op.to_string());
216                ret.push(' ');
217                ret.push_str(&rhs.to_sql_string(context));
218                if let Some(escape) = escape {
219                    ret.push_str(" ESCAPE ");
220                    ret.push_str(&escape.to_sql_string(context));
221                }
222            }
223            Expr::Literal(literal) => {
224                ret.push_str(&literal.to_string());
225            }
226            Expr::Name(name) => {
227                ret.push_str(&name.0);
228            }
229            Expr::NotNull(expr) => {
230                ret.push_str(&expr.to_sql_string(context));
231                ret.push_str(" NOT NULL");
232            }
233            Expr::Parenthesized(exprs) => {
234                ret.push('(');
235                let joined_args = exprs
236                    .iter()
237                    .map(|expr| expr.to_sql_string(context))
238                    .collect::<Vec<_>>()
239                    .join(", ");
240                ret.push_str(&joined_args);
241                ret.push(')');
242            }
243            Expr::Qualified(name, name1) => {
244                ret.push_str(&name.0);
245                ret.push('.');
246                ret.push_str(&name1.0);
247            }
248            Expr::Raise(resolve_type, expr) => {
249                ret.push_str("RAISE(");
250                ret.push_str(&resolve_type.to_string());
251                if let Some(expr) = expr {
252                    ret.push_str(", ");
253                    ret.push_str(&expr.to_sql_string(context));
254                }
255                ret.push(')');
256            }
257            Expr::Subquery(select) => {
258                ret.push('(');
259                ret.push_str(&select.to_sql_string(context));
260                ret.push(')');
261            }
262            Expr::Unary(unary_operator, expr) => {
263                ret.push_str(&unary_operator.to_string());
264                ret.push(' ');
265                ret.push_str(&expr.to_sql_string(context));
266            }
267            Expr::Variable(variable) => {
268                ret.push_str(variable);
269            }
270        };
271        ret
272    }
273}
274
275impl Display for ast::Operator {
276    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277        let value = match self {
278            Self::Add => "+",
279            Self::And => "AND",
280            Self::ArrowRight => "->",
281            Self::ArrowRightShift => "->>",
282            Self::BitwiseAnd => "&",
283            Self::BitwiseNot => "~",
284            Self::BitwiseOr => "|",
285            Self::Concat => "||",
286            Self::Divide => "/",
287            Self::Equals => "=",
288            Self::Greater => ">",
289            Self::GreaterEquals => ">=",
290            Self::Is => "IS",
291            Self::IsNot => "IS NOT",
292            Self::LeftShift => "<<",
293            Self::Less => "<",
294            Self::LessEquals => "<=",
295            Self::Modulus => "%",
296            Self::Multiply => "*",
297            Self::NotEquals => "!=",
298            Self::Or => "OR",
299            Self::RightShift => ">>",
300            Self::Subtract => "-",
301        };
302        write!(f, "{}", value)
303    }
304}
305
306impl ToSqlString for ast::Type {
307    fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
308        let mut ret = self.name.clone();
309        if let Some(size) = &self.size {
310            ret.push(' ');
311            ret.push('(');
312            ret.push_str(&size.to_sql_string(context));
313            ret.push(')');
314        }
315        ret
316    }
317}
318
319impl ToSqlString for ast::TypeSize {
320    fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
321        let mut ret = String::new();
322        match self {
323            Self::MaxSize(e) => {
324                ret.push_str(&e.to_sql_string(context));
325            }
326            Self::TypeSize(lhs, rhs) => {
327                ret.push_str(&lhs.to_sql_string(context));
328                ret.push_str(", ");
329                ret.push_str(&rhs.to_sql_string(context));
330            }
331        };
332        ret
333    }
334}
335
336impl Display for ast::Distinctness {
337    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338        write!(
339            f,
340            "{}",
341            match self {
342                Self::All => "ALL",
343                Self::Distinct => "DISTINCT",
344            }
345        )
346    }
347}
348
349// Can't impl Display here as it is already implemented for it
350impl ToSqlString for ast::QualifiedName {
351    fn to_sql_string<C: super::ToSqlContext>(&self, _context: &C) -> String {
352        let mut ret = String::new();
353        if let Some(db_name) = &self.db_name {
354            ret.push_str(&db_name.0);
355            ret.push('.');
356        }
357        if let Some(alias) = &self.alias {
358            ret.push_str(&alias.0);
359            ret.push('.');
360        }
361        ret.push_str(&self.name.0);
362        ret
363    }
364}
365
366impl Display for ast::LikeOperator {
367    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368        self.to_fmt(f)
369    }
370}
371
372impl Display for ast::Literal {
373    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
374        write!(
375            f,
376            "{}",
377            match self {
378                Self::Blob(b) => format!("x'{b}'"),
379                Self::CurrentDate => "CURRENT_DATE".to_string(),
380                Self::CurrentTime => "CURRENT_TIME".to_string(),
381                Self::CurrentTimestamp => "CURRENT_TIMESTAMP".to_string(),
382                Self::Keyword(keyword) => keyword.clone(),
383                Self::Null => "NULL".to_string(),
384                Self::Numeric(num) => num.clone(),
385                Self::String(s) => s.clone(),
386            }
387        )
388    }
389}
390
391impl Display for ast::ResolveType {
392    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393        self.to_fmt(f)
394    }
395}
396
397impl Display for ast::UnaryOperator {
398    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
399        write!(
400            f,
401            "{}",
402            match self {
403                Self::BitwiseNot => "~",
404                Self::Negative => "-",
405                Self::Not => "NOT",
406                Self::Positive => "+",
407            }
408        )
409    }
410}
411
412impl ToSqlString for ast::Over {
413    fn to_sql_string<C: super::ToSqlContext>(&self, context: &C) -> String {
414        let mut ret = vec!["OVER".to_string()];
415        match self {
416            Self::Name(name) => {
417                ret.push(name.0.clone());
418            }
419            Self::Window(window) => {
420                ret.push(window.to_sql_string(context));
421            }
422        }
423        ret.join(" ")
424    }
425}
426
427#[cfg(test)]
428mod tests {}