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}