Skip to main content

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}