Skip to main content

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 {}