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}