grafbase_sql_ast/ast/
expression.rs

1use serde_json::Value;
2
3use crate::ast::{
4    Aliasable, Column, Comparable, Compare, ConditionTree, Function, Row, Select, SqlOp, Table,
5    Values,
6};
7
8#[cfg(any(feature = "postgresql", feature = "mysql"))]
9use super::compare::{JsonCompare, JsonType};
10use std::borrow::Cow;
11
12/// An expression that can be positioned in a query. Can be a single value or a
13/// statement that is evaluated into a value.
14#[derive(Debug, Clone, PartialEq)]
15pub struct Expression<'a> {
16    pub(crate) kind: ExpressionKind<'a>,
17    pub(crate) alias: Option<Cow<'a, str>>,
18}
19
20impl<'a> Expression<'a> {
21    /// The type of the expression, dictates how it's implemented in the query.
22    pub fn kind(&self) -> &ExpressionKind<'a> {
23        &self.kind
24    }
25}
26
27/// An expression we can compare and use in database queries.
28#[derive(Debug, Clone, PartialEq)]
29pub enum ExpressionKind<'a> {
30    /// Anything that we must parameterize before querying
31    Parameterized(Value),
32    /// Will be rendered as-is to the SQL statement. Carefully escape, if needed.
33    Raw(&'a str),
34    /// A database column
35    Column(Box<Column<'a>>),
36    /// A database column
37    Table(Box<Table<'a>>),
38    /// Data in a row form, e.g. (1, 2, 3)
39    Row(Row<'a>),
40    /// A nested `SELECT` or `SELECT .. UNION` statement
41    Selection(Box<Select<'a>>),
42    /// A database function call
43    Function(Box<Function<'a>>),
44    /// A qualified asterisk to a table
45    Asterisk(Option<Box<Table<'a>>>),
46    /// An operation: sum, sub, mul or div.
47    Op(Box<SqlOp<'a>>),
48    /// A tree of expressions to evaluate from the deepest value to up
49    ConditionTree(ConditionTree<'a>),
50    /// A comparison expression
51    Compare(Compare<'a>),
52    /// A single value, column, row or a nested select
53    Value(Box<Expression<'a>>),
54    /// Multiple values
55    Values(Values<'a>),
56    /// DEFAULT keyword, e.g. for `INSERT INTO ... VALUES (..., DEFAULT, ...)`
57    Default,
58}
59
60/// A quick alias to create a raw value expression.
61pub fn raw(value: &str) -> Expression<'_> {
62    Expression {
63        kind: ExpressionKind::Raw(value),
64        alias: None,
65    }
66}
67
68/// A quick alias to create an asterisk to a table.
69pub fn asterisk() -> Expression<'static> {
70    Expression {
71        kind: ExpressionKind::Asterisk(None),
72        alias: None,
73    }
74}
75
76/// A quick alias to create a default value expression.
77pub fn default_value() -> Expression<'static> {
78    Expression {
79        kind: ExpressionKind::Default,
80        alias: None,
81    }
82}
83
84impl<'a> From<Function<'a>> for Expression<'a> {
85    fn from(f: Function<'a>) -> Self {
86        Expression {
87            kind: ExpressionKind::Function(Box::new(f)),
88            alias: None,
89        }
90    }
91}
92
93impl<'a> From<SqlOp<'a>> for Expression<'a> {
94    fn from(p: SqlOp<'a>) -> Self {
95        Expression {
96            kind: ExpressionKind::Op(Box::new(p)),
97            alias: None,
98        }
99    }
100}
101
102impl<'a> From<Values<'a>> for Expression<'a> {
103    fn from(value: Values<'a>) -> Self {
104        Expression {
105            kind: ExpressionKind::Values(value),
106            alias: None,
107        }
108    }
109}
110
111impl<'a, T> From<T> for Expression<'a>
112where
113    T: Into<Value>,
114{
115    fn from(p: T) -> Self {
116        Expression {
117            kind: ExpressionKind::Parameterized(p.into()),
118            alias: None,
119        }
120    }
121}
122
123impl<'a> From<Row<'a>> for Expression<'a> {
124    fn from(value: Row<'a>) -> Self {
125        Expression {
126            kind: ExpressionKind::Row(value),
127            alias: None,
128        }
129    }
130}
131
132impl<'a> From<Table<'a>> for Expression<'a> {
133    fn from(value: Table<'a>) -> Self {
134        Self {
135            kind: ExpressionKind::Table(Box::new(value)),
136            alias: None,
137        }
138    }
139}
140
141impl<'a> From<ExpressionKind<'a>> for Expression<'a> {
142    fn from(kind: ExpressionKind<'a>) -> Self {
143        Self { kind, alias: None }
144    }
145}
146
147impl<'a> Aliasable<'a> for Expression<'a> {
148    type Target = Expression<'a>;
149
150    fn alias<T>(mut self, alias: T) -> Self::Target
151    where
152        T: Into<Cow<'a, str>>,
153    {
154        self.alias = Some(alias.into());
155        self
156    }
157}
158
159impl<'a> Comparable<'a> for Expression<'a> {
160    fn equals<T>(self, comparison: T) -> Compare<'a>
161    where
162        T: Into<Expression<'a>>,
163    {
164        Compare::Equals(Box::new(self), Box::new(comparison.into()))
165    }
166
167    fn not_equals<T>(self, comparison: T) -> Compare<'a>
168    where
169        T: Into<Expression<'a>>,
170    {
171        Compare::NotEquals(Box::new(self), Box::new(comparison.into()))
172    }
173
174    fn less_than<T>(self, comparison: T) -> Compare<'a>
175    where
176        T: Into<Expression<'a>>,
177    {
178        Compare::LessThan(Box::new(self), Box::new(comparison.into()))
179    }
180
181    fn less_than_or_equals<T>(self, comparison: T) -> Compare<'a>
182    where
183        T: Into<Expression<'a>>,
184    {
185        Compare::LessThanOrEquals(Box::new(self), Box::new(comparison.into()))
186    }
187
188    fn greater_than<T>(self, comparison: T) -> Compare<'a>
189    where
190        T: Into<Expression<'a>>,
191    {
192        Compare::GreaterThan(Box::new(self), Box::new(comparison.into()))
193    }
194
195    fn greater_than_or_equals<T>(self, comparison: T) -> Compare<'a>
196    where
197        T: Into<Expression<'a>>,
198    {
199        Compare::GreaterThanOrEquals(Box::new(self), Box::new(comparison.into()))
200    }
201
202    fn in_selection<T>(self, selection: T) -> Compare<'a>
203    where
204        T: Into<Expression<'a>>,
205    {
206        Compare::In(Box::new(self), Box::new(selection.into()))
207    }
208
209    fn not_in_selection<T>(self, selection: T) -> Compare<'a>
210    where
211        T: Into<Expression<'a>>,
212    {
213        Compare::NotIn(Box::new(self), Box::new(selection.into()))
214    }
215
216    fn like<T>(self, pattern: T) -> Compare<'a>
217    where
218        T: Into<Expression<'a>>,
219    {
220        Compare::Like(Box::new(self), Box::new(pattern.into()))
221    }
222
223    fn not_like<T>(self, pattern: T) -> Compare<'a>
224    where
225        T: Into<Expression<'a>>,
226    {
227        Compare::NotLike(Box::new(self), Box::new(pattern.into()))
228    }
229
230    #[allow(clippy::wrong_self_convention)]
231    fn is_null(self) -> Compare<'a> {
232        Compare::Null(Box::new(self))
233    }
234
235    #[allow(clippy::wrong_self_convention)]
236    fn is_not_null(self) -> Compare<'a> {
237        Compare::NotNull(Box::new(self))
238    }
239
240    fn between<T, V>(self, left: T, right: V) -> Compare<'a>
241    where
242        T: Into<Expression<'a>>,
243        V: Into<Expression<'a>>,
244    {
245        Compare::Between(
246            Box::new(self),
247            Box::new(left.into()),
248            Box::new(right.into()),
249        )
250    }
251
252    fn not_between<T, V>(self, left: T, right: V) -> Compare<'a>
253    where
254        T: Into<Expression<'a>>,
255        V: Into<Expression<'a>>,
256    {
257        Compare::NotBetween(
258            Box::new(self),
259            Box::new(left.into()),
260            Box::new(right.into()),
261        )
262    }
263
264    fn compare_raw<T, V>(self, raw_comparator: T, right: V) -> Compare<'a>
265    where
266        T: Into<Cow<'a, str>>,
267        V: Into<Expression<'a>>,
268    {
269        Compare::Raw(
270            Box::new(self),
271            raw_comparator.into(),
272            Box::new(right.into()),
273        )
274    }
275
276    #[cfg(all(feature = "postgresql", feature = "mysql"))]
277    fn array_contains<T>(self, item: T) -> Compare<'a>
278    where
279        T: Into<Expression<'a>>,
280    {
281        Compare::JsonCompare(JsonCompare::ArrayContains(
282            Box::new(self),
283            Box::new(item.into()),
284        ))
285    }
286
287    #[cfg(all(feature = "postgresql", feature = "mysql"))]
288    fn array_contained<T>(self, item: T) -> Compare<'a>
289    where
290        T: Into<Expression<'a>>,
291    {
292        Compare::JsonCompare(JsonCompare::ArrayContained(
293            Box::new(self),
294            Box::new(item.into()),
295        ))
296    }
297
298    #[cfg(all(feature = "postgresql", feature = "mysql"))]
299    fn array_overlaps<T>(self, item: T) -> Compare<'a>
300    where
301        T: Into<Expression<'a>>,
302    {
303        Compare::JsonCompare(JsonCompare::ArrayOverlaps(
304            Box::new(self),
305            Box::new(item.into()),
306        ))
307    }
308
309    #[cfg(all(feature = "postgresql", feature = "mysql"))]
310    fn json_array_not_contains<T>(self, item: T) -> Compare<'a>
311    where
312        T: Into<Expression<'a>>,
313    {
314        Compare::JsonCompare(JsonCompare::ArrayNotContains(
315            Box::new(self),
316            Box::new(item.into()),
317        ))
318    }
319
320    #[cfg(all(feature = "postgresql", feature = "mysql"))]
321    fn json_array_begins_with<T>(self, item: T) -> Compare<'a>
322    where
323        T: Into<Expression<'a>>,
324    {
325        let array_starts_with: Expression =
326            super::function::json_extract_first_array_elem(self).into();
327
328        Compare::Equals(Box::new(array_starts_with), Box::new(item.into()))
329    }
330
331    #[cfg(all(feature = "postgresql", feature = "mysql"))]
332    fn json_array_not_begins_with<T>(self, item: T) -> Compare<'a>
333    where
334        T: Into<Expression<'a>>,
335    {
336        let array_starts_with: Expression =
337            super::function::json_extract_first_array_elem(self).into();
338
339        Compare::NotEquals(Box::new(array_starts_with), Box::new(item.into()))
340    }
341
342    #[cfg(all(feature = "postgresql", feature = "mysql"))]
343    fn json_array_ends_into<T>(self, item: T) -> Compare<'a>
344    where
345        T: Into<Expression<'a>>,
346    {
347        let array_ends_into: Expression =
348            super::function::json_extract_last_array_elem(self).into();
349
350        Compare::Equals(Box::new(array_ends_into), Box::new(item.into()))
351    }
352
353    #[cfg(all(feature = "postgresql", feature = "mysql"))]
354    fn json_array_not_ends_into<T>(self, item: T) -> Compare<'a>
355    where
356        T: Into<Expression<'a>>,
357    {
358        let array_ends_into: Expression =
359            super::function::json_extract_last_array_elem(self).into();
360
361        Compare::NotEquals(Box::new(array_ends_into), Box::new(item.into()))
362    }
363
364    #[cfg(all(feature = "postgresql", feature = "mysql"))]
365    fn json_type_equals<T>(self, json_type: T) -> Compare<'a>
366    where
367        T: Into<JsonType<'a>>,
368    {
369        Compare::JsonCompare(JsonCompare::TypeEquals(Box::new(self), json_type.into()))
370    }
371
372    #[cfg(all(feature = "postgresql", feature = "mysql"))]
373    fn json_type_not_equals<T>(self, json_type: T) -> Compare<'a>
374    where
375        T: Into<JsonType<'a>>,
376    {
377        Compare::JsonCompare(JsonCompare::TypeNotEquals(Box::new(self), json_type.into()))
378    }
379
380    #[cfg(feature = "postgresql")]
381    fn any(self) -> Compare<'a> {
382        Compare::Any(Box::new(self))
383    }
384
385    #[cfg(feature = "postgresql")]
386    fn all(self) -> Compare<'a> {
387        Compare::All(Box::new(self))
388    }
389}