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