rust_query/value/operations.rs
1use sea_query::{Alias, ExprTrait, extension::sqlite::SqliteExpr};
2
3use crate::{ast::CONST_0, value::BuffTyp};
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: EqTyp> 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 let lhs = self.inner.clone();
341 let rhs = rhs.into_expr().inner;
342 let maybe_optional = rhs.maybe_optional;
343 Expr::adhoc_promise(
344 move |b| sea_query::Expr::expr(lhs.build_expr(b)).if_null(rhs.build_expr(b)),
345 maybe_optional,
346 )
347 }
348
349 /// Check that the expression is [Some].
350 ///
351 /// ```
352 /// # use rust_query::IntoExpr;
353 /// # rust_query::private::doctest::get_txn(|txn| {
354 /// assert_eq!(txn.query_one(Some(10).into_expr().is_some()), true);
355 /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_some()), false);
356 /// # });
357 /// ```
358 pub fn is_some(&self) -> Expr<'column, S, bool> {
359 let val = self.inner.clone();
360 Expr::adhoc(move |b| val.build_expr(b).is_not_null())
361 }
362
363 /// Check that the expression is [None].
364 ///
365 /// ```
366 /// # use rust_query::IntoExpr;
367 /// # rust_query::private::doctest::get_txn(|txn| {
368 /// assert_eq!(txn.query_one(Some(10).into_expr().is_none()), false);
369 /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_none()), true);
370 /// # });
371 /// ```
372 pub fn is_none(&self) -> Expr<'column, S, bool> {
373 let val = self.inner.clone();
374 Expr::adhoc(move |b| val.build_expr(b).is_null())
375 }
376}
377
378impl<'column, S> Expr<'column, S, i64> {
379 /// Convert the [i64] expression to [f64] type.
380 ///
381 /// The conversion may not be lossless for integers larger than 2^53 or smaller than (-2^53).
382 ///
383 /// ```
384 /// # use rust_query::IntoExpr;
385 /// # rust_query::private::doctest::get_txn(|txn| {
386 /// assert_eq!(txn.query_one(10.into_expr().to_f64()), 10.0);
387 /// # });
388 /// ```
389 pub fn to_f64(&self) -> Expr<'column, S, f64> {
390 let val = self.inner.clone();
391 Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("REAL")))
392 }
393
394 /// Calculate the remainder for integer division.
395 ///
396 /// The remainder is the missing part after division.
397 ///
398 /// ```
399 /// # use rust_query::IntoExpr;
400 /// # rust_query::private::doctest::get_txn(|txn| {
401 /// assert_eq!(txn.query_one(5.into_expr().div(3)), 1);
402 /// assert_eq!(txn.query_one(5.into_expr().modulo(3)), 2);
403 /// assert_eq!(txn.query_one((-5).into_expr().div(3)), -1);
404 /// assert_eq!(txn.query_one((-5).into_expr().modulo(3)), -2);
405 /// # });
406 /// ```
407 pub fn modulo(&self, rhs: impl IntoExpr<'column, S, Typ = i64>) -> Self {
408 let lhs = self.inner.clone();
409 let rhs = rhs.into_expr().inner;
410 Expr::adhoc(move |b| lhs.build_expr(b).modulo(rhs.build_expr(b)))
411 }
412
413 /// Get the current timestamp as milliseconds since unix epoch.
414 pub fn unix_epoch() -> Self {
415 Expr::adhoc(|_| sea_query::Expr::cust("unixepoch('now')"))
416 }
417}
418
419impl<'column, S> Expr<'column, S, f64> {
420 /// Convert the [f64] expression to [i64] type.
421 ///
422 /// Always rounds towards zero for floats that are not already an integer.
423 ///
424 /// Values outside the range `i64::MIN..=i64::MAX`, will be converted to
425 /// the closest integer in that range.
426 ///
427 /// ```
428 /// # use rust_query::IntoExpr;
429 /// # rust_query::private::doctest::get_txn(|txn| {
430 /// assert_eq!(txn.query_one(10.9.into_expr().to_i64()), 10);
431 /// assert_eq!(txn.query_one((-10.9).into_expr().to_i64()), -10);
432 /// assert_eq!(txn.query_one((342143124.0).into_expr().to_i64()), 342143124);
433 /// assert_eq!(txn.query_one((f64::MIN).into_expr().to_i64()), i64::MIN);
434 /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().to_i64()), i64::MIN);
435 /// # });
436 /// ```
437 pub fn to_i64(&self) -> Expr<'column, S, i64> {
438 let val = self.inner.clone();
439 Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("INTEGER")))
440 }
441
442 /// Round the [f64] expression down.
443 ///
444 /// ```
445 /// # use rust_query::IntoExpr;
446 /// # rust_query::private::doctest::get_txn(|txn| {
447 /// assert_eq!(txn.query_one(10.9.into_expr().floor()), 10.0);
448 /// assert_eq!(txn.query_one((-10.9).into_expr().floor()), -11.0);
449 /// assert_eq!(txn.query_one((f64::MIN).into_expr().floor()), f64::MIN);
450 /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().floor()), f64::NEG_INFINITY);
451 /// # });
452 /// ```
453 pub fn floor(&self) -> Expr<'column, S, f64> {
454 let val = self.inner.clone();
455 Expr::adhoc(move |b| sea_query::Func::cust("floor").arg(val.build_expr(b)).into())
456 }
457
458 /// Round the [f64] expression up.
459 ///
460 /// ```
461 /// # use rust_query::IntoExpr;
462 /// # rust_query::private::doctest::get_txn(|txn| {
463 /// assert_eq!(txn.query_one(10.9.into_expr().ceil()), 11.0);
464 /// assert_eq!(txn.query_one((-10.9).into_expr().ceil()), -10.0);
465 /// assert_eq!(txn.query_one((f64::MIN).into_expr().ceil()), f64::MIN);
466 /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().ceil()), f64::NEG_INFINITY);
467 /// # });
468 /// ```
469 pub fn ceil(&self) -> Expr<'column, S, f64> {
470 let val = self.inner.clone();
471 Expr::adhoc(move |b| sea_query::Func::cust("ceil").arg(val.build_expr(b)).into())
472 }
473
474 /// Round the [f64] expression to the specified precision.
475 ///
476 /// Precision specifies the number of decimal places to keep.
477 /// A negative precision is treated as zero.
478 ///
479 /// ```
480 /// # use rust_query::IntoExpr;
481 /// # rust_query::private::doctest::get_txn(|txn| {
482 /// assert_eq!(txn.query_one(10.85.into_expr().round_with_precision(0)), 11.0);
483 /// assert_eq!(txn.query_one((-10.85).into_expr().round_with_precision(0)), -11.0);
484 /// assert_eq!(txn.query_one(10.85.into_expr().round_with_precision(1)), 10.8);
485 /// assert_eq!(txn.query_one((-10.85).into_expr().round_with_precision(1)), -10.8);
486 /// assert_eq!(txn.query_one((f64::MIN).into_expr().round_with_precision(0)), f64::MIN);
487 /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().round_with_precision(0)), f64::NEG_INFINITY);
488 /// # });
489 /// ```
490 pub fn round_with_precision(&self, precision: impl IntoExpr<'column, S, Typ = i64>) -> Self {
491 let val = self.inner.clone();
492 let precision = precision.into_expr().inner;
493 Expr::adhoc(move |b| {
494 sea_query::Func::round_with_precision(val.build_expr(b), precision.build_expr(b)).into()
495 })
496 }
497}
498
499impl<'column, S> Expr<'column, S, String> {
500 /// Check if the expression starts with the string pattern.
501 ///
502 /// Matches case-sensitive. The pattern gets automatically escaped.
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().starts_with("hello")), true);
508 /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("Hello")), false);
509 /// # });
510 /// ```
511 pub fn starts_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
512 self.glob(format!("{}*", escape_glob(pattern)))
513 }
514
515 /// Check if the expression ends with the string pattern.
516 ///
517 /// Matches case-sensitive. The pattern gets automatically escaped.
518 ///
519 /// ```
520 /// # use rust_query::IntoExpr;
521 /// # rust_query::private::doctest::get_txn(|txn| {
522 /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("world")), true);
523 /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("World")), false);
524 /// # });
525 /// ```
526 pub fn ends_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
527 self.glob(format!("*{}", escape_glob(pattern)))
528 }
529
530 /// Check if the expression contains the string pattern.
531 ///
532 /// Matches case-sensitive. The pattern gets automatically escaped.
533 ///
534 /// ```
535 /// # use rust_query::IntoExpr;
536 /// # rust_query::private::doctest::get_txn(|txn| {
537 /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("bar")), true);
538 /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("Bar")), false);
539 /// # });
540 /// ```
541 #[doc(alias = "instr")]
542 pub fn contains(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
543 let lhs = self.inner.clone();
544 let rhs = rhs.into_expr().inner;
545 Expr::adhoc(move |b| {
546 sea_query::Expr::expr(
547 sea_query::Func::cust("instr")
548 .arg(lhs.build_expr(b))
549 .arg(rhs.build_expr(b)),
550 )
551 .is_not(CONST_0)
552 })
553 }
554
555 /// Replace all occurences of a string with another string.
556 ///
557 /// ```
558 /// # use rust_query::IntoExpr;
559 /// # rust_query::private::doctest::get_txn(|txn| {
560 /// assert_eq!(txn.query_one("very,cool,list".into_expr().replace(",", "::")), "very::cool::list");
561 /// assert_eq!(txn.query_one("rarar".into_expr().replace("rar", "rer")), "rerar");
562 /// # });
563 /// ```
564 pub fn replace(
565 &self,
566 pattern: impl IntoExpr<'column, S, Typ = String>,
567 new: impl IntoExpr<'column, S, Typ = String>,
568 ) -> Self {
569 let lhs = self.inner.clone();
570 let pat = pattern.into_expr().inner;
571 let new = new.into_expr().inner;
572 Expr::adhoc(move |b| {
573 sea_query::Func::cust("replace")
574 .args([lhs.build_expr(b), pat.build_expr(b), new.build_expr(b)])
575 .into()
576 })
577 }
578
579 /// Removes all characters from a set from the start of a string.
580 ///
581 /// ```
582 /// # use rust_query::IntoExpr;
583 /// # rust_query::private::doctest::get_txn(|txn| {
584 /// assert_eq!(txn.query_one("abacda".into_expr().ltrim("ab")), "cda");
585 /// # });
586 /// ```
587 pub fn ltrim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
588 let lhs = self.inner.clone();
589 let char_set = char_set.into_expr().inner;
590 Expr::adhoc(move |b| {
591 sea_query::Func::cust("ltrim")
592 .args([lhs.build_expr(b), char_set.build_expr(b)])
593 .into()
594 })
595 }
596
597 /// Removes all characters from a set from the end of a string.
598 ///
599 /// ```
600 /// # use rust_query::IntoExpr;
601 /// # rust_query::private::doctest::get_txn(|txn| {
602 /// assert_eq!(txn.query_one("adcaba".into_expr().rtrim("ab")), "adc");
603 /// # });
604 /// ```
605 pub fn rtrim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
606 let lhs = self.inner.clone();
607 let char_set = char_set.into_expr().inner;
608 Expr::adhoc(move |b| {
609 sea_query::Func::cust("rtrim")
610 .args([lhs.build_expr(b), char_set.build_expr(b)])
611 .into()
612 })
613 }
614
615 /// Removes all characters from a set from both the start and end of a string.
616 ///
617 /// ```
618 /// # use rust_query::IntoExpr;
619 /// # rust_query::private::doctest::get_txn(|txn| {
620 /// assert_eq!(txn.query_one("ffzfzf".into_expr().trim("f")), "zfz");
621 /// # });
622 /// ```
623 pub fn trim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
624 let lhs = self.inner.clone();
625 let char_set = char_set.into_expr().inner;
626 Expr::adhoc(move |b| {
627 sea_query::Func::cust("trim")
628 .args([lhs.build_expr(b), char_set.build_expr(b)])
629 .into()
630 })
631 }
632
633 /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
634 ///
635 /// This is a case-sensitive version of [like](Self::like). It uses Unix file globbing syntax for wild
636 /// cards. `*` matches any sequence of characters and `?` matches any single character. `[0-9]` matches
637 /// any single digit and `[a-z]` matches any single lowercase letter. `^` negates the pattern.
638 ///
639 /// ```
640 /// # use rust_query::IntoExpr;
641 /// # rust_query::private::doctest::get_txn(|txn| {
642 /// assert_eq!(txn.query_one("hello world".into_expr().glob("?ello*")), true);
643 /// assert_eq!(txn.query_one("hello world".into_expr().glob("Hell*")), false);
644 /// # });
645 /// ```
646 pub fn glob(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
647 let lhs = self.inner.clone();
648 let rhs = rhs.into_expr().inner;
649 Expr::adhoc(move |b| sea_query::Expr::expr(lhs.build_expr(b)).glob(rhs.build_expr(b)))
650 }
651
652 /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
653 ///
654 /// As noted in the docs, it is **case-insensitive** for ASCII characters. Other characters are case-sensitive.
655 /// For creating patterns it uses `%` as a wildcard for any sequence of characters and `_` for any single character.
656 /// Special characters should be escaped with `\`.
657 ///
658 /// ```
659 /// # use rust_query::IntoExpr;
660 /// # rust_query::private::doctest::get_txn(|txn| {
661 /// assert_eq!(txn.query_one("hello world".into_expr().like("HELLO%")), true);
662 /// assert_eq!(txn.query_one("hello world".into_expr().like("he_o%")), false);
663 /// # });
664 /// ```
665 pub fn like(&self, pattern: impl Into<String>) -> Expr<'column, S, bool> {
666 let lhs = self.inner.clone();
667 let rhs = pattern.into();
668 Expr::adhoc(move |b| {
669 sea_query::Expr::expr(lhs.build_expr(b))
670 .like(sea_query::LikeExpr::new(&rhs).escape('\\'))
671 })
672 }
673
674 /// Concatenate two strings.
675 ///
676 /// ```
677 /// # use rust_query::IntoExpr;
678 /// # rust_query::private::doctest::get_txn(|txn| {
679 /// assert_eq!(txn.query_one("hello ".into_expr().concat("world").concat("!")), "hello world!");
680 /// # });
681 /// ```
682 pub fn concat(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Self {
683 let lhs = self.inner.clone();
684 let rhs = rhs.into_expr().inner;
685 Expr::adhoc(move |b| {
686 sea_query::Expr::expr(
687 sea_query::Func::cust("concat")
688 .arg(lhs.build_expr(b))
689 .arg(rhs.build_expr(b)),
690 )
691 })
692 }
693
694 /// Convert ascii to lowercase.
695 ///
696 /// ```
697 /// # use rust_query::IntoExpr;
698 /// # rust_query::private::doctest::get_txn(|txn| {
699 /// assert_eq!(txn.query_one("Hello".into_expr().lower()), "hello");
700 /// assert_eq!(txn.query_one("WHAT".into_expr().lower()), "what");
701 /// # });
702 /// ```
703 pub fn lower(&self) -> Self {
704 let lhs = self.inner.clone();
705 Expr::adhoc(move |b| {
706 sea_query::Expr::expr(sea_query::Func::cust("lower").arg(lhs.build_expr(b)))
707 })
708 }
709
710 /// Convert ascii to uppercase.
711 ///
712 /// ```
713 /// # use rust_query::IntoExpr;
714 /// # rust_query::private::doctest::get_txn(|txn| {
715 /// assert_eq!(txn.query_one("Hello".into_expr().upper()), "HELLO");
716 /// assert_eq!(txn.query_one("what".into_expr().upper()), "WHAT");
717 /// # });
718 /// ```
719 pub fn upper(&self) -> Self {
720 let lhs = self.inner.clone();
721 Expr::adhoc(move |b| {
722 sea_query::Expr::expr(sea_query::Func::cust("upper").arg(lhs.build_expr(b)))
723 })
724 }
725
726 /// The number of unicode code points in the string.
727 ///
728 /// ```
729 /// # use rust_query::IntoExpr;
730 /// # rust_query::private::doctest::get_txn(|txn| {
731 /// assert_eq!(txn.query_one("€".into_expr().char_len()), 1);
732 /// assert_eq!(txn.query_one("what".into_expr().char_len()), 4);
733 /// # });
734 /// ```
735 pub fn char_len(&self) -> Expr<'column, S, i64> {
736 let lhs = self.inner.clone();
737 Expr::adhoc(move |b| {
738 sea_query::Expr::expr(sea_query::Func::cust("length").arg(lhs.build_expr(b)))
739 })
740 }
741}
742
743impl<'column, S, T: BuffTyp> Expr<'column, S, T> {
744 /// The length of the value in bytes.
745 ///
746 /// The byte length of strings can depend on the encoding (UTF-8 or UTF-16).
747 ///
748 /// ```
749 /// # use rust_query::IntoExpr;
750 /// # rust_query::private::doctest::get_txn(|txn| {
751 /// assert_eq!(txn.query_one("€".into_expr().byte_len()), 3);
752 /// assert_eq!(txn.query_one("what".into_expr().byte_len()), 4);
753 /// assert_eq!(txn.query_one(vec![1, 2].into_expr().byte_len()), 2);
754 /// # });
755 /// ```
756 pub fn byte_len(&self) -> Expr<'column, S, i64> {
757 let lhs = self.inner.clone();
758 Expr::adhoc(move |b| {
759 sea_query::Expr::expr(sea_query::Func::cust("octet_length").arg(lhs.build_expr(b)))
760 })
761 }
762}
763
764impl<'column, S> Expr<'column, S, Vec<u8>> {
765 /// Create a new blob of zero bytes of the specified length.
766 ///
767 /// ```
768 /// # use rust_query::Expr;
769 /// # rust_query::private::doctest::get_txn(|txn| {
770 /// assert_eq!(txn.query_one(Expr::zero_blob(40)), vec![0; 40]);
771 /// # });
772 /// ```
773 pub fn zero_blob(len: impl IntoExpr<'column, S, Typ = i64>) -> Self {
774 let len = len.into_expr().inner;
775 Expr::adhoc(move |b| {
776 sea_query::Expr::expr(sea_query::Func::cust("zeroblob").arg(len.build_expr(b)))
777 })
778 }
779}
780
781// This is a copy of the function from the glob crate https://github.com/rust-lang/glob/blob/49ee1e92bd6e8c5854c0b339634f9b4b733aba4f/src/lib.rs#L720-L737.
782fn escape_glob(s: impl AsRef<str>) -> String {
783 let mut escaped = String::new();
784 for c in s.as_ref().chars() {
785 match c {
786 // note that ! does not need escaping because it is only special
787 // inside brackets
788 '?' | '*' | '[' | ']' => {
789 escaped.push('[');
790 escaped.push(c);
791 escaped.push(']');
792 }
793 c => {
794 escaped.push(c);
795 }
796 }
797 }
798 escaped
799}