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}