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