rust_query/value/
operations.rs

1use sea_query::{Alias, ExprTrait, extension::sqlite::SqliteExpr};
2
3use crate::{
4    ast::CONST_0,
5    value::{BuffTyp, MyTyp},
6};
7
8use super::{EqTyp, Expr, IntoExpr, NumTyp};
9
10impl<'column, S, T: NumTyp> Expr<'column, S, T> {
11    /// Add two expressions together.
12    ///
13    /// ```
14    /// # use rust_query::IntoExpr;
15    /// # rust_query::private::doctest::get_txn(|txn| {
16    /// assert_eq!(txn.query_one(1.into_expr().add(2)), 3);
17    /// assert_eq!(txn.query_one(1.0.into_expr().add(2.0)), 3.0);
18    /// # });
19    /// ```
20    pub fn add(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
21        let lhs = self.inner.clone();
22        let rhs = rhs.into_expr().inner;
23        Expr::adhoc(move |b| lhs.build_expr(b).add(rhs.build_expr(b)))
24    }
25
26    /// Subtract one expression from another.
27    ///
28    /// ```
29    /// # use rust_query::IntoExpr;
30    /// # rust_query::private::doctest::get_txn(|txn| {
31    /// assert_eq!(txn.query_one(1.into_expr().sub(2)), -1);
32    /// assert_eq!(txn.query_one(1.0.into_expr().sub(2.0)), -1.0);
33    /// # });
34    /// ```
35    pub fn sub(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
36        let lhs = self.inner.clone();
37        let rhs = rhs.into_expr().inner;
38        Expr::adhoc(move |b| lhs.build_expr(b).sub(rhs.build_expr(b)))
39    }
40
41    /// Multiply two expressions together.
42    ///
43    /// ```
44    /// # use rust_query::IntoExpr;
45    /// # rust_query::private::doctest::get_txn(|txn| {
46    /// assert_eq!(txn.query_one(2.into_expr().mul(3)), 6);
47    /// assert_eq!(txn.query_one(2.0.into_expr().mul(3.0)), 6.0);
48    /// # });
49    /// ```
50    pub fn mul(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
51        let lhs = self.inner.clone();
52        let rhs = rhs.into_expr().inner;
53        Expr::adhoc(move |b| lhs.build_expr(b).mul(rhs.build_expr(b)))
54    }
55
56    /// Divide one expression by another.
57    ///
58    /// For integers, the result is truncated towards zero.
59    /// See also [Expr::modulo].
60    ///
61    /// ```
62    /// # use rust_query::IntoExpr;
63    /// # rust_query::private::doctest::get_txn(|txn| {
64    /// assert_eq!(txn.query_one(5.into_expr().div(3)), 1);
65    /// assert_eq!(txn.query_one((-5).into_expr().div(3)), -1);
66    /// assert_eq!(txn.query_one(1.0.into_expr().div(2.0)), 0.5);
67    /// # });
68    /// ```
69    pub fn div(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
70        let lhs = self.inner.clone();
71        let rhs = rhs.into_expr().inner;
72        Expr::adhoc(move |b| lhs.build_expr(b).div(rhs.build_expr(b)))
73    }
74
75    /// Compute the less than operator (<) of two expressions.
76    ///
77    /// ```
78    /// # use rust_query::IntoExpr;
79    /// # rust_query::private::doctest::get_txn(|txn| {
80    /// assert_eq!(txn.query_one(2.into_expr().lt(3)), true);
81    /// assert_eq!(txn.query_one(1.into_expr().lt(1)), false);
82    /// assert_eq!(txn.query_one(3.0.into_expr().lt(1.0)), false);
83    /// # });
84    /// ```
85    pub fn lt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
86        let lhs = self.inner.clone();
87        let rhs = rhs.into_expr().inner;
88        Expr::adhoc(move |b| lhs.build_expr(b).lt(rhs.build_expr(b)))
89    }
90
91    /// Compute the less than or equal operator (<=) of two expressions.
92    ///
93    /// ```
94    /// # use rust_query::IntoExpr;
95    /// # rust_query::private::doctest::get_txn(|txn| {
96    /// assert_eq!(txn.query_one(2.into_expr().lte(2)), true);
97    /// assert_eq!(txn.query_one(3.0.into_expr().lte(1.0)), false);
98    /// # });
99    /// ```
100    pub fn lte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
101        let lhs = self.inner.clone();
102        let rhs = rhs.into_expr().inner;
103        Expr::adhoc(move |b| lhs.build_expr(b).lte(rhs.build_expr(b)))
104    }
105
106    /// Compute the greater than operator (>) of two expressions.
107    ///
108    /// ```
109    /// # use rust_query::IntoExpr;
110    /// # rust_query::private::doctest::get_txn(|txn| {
111    /// assert_eq!(txn.query_one(2.into_expr().gt(2)), false);
112    /// assert_eq!(txn.query_one(3.0.into_expr().gt(1.0)), true);
113    /// # });
114    /// ```
115    pub fn gt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
116        let lhs = self.inner.clone();
117        let rhs = rhs.into_expr().inner;
118        Expr::adhoc(move |b| lhs.build_expr(b).gt(rhs.build_expr(b)))
119    }
120
121    /// Compute the greater than or equal (>=) operator of two expressions.
122    ///
123    /// ```
124    /// # use rust_query::IntoExpr;
125    /// # rust_query::private::doctest::get_txn(|txn| {
126    /// assert_eq!(txn.query_one(2.into_expr().gte(3)), false);
127    /// assert_eq!(txn.query_one(3.0.into_expr().gte(3.0)), true);
128    /// # });
129    /// ```
130    pub fn gte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
131        let lhs = self.inner.clone();
132        let rhs = rhs.into_expr().inner;
133        Expr::adhoc(move |b| lhs.build_expr(b).gte(rhs.build_expr(b)))
134    }
135
136    /// Get the maximum of two values.
137    ///
138    /// ```
139    /// # use rust_query::IntoExpr;
140    /// # rust_query::private::doctest::get_txn(|txn| {
141    /// assert_eq!(txn.query_one(2.into_expr().max(3)), 3);
142    /// assert_eq!(txn.query_one(5.0.into_expr().max(3.0)), 5.0);
143    /// # });
144    /// ```
145    pub fn max(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
146        let lhs = self.inner.clone();
147        let rhs = rhs.into_expr().inner;
148        Expr::adhoc(move |b| {
149            sea_query::Expr::expr(
150                sea_query::Func::cust("max")
151                    .arg(lhs.build_expr(b))
152                    .arg(rhs.build_expr(b)),
153            )
154        })
155    }
156
157    /// Get the minimum of two values.
158    ///
159    /// ```
160    /// # use rust_query::IntoExpr;
161    /// # rust_query::private::doctest::get_txn(|txn| {
162    /// assert_eq!(txn.query_one(2.into_expr().min(3)), 2);
163    /// assert_eq!(txn.query_one(5.0.into_expr().min(3.0)), 3.0);
164    /// # });
165    /// ```
166    pub fn min(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
167        let lhs = self.inner.clone();
168        let rhs = rhs.into_expr().inner;
169        Expr::adhoc(move |b| {
170            sea_query::Expr::expr(
171                sea_query::Func::cust("min")
172                    .arg(lhs.build_expr(b))
173                    .arg(rhs.build_expr(b)),
174            )
175        })
176    }
177
178    /// Get the sign of the expression.
179    ///
180    /// The result is -1, 0 or 1 depending on if the expression is
181    /// negative, zero or positive.
182    ///
183    /// ```
184    /// # use rust_query::IntoExpr;
185    /// # rust_query::private::doctest::get_txn(|txn| {
186    /// assert_eq!(txn.query_one(2.into_expr().sign()), 1);
187    /// assert_eq!(txn.query_one((-5.0).into_expr().sign()), -1);
188    /// assert_eq!(txn.query_one((-0.0).into_expr().sign()), 0);
189    /// # });
190    /// ```
191    pub fn sign(&self) -> Expr<'column, S, i64> {
192        let lhs = self.inner.clone();
193        Expr::adhoc(move |b| {
194            sea_query::Expr::expr(sea_query::Func::cust("sign").arg(lhs.build_expr(b)))
195        })
196    }
197
198    /// Get the absolute value of the expression.
199    ///
200    /// ```
201    /// # use rust_query::IntoExpr;
202    /// # rust_query::private::doctest::get_txn(|txn| {
203    /// assert_eq!(txn.query_one(2.into_expr().abs()), 2);
204    /// assert_eq!(txn.query_one((-5.0).into_expr().abs()), 5.0);
205    /// # });
206    /// ```
207    pub fn abs(&self) -> Self {
208        let lhs = self.inner.clone();
209        Expr::adhoc(move |b| {
210            sea_query::Expr::expr(sea_query::Func::cust("abs").arg(lhs.build_expr(b)))
211        })
212    }
213
214    /// Check if a value is between two other values.
215    ///
216    /// The range is inclusive on both sides.
217    ///
218    /// ```
219    /// # use rust_query::IntoExpr;
220    /// # rust_query::private::doctest::get_txn(|txn| {
221    /// assert_eq!(txn.query_one(2.into_expr().between(2, 3)), true);
222    /// assert_eq!(txn.query_one(3.into_expr().between(2, 3)), true);
223    /// assert_eq!(txn.query_one(5.into_expr().between(2, 3)), false);
224    /// assert_eq!(txn.query_one(1.into_expr().between(2, 3)), false);
225    /// # });
226    /// ```
227    pub fn between(
228        &self,
229        low: impl IntoExpr<'column, S, Typ = T>,
230        high: impl IntoExpr<'column, S, Typ = T>,
231    ) -> Expr<'column, S, bool> {
232        let lhs = self.inner.clone();
233        let low = low.into_expr().inner;
234        let high = high.into_expr().inner;
235        Expr::adhoc(move |b| {
236            lhs.build_expr(b)
237                .between(low.build_expr(b), high.build_expr(b))
238        })
239    }
240}
241
242impl<'column, S, T: EqTyp + 'static> Expr<'column, S, T> {
243    /// Check whether two expressions are equal.
244    ///
245    /// ```
246    /// # use rust_query::IntoExpr;
247    /// # rust_query::private::doctest::get_txn(|txn| {
248    /// assert_eq!(txn.query_one(2.into_expr().eq(2)), true);
249    /// assert_eq!(txn.query_one(3.0.into_expr().eq(3.0)), true);
250    /// assert_eq!(txn.query_one("test".into_expr().eq("test")), true);
251    /// assert_eq!(txn.query_one(b"test".into_expr().eq(b"test" as &[u8])), true);
252    /// assert_eq!(txn.query_one(false.into_expr().eq(false)), true);
253    ///
254    /// assert_eq!(txn.query_one(1.into_expr().eq(2)), false);
255    /// # });
256    /// ```
257    pub fn eq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
258        let lhs = self.inner.clone();
259        let rhs = rhs.into_expr().inner;
260        Expr::adhoc(move |b| lhs.build_expr(b).is(rhs.build_expr(b)))
261    }
262
263    /// Check whether two expressions are not equal.
264    ///
265    /// ```
266    /// # use rust_query::IntoExpr;
267    /// # rust_query::private::doctest::get_txn(|txn| {
268    /// assert_eq!(txn.query_one(2.into_expr().neq(2)), false);
269    /// assert_eq!(txn.query_one(3.0.into_expr().neq(3.1)), true);
270    /// assert_eq!(txn.query_one("test".into_expr().neq("test")), false);
271    /// assert_eq!(txn.query_one(b"test".into_expr().neq(b"test" as &[u8])), false);
272    /// assert_eq!(txn.query_one(false.into_expr().neq(false)), false);
273    ///
274    /// assert_eq!(txn.query_one(1.into_expr().neq(2)), true);
275    /// # });
276    /// ```
277    pub fn neq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
278        let lhs = self.inner.clone();
279        let rhs = rhs.into_expr().inner;
280        Expr::adhoc(move |b| lhs.build_expr(b).is_not(rhs.build_expr(b)))
281    }
282}
283
284impl<'column, S> Expr<'column, S, bool> {
285    /// Checks whether an expression is false.
286    ///
287    /// ```
288    /// # use rust_query::IntoExpr;
289    /// # rust_query::private::doctest::get_txn(|txn| {
290    /// assert_eq!(txn.query_one(true.into_expr().not()), false);
291    /// assert_eq!(txn.query_one(false.into_expr().not()), true);
292    /// # });
293    /// ```
294    pub fn not(&self) -> Self {
295        let val = self.inner.clone();
296        Expr::adhoc(move |b| val.build_expr(b).not())
297    }
298
299    /// Check if two expressions are both true.
300    ///
301    /// ```
302    /// # use rust_query::IntoExpr;
303    /// # rust_query::private::doctest::get_txn(|txn| {
304    /// assert_eq!(txn.query_one(true.into_expr().and(true)), true);
305    /// assert_eq!(txn.query_one(false.into_expr().and(true)), false);
306    /// assert_eq!(txn.query_one(false.into_expr().and(false)), false);
307    /// # });
308    /// ```
309    pub fn and(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Self {
310        let lhs = self.inner.clone();
311        let rhs = rhs.into_expr().inner;
312        Expr::adhoc(move |b| lhs.build_expr(b).and(rhs.build_expr(b)))
313    }
314
315    /// Check if one of two expressions is true.
316    ///
317    /// ```
318    /// # use rust_query::IntoExpr;
319    /// # rust_query::private::doctest::get_txn(|txn| {
320    /// assert_eq!(txn.query_one(true.into_expr().or(true)), true);
321    /// assert_eq!(txn.query_one(false.into_expr().or(true)), true);
322    /// assert_eq!(txn.query_one(false.into_expr().or(false)), false);
323    /// # });
324    /// ```
325    pub fn or(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Self {
326        let lhs = self.inner.clone();
327        let rhs = rhs.into_expr().inner;
328        Expr::adhoc(move |b| lhs.build_expr(b).or(rhs.build_expr(b)))
329    }
330}
331
332impl<'column, S, Typ: MyTyp> Expr<'column, S, Option<Typ>> {
333    /// Use the first expression if it is [Some], otherwise use the second expression.
334    ///
335    /// ```
336    /// # use rust_query::IntoExpr;
337    /// # rust_query::private::doctest::get_txn(|txn| {
338    /// assert_eq!(txn.query_one(Some(10).into_expr().unwrap_or(5)), 10);
339    /// assert_eq!(txn.query_one(None::<String>.into_expr().unwrap_or("foo")), "foo");
340    /// # });
341    /// ```
342    pub fn unwrap_or(&self, rhs: impl IntoExpr<'column, S, Typ = Typ>) -> Expr<'column, S, Typ>
343    where
344        Self: IntoExpr<'column, S, Typ = Option<Typ>>,
345    {
346        let lhs = self.inner.clone();
347        let rhs = rhs.into_expr().inner;
348        let maybe_optional = rhs.maybe_optional();
349        Expr::adhoc_promise(
350            move |b| sea_query::Expr::expr(lhs.build_expr(b)).if_null(rhs.build_expr(b)),
351            maybe_optional,
352        )
353    }
354
355    /// Check that the expression is [Some].
356    ///
357    /// ```
358    /// # use rust_query::IntoExpr;
359    /// # rust_query::private::doctest::get_txn(|txn| {
360    /// assert_eq!(txn.query_one(Some(10).into_expr().is_some()), true);
361    /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_some()), false);
362    /// # });
363    /// ```
364    pub fn is_some(&self) -> Expr<'column, S, bool> {
365        let val = self.inner.clone();
366        Expr::adhoc(move |b| val.build_expr(b).is_not_null())
367    }
368
369    /// Check that the expression is [None].
370    ///
371    /// ```
372    /// # use rust_query::IntoExpr;
373    /// # rust_query::private::doctest::get_txn(|txn| {
374    /// assert_eq!(txn.query_one(Some(10).into_expr().is_none()), false);
375    /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_none()), true);
376    /// # });
377    /// ```
378    pub fn is_none(&self) -> Expr<'column, S, bool> {
379        let val = self.inner.clone();
380        Expr::adhoc(move |b| val.build_expr(b).is_null())
381    }
382}
383
384impl<'column, S> Expr<'column, S, i64> {
385    /// Convert the [i64] expression to [f64] type.
386    ///
387    /// ```
388    /// # use rust_query::IntoExpr;
389    /// # rust_query::private::doctest::get_txn(|txn| {
390    /// assert_eq!(txn.query_one(10.into_expr().as_float()), 10.0);
391    /// # });
392    /// ```
393    pub fn as_float(&self) -> Expr<'column, S, f64> {
394        let val = self.inner.clone();
395        Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("REAL")))
396    }
397
398    /// Calculate the remainder for integer division.
399    ///
400    /// The remainder is the missing part after division.
401    ///
402    /// ```
403    /// # use rust_query::IntoExpr;
404    /// # rust_query::private::doctest::get_txn(|txn| {
405    /// assert_eq!(txn.query_one(5.into_expr().div(3)), 1);
406    /// assert_eq!(txn.query_one(5.into_expr().modulo(3)), 2);
407    /// assert_eq!(txn.query_one((-5).into_expr().div(3)), -1);
408    /// assert_eq!(txn.query_one((-5).into_expr().modulo(3)), -2);
409    /// # });
410    /// ```
411    pub fn modulo(&self, rhs: impl IntoExpr<'column, S, Typ = i64>) -> Self {
412        let lhs = self.inner.clone();
413        let rhs = rhs.into_expr().inner;
414        Expr::adhoc(move |b| lhs.build_expr(b).modulo(rhs.build_expr(b)))
415    }
416
417    /// Get the current timestamp as milliseconds since unix epoch.
418    pub fn unix_epoch() -> Self {
419        #[expect(deprecated)]
420        crate::value::UnixEpoch.into_expr()
421    }
422}
423
424impl<'column, S> Expr<'column, S, f64> {
425    /// Convert the [f64] expression to [i64] type.
426    ///
427    /// Always rounds towards zero.
428    ///
429    /// ```
430    /// # use rust_query::IntoExpr;
431    /// # rust_query::private::doctest::get_txn(|txn| {
432    /// assert_eq!(txn.query_one(10.9.into_expr().truncate()), 10);
433    /// assert_eq!(txn.query_one((-10.9).into_expr().truncate()), -10);
434    /// # });
435    /// ```
436    pub fn truncate(&self) -> Expr<'column, S, i64> {
437        let val = self.inner.clone();
438        Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("INTEGER")))
439    }
440}
441
442impl<'column, S> Expr<'column, S, String> {
443    /// Check if the expression starts with the string pattern.
444    ///
445    /// Matches case-sensitive. The pattern gets automatically escaped.
446    ///
447    /// ```
448    /// # use rust_query::IntoExpr;
449    /// # rust_query::private::doctest::get_txn(|txn| {
450    /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("hello")), true);
451    /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("Hello")), false);
452    /// # });
453    /// ```
454    pub fn starts_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
455        self.glob(format!("{}*", escape_glob(pattern)))
456    }
457
458    /// Check if the expression ends with the string pattern.
459    ///
460    /// Matches case-sensitive. The pattern gets automatically escaped.
461    ///
462    /// ```
463    /// # use rust_query::IntoExpr;
464    /// # rust_query::private::doctest::get_txn(|txn| {
465    /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("world")), true);
466    /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("World")), false);
467    /// # });
468    /// ```
469    pub fn ends_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
470        self.glob(format!("*{}", escape_glob(pattern)))
471    }
472
473    /// Check if the expression contains the string pattern.
474    ///
475    /// Matches case-sensitive. The pattern gets automatically escaped.
476    ///
477    /// ```
478    /// # use rust_query::IntoExpr;
479    /// # rust_query::private::doctest::get_txn(|txn| {
480    /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("bar")), true);
481    /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("Bar")), false);
482    /// # });
483    /// ```
484    #[doc(alias = "instr")]
485    pub fn contains(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
486        let lhs = self.inner.clone();
487        let rhs = rhs.into_expr().inner;
488        Expr::adhoc(move |b| {
489            sea_query::Expr::expr(
490                sea_query::Func::cust("instr")
491                    .arg(lhs.build_expr(b))
492                    .arg(rhs.build_expr(b)),
493            )
494            .is_not(CONST_0)
495        })
496    }
497
498    /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
499    ///
500    /// This is a case-sensitive version of [like](Self::like). It uses Unix file globbing syntax for wild
501    /// cards. `*` matches any sequence of characters and `?` matches any single character. `[0-9]` matches
502    /// any single digit and `[a-z]` matches any single lowercase letter. `^` negates the pattern.
503    ///
504    /// ```
505    /// # use rust_query::IntoExpr;
506    /// # rust_query::private::doctest::get_txn(|txn| {
507    /// assert_eq!(txn.query_one("hello world".into_expr().glob("?ello*")), true);
508    /// assert_eq!(txn.query_one("hello world".into_expr().glob("Hell*")), false);
509    /// # });
510    /// ```
511    pub fn glob(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
512        let lhs = self.inner.clone();
513        let rhs = rhs.into_expr().inner;
514        Expr::adhoc(move |b| sea_query::Expr::expr(lhs.build_expr(b)).glob(rhs.build_expr(b)))
515    }
516
517    /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
518    ///
519    /// As noted in the docs, it is **case-insensitive** for ASCII characters. Other characters are case-sensitive.
520    /// For creating patterns it uses `%` as a wildcard for any sequence of characters and `_` for any single character.
521    /// Special characters should be escaped with `\`.
522    ///
523    /// ```
524    /// # use rust_query::IntoExpr;
525    /// # rust_query::private::doctest::get_txn(|txn| {
526    /// assert_eq!(txn.query_one("hello world".into_expr().like("HELLO%")), true);
527    /// assert_eq!(txn.query_one("hello world".into_expr().like("he_o%")), false);
528    /// # });
529    /// ```
530    pub fn like(&self, pattern: impl Into<String>) -> Expr<'column, S, bool> {
531        let lhs = self.inner.clone();
532        let rhs = pattern.into();
533        Expr::adhoc(move |b| {
534            sea_query::Expr::expr(lhs.build_expr(b))
535                .like(sea_query::LikeExpr::new(&rhs).escape('\\'))
536        })
537    }
538
539    /// Concatenate two strings.
540    ///
541    /// ```
542    /// # use rust_query::IntoExpr;
543    /// # rust_query::private::doctest::get_txn(|txn| {
544    /// assert_eq!(txn.query_one("hello ".into_expr().concat("world").concat("!")), "hello world!");
545    /// # });
546    /// ```
547    pub fn concat(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Self {
548        let lhs = self.inner.clone();
549        let rhs = rhs.into_expr().inner;
550        Expr::adhoc(move |b| {
551            sea_query::Expr::expr(
552                sea_query::Func::cust("concat")
553                    .arg(lhs.build_expr(b))
554                    .arg(rhs.build_expr(b)),
555            )
556        })
557    }
558
559    /// Convert ascii to lowercase.
560    ///
561    /// ```
562    /// # use rust_query::IntoExpr;
563    /// # rust_query::private::doctest::get_txn(|txn| {
564    /// assert_eq!(txn.query_one("Hello".into_expr().lower()), "hello");
565    /// assert_eq!(txn.query_one("WHAT".into_expr().lower()), "what");
566    /// # });
567    /// ```
568    pub fn lower(&self) -> Self {
569        let lhs = self.inner.clone();
570        Expr::adhoc(move |b| {
571            sea_query::Expr::expr(sea_query::Func::cust("lower").arg(lhs.build_expr(b)))
572        })
573    }
574
575    /// Convert ascii to uppercase.
576    ///
577    /// ```
578    /// # use rust_query::IntoExpr;
579    /// # rust_query::private::doctest::get_txn(|txn| {
580    /// assert_eq!(txn.query_one("Hello".into_expr().upper()), "HELLO");
581    /// assert_eq!(txn.query_one("what".into_expr().upper()), "WHAT");
582    /// # });
583    /// ```
584    pub fn upper(&self) -> Self {
585        let lhs = self.inner.clone();
586        Expr::adhoc(move |b| {
587            sea_query::Expr::expr(sea_query::Func::cust("upper").arg(lhs.build_expr(b)))
588        })
589    }
590
591    /// The number of unicode code points in the string.
592    ///
593    /// ```
594    /// # use rust_query::IntoExpr;
595    /// # rust_query::private::doctest::get_txn(|txn| {
596    /// assert_eq!(txn.query_one("€".into_expr().char_len()), 1);
597    /// assert_eq!(txn.query_one("what".into_expr().char_len()), 4);
598    /// # });
599    /// ```
600    pub fn char_len(&self) -> Expr<'column, S, i64> {
601        let lhs = self.inner.clone();
602        Expr::adhoc(move |b| {
603            sea_query::Expr::expr(sea_query::Func::cust("length").arg(lhs.build_expr(b)))
604        })
605    }
606}
607
608impl<'column, S, T: BuffTyp> Expr<'column, S, T> {
609    /// The length of the value in bytes.
610    ///
611    /// The byte length of strings can depend on the encoding (UTF-8 or UTF-16).
612    ///
613    /// ```
614    /// # use rust_query::IntoExpr;
615    /// # rust_query::private::doctest::get_txn(|txn| {
616    /// assert_eq!(txn.query_one("€".into_expr().byte_len()), 3);
617    /// assert_eq!(txn.query_one("what".into_expr().byte_len()), 4);
618    /// assert_eq!(txn.query_one(vec![1, 2].into_expr().byte_len()), 2);
619    /// # });
620    /// ```
621    pub fn byte_len(&self) -> Expr<'column, S, i64> {
622        let lhs = self.inner.clone();
623        Expr::adhoc(move |b| {
624            sea_query::Expr::expr(sea_query::Func::cust("octet_length").arg(lhs.build_expr(b)))
625        })
626    }
627}
628
629impl<'column, S> Expr<'column, S, Vec<u8>> {
630    /// Create a new blob of zero bytes of the specified length.
631    ///
632    /// ```
633    /// # use rust_query::Expr;
634    /// # rust_query::private::doctest::get_txn(|txn| {
635    /// assert_eq!(txn.query_one(Expr::zero_blob(40)), vec![0; 40]);
636    /// # });
637    /// ```
638    pub fn zero_blob(len: impl IntoExpr<'column, S, Typ = i64>) -> Self {
639        let len = len.into_expr().inner;
640        Expr::adhoc(move |b| {
641            sea_query::Expr::expr(sea_query::Func::cust("zeroblob").arg(len.build_expr(b)))
642        })
643    }
644}
645
646// This is a copy of the function from the glob crate https://github.com/rust-lang/glob/blob/49ee1e92bd6e8c5854c0b339634f9b4b733aba4f/src/lib.rs#L720-L737.
647fn escape_glob(s: impl AsRef<str>) -> String {
648    let mut escaped = String::new();
649    for c in s.as_ref().chars() {
650        match c {
651            // note that ! does not need escaping because it is only special
652            // inside brackets
653            '?' | '*' | '[' | ']' => {
654                escaped.push('[');
655                escaped.push(c);
656                escaped.push(']');
657            }
658            c => {
659                escaped.push(c);
660            }
661        }
662    }
663    escaped
664}