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}