drizzle_postgres/expr/ilike.rs
1//! `PostgreSQL` `ILIKE` operators. Documented in [`crate::expr`].
2
3use crate::values::PostgresValue;
4use drizzle_core::expr::{AggregateKind, ComparisonOperand, Expr, NonNull, SQLExpr};
5use drizzle_core::scope::Arg;
6use drizzle_core::sql::{SQLChunk, Token};
7use drizzle_types::postgres::types::Boolean;
8use drizzle_types::{Compatible, Textual};
9
10/// Result of [`ilike`] and [`not_ilike`]: `boolean`, NULL when either operand
11/// is NULL, and an aggregate when either operand is one.
12type IlikeExpr<'a, E, P> = SQLExpr<
13 'a,
14 PostgresValue<'a>,
15 Boolean,
16 NonNull,
17 <<E as Expr<'a, PostgresValue<'a>>>::Aggregate as AggregateKind>::Or<
18 <P as ComparisonOperand<'a, PostgresValue<'a>, E>>::Aggregate,
19 >,
20 (
21 Arg<
22 <E as Expr<'a, PostgresValue<'a>>>::Nullable,
23 <E as drizzle_core::expr::ExprSources>::Sources,
24 >,
25 Arg<
26 <P as ComparisonOperand<'a, PostgresValue<'a>, E>>::Nullable,
27 <P as ComparisonOperand<'a, PostgresValue<'a>, E>>::Sources,
28 >,
29 ),
30>;
31
32/// Matches a text expression against a `LIKE` pattern, ignoring case (`ILIKE`).
33///
34/// In the pattern, `%` matches any run of characters and `_` matches one
35/// character. The result is a boolean condition for `WHERE`, `HAVING` or a
36/// join, and is NULL when either operand is NULL.
37///
38/// # Examples
39///
40/// ```
41/// use drizzle_core::{ToSQL, expr::raw_non_null};
42/// use drizzle_postgres::expr::ilike;
43/// use drizzle_postgres::values::PostgresValue;
44/// use drizzle_types::postgres::types::Text;
45///
46/// // Stands in for a `name text NOT NULL` column.
47/// let name = raw_non_null::<PostgresValue, Text>("name");
48/// let cond = ilike(name, "%john%"); // matches "John", "JOHNNY", ...
49/// assert_eq!(cond.to_sql().sql(), "name ILIKE $1");
50/// ```
51///
52/// # Type safety
53///
54/// Both sides must be text.
55///
56/// ```compile_fail
57/// use drizzle_core::expr::raw_non_null;
58/// use drizzle_postgres::expr::ilike;
59/// use drizzle_postgres::values::PostgresValue;
60/// use drizzle_types::postgres::types::Int4;
61///
62/// let age = raw_non_null::<PostgresValue, Int4>("age");
63/// let _ = ilike(age, "%1%"); // `int4` is not textual
64/// ```
65pub fn ilike<'a, E, P>(expr: E, pattern: P) -> IlikeExpr<'a, E, P>
66where
67 E: Expr<'a, PostgresValue<'a>>,
68 P: ComparisonOperand<'a, PostgresValue<'a>, E>,
69 E::SQLType: Compatible<P::SQLType> + Textual,
70 P::SQLType: Textual,
71{
72 SQLExpr::new(
73 expr.to_sql()
74 .push(SQLChunk::Raw("ILIKE".into()))
75 .append(pattern.into_comparison_sql()),
76 )
77}
78
79/// Tests that a text expression does not match a `LIKE` pattern, ignoring case
80/// (`NOT ILIKE`).
81///
82/// Operand rules and NULL handling are the same as [`ilike`].
83///
84/// # Examples
85///
86/// ```
87/// use drizzle_core::{ToSQL, expr::raw_non_null};
88/// use drizzle_postgres::expr::not_ilike;
89/// use drizzle_postgres::values::PostgresValue;
90/// use drizzle_types::postgres::types::Varchar;
91///
92/// let email = raw_non_null::<PostgresValue, Varchar>("email");
93/// let cond = not_ilike(email, "%@example.com");
94/// assert_eq!(cond.to_sql().sql(), "email NOT ILIKE $1");
95/// ```
96pub fn not_ilike<'a, E, P>(expr: E, pattern: P) -> IlikeExpr<'a, E, P>
97where
98 E: Expr<'a, PostgresValue<'a>>,
99 P: ComparisonOperand<'a, PostgresValue<'a>, E>,
100 E::SQLType: Compatible<P::SQLType> + Textual,
101 P::SQLType: Textual,
102{
103 SQLExpr::new(
104 expr.to_sql()
105 .push(Token::NOT)
106 .push(SQLChunk::Raw("ILIKE".into()))
107 .append(pattern.into_comparison_sql()),
108 )
109}