drizzle_postgres/expr/regex.rs
1//! `PostgreSQL` POSIX regular expression operators. Documented in [`crate::expr`].
2
3use crate::values::PostgresValue;
4use drizzle_core::expr::{Expr, NonNull, SQLExpr};
5use drizzle_core::scope::Arg;
6use drizzle_core::sql::{SQL, SQLChunk};
7use drizzle_types::postgres::types::Boolean;
8
9/// Tests whether text matches a regular expression, case-sensitively (`~`).
10///
11/// `expr` must be textual (`text`, `varchar`, `char` or an enum); `pattern`
12/// is bound as a `text` parameter. The result is NULL when `expr` is NULL.
13///
14/// # Examples
15///
16/// ```
17/// use drizzle_postgres::expr::regex_match;
18/// use drizzle_core::{ToSQL, expr::raw_non_null};
19/// use drizzle_postgres::values::PostgresValue;
20/// use drizzle_types::postgres::types::Text;
21///
22/// let name = raw_non_null::<PostgresValue, Text>("name");
23/// let cond = regex_match(name, "^[A-Z]");
24/// assert_eq!(cond.to_sql().sql(), "name ~ $1");
25/// ```
26#[allow(clippy::type_complexity)]
27pub fn regex_match<'a, E>(
28 expr: E,
29 pattern: &'a str,
30) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, E::Aggregate, Arg<E::Nullable, E::Sources>>
31where
32 E: Expr<'a, PostgresValue<'a>>,
33 E::SQLType: drizzle_types::Textual,
34{
35 SQLExpr::new(
36 expr.to_sql()
37 .push(SQLChunk::Raw("~".into()))
38 .append(SQL::param(PostgresValue::Text(pattern.into()))),
39 )
40}
41
42/// Tests whether text matches a regular expression, ignoring case (`~*`).
43///
44/// `expr` must be textual (`text`, `varchar`, `char` or an enum); `pattern`
45/// is bound as a `text` parameter. The result is NULL when `expr` is NULL.
46///
47/// # Examples
48///
49/// ```
50/// use drizzle_postgres::expr::regex_match_ci;
51/// use drizzle_core::{ToSQL, expr::raw_non_null};
52/// use drizzle_postgres::values::PostgresValue;
53/// use drizzle_types::postgres::types::Text;
54///
55/// let name = raw_non_null::<PostgresValue, Text>("name");
56/// let cond = regex_match_ci(name, "^john");
57/// assert_eq!(cond.to_sql().sql(), "name ~* $1");
58/// ```
59#[allow(clippy::type_complexity)]
60pub fn regex_match_ci<'a, E>(
61 expr: E,
62 pattern: &'a str,
63) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, E::Aggregate, Arg<E::Nullable, E::Sources>>
64where
65 E: Expr<'a, PostgresValue<'a>>,
66 E::SQLType: drizzle_types::Textual,
67{
68 SQLExpr::new(
69 expr.to_sql()
70 .push(SQLChunk::Raw("~*".into()))
71 .append(SQL::param(PostgresValue::Text(pattern.into()))),
72 )
73}
74
75/// Tests whether text does not match a regular expression, case-sensitively (`!~`).
76///
77/// `expr` must be textual (`text`, `varchar`, `char` or an enum); `pattern`
78/// is bound as a `text` parameter. The result is NULL when `expr` is NULL.
79///
80/// # Examples
81///
82/// ```
83/// use drizzle_postgres::expr::regex_not_match;
84/// use drizzle_core::{ToSQL, expr::raw_non_null};
85/// use drizzle_postgres::values::PostgresValue;
86/// use drizzle_types::postgres::types::Text;
87///
88/// let name = raw_non_null::<PostgresValue, Text>("name");
89/// let cond = regex_not_match(name, "^[0-9]");
90/// assert_eq!(cond.to_sql().sql(), "name !~ $1");
91/// ```
92#[allow(clippy::type_complexity)]
93pub fn regex_not_match<'a, E>(
94 expr: E,
95 pattern: &'a str,
96) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, E::Aggregate, Arg<E::Nullable, E::Sources>>
97where
98 E: Expr<'a, PostgresValue<'a>>,
99 E::SQLType: drizzle_types::Textual,
100{
101 SQLExpr::new(
102 expr.to_sql()
103 .push(SQLChunk::Raw("!~".into()))
104 .append(SQL::param(PostgresValue::Text(pattern.into()))),
105 )
106}
107
108/// Tests whether text does not match a regular expression, ignoring case (`!~*`).
109///
110/// `expr` must be textual (`text`, `varchar`, `char` or an enum); `pattern`
111/// is bound as a `text` parameter. The result is NULL when `expr` is NULL.
112///
113/// # Examples
114///
115/// ```
116/// use drizzle_postgres::expr::regex_not_match_ci;
117/// use drizzle_core::{ToSQL, expr::raw_non_null};
118/// use drizzle_postgres::values::PostgresValue;
119/// use drizzle_types::postgres::types::Text;
120///
121/// let name = raw_non_null::<PostgresValue, Text>("name");
122/// let cond = regex_not_match_ci(name, "^admin");
123/// assert_eq!(cond.to_sql().sql(), "name !~* $1");
124/// ```
125#[allow(clippy::type_complexity)]
126pub fn regex_not_match_ci<'a, E>(
127 expr: E,
128 pattern: &'a str,
129) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, E::Aggregate, Arg<E::Nullable, E::Sources>>
130where
131 E: Expr<'a, PostgresValue<'a>>,
132 E::SQLType: drizzle_types::Textual,
133{
134 SQLExpr::new(
135 expr.to_sql()
136 .push(SQLChunk::Raw("!~*".into()))
137 .append(SQL::param(PostgresValue::Text(pattern.into()))),
138 )
139}
140
141/// Method forms of the regex operators, available on every `PostgreSQL` expression.
142///
143/// Each method calls the free function of the same name and has the same operand rules.
144pub trait RegexExprExt<'a>: Expr<'a, PostgresValue<'a>> + Sized {
145 /// Tests whether `self` matches `pattern`, case-sensitively (`~`). See [`regex_match`].
146 #[allow(clippy::type_complexity)]
147 fn regex_match(
148 self,
149 pattern: &'a str,
150 ) -> SQLExpr<
151 'a,
152 PostgresValue<'a>,
153 Boolean,
154 NonNull,
155 Self::Aggregate,
156 Arg<Self::Nullable, Self::Sources>,
157 >
158 where
159 Self::SQLType: drizzle_types::Textual,
160 {
161 regex_match(self, pattern)
162 }
163
164 /// Tests whether `self` matches `pattern`, ignoring case (`~*`). See [`regex_match_ci`].
165 #[allow(clippy::type_complexity)]
166 fn regex_match_ci(
167 self,
168 pattern: &'a str,
169 ) -> SQLExpr<
170 'a,
171 PostgresValue<'a>,
172 Boolean,
173 NonNull,
174 Self::Aggregate,
175 Arg<Self::Nullable, Self::Sources>,
176 >
177 where
178 Self::SQLType: drizzle_types::Textual,
179 {
180 regex_match_ci(self, pattern)
181 }
182
183 /// Tests whether `self` does not match `pattern`, case-sensitively (`!~`). See [`regex_not_match`].
184 #[allow(clippy::type_complexity)]
185 fn regex_not_match(
186 self,
187 pattern: &'a str,
188 ) -> SQLExpr<
189 'a,
190 PostgresValue<'a>,
191 Boolean,
192 NonNull,
193 Self::Aggregate,
194 Arg<Self::Nullable, Self::Sources>,
195 >
196 where
197 Self::SQLType: drizzle_types::Textual,
198 {
199 regex_not_match(self, pattern)
200 }
201
202 /// Tests whether `self` does not match `pattern`, ignoring case (`!~*`). See [`regex_not_match_ci`].
203 #[allow(clippy::type_complexity)]
204 fn regex_not_match_ci(
205 self,
206 pattern: &'a str,
207 ) -> SQLExpr<
208 'a,
209 PostgresValue<'a>,
210 Boolean,
211 NonNull,
212 Self::Aggregate,
213 Arg<Self::Nullable, Self::Sources>,
214 >
215 where
216 Self::SQLType: drizzle_types::Textual,
217 {
218 regex_not_match_ci(self, pattern)
219 }
220}
221
222impl<'a, E: Expr<'a, PostgresValue<'a>>> RegexExprExt<'a> for E {}