grafbase_sql_ast/ast/
compare.rs

1use super::ExpressionKind;
2use crate::ast::{Column, ConditionTree, Expression};
3use std::borrow::Cow;
4
5/// For modeling comparison expressions.
6#[derive(Debug, Clone, PartialEq)]
7pub enum Compare<'a> {
8    /// `left = right`
9    Equals(Box<Expression<'a>>, Box<Expression<'a>>),
10    /// `left <> right`
11    NotEquals(Box<Expression<'a>>, Box<Expression<'a>>),
12    /// `left < right`
13    LessThan(Box<Expression<'a>>, Box<Expression<'a>>),
14    /// `left <= right`
15    LessThanOrEquals(Box<Expression<'a>>, Box<Expression<'a>>),
16    /// `left > right`
17    GreaterThan(Box<Expression<'a>>, Box<Expression<'a>>),
18    /// `left >= right`
19    GreaterThanOrEquals(Box<Expression<'a>>, Box<Expression<'a>>),
20    /// `left IN (..)`
21    In(Box<Expression<'a>>, Box<Expression<'a>>),
22    /// `left NOT IN (..)`
23    NotIn(Box<Expression<'a>>, Box<Expression<'a>>),
24    /// `left LIKE %..%`
25    Like(Box<Expression<'a>>, Box<Expression<'a>>),
26    /// `left NOT LIKE %..%`
27    NotLike(Box<Expression<'a>>, Box<Expression<'a>>),
28    /// `value IS NULL`
29    Null(Box<Expression<'a>>),
30    /// `value IS NOT NULL`
31    NotNull(Box<Expression<'a>>),
32    /// `value` BETWEEN `left` AND `right`
33    Between(
34        Box<Expression<'a>>,
35        Box<Expression<'a>>,
36        Box<Expression<'a>>,
37    ),
38    /// `value` NOT BETWEEN `left` AND `right`
39    NotBetween(
40        Box<Expression<'a>>,
41        Box<Expression<'a>>,
42        Box<Expression<'a>>,
43    ),
44    /// Raw comparator, allows to use an operator `left <raw> right` as is,
45    /// without visitor transformation in between.
46    Raw(Box<Expression<'a>>, Cow<'a, str>, Box<Expression<'a>>),
47    /// All json related comparators
48    #[cfg(any(feature = "postgresql", feature = "mysql"))]
49    JsonCompare(JsonCompare<'a>),
50    /// ANY (`left`)
51    #[cfg(feature = "postgresql")]
52    Any(Box<Expression<'a>>),
53    /// ALL (`left`)
54    #[cfg(feature = "postgresql")]
55    All(Box<Expression<'a>>),
56}
57
58#[derive(Debug, Clone, PartialEq)]
59pub enum JsonCompare<'a> {
60    ArrayOverlaps(Box<Expression<'a>>, Box<Expression<'a>>),
61    ArrayContains(Box<Expression<'a>>, Box<Expression<'a>>),
62    ArrayContained(Box<Expression<'a>>, Box<Expression<'a>>),
63    ArrayNotContains(Box<Expression<'a>>, Box<Expression<'a>>),
64    TypeEquals(Box<Expression<'a>>, JsonType<'a>),
65    TypeNotEquals(Box<Expression<'a>>, JsonType<'a>),
66}
67
68#[derive(Debug, Clone, PartialEq)]
69pub enum JsonType<'a> {
70    Array,
71    Object,
72    String,
73    Number,
74    Boolean,
75    Null,
76    ColumnRef(Box<Column<'a>>),
77}
78
79impl<'a> From<Column<'a>> for JsonType<'a> {
80    fn from(col: Column<'a>) -> Self {
81        JsonType::ColumnRef(Box::new(col))
82    }
83}
84
85impl<'a> From<Compare<'a>> for ConditionTree<'a> {
86    fn from(cmp: Compare<'a>) -> Self {
87        ConditionTree::single(Expression::from(cmp))
88    }
89}
90
91impl<'a> From<Compare<'a>> for Expression<'a> {
92    fn from(cmp: Compare<'a>) -> Self {
93        Expression {
94            kind: ExpressionKind::Compare(cmp),
95            alias: None,
96        }
97    }
98}
99
100/// An item that can be compared against other values in the database.
101pub trait Comparable<'a> {
102    /// Tests if both sides are the same value.
103    fn equals<T>(self, comparison: T) -> Compare<'a>
104    where
105        T: Into<Expression<'a>>;
106
107    /// Tests if both sides are not the same value.
108    fn not_equals<T>(self, comparison: T) -> Compare<'a>
109    where
110        T: Into<Expression<'a>>;
111
112    /// Tests if the left side is smaller than the right side.
113    fn less_than<T>(self, comparison: T) -> Compare<'a>
114    where
115        T: Into<Expression<'a>>;
116
117    /// Tests if the left side is smaller than the right side or the same.
118    fn less_than_or_equals<T>(self, comparison: T) -> Compare<'a>
119    where
120        T: Into<Expression<'a>>;
121
122    /// Tests if the left side is bigger than the right side.
123    fn greater_than<T>(self, comparison: T) -> Compare<'a>
124    where
125        T: Into<Expression<'a>>;
126
127    /// Tests if the left side is bigger than the right side or the same.
128    fn greater_than_or_equals<T>(self, comparison: T) -> Compare<'a>
129    where
130        T: Into<Expression<'a>>;
131
132    /// Tests if the left side is included in the right side collection.
133    fn in_selection<T>(self, selection: T) -> Compare<'a>
134    where
135        T: Into<Expression<'a>>;
136
137    /// Tests if the left side is not included in the right side collection.
138    fn not_in_selection<T>(self, selection: T) -> Compare<'a>
139    where
140        T: Into<Expression<'a>>;
141
142    /// Tests if the left side includes the right side string.
143    fn like<T>(self, pattern: T) -> Compare<'a>
144    where
145        T: Into<Expression<'a>>;
146
147    /// Tests if the left side does not include the right side string.
148    fn not_like<T>(self, pattern: T) -> Compare<'a>
149    where
150        T: Into<Expression<'a>>;
151
152    /// Tests if the left side is `NULL`.
153    #[allow(clippy::wrong_self_convention)]
154    fn is_null(self) -> Compare<'a>;
155
156    /// Tests if the left side is not `NULL`.
157    #[allow(clippy::wrong_self_convention)]
158    fn is_not_null(self) -> Compare<'a>;
159
160    /// Tests if the value is between two given values.
161    fn between<T, V>(self, left: T, right: V) -> Compare<'a>
162    where
163        T: Into<Expression<'a>>,
164        V: Into<Expression<'a>>;
165
166    /// Tests if the value is not between two given values.
167    fn not_between<T, V>(self, left: T, right: V) -> Compare<'a>
168    where
169        T: Into<Expression<'a>>,
170        V: Into<Expression<'a>>;
171
172    /// Tests if the array overlaps with another array.
173    #[cfg(any(feature = "postgresql", feature = "mysql"))]
174    fn array_overlaps<T>(self, item: T) -> Compare<'a>
175    where
176        T: Into<Expression<'a>>;
177
178    /// Tests if the array contains another array.
179    #[cfg(any(feature = "postgresql", feature = "mysql"))]
180    fn array_contains<T>(self, item: T) -> Compare<'a>
181    where
182        T: Into<Expression<'a>>;
183
184    /// Tests if the JSON array contains a value.
185    #[cfg(any(feature = "postgresql", feature = "mysql"))]
186    fn array_contained<T>(self, item: T) -> Compare<'a>
187    where
188        T: Into<Expression<'a>>;
189
190    /// Tests if the JSON array does not contain a value.
191    #[cfg(any(feature = "postgresql", feature = "mysql"))]
192    fn json_array_not_contains<T>(self, item: T) -> Compare<'a>
193    where
194        T: Into<Expression<'a>>;
195
196    /// Tests if the JSON array starts with a value.
197    #[cfg(any(feature = "postgresql", feature = "mysql"))]
198    fn json_array_begins_with<T>(self, item: T) -> Compare<'a>
199    where
200        T: Into<Expression<'a>>;
201
202    /// Tests if the JSON array does not start with a value.
203    #[cfg(any(feature = "postgresql", feature = "mysql"))]
204    fn json_array_not_begins_with<T>(self, item: T) -> Compare<'a>
205    where
206        T: Into<Expression<'a>>;
207
208    /// Tests if the JSON array ends with a value.
209    #[cfg(any(feature = "postgresql", feature = "mysql"))]
210    fn json_array_ends_into<T>(self, item: T) -> Compare<'a>
211    where
212        T: Into<Expression<'a>>;
213
214    /// Tests if the JSON array does not end with a value.
215    #[cfg(any(feature = "postgresql", feature = "mysql"))]
216    fn json_array_not_ends_into<T>(self, item: T) -> Compare<'a>
217    where
218        T: Into<Expression<'a>>;
219
220    /// Tests if the JSON value is of a certain type.
221    #[cfg(any(feature = "postgresql", feature = "mysql"))]
222    fn json_type_equals<T>(self, json_type: T) -> Compare<'a>
223    where
224        T: Into<JsonType<'a>>;
225
226    /// Tests if the JSON value is not of a certain type.
227    #[cfg(any(feature = "postgresql", feature = "mysql"))]
228    fn json_type_not_equals<T>(self, json_type: T) -> Compare<'a>
229    where
230        T: Into<JsonType<'a>>;
231
232    /// Matches at least one elem of a list of values.
233    #[cfg(feature = "postgresql")]
234    fn any(self) -> Compare<'a>;
235
236    /// Matches all elem of a list of values.
237    #[cfg(feature = "postgresql")]
238    fn all(self) -> Compare<'a>;
239
240    /// Compares two expressions with a custom operator.
241    fn compare_raw<T, V>(self, raw_comparator: T, right: V) -> Compare<'a>
242    where
243        T: Into<Cow<'a, str>>,
244        V: Into<Expression<'a>>;
245}
246
247impl<'a, U> Comparable<'a> for U
248where
249    U: Into<Column<'a>>,
250{
251    fn equals<T>(self, comparison: T) -> Compare<'a>
252    where
253        T: Into<Expression<'a>>,
254    {
255        let col: Column<'a> = self.into();
256        let val: Expression<'a> = col.into();
257
258        val.equals(comparison)
259    }
260
261    fn not_equals<T>(self, comparison: T) -> Compare<'a>
262    where
263        T: Into<Expression<'a>>,
264    {
265        let col: Column<'a> = self.into();
266        let val: Expression<'a> = col.into();
267        val.not_equals(comparison)
268    }
269
270    fn less_than<T>(self, comparison: T) -> Compare<'a>
271    where
272        T: Into<Expression<'a>>,
273    {
274        let col: Column<'a> = self.into();
275        let val: Expression<'a> = col.into();
276        val.less_than(comparison)
277    }
278
279    fn less_than_or_equals<T>(self, comparison: T) -> Compare<'a>
280    where
281        T: Into<Expression<'a>>,
282    {
283        let col: Column<'a> = self.into();
284        let val: Expression<'a> = col.into();
285        val.less_than_or_equals(comparison)
286    }
287
288    fn greater_than<T>(self, comparison: T) -> Compare<'a>
289    where
290        T: Into<Expression<'a>>,
291    {
292        let col: Column<'a> = self.into();
293        let val: Expression<'a> = col.into();
294        val.greater_than(comparison)
295    }
296
297    fn greater_than_or_equals<T>(self, comparison: T) -> Compare<'a>
298    where
299        T: Into<Expression<'a>>,
300    {
301        let col: Column<'a> = self.into();
302        let val: Expression<'a> = col.into();
303        val.greater_than_or_equals(comparison)
304    }
305
306    fn in_selection<T>(self, selection: T) -> Compare<'a>
307    where
308        T: Into<Expression<'a>>,
309    {
310        let col: Column<'a> = self.into();
311        let val: Expression<'a> = col.into();
312        val.in_selection(selection)
313    }
314
315    fn not_in_selection<T>(self, selection: T) -> Compare<'a>
316    where
317        T: Into<Expression<'a>>,
318    {
319        let col: Column<'a> = self.into();
320        let val: Expression<'a> = col.into();
321        val.not_in_selection(selection)
322    }
323
324    fn like<T>(self, pattern: T) -> Compare<'a>
325    where
326        T: Into<Expression<'a>>,
327    {
328        let col: Column<'a> = self.into();
329        let val: Expression<'a> = col.into();
330        val.like(pattern)
331    }
332
333    fn not_like<T>(self, pattern: T) -> Compare<'a>
334    where
335        T: Into<Expression<'a>>,
336    {
337        let col: Column<'a> = self.into();
338        let val: Expression<'a> = col.into();
339        val.not_like(pattern)
340    }
341
342    #[allow(clippy::wrong_self_convention)]
343    fn is_null(self) -> Compare<'a> {
344        let col: Column<'a> = self.into();
345        let val: Expression<'a> = col.into();
346        val.is_null()
347    }
348
349    #[allow(clippy::wrong_self_convention)]
350    fn is_not_null(self) -> Compare<'a> {
351        let col: Column<'a> = self.into();
352        let val: Expression<'a> = col.into();
353        val.is_not_null()
354    }
355
356    fn between<T, V>(self, left: T, right: V) -> Compare<'a>
357    where
358        T: Into<Expression<'a>>,
359        V: Into<Expression<'a>>,
360    {
361        let col: Column<'a> = self.into();
362        let val: Expression<'a> = col.into();
363        val.between(left, right)
364    }
365
366    fn not_between<T, V>(self, left: T, right: V) -> Compare<'a>
367    where
368        T: Into<Expression<'a>>,
369        V: Into<Expression<'a>>,
370    {
371        let col: Column<'a> = self.into();
372        let val: Expression<'a> = col.into();
373        val.not_between(left, right)
374    }
375
376    fn compare_raw<T, V>(self, raw_comparator: T, right: V) -> Compare<'a>
377    where
378        T: Into<Cow<'a, str>>,
379        V: Into<Expression<'a>>,
380    {
381        let left: Column<'a> = self.into();
382        let left: Expression<'a> = left.into();
383        let right: Expression<'a> = right.into();
384
385        left.compare_raw(raw_comparator.into(), right)
386    }
387
388    #[cfg(any(feature = "postgresql", feature = "mysql"))]
389    fn array_overlaps<T>(self, item: T) -> Compare<'a>
390    where
391        T: Into<Expression<'a>>,
392    {
393        let col: Column<'a> = self.into();
394        let val: Expression<'a> = col.into();
395
396        val.array_overlaps(item)
397    }
398
399    #[cfg(any(feature = "postgresql", feature = "mysql"))]
400    fn array_contains<T>(self, item: T) -> Compare<'a>
401    where
402        T: Into<Expression<'a>>,
403    {
404        let col: Column<'a> = self.into();
405        let val: Expression<'a> = col.into();
406
407        val.array_contains(item)
408    }
409
410    #[cfg(any(feature = "postgresql", feature = "mysql"))]
411    fn array_contained<T>(self, item: T) -> Compare<'a>
412    where
413        T: Into<Expression<'a>>,
414    {
415        let col: Column<'a> = self.into();
416        let val: Expression<'a> = col.into();
417
418        val.array_contained(item)
419    }
420
421    #[cfg(any(feature = "postgresql", feature = "mysql"))]
422    fn json_array_not_contains<T>(self, item: T) -> Compare<'a>
423    where
424        T: Into<Expression<'a>>,
425    {
426        let col: Column<'a> = self.into();
427        let val: Expression<'a> = col.into();
428
429        val.json_array_not_contains(item)
430    }
431
432    #[cfg(any(feature = "postgresql", feature = "mysql"))]
433    fn json_array_begins_with<T>(self, item: T) -> Compare<'a>
434    where
435        T: Into<Expression<'a>>,
436    {
437        let col: Column<'a> = self.into();
438        let val: Expression<'a> = col.into();
439
440        val.json_array_begins_with(item)
441    }
442
443    #[cfg(any(feature = "postgresql", feature = "mysql"))]
444    fn json_array_not_begins_with<T>(self, item: T) -> Compare<'a>
445    where
446        T: Into<Expression<'a>>,
447    {
448        let col: Column<'a> = self.into();
449        let val: Expression<'a> = col.into();
450
451        val.json_array_not_begins_with(item)
452    }
453
454    #[cfg(any(feature = "postgresql", feature = "mysql"))]
455    fn json_array_ends_into<T>(self, item: T) -> Compare<'a>
456    where
457        T: Into<Expression<'a>>,
458    {
459        let col: Column<'a> = self.into();
460        let val: Expression<'a> = col.into();
461
462        val.json_array_ends_into(item)
463    }
464
465    #[cfg(any(feature = "postgresql", feature = "mysql"))]
466    fn json_array_not_ends_into<T>(self, item: T) -> Compare<'a>
467    where
468        T: Into<Expression<'a>>,
469    {
470        let col: Column<'a> = self.into();
471        let val: Expression<'a> = col.into();
472
473        val.json_array_not_ends_into(item)
474    }
475
476    #[cfg(any(feature = "postgresql", feature = "mysql"))]
477    fn json_type_equals<T>(self, json_type: T) -> Compare<'a>
478    where
479        T: Into<JsonType<'a>>,
480    {
481        let col: Column<'a> = self.into();
482        let val: Expression<'a> = col.into();
483
484        val.json_type_equals(json_type)
485    }
486
487    #[cfg(any(feature = "postgresql", feature = "mysql"))]
488    fn json_type_not_equals<T>(self, json_type: T) -> Compare<'a>
489    where
490        T: Into<JsonType<'a>>,
491    {
492        let col: Column<'a> = self.into();
493        let val: Expression<'a> = col.into();
494
495        val.json_type_not_equals(json_type)
496    }
497
498    #[cfg(feature = "postgresql")]
499    fn any(self) -> Compare<'a> {
500        let col: Column<'a> = self.into();
501        let val: Expression<'a> = col.into();
502
503        val.any()
504    }
505
506    #[cfg(feature = "postgresql")]
507    fn all(self) -> Compare<'a> {
508        let col: Column<'a> = self.into();
509        let val: Expression<'a> = col.into();
510
511        val.all()
512    }
513}