Skip to main content

rust_query/value/
operations.rs

1use std::rc::Rc;
2
3use crate::{
4    lower::{self, CONST_0, CONST_NULL},
5    value::{BuffTyp, OrdTyp},
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(lower::Expr::Infix(lhs, "+", rhs))
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(lower::Expr::Infix(lhs, "-", rhs))
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(lower::Expr::Infix(lhs, "*", rhs))
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(lower::Expr::Infix(lhs, "/", rhs))
73    }
74
75    /// Get the sign of the expression.
76    ///
77    /// The result is -1, 0 or 1 depending on if the expression is
78    /// negative, zero or positive.
79    ///
80    /// ```
81    /// # use rust_query::IntoExpr;
82    /// # rust_query::private::doctest::get_txn(|txn| {
83    /// assert_eq!(txn.query_one(2.into_expr().sign()), 1);
84    /// assert_eq!(txn.query_one((-5.0).into_expr().sign()), -1);
85    /// assert_eq!(txn.query_one((-0.0).into_expr().sign()), 0);
86    /// # });
87    /// ```
88    pub fn sign(&self) -> Expr<'column, S, i64> {
89        let lhs = self.inner.clone();
90        Expr::adhoc(lower::Expr::Func("sign", Box::new([lhs])))
91    }
92
93    /// Get the absolute value of the expression.
94    ///
95    /// ```
96    /// # use rust_query::IntoExpr;
97    /// # rust_query::private::doctest::get_txn(|txn| {
98    /// assert_eq!(txn.query_one(2.into_expr().abs()), 2);
99    /// assert_eq!(txn.query_one((-5.0).into_expr().abs()), 5.0);
100    /// # });
101    /// ```
102    pub fn abs(&self) -> Self {
103        let lhs = self.inner.clone();
104        Expr::adhoc(lower::Expr::Func("abs", Box::new([lhs])))
105    }
106}
107
108impl<'column, S, T: OrdTyp> Expr<'column, S, T> {
109    /// Compute the less than operator (<) of two expressions.
110    ///
111    /// ```
112    /// # use rust_query::IntoExpr;
113    /// # rust_query::private::doctest::get_txn(|txn| {
114    /// assert_eq!(txn.query_one(2.into_expr().lt(3)), true);
115    /// assert_eq!(txn.query_one(1.into_expr().lt(1)), false);
116    /// assert_eq!(txn.query_one(3.0.into_expr().lt(1.0)), false);
117    /// # });
118    /// ```
119    pub fn lt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
120        let lhs = self.inner.clone();
121        let rhs = rhs.into_expr().inner;
122        Expr::adhoc(lower::Expr::Infix(lhs, "<", rhs))
123    }
124
125    /// Compute the less than or equal operator (<=) of two expressions.
126    ///
127    /// ```
128    /// # use rust_query::IntoExpr;
129    /// # rust_query::private::doctest::get_txn(|txn| {
130    /// assert_eq!(txn.query_one(2.into_expr().lte(2)), true);
131    /// assert_eq!(txn.query_one(3.0.into_expr().lte(1.0)), false);
132    /// # });
133    /// ```
134    pub fn lte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
135        let lhs = self.inner.clone();
136        let rhs = rhs.into_expr().inner;
137        Expr::adhoc(lower::Expr::Infix(lhs, "<=", rhs))
138    }
139
140    /// Compute the greater than operator (>) of two expressions.
141    ///
142    /// ```
143    /// # use rust_query::IntoExpr;
144    /// # rust_query::private::doctest::get_txn(|txn| {
145    /// assert_eq!(txn.query_one(2.into_expr().gt(2)), false);
146    /// assert_eq!(txn.query_one(3.0.into_expr().gt(1.0)), true);
147    /// # });
148    /// ```
149    pub fn gt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
150        let lhs = self.inner.clone();
151        let rhs = rhs.into_expr().inner;
152        Expr::adhoc(lower::Expr::Infix(lhs, ">", rhs))
153    }
154
155    /// Compute the greater than or equal (>=) operator of two expressions.
156    ///
157    /// ```
158    /// # use rust_query::IntoExpr;
159    /// # rust_query::private::doctest::get_txn(|txn| {
160    /// assert_eq!(txn.query_one(2.into_expr().gte(3)), false);
161    /// assert_eq!(txn.query_one(3.0.into_expr().gte(3.0)), true);
162    /// # });
163    /// ```
164    pub fn gte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
165        let lhs = self.inner.clone();
166        let rhs = rhs.into_expr().inner;
167        Expr::adhoc(lower::Expr::Infix(lhs, ">=", rhs))
168    }
169
170    /// Get the maximum of two values.
171    ///
172    /// ```
173    /// # use rust_query::IntoExpr;
174    /// # rust_query::private::doctest::get_txn(|txn| {
175    /// assert_eq!(txn.query_one(2.into_expr().max(3)), 3);
176    /// assert_eq!(txn.query_one(5.0.into_expr().max(3.0)), 5.0);
177    /// # });
178    /// ```
179    pub fn max(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
180        let lhs = self.inner.clone();
181        let rhs = rhs.into_expr().inner;
182        Expr::adhoc(lower::Expr::Func("max", Box::new([lhs, rhs])))
183    }
184
185    /// Get the minimum of two values.
186    ///
187    /// ```
188    /// # use rust_query::IntoExpr;
189    /// # rust_query::private::doctest::get_txn(|txn| {
190    /// assert_eq!(txn.query_one(2.into_expr().min(3)), 2);
191    /// assert_eq!(txn.query_one(5.0.into_expr().min(3.0)), 3.0);
192    /// # });
193    /// ```
194    pub fn min(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
195        let lhs = self.inner.clone();
196        let rhs = rhs.into_expr().inner;
197        Expr::adhoc(lower::Expr::Func("min", Box::new([lhs, rhs])))
198    }
199
200    /// Check if a value is between two other values.
201    ///
202    /// The range is inclusive on both sides.
203    ///
204    /// ```
205    /// # use rust_query::IntoExpr;
206    /// # rust_query::private::doctest::get_txn(|txn| {
207    /// assert_eq!(txn.query_one(2.into_expr().between(2, 3)), true);
208    /// assert_eq!(txn.query_one(3.into_expr().between(2, 3)), true);
209    /// assert_eq!(txn.query_one(5.into_expr().between(2, 3)), false);
210    /// assert_eq!(txn.query_one(1.into_expr().between(2, 3)), false);
211    /// # });
212    /// ```
213    pub fn between(
214        &self,
215        low: impl IntoExpr<'column, S, Typ = T>,
216        high: impl IntoExpr<'column, S, Typ = T>,
217    ) -> Expr<'column, S, bool> {
218        let lhs = self.inner.clone();
219        let low = low.into_expr().inner;
220        let high = high.into_expr().inner;
221        Expr::adhoc(lower::Expr::Between(lhs, low, high))
222    }
223}
224
225impl<'column, S, T: EqTyp + 'static> Expr<'column, S, T> {
226    /// Check whether two expressions are equal.
227    ///
228    /// ```
229    /// # use rust_query::IntoExpr;
230    /// # rust_query::private::doctest::get_txn(|txn| {
231    /// assert_eq!(txn.query_one(2.into_expr().eq(2)), true);
232    /// assert_eq!(txn.query_one(3.0.into_expr().eq(3.0)), true);
233    /// assert_eq!(txn.query_one("test".into_expr().eq("test")), true);
234    /// assert_eq!(txn.query_one(b"test".into_expr().eq(b"test" as &[u8])), true);
235    /// assert_eq!(txn.query_one(false.into_expr().eq(false)), true);
236    ///
237    /// assert_eq!(txn.query_one(1.into_expr().eq(2)), false);
238    /// # });
239    /// ```
240    pub fn eq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
241        let lhs = self.inner.clone();
242        let rhs = rhs.into_expr().inner;
243        Expr::adhoc(lower::Expr::Infix(lhs, "IS", rhs))
244    }
245
246    /// Check whether two expressions are not equal.
247    ///
248    /// ```
249    /// # use rust_query::IntoExpr;
250    /// # rust_query::private::doctest::get_txn(|txn| {
251    /// assert_eq!(txn.query_one(2.into_expr().neq(2)), false);
252    /// assert_eq!(txn.query_one(3.0.into_expr().neq(3.1)), true);
253    /// assert_eq!(txn.query_one("test".into_expr().neq("test")), false);
254    /// assert_eq!(txn.query_one(b"test".into_expr().neq(b"test" as &[u8])), false);
255    /// assert_eq!(txn.query_one(false.into_expr().neq(false)), false);
256    ///
257    /// assert_eq!(txn.query_one(1.into_expr().neq(2)), true);
258    /// # });
259    /// ```
260    pub fn neq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
261        let lhs = self.inner.clone();
262        let rhs = rhs.into_expr().inner;
263        Expr::adhoc(lower::Expr::Infix(lhs, "IS NOT", rhs))
264    }
265}
266
267impl<'column, S> Expr<'column, S, bool> {
268    /// Checks whether an expression is false.
269    ///
270    /// ```
271    /// # use rust_query::IntoExpr;
272    /// # rust_query::private::doctest::get_txn(|txn| {
273    /// assert_eq!(txn.query_one(true.into_expr().not()), false);
274    /// assert_eq!(txn.query_one(false.into_expr().not()), true);
275    /// # });
276    /// ```
277    pub fn not(&self) -> Self {
278        let val = self.inner.clone();
279        Expr::adhoc(lower::Expr::Prefix("NOT ", val))
280    }
281
282    /// Check if two expressions are both true.
283    ///
284    /// ```
285    /// # use rust_query::IntoExpr;
286    /// # rust_query::private::doctest::get_txn(|txn| {
287    /// assert_eq!(txn.query_one(true.into_expr().and(true)), true);
288    /// assert_eq!(txn.query_one(false.into_expr().and(true)), false);
289    /// assert_eq!(txn.query_one(false.into_expr().and(false)), false);
290    /// # });
291    /// ```
292    pub fn and(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Self {
293        let lhs = self.inner.clone();
294        let rhs = rhs.into_expr().inner;
295        Expr::adhoc(lower::Expr::Infix(lhs, "AND", rhs))
296    }
297
298    /// Check if one of two expressions is true.
299    ///
300    /// ```
301    /// # use rust_query::IntoExpr;
302    /// # rust_query::private::doctest::get_txn(|txn| {
303    /// assert_eq!(txn.query_one(true.into_expr().or(true)), true);
304    /// assert_eq!(txn.query_one(false.into_expr().or(true)), true);
305    /// assert_eq!(txn.query_one(false.into_expr().or(false)), false);
306    /// # });
307    /// ```
308    pub fn or(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Self {
309        let lhs = self.inner.clone();
310        let rhs = rhs.into_expr().inner;
311        Expr::adhoc(lower::Expr::Infix(lhs, "OR", rhs))
312    }
313}
314
315impl<'column, S, Typ: EqTyp> Expr<'column, S, Option<Typ>> {
316    /// Use the first expression if it is [Some], otherwise use the second expression.
317    ///
318    /// ```
319    /// # use rust_query::IntoExpr;
320    /// # rust_query::private::doctest::get_txn(|txn| {
321    /// assert_eq!(txn.query_one(Some(10).into_expr().unwrap_or(5)), 10);
322    /// assert_eq!(txn.query_one(None::<String>.into_expr().unwrap_or("foo")), "foo");
323    /// # });
324    /// ```
325    pub fn unwrap_or(&self, rhs: impl IntoExpr<'column, S, Typ = Typ>) -> Expr<'column, S, Typ> {
326        let lhs = self.inner.clone();
327        let rhs = rhs.into_expr().inner;
328        Expr::adhoc(lower::Expr::Func("ifnull", Box::new([lhs, rhs])))
329    }
330
331    /// Check that the expression is [Some].
332    ///
333    /// ```
334    /// # use rust_query::IntoExpr;
335    /// # rust_query::private::doctest::get_txn(|txn| {
336    /// assert_eq!(txn.query_one(Some(10).into_expr().is_some()), true);
337    /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_some()), false);
338    /// # });
339    /// ```
340    pub fn is_some(&self) -> Expr<'column, S, bool> {
341        let val = self.inner.clone();
342        Expr::adhoc(lower::Expr::Infix(val, "IS NOT", Rc::new(CONST_NULL)))
343    }
344
345    /// Check that the expression is [None].
346    ///
347    /// ```
348    /// # use rust_query::IntoExpr;
349    /// # rust_query::private::doctest::get_txn(|txn| {
350    /// assert_eq!(txn.query_one(Some(10).into_expr().is_none()), false);
351    /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_none()), true);
352    /// # });
353    /// ```
354    pub fn is_none(&self) -> Expr<'column, S, bool> {
355        let val = self.inner.clone();
356        Expr::adhoc(lower::Expr::Infix(val, "IS", Rc::new(CONST_NULL)))
357    }
358}
359
360impl<'column, S> Expr<'column, S, i64> {
361    /// Convert the [i64] expression to [f64] type.
362    ///
363    /// The conversion may not be lossless for integers larger than 2^53 or smaller than (-2^53).
364    ///
365    /// ```
366    /// # use rust_query::IntoExpr;
367    /// # rust_query::private::doctest::get_txn(|txn| {
368    /// assert_eq!(txn.query_one(10.into_expr().to_f64()), 10.0);
369    /// # });
370    /// ```
371    pub fn to_f64(&self) -> Expr<'column, S, f64> {
372        let val = self.inner.clone();
373        Expr::adhoc(lower::Expr::Cast(val, "REAL"))
374    }
375
376    /// Calculate the remainder for integer division.
377    ///
378    /// The remainder is the missing part after division.
379    ///
380    /// ```
381    /// # use rust_query::IntoExpr;
382    /// # rust_query::private::doctest::get_txn(|txn| {
383    /// assert_eq!(txn.query_one(5.into_expr().div(3)), 1);
384    /// assert_eq!(txn.query_one(5.into_expr().modulo(3)), 2);
385    /// assert_eq!(txn.query_one((-5).into_expr().div(3)), -1);
386    /// assert_eq!(txn.query_one((-5).into_expr().modulo(3)), -2);
387    /// # });
388    /// ```
389    pub fn modulo(&self, rhs: impl IntoExpr<'column, S, Typ = i64>) -> Self {
390        let lhs = self.inner.clone();
391        let rhs = rhs.into_expr().inner;
392        Expr::adhoc(lower::Expr::Infix(lhs, "%", rhs))
393    }
394}
395
396impl<'column, S> Expr<'column, S, f64> {
397    /// Convert the [f64] expression to [i64] type.
398    ///
399    /// Always rounds towards zero for floats that are not already an integer.
400    ///
401    /// Values outside the range `i64::MIN..=i64::MAX`, will be converted to
402    /// the closest integer in that range.
403    ///
404    /// ```
405    /// # use rust_query::IntoExpr;
406    /// # rust_query::private::doctest::get_txn(|txn| {
407    /// assert_eq!(txn.query_one(10.9.into_expr().to_i64()), 10);
408    /// assert_eq!(txn.query_one((-10.9).into_expr().to_i64()), -10);
409    /// assert_eq!(txn.query_one((342143124.0).into_expr().to_i64()), 342143124);
410    /// assert_eq!(txn.query_one((f64::MIN).into_expr().to_i64()), i64::MIN);
411    /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().to_i64()), i64::MIN);
412    /// # });
413    /// ```
414    pub fn to_i64(&self) -> Expr<'column, S, i64> {
415        let val = self.inner.clone();
416        Expr::adhoc(lower::Expr::Cast(val, "INTEGER"))
417    }
418
419    /// Round the [f64] expression down.
420    ///
421    /// ```
422    /// # use rust_query::IntoExpr;
423    /// # rust_query::private::doctest::get_txn(|txn| {
424    /// assert_eq!(txn.query_one(10.9.into_expr().floor()), 10.0);
425    /// assert_eq!(txn.query_one((-10.9).into_expr().floor()), -11.0);
426    /// assert_eq!(txn.query_one((f64::MIN).into_expr().floor()), f64::MIN);
427    /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().floor()), f64::NEG_INFINITY);
428    /// # });
429    /// ```
430    pub fn floor(&self) -> Expr<'column, S, f64> {
431        let val = self.inner.clone();
432        Expr::adhoc(lower::Expr::Func("floor", Box::new([val])))
433    }
434
435    /// Round the [f64] expression up.
436    ///
437    /// ```
438    /// # use rust_query::IntoExpr;
439    /// # rust_query::private::doctest::get_txn(|txn| {
440    /// assert_eq!(txn.query_one(10.9.into_expr().ceil()), 11.0);
441    /// assert_eq!(txn.query_one((-10.9).into_expr().ceil()), -10.0);
442    /// assert_eq!(txn.query_one((f64::MIN).into_expr().ceil()), f64::MIN);
443    /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().ceil()), f64::NEG_INFINITY);
444    /// # });
445    /// ```
446    pub fn ceil(&self) -> Expr<'column, S, f64> {
447        let val = self.inner.clone();
448        Expr::adhoc(lower::Expr::Func("ceil", Box::new([val])))
449    }
450
451    /// Round the [f64] expression to the specified precision.
452    ///
453    /// Precision specifies the number of decimal places to keep.
454    /// A negative precision is treated as zero.
455    ///
456    /// ```
457    /// # use rust_query::IntoExpr;
458    /// # rust_query::private::doctest::get_txn(|txn| {
459    /// assert_eq!(txn.query_one(10.85.into_expr().round_with_precision(0)), 11.0);
460    /// assert_eq!(txn.query_one((-10.85).into_expr().round_with_precision(0)), -11.0);
461    /// assert_eq!(txn.query_one(10.85.into_expr().round_with_precision(1)), 10.8);
462    /// assert_eq!(txn.query_one((-10.85).into_expr().round_with_precision(1)), -10.8);
463    /// assert_eq!(txn.query_one((f64::MIN).into_expr().round_with_precision(0)), f64::MIN);
464    /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().round_with_precision(0)), f64::NEG_INFINITY);
465    /// # });
466    /// ```
467    pub fn round_with_precision(&self, precision: impl IntoExpr<'column, S, Typ = i64>) -> Self {
468        let val = self.inner.clone();
469        let precision = precision.into_expr().inner;
470        Expr::adhoc(lower::Expr::Func("round", Box::new([val, precision])))
471    }
472}
473
474impl<'column, S> Expr<'column, S, String> {
475    /// Check if the expression starts with the string pattern.
476    ///
477    /// Matches case-sensitive. The pattern gets automatically escaped.
478    ///
479    /// ```
480    /// # use rust_query::IntoExpr;
481    /// # rust_query::private::doctest::get_txn(|txn| {
482    /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("hello")), true);
483    /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("Hello")), false);
484    /// # });
485    /// ```
486    pub fn starts_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
487        self.glob(format!("{}*", escape_glob(pattern)))
488    }
489
490    /// Check if the expression ends with the string pattern.
491    ///
492    /// Matches case-sensitive. The pattern gets automatically escaped.
493    ///
494    /// ```
495    /// # use rust_query::IntoExpr;
496    /// # rust_query::private::doctest::get_txn(|txn| {
497    /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("world")), true);
498    /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("World")), false);
499    /// # });
500    /// ```
501    pub fn ends_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
502        self.glob(format!("*{}", escape_glob(pattern)))
503    }
504
505    /// Check if the expression contains the string pattern.
506    ///
507    /// Matches case-sensitive. The pattern gets automatically escaped.
508    ///
509    /// ```
510    /// # use rust_query::IntoExpr;
511    /// # rust_query::private::doctest::get_txn(|txn| {
512    /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("bar")), true);
513    /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("Bar")), false);
514    /// # });
515    /// ```
516    #[doc(alias = "instr")]
517    pub fn contains(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
518        let lhs = self.inner.clone();
519        let rhs = rhs.into_expr().inner;
520        Expr::adhoc(lower::Expr::Infix(
521            Rc::new(lower::Expr::Func("instr", Box::new([lhs, rhs]))),
522            "IS NOT",
523            Rc::new(CONST_0),
524        ))
525    }
526
527    /// Replace all occurences of a string with another string.
528    ///
529    /// ```
530    /// # use rust_query::IntoExpr;
531    /// # rust_query::private::doctest::get_txn(|txn| {
532    /// assert_eq!(txn.query_one("very,cool,list".into_expr().replace(",", "::")), "very::cool::list");
533    /// assert_eq!(txn.query_one("rarar".into_expr().replace("rar", "rer")), "rerar");
534    /// # });
535    /// ```
536    pub fn replace(
537        &self,
538        pattern: impl IntoExpr<'column, S, Typ = String>,
539        new: impl IntoExpr<'column, S, Typ = String>,
540    ) -> Self {
541        let lhs = self.inner.clone();
542        let pat = pattern.into_expr().inner;
543        let new = new.into_expr().inner;
544        Expr::adhoc(lower::Expr::Func("replace", Box::new([lhs, pat, new])))
545    }
546
547    /// Removes all characters from a set from the start of a string.
548    ///
549    /// ```
550    /// # use rust_query::IntoExpr;
551    /// # rust_query::private::doctest::get_txn(|txn| {
552    /// assert_eq!(txn.query_one("abacda".into_expr().ltrim("ab")), "cda");
553    /// # });
554    /// ```
555    pub fn ltrim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
556        let lhs = self.inner.clone();
557        let char_set = char_set.into_expr().inner;
558        Expr::adhoc(lower::Expr::Func("ltrim", Box::new([lhs, char_set])))
559    }
560
561    /// Removes all characters from a set from the end of a string.
562    ///
563    /// ```
564    /// # use rust_query::IntoExpr;
565    /// # rust_query::private::doctest::get_txn(|txn| {
566    /// assert_eq!(txn.query_one("adcaba".into_expr().rtrim("ab")), "adc");
567    /// # });
568    /// ```
569    pub fn rtrim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
570        let lhs = self.inner.clone();
571        let char_set = char_set.into_expr().inner;
572        Expr::adhoc(lower::Expr::Func("rtrim", Box::new([lhs, char_set])))
573    }
574
575    /// Removes all characters from a set from both the start and end of a string.
576    ///
577    /// ```
578    /// # use rust_query::IntoExpr;
579    /// # rust_query::private::doctest::get_txn(|txn| {
580    /// assert_eq!(txn.query_one("ffzfzf".into_expr().trim("f")), "zfz");
581    /// # });
582    /// ```
583    pub fn trim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
584        let lhs = self.inner.clone();
585        let char_set = char_set.into_expr().inner;
586        Expr::adhoc(lower::Expr::Func("trim", Box::new([lhs, char_set])))
587    }
588
589    /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
590    ///
591    /// This is a case-sensitive version of [like](Self::like). It uses Unix file globbing syntax for wild
592    /// cards. `*` matches any sequence of characters and `?` matches any single character. `[0-9]` matches
593    /// any single digit and `[a-z]` matches any single lowercase letter. `^` negates the pattern.
594    ///
595    /// ```
596    /// # use rust_query::IntoExpr;
597    /// # rust_query::private::doctest::get_txn(|txn| {
598    /// assert_eq!(txn.query_one("hello world".into_expr().glob("?ello*")), true);
599    /// assert_eq!(txn.query_one("hello world".into_expr().glob("Hell*")), false);
600    /// # });
601    /// ```
602    pub fn glob(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
603        let lhs = self.inner.clone();
604        let rhs = rhs.into_expr().inner;
605        Expr::adhoc(lower::Expr::Func("glob", Box::new([rhs, lhs])))
606    }
607
608    /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
609    ///
610    /// As noted in the docs, it is **case-insensitive** for ASCII characters. Other characters are case-sensitive.
611    /// For creating patterns it uses `%` as a wildcard for any sequence of characters and `_` for any single character.
612    /// Special characters should be escaped with `\`.
613    ///
614    /// ```
615    /// # use rust_query::IntoExpr;
616    /// # rust_query::private::doctest::get_txn(|txn| {
617    /// assert_eq!(txn.query_one("hello world".into_expr().like("HELLO%")), true);
618    /// assert_eq!(txn.query_one("hello world".into_expr().like("he_o%")), false);
619    /// # });
620    /// ```
621    pub fn like(&self, pattern: impl Into<String>) -> Expr<'column, S, bool> {
622        let lhs = self.inner.clone();
623        let rhs: Expr<S, _> = pattern.into().into_expr();
624        let rhs = rhs.inner;
625        Expr::adhoc(lower::Expr::Func(
626            "like",
627            Box::new([rhs, lhs, Rc::new(lower::Expr::Constant("'\\'"))]),
628        ))
629    }
630
631    /// Concatenate two strings.
632    ///
633    /// ```
634    /// # use rust_query::IntoExpr;
635    /// # rust_query::private::doctest::get_txn(|txn| {
636    /// assert_eq!(txn.query_one("hello ".into_expr().concat("world").concat("!")), "hello world!");
637    /// # });
638    /// ```
639    pub fn concat(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Self {
640        let lhs = self.inner.clone();
641        let rhs = rhs.into_expr().inner;
642        Expr::adhoc(lower::Expr::Infix(lhs, "||", rhs))
643    }
644
645    /// Convert ascii to lowercase.
646    ///
647    /// ```
648    /// # use rust_query::IntoExpr;
649    /// # rust_query::private::doctest::get_txn(|txn| {
650    /// assert_eq!(txn.query_one("Hello".into_expr().lower()), "hello");
651    /// assert_eq!(txn.query_one("WHAT".into_expr().lower()), "what");
652    /// # });
653    /// ```
654    pub fn lower(&self) -> Self {
655        let lhs = self.inner.clone();
656        Expr::adhoc(lower::Expr::Func("lower", Box::new([lhs])))
657    }
658
659    /// Convert ascii to uppercase.
660    ///
661    /// ```
662    /// # use rust_query::IntoExpr;
663    /// # rust_query::private::doctest::get_txn(|txn| {
664    /// assert_eq!(txn.query_one("Hello".into_expr().upper()), "HELLO");
665    /// assert_eq!(txn.query_one("what".into_expr().upper()), "WHAT");
666    /// # });
667    /// ```
668    pub fn upper(&self) -> Self {
669        let lhs = self.inner.clone();
670        Expr::adhoc(lower::Expr::Func("upper", Box::new([lhs])))
671    }
672
673    /// The number of unicode code points in the string.
674    ///
675    /// ```
676    /// # use rust_query::IntoExpr;
677    /// # rust_query::private::doctest::get_txn(|txn| {
678    /// assert_eq!(txn.query_one("€".into_expr().char_len()), 1);
679    /// assert_eq!(txn.query_one("what".into_expr().char_len()), 4);
680    /// # });
681    /// ```
682    pub fn char_len(&self) -> Expr<'column, S, i64> {
683        let lhs = self.inner.clone();
684        Expr::adhoc(lower::Expr::Func("length", Box::new([lhs])))
685    }
686}
687
688impl<'column, S, T: BuffTyp> Expr<'column, S, T> {
689    /// The length of the value in bytes.
690    ///
691    /// The byte length of strings can depend on the encoding (UTF-8 or UTF-16).
692    ///
693    /// ```
694    /// # use rust_query::IntoExpr;
695    /// # rust_query::private::doctest::get_txn(|txn| {
696    /// assert_eq!(txn.query_one("€".into_expr().byte_len()), 3);
697    /// assert_eq!(txn.query_one("what".into_expr().byte_len()), 4);
698    /// assert_eq!(txn.query_one(vec![1, 2].into_expr().byte_len()), 2);
699    /// # });
700    /// ```
701    pub fn byte_len(&self) -> Expr<'column, S, i64> {
702        let lhs = self.inner.clone();
703        Expr::adhoc(lower::Expr::Func("octet_length", Box::new([lhs])))
704    }
705}
706
707impl<'column, S> Expr<'column, S, Vec<u8>> {
708    /// Create a new blob of zero bytes of the specified length.
709    ///
710    /// ```
711    /// # use rust_query::Expr;
712    /// # rust_query::private::doctest::get_txn(|txn| {
713    /// assert_eq!(txn.query_one(Expr::zero_blob(40)), vec![0; 40]);
714    /// # });
715    /// ```
716    pub fn zero_blob(len: impl IntoExpr<'column, S, Typ = i64>) -> Self {
717        let len = len.into_expr().inner;
718        Expr::adhoc(lower::Expr::Func("zeroblob", Box::new([len])))
719    }
720}
721
722// This is a copy of the function from the glob crate https://github.com/rust-lang/glob/blob/49ee1e92bd6e8c5854c0b339634f9b4b733aba4f/src/lib.rs#L720-L737.
723fn escape_glob(s: impl AsRef<str>) -> String {
724    let mut escaped = String::new();
725    for c in s.as_ref().chars() {
726        match c {
727            // note that ! does not need escaping because it is only special
728            // inside brackets
729            '?' | '*' | '[' | ']' => {
730                escaped.push('[');
731                escaped.push(c);
732                escaped.push(']');
733            }
734            c => {
735                escaped.push(c);
736            }
737        }
738    }
739    escaped
740}