gluesql_core/ast_builder/
expr.rs

1mod binary_op;
2mod case;
3mod exists;
4mod is_null;
5mod like;
6mod nested;
7mod unary_op;
8
9pub mod aggregate;
10pub mod alias_as;
11pub mod between;
12pub mod function;
13pub mod in_list;
14pub mod numeric;
15
16use {
17    crate::{
18        ast::{Aggregate, AstLiteral, BinaryOperator, Expr, Function, Query, UnaryOperator},
19        ast_builder::QueryNode,
20        parse_sql::{parse_comma_separated_exprs, parse_expr, parse_query},
21        prelude::DataType,
22        result::{Error, Result},
23        translate::{translate_expr, translate_query},
24    },
25    aggregate::AggregateNode,
26    bigdecimal::BigDecimal,
27    function::FunctionNode,
28    in_list::InListNode,
29    numeric::NumericNode,
30    std::borrow::Cow,
31};
32pub use {
33    case::case,
34    exists::{exists, not_exists},
35    nested::nested,
36    unary_op::{bitwise_not, factorial, minus, not, plus},
37};
38
39#[derive(Clone, Debug)]
40pub enum ExprNode<'a> {
41    Expr(Cow<'a, Expr>),
42    SqlExpr(Cow<'a, str>),
43    Identifier(Cow<'a, str>),
44    Numeric(NumericNode<'a>),
45    QuotedString(Cow<'a, str>),
46    TypedString {
47        data_type: DataType,
48        value: Cow<'a, str>,
49    },
50    Between {
51        expr: Box<ExprNode<'a>>,
52        negated: bool,
53        low: Box<ExprNode<'a>>,
54        high: Box<ExprNode<'a>>,
55    },
56    Like {
57        expr: Box<ExprNode<'a>>,
58        negated: bool,
59        pattern: Box<ExprNode<'a>>,
60    },
61    ILike {
62        expr: Box<ExprNode<'a>>,
63        negated: bool,
64        pattern: Box<ExprNode<'a>>,
65    },
66    BinaryOp {
67        left: Box<ExprNode<'a>>,
68        op: BinaryOperator,
69        right: Box<ExprNode<'a>>,
70    },
71    UnaryOp {
72        op: UnaryOperator,
73        expr: Box<ExprNode<'a>>,
74    },
75    IsNull(Box<ExprNode<'a>>),
76    IsNotNull(Box<ExprNode<'a>>),
77    InList {
78        expr: Box<ExprNode<'a>>,
79        list: Box<InListNode<'a>>,
80        negated: bool,
81    },
82    Nested(Box<ExprNode<'a>>),
83    Function(Box<FunctionNode<'a>>),
84    Aggregate(Box<AggregateNode<'a>>),
85    Exists {
86        subquery: Box<QueryNode<'a>>,
87        negated: bool,
88    },
89    Subquery(Box<QueryNode<'a>>),
90    Case {
91        operand: Option<Box<ExprNode<'a>>>,
92        when_then: Vec<(ExprNode<'a>, ExprNode<'a>)>,
93        else_result: Option<Box<ExprNode<'a>>>,
94    },
95}
96
97impl<'a> TryFrom<ExprNode<'a>> for Expr {
98    type Error = Error;
99
100    fn try_from(expr_node: ExprNode<'a>) -> Result<Self> {
101        match expr_node {
102            ExprNode::Expr(expr) => Ok(expr.into_owned()),
103            ExprNode::SqlExpr(expr) => {
104                let expr = parse_expr(expr)?;
105
106                translate_expr(&expr)
107            }
108            ExprNode::Identifier(value) => {
109                let idents = value.as_ref().split('.').collect::<Vec<_>>();
110
111                Ok(match idents.as_slice() {
112                    [alias, ident] => Expr::CompoundIdentifier {
113                        alias: alias.to_string(),
114                        ident: ident.to_string(),
115                    },
116                    _ => Expr::Identifier(value.into_owned()),
117                })
118            }
119            ExprNode::Numeric(node) => node.try_into().map(Expr::Literal),
120            ExprNode::QuotedString(value) => {
121                let value = value.into_owned();
122
123                Ok(Expr::Literal(AstLiteral::QuotedString(value)))
124            }
125            ExprNode::TypedString { data_type, value } => Ok(Expr::TypedString {
126                data_type,
127                value: value.into_owned(),
128            }),
129            ExprNode::Between {
130                expr,
131                negated,
132                low,
133                high,
134            } => {
135                let expr = Expr::try_from(*expr).map(Box::new)?;
136                let low = Expr::try_from(*low).map(Box::new)?;
137                let high = Expr::try_from(*high).map(Box::new)?;
138
139                Ok(Expr::Between {
140                    expr,
141                    negated,
142                    low,
143                    high,
144                })
145            }
146            ExprNode::Like {
147                expr,
148                negated,
149                pattern,
150            } => {
151                let expr = Expr::try_from(*expr).map(Box::new)?;
152                let pattern = Expr::try_from(*pattern).map(Box::new)?;
153
154                Ok(Expr::Like {
155                    expr,
156                    negated,
157                    pattern,
158                })
159            }
160            ExprNode::ILike {
161                expr,
162                negated,
163                pattern,
164            } => {
165                let expr = Expr::try_from(*expr).map(Box::new)?;
166                let pattern = Expr::try_from(*pattern).map(Box::new)?;
167
168                Ok(Expr::ILike {
169                    expr,
170                    negated,
171                    pattern,
172                })
173            }
174            ExprNode::BinaryOp { left, op, right } => {
175                let left = Expr::try_from(*left).map(Box::new)?;
176                let right = Expr::try_from(*right).map(Box::new)?;
177
178                Ok(Expr::BinaryOp { left, op, right })
179            }
180            ExprNode::UnaryOp { op, expr } => {
181                let expr = Expr::try_from(*expr).map(Box::new)?;
182                Ok(Expr::UnaryOp { op, expr })
183            }
184            ExprNode::IsNull(expr) => Expr::try_from(*expr).map(Box::new).map(Expr::IsNull),
185            ExprNode::IsNotNull(expr) => Expr::try_from(*expr).map(Box::new).map(Expr::IsNotNull),
186            ExprNode::InList {
187                expr,
188                list,
189                negated,
190            } => {
191                let expr = Expr::try_from(*expr).map(Box::new)?;
192
193                match *list {
194                    InListNode::InList(list) => {
195                        let list = list
196                            .into_iter()
197                            .map(Expr::try_from)
198                            .collect::<Result<Vec<_>>>()?;
199                        Ok(Expr::InList {
200                            expr,
201                            list,
202                            negated,
203                        })
204                    }
205                    InListNode::Query(subquery) => {
206                        let subquery = Query::try_from(*subquery).map(Box::new)?;
207                        Ok(Expr::InSubquery {
208                            expr,
209                            subquery,
210                            negated,
211                        })
212                    }
213                    InListNode::Text(value) => {
214                        let subquery = parse_query(value.clone())
215                            .and_then(|item| translate_query(&item))
216                            .map(Box::new);
217
218                        if let Ok(subquery) = subquery {
219                            return Ok(Expr::InSubquery {
220                                expr,
221                                subquery,
222                                negated,
223                            });
224                        }
225
226                        parse_comma_separated_exprs(&*value)?
227                            .iter()
228                            .map(translate_expr)
229                            .collect::<Result<Vec<_>>>()
230                            .map(|list| Expr::InList {
231                                expr,
232                                list,
233                                negated,
234                            })
235                    }
236                }
237            }
238            ExprNode::Nested(expr) => Expr::try_from(*expr).map(Box::new).map(Expr::Nested),
239            ExprNode::Function(func_expr) => Function::try_from(*func_expr)
240                .map(Box::new)
241                .map(Expr::Function),
242            ExprNode::Aggregate(aggr_expr) => Aggregate::try_from(*aggr_expr)
243                .map(Box::new)
244                .map(Expr::Aggregate),
245            ExprNode::Exists { subquery, negated } => Query::try_from(*subquery)
246                .map(Box::new)
247                .map(|subquery| Expr::Exists { subquery, negated }),
248            ExprNode::Subquery(subquery) => {
249                Query::try_from(*subquery).map(Box::new).map(Expr::Subquery)
250            }
251            ExprNode::Case {
252                operand,
253                when_then,
254                else_result,
255            } => {
256                let operand = operand
257                    .map(|expr| Expr::try_from(*expr))
258                    .transpose()?
259                    .map(Box::new);
260                let when_then = when_then
261                    .into_iter()
262                    .map(|(when, then)| {
263                        let when = Expr::try_from(when)?;
264                        let then = Expr::try_from(then)?;
265                        Ok((when, then))
266                    })
267                    .collect::<Result<Vec<_>>>()?;
268
269                let else_result = else_result
270                    .map(|expr| Expr::try_from(*expr))
271                    .transpose()?
272                    .map(Box::new);
273                Ok(Expr::Case {
274                    operand,
275                    when_then,
276                    else_result,
277                })
278            }
279        }
280    }
281}
282
283impl<'a> From<&'a str> for ExprNode<'a> {
284    fn from(expr: &'a str) -> Self {
285        ExprNode::SqlExpr(Cow::Borrowed(expr))
286    }
287}
288
289impl<'a> From<String> for ExprNode<'a> {
290    fn from(expr: String) -> Self {
291        ExprNode::SqlExpr(Cow::Owned(expr))
292    }
293}
294
295impl<'a> From<i64> for ExprNode<'a> {
296    fn from(n: i64) -> Self {
297        ExprNode::Expr(Cow::Owned(Expr::Literal(AstLiteral::Number(
298            BigDecimal::from(n),
299        ))))
300    }
301}
302
303impl<'a> From<bool> for ExprNode<'a> {
304    fn from(b: bool) -> Self {
305        ExprNode::Expr(Cow::Owned(Expr::Literal(AstLiteral::Boolean(b))))
306    }
307}
308
309impl<'a> From<QueryNode<'a>> for ExprNode<'a> {
310    fn from(node: QueryNode<'a>) -> Self {
311        ExprNode::Subquery(Box::new(node))
312    }
313}
314
315impl<'a> From<Expr> for ExprNode<'a> {
316    fn from(expr: Expr) -> Self {
317        ExprNode::Expr(Cow::Owned(expr))
318    }
319}
320
321impl<'a> From<&'a Expr> for ExprNode<'a> {
322    fn from(expr: &'a Expr) -> Self {
323        ExprNode::Expr(Cow::Borrowed(expr))
324    }
325}
326
327pub fn expr<'a, T: Into<Cow<'a, str>>>(value: T) -> ExprNode<'a> {
328    ExprNode::SqlExpr(value.into())
329}
330
331pub fn col<'a, T: Into<Cow<'a, str>>>(value: T) -> ExprNode<'a> {
332    ExprNode::Identifier(value.into())
333}
334
335pub fn num<'a, T: Into<NumericNode<'a>>>(value: T) -> ExprNode<'a> {
336    ExprNode::Numeric(value.into())
337}
338
339pub fn text<'a, T: Into<Cow<'a, str>>>(value: T) -> ExprNode<'a> {
340    ExprNode::QuotedString(value.into())
341}
342
343pub fn date<'a, T: Into<Cow<'a, str>>>(date: T) -> ExprNode<'a> {
344    ExprNode::TypedString {
345        data_type: DataType::Date,
346        value: date.into(),
347    }
348}
349
350pub fn timestamp<'a, T: Into<Cow<'a, str>>>(timestamp: T) -> ExprNode<'a> {
351    ExprNode::TypedString {
352        data_type: DataType::Timestamp,
353        value: timestamp.into(),
354    }
355}
356
357pub fn time<'a, T: Into<Cow<'a, str>>>(time: T) -> ExprNode<'a> {
358    ExprNode::TypedString {
359        data_type: DataType::Time,
360        value: time.into(),
361    }
362}
363
364pub fn uuid<'a, T: Into<Cow<'a, str>>>(uuid: T) -> ExprNode<'a> {
365    ExprNode::TypedString {
366        data_type: DataType::Uuid,
367        value: uuid.into(),
368    }
369}
370
371/// Returns an AST ExprNode containing the provided Bytea.
372///
373/// # Arguments
374/// * `bytea` - A byte array to be converted to a Bytea AST node.
375///
376pub fn bytea<'a, T: AsRef<[u8]>>(bytea: T) -> ExprNode<'a> {
377    ExprNode::TypedString {
378        data_type: DataType::Bytea,
379        value: hex::encode(bytea).into(),
380    }
381}
382
383pub fn subquery<'a, T: Into<QueryNode<'a>>>(query_node: T) -> ExprNode<'a> {
384    ExprNode::Subquery(Box::new(query_node.into()))
385}
386
387pub fn null() -> ExprNode<'static> {
388    ExprNode::Expr(Cow::Owned(Expr::Literal(AstLiteral::Null)))
389}
390
391#[cfg(test)]
392mod tests {
393    use {
394        super::ExprNode,
395        crate::{
396            ast::Expr,
397            ast_builder::{
398                QueryNode, bytea, col, date, expr, null, num, subquery, table, test_expr, text,
399                time, timestamp, uuid,
400            },
401        },
402    };
403
404    #[test]
405    fn into_expr_node() {
406        let actual: ExprNode = "id IS NOT NULL".into();
407        let expected = "id IS NOT NULL";
408        test_expr(actual, expected);
409
410        let actual: ExprNode = String::from("1 + 10)").into();
411        let expected = "1 + 10";
412        test_expr(actual, expected);
413
414        let actual: ExprNode = 1024.into();
415        let expected = "1024";
416        test_expr(actual, expected);
417
418        let actual: ExprNode = true.into();
419        let expected = "True";
420        test_expr(actual, expected);
421
422        let actual: ExprNode = QueryNode::from(table("Foo").select().project("id")).into();
423        let expected = "(SELECT id FROM Foo)";
424        test_expr(actual, expected);
425
426        let expr = Expr::Identifier("id".to_owned());
427        let actual: ExprNode = (&expr).into();
428        let expected = "id";
429        test_expr(actual, expected);
430
431        let actual: ExprNode = expr.into();
432        test_expr(actual, expected);
433    }
434
435    #[test]
436    fn syntactic_sugar() {
437        let actual = expr("col1 > 10");
438        let expected = "col1 > 10";
439        test_expr(actual, expected);
440
441        let actual = col("id");
442        let expected = "id";
443        test_expr(actual, expected);
444
445        let actual = col("Foo.id");
446        let expected = "Foo.id";
447        test_expr(actual, expected);
448
449        let actual = num(2048);
450        let expected = "2048";
451        test_expr(actual, expected);
452
453        let actual = num(6.5);
454        let expected = "6.5";
455        test_expr(actual, expected);
456
457        let actual = num("123.456");
458        let expected = "123.456";
459        test_expr(actual, expected);
460
461        let actual = text("hello world");
462        let expected = "'hello world'";
463        test_expr(actual, expected);
464
465        let actual = date("2022-10-11");
466        let expected = "DATE '2022-10-11'";
467        test_expr(actual, expected);
468
469        let actual = timestamp("2022-10-11 13:34:49");
470        let expected = "TIMESTAMP '2022-10-11 13:34:49'";
471        test_expr(actual, expected);
472
473        let actual = time("15:00:07");
474        let expected = "TIME '15:00:07'";
475        test_expr(actual, expected);
476
477        let actual = uuid("936DA01F9ABD4d9d80C702AF85C822A8");
478        let expected = "UUID '936DA01F9ABD4d9d80C702AF85C822A8'";
479        test_expr(actual, expected);
480
481        let actual = bytea(b"hello world");
482        let expected = "BYTEA '68656c6c6f20776f726c64'";
483        test_expr(actual, expected);
484
485        let actual = subquery(table("Foo").select().filter("id IS NOT NULL"));
486        let expected = "(SELECT * FROM Foo WHERE id IS NOT NULL)";
487        test_expr(actual, expected);
488
489        let actual = null();
490        let expected = "NULL";
491        test_expr(actual, expected);
492    }
493}