Skip to main content

diesel/expression_methods/
text_expression_methods.rs

1use self::private::TextOrNullableText;
2use crate::dsl;
3use crate::expression::grouped::Grouped;
4use crate::expression::operators::{Collate, Concat, Like, NotLike};
5use crate::expression::{AsExpression, Expression};
6
7use crate::sql_types::SqlType;
8
9/// Methods present on text expressions
10pub trait TextExpressionMethods: Expression + Sized {
11    /// Concatenates two strings using the `||` operator.
12    ///
13    /// # Example
14    ///
15    /// ```rust
16    /// # include!("../doctest_setup.rs");
17    /// #
18    /// # table! {
19    /// #     users {
20    /// #         id -> Integer,
21    /// #         name -> VarChar,
22    /// #         hair_color -> Nullable<Text>,
23    /// #     }
24    /// # }
25    /// #
26    /// # // Reading the query result requires reading memory allocated by the
27    /// # // native sqlite library, which miri cannot track provenance for.
28    /// # #[cfg(miri)]
29    /// # fn main() {}
30    /// # #[cfg(not(miri))]
31    /// # fn main() {
32    /// #     use self::users::dsl::*;
33    /// #     use diesel::insert_into;
34    /// #
35    /// #     let connection = &mut connection_no_data();
36    /// #     diesel::sql_query("CREATE TEMPORARY TABLE users (
37    /// #         id INTEGER PRIMARY KEY,
38    /// #         name VARCHAR(255) NOT NULL,
39    /// #         hair_color VARCHAR(255)
40    /// #     )").execute(connection).unwrap();
41    /// #
42    /// #     insert_into(users)
43    /// #         .values(&vec![
44    /// #             (id.eq(1), name.eq("Sean"), hair_color.eq(Some("Green"))),
45    /// #             (id.eq(2), name.eq("Tess"), hair_color.eq(None)),
46    /// #         ])
47    /// #         .execute(connection)
48    /// #         .unwrap();
49    /// #
50    /// let names = users.select(name.concat(" the Greatest")).load(connection);
51    /// let expected_names = vec![
52    ///     "Sean the Greatest".to_string(),
53    ///     "Tess the Greatest".to_string(),
54    /// ];
55    /// assert_eq!(Ok(expected_names), names);
56    ///
57    /// // If the value is nullable, the output will be nullable
58    /// let names = users.select(hair_color.concat("ish")).load(connection);
59    /// let expected_names = vec![Some("Greenish".to_string()), None];
60    /// assert_eq!(Ok(expected_names), names);
61    /// # }
62    /// ```
63    fn concat<T>(self, other: T) -> dsl::Concat<Self, T>
64    where
65        Self::SqlType: SqlType,
66        T: AsExpression<Self::SqlType>,
67    {
68        Grouped(Concat::new(self, other.as_expression()))
69    }
70
71    /// Returns a SQL `COLLATE` expression.
72    ///
73    /// This method can be used to control the collation of a column or expression.
74    ///
75    /// # Examples
76    ///
77    /// ```rust
78    /// # include!("../doctest_setup.rs");
79    /// #
80    /// # // Reading the query result requires reading memory allocated by the
81    /// # // native sqlite library, which miri cannot track provenance for.
82    /// # #[cfg(miri)]
83    /// # fn main() {}
84    /// # #[cfg(not(miri))]
85    /// # fn main() {
86    /// #     run_test().unwrap();
87    /// # }
88    /// #
89    /// # fn run_test() -> QueryResult<()> {
90    /// #     use schema::users::dsl::*;
91    /// #     let connection = &mut establish_connection();
92    /// #     #[cfg(not(feature = "sqlite"))]
93    /// #     return Ok(());
94    /// #
95    /// #     #[cfg(feature = "sqlite")]
96    /// #     {
97    ///     use diesel::collation::NoCase;
98    ///
99    ///     let names = users
100    ///         .select(name)
101    ///         .filter(name.collate(NoCase).eq("sean"))
102    ///         .load::<String>(connection)?;
103    ///     assert_eq!(vec!["Sean"], names);
104    /// #     }
105    /// #     Ok(())
106    /// # }
107    /// ```
108    fn collate<C>(self, collation: C) -> dsl::Collate<Self, C>
109    where
110        C: crate::collation::Collation,
111    {
112        Grouped(Collate::new(self, collation))
113    }
114
115    /// Returns a SQL `LIKE` expression
116    ///
117    /// This method is case insensitive for SQLite and MySQL.
118    /// On PostgreSQL, `LIKE` is case sensitive. You may use
119    /// [`ilike()`](../expression_methods/trait.PgTextExpressionMethods.html#method.ilike)
120    /// for case insensitive comparison on PostgreSQL.
121    ///
122    /// # Examples
123    ///
124    /// ```rust
125    /// # include!("../doctest_setup.rs");
126    /// #
127    /// # // Reading the query result requires reading memory allocated by the
128    /// # // native sqlite library, which miri cannot track provenance for.
129    /// # #[cfg(miri)]
130    /// # fn main() {}
131    /// # #[cfg(not(miri))]
132    /// # fn main() {
133    /// #     run_test().unwrap();
134    /// # }
135    /// #
136    /// # fn run_test() -> QueryResult<()> {
137    /// #     use schema::users::dsl::*;
138    /// #     let connection = &mut establish_connection();
139    /// #
140    /// let starts_with_s = users
141    ///     .select(name)
142    ///     .filter(name.like("S%"))
143    ///     .load::<String>(connection)?;
144    /// assert_eq!(vec!["Sean"], starts_with_s);
145    /// #     Ok(())
146    /// # }
147    /// ```
148    fn like<T>(self, other: T) -> dsl::Like<Self, T>
149    where
150        Self::SqlType: SqlType,
151        T: AsExpression<Self::SqlType>,
152    {
153        Grouped(Like::new(self, other.as_expression()))
154    }
155
156    /// Returns a SQL `NOT LIKE` expression
157    ///
158    /// This method is case insensitive for SQLite and MySQL.
159    /// On PostgreSQL `NOT LIKE` is case sensitive. You may use
160    /// [`not_ilike()`](../expression_methods/trait.PgTextExpressionMethods.html#method.not_ilike)
161    /// for case insensitive comparison on PostgreSQL.
162    ///
163    /// # Examples
164    ///
165    /// ```rust
166    /// # include!("../doctest_setup.rs");
167    /// #
168    /// # // Reading the query result requires reading memory allocated by the
169    /// # // native sqlite library, which miri cannot track provenance for.
170    /// # #[cfg(miri)]
171    /// # fn main() {}
172    /// # #[cfg(not(miri))]
173    /// # fn main() {
174    /// #     run_test().unwrap();
175    /// # }
176    /// #
177    /// # fn run_test() -> QueryResult<()> {
178    /// #     use schema::users::dsl::*;
179    /// #     let connection = &mut establish_connection();
180    /// #
181    /// let doesnt_start_with_s = users
182    ///     .select(name)
183    ///     .filter(name.not_like("S%"))
184    ///     .load::<String>(connection)?;
185    /// assert_eq!(vec!["Tess"], doesnt_start_with_s);
186    /// #     Ok(())
187    /// # }
188    /// ```
189    fn not_like<T>(self, other: T) -> dsl::NotLike<Self, T>
190    where
191        Self::SqlType: SqlType,
192        T: AsExpression<Self::SqlType>,
193    {
194        Grouped(NotLike::new(self, other.as_expression()))
195    }
196}
197
198impl<T> TextExpressionMethods for T
199where
200    T: Expression,
201    T::SqlType: TextOrNullableText,
202{
203}
204
205mod private {
206    use crate::sql_types::{Nullable, Text};
207
208    /// Marker trait used to implement `TextExpressionMethods` on the appropriate
209    /// types. Once coherence takes associated types into account, we can remove
210    /// this trait.
211    pub trait TextOrNullableText {}
212
213    impl TextOrNullableText for Text {}
214    impl TextOrNullableText for Nullable<Text> {}
215
216    #[cfg(feature = "postgres_backend")]
217    impl TextOrNullableText for crate::pg::sql_types::Citext {}
218    #[cfg(feature = "postgres_backend")]
219    impl TextOrNullableText for Nullable<crate::pg::sql_types::Citext> {}
220}