rust_query/value/
operations.rs

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