Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add like, not_like and concat for PostgreSQL Bytea #3150

Merged
merged 2 commits into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 178 additions & 2 deletions diesel/src/pg/expression/expression_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use crate::dsl;
use crate::expression::grouped::Grouped;
use crate::expression::operators::{Asc, Desc};
use crate::expression::{AsExpression, Expression, IntoSql, TypedExpressionType};
use crate::sql_types::{Array, Inet, Integer, Jsonb, SqlType, Text, VarChar};
use crate::pg::expression::expression_methods::private::BinaryOrNullableBinary;
use crate::sql_types::{Array, Binary, Inet, Integer, Jsonb, SqlType, Text, VarChar};
use crate::EscapeExpressionMethods;

/// PostgreSQL specific methods which are present on all expressions.
Expand Down Expand Up @@ -2177,9 +2178,179 @@ where
{
}

/// PostgreSQL specific methods present on Binary expressions.
#[cfg(feature = "postgres_backend")]
pub trait PgBinaryExpressionMethods: Expression + Sized {
/// Concatenates two PostgreSQL byte arrays using the `||` operator.
///
/// # Example
///
/// ```rust
/// # include!("../../doctest_setup.rs");
/// #
/// # table! {
/// # users {
/// # id -> Integer,
/// # name -> Binary,
/// # hair_color -> Nullable<Binary>,
/// # }
/// # }
/// #
/// # fn main() {
/// # use self::users::dsl::*;
/// # use diesel::insert_into;
/// #
/// # let connection = &mut connection_no_data();
/// # diesel::sql_query("CREATE TABLE users (
/// # id INTEGER PRIMARY KEY,
/// # name BYTEA NOT NULL,
/// # hair_color BYTEA
/// # )").execute(connection).unwrap();
/// #
/// # insert_into(users)
/// # .values(&vec![
/// # (id.eq(1), name.eq("Sean".as_bytes()), Some(hair_color.eq(Some("Green".as_bytes())))),
/// # (id.eq(2), name.eq("Tess".as_bytes()), None),
/// # ])
/// # .execute(connection)
/// # .unwrap();
/// #
/// let names = users.select(name.concat(" the Greatest".as_bytes())).load(connection);
/// let expected_names = vec![
/// b"Sean the Greatest".to_vec(),
/// b"Tess the Greatest".to_vec()
/// ];
/// assert_eq!(Ok(expected_names), names);
///
/// // If the value is nullable, the output will be nullable
/// let names = users.select(hair_color.concat("ish".as_bytes())).load(connection);
/// let expected_names = vec![
/// Some(b"Greenish".to_vec()),
/// None,
/// ];
/// assert_eq!(Ok(expected_names), names);
/// # }
/// ```
fn concat<T>(self, other: T) -> dsl::ConcatBinary<Self, T>
where
Self::SqlType: SqlType,
T: AsExpression<Binary>,
{
Grouped(ConcatBinary::new(self, other.as_expression()))
}

/// Creates a PostgreSQL binary `LIKE` expression.
///
/// This method is case sensitive. There is no case-insensitive
/// equivalent as of PostgreSQL 14.
///
/// # Examples
///
/// ```rust
/// # include!("../../doctest_setup.rs");
/// #
/// # table! {
/// # users {
/// # id -> Integer,
/// # name -> Binary,
/// # }
/// # }
/// #
/// # fn main() {
/// # use self::users::dsl::*;
/// # use diesel::insert_into;
/// #
/// # let connection = &mut connection_no_data();
/// # diesel::sql_query("CREATE TABLE users (
/// # id INTEGER PRIMARY KEY,
/// # name BYTEA NOT NULL
/// # )").execute(connection).unwrap();
/// #
/// # insert_into(users)
/// # .values(&vec![
/// # (id.eq(1), name.eq("Sean".as_bytes())),
/// # (id.eq(2), name.eq("Tess".as_bytes()))
/// # ])
/// # .execute(connection)
/// # .unwrap();
/// #
/// let starts_with_s = users
/// .select(name)
/// .filter(name.like(b"S%".to_vec()))
/// .load(connection);
/// assert_eq!(Ok(vec![b"Sean".to_vec()]), starts_with_s);
/// # }
/// ```
fn like<T>(self, other: T) -> dsl::LikeBinary<Self, T>
where
Self::SqlType: SqlType,
T: AsExpression<Binary>,
{
Grouped(LikeBinary::new(self, other.as_expression()))
}

/// Creates a PostgreSQL binary `LIKE` expression.
///
/// This method is case sensitive. There is no case-insensitive
/// equivalent as of PostgreSQL 14.
///
/// # Examples
///
/// ```rust
/// # include!("../../doctest_setup.rs");
/// #
/// # table! {
/// # users {
/// # id -> Integer,
/// # name -> Binary,
/// # }
/// # }
/// #
/// # fn main() {
/// # use self::users::dsl::*;
/// # use diesel::insert_into;
/// #
/// # let connection = &mut connection_no_data();
/// # diesel::sql_query("CREATE TABLE users (
/// # id INTEGER PRIMARY KEY,
/// # name BYTEA NOT NULL
/// # )").execute(connection).unwrap();
/// #
/// # insert_into(users)
/// # .values(&vec![
/// # (id.eq(1), name.eq("Sean".as_bytes())),
/// # (id.eq(2), name.eq("Tess".as_bytes()))
/// # ])
/// # .execute(connection)
/// # .unwrap();
/// #
/// let starts_with_s = users
/// .select(name)
/// .filter(name.not_like(b"S%".to_vec()))
/// .load(connection);
/// assert_eq!(Ok(vec![b"Tess".to_vec()]), starts_with_s);
/// # }
/// ```
fn not_like<T>(self, other: T) -> dsl::NotLikeBinary<Self, T>
where
Self::SqlType: SqlType,
T: AsExpression<Binary>,
{
Grouped(NotLikeBinary::new(self, other.as_expression()))
}
}

#[doc(hidden)]
impl<T> PgBinaryExpressionMethods for T
where
T: Expression,
T::SqlType: BinaryOrNullableBinary,
{
}

mod private {
use crate::sql_types::{
Array, Cidr, Inet, Integer, Json, Jsonb, Nullable, Range, SqlType, Text,
Array, Binary, Cidr, Inet, Integer, Json, Jsonb, Nullable, Range, SqlType, Text,
};
use crate::{Expression, IntoSql};

Expand Down Expand Up @@ -2361,4 +2532,9 @@ mod private {
pub trait TextOrInteger {}
impl TextOrInteger for Text {}
impl TextOrInteger for Integer {}

pub trait BinaryOrNullableBinary {}

impl BinaryOrNullableBinary for Binary {}
impl BinaryOrNullableBinary for Nullable<Binary> {}
}
16 changes: 15 additions & 1 deletion diesel/src/pg/expression/helper_types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::dsl::{AsExpr, AsExprOf, SqlTypeOf};
use crate::expression::grouped::Grouped;
use crate::pg::types::sql_types::Array;
use crate::sql_types::{Inet, Integer, Jsonb, VarChar};
use crate::sql_types::{Binary, Inet, Integer, Jsonb, VarChar};

/// The return type of [`lhs.ilike(rhs)`](super::expression_methods::PgTextExpressionMethods::ilike)
#[cfg(feature = "postgres_backend")]
Expand Down Expand Up @@ -170,3 +170,17 @@ pub type RetrieveByPathAsTextJson<Lhs, Rhs> =
#[cfg(feature = "postgres_backend")]
pub type RemoveByPathFromJsonb<Lhs, Rhs> =
Grouped<super::operators::RemoveByPathFromJsonb<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;

/// The return type of [`lhs.remove_by_path(rhs)`](super::expression_methods::PgBinaryExpressionMethods::concat)
#[cfg(feature = "postgres_backend")]
pub type ConcatBinary<Lhs, Rhs> =
Grouped<super::operators::ConcatBinary<Lhs, AsExprOf<Rhs, Binary>>>;

/// The return type of [`lhs.remove_by_path(rhs)`](super::expression_methods::PgBinaryExpressionMethods::like)
#[cfg(feature = "postgres_backend")]
pub type LikeBinary<Lhs, Rhs> = Grouped<super::operators::LikeBinary<Lhs, AsExprOf<Rhs, Binary>>>;

/// The return type of [`lhs.remove_by_path(rhs)`](super::expression_methods::PgBinaryExpressionMethods::not_like)
#[cfg(feature = "postgres_backend")]
pub type NotLikeBinary<Lhs, Rhs> =
Grouped<super::operators::NotLikeBinary<Lhs, AsExprOf<Rhs, Binary>>>;
5 changes: 4 additions & 1 deletion diesel/src/pg/expression/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::pg::Pg;
use crate::query_builder::update_statement::changeset::AssignmentTarget;
use crate::query_builder::{AstPass, QueryFragment, QueryId};
use crate::sql_types::{
Array, Bigint, Bool, DieselNumericOps, Inet, Integer, Jsonb, SqlType, Text,
Array, Bigint, Binary, Bool, DieselNumericOps, Inet, Integer, Jsonb, SqlType, Text,
};
use crate::{Column, QueryResult};

Expand Down Expand Up @@ -49,6 +49,9 @@ __diesel_infix_operator!(
);
infix_operator!(RetrieveByPathAsTextJson, " #>> ", Text, backend: Pg);
infix_operator!(RemoveByPathFromJsonb, " #-", Jsonb, backend: Pg);
infix_operator!(ConcatBinary, " || ", Binary, backend: Pg);
infix_operator!(LikeBinary, " LIKE ", backend: Pg);
infix_operator!(NotLikeBinary, " NOT LIKE ", backend: Pg);

#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)]
#[doc(hidden)]
Expand Down
4 changes: 2 additions & 2 deletions diesel_compile_tests/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ error[E0277]: the trait bound `{integer}: SelectableExpression<NoFromClause>` is
<(T0, T1) as SelectableExpression<QS>>
<(T0, T1, T2) as SelectableExpression<QS>>
<(T0, T1, T2, T3) as SelectableExpression<QS>>
and 155 others
and 158 others
= note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
= note: 1 redundant requirements hidden
= note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
Expand All @@ -134,7 +134,7 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
<(T0, T1) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2, T3) as ValidGrouping<__GroupByClause>>
and 140 others
and 143 others
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
= note: 1 redundant requirements hidden
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
Expand All @@ -152,7 +152,7 @@ error[E0277]: the trait bound `{integer}: SelectableExpression<NoFromClause>` is
<(T0, T1) as SelectableExpression<QS>>
<(T0, T1, T2) as SelectableExpression<QS>>
<(T0, T1, T2, T3) as SelectableExpression<QS>>
and 155 others
and 158 others
= note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
= note: 1 redundant requirements hidden
= note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
Expand All @@ -171,7 +171,7 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
<(T0, T1) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2, T3) as ValidGrouping<__GroupByClause>>
and 140 others
and 143 others
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
= note: 1 redundant requirements hidden
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
Expand All @@ -189,7 +189,7 @@ error[E0277]: the trait bound `{integer}: QueryFragment<Pg>` is not satisfied
<() as QueryFragment<DB>>
<(T0, T1) as QueryFragment<__DB>>
<(T0, T1, T2) as QueryFragment<__DB>>
and 269 others
and 272 others
= note: required because of the requirements on the impl of `QueryFragment<Pg>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
= note: 3 redundant requirements hidden
= note: required because of the requirements on the impl of `QueryFragment<Pg>` for `SelectStatement<NoFromClause, diesel::query_builder::select_clause::SelectClause<diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>>>`
Expand All @@ -206,7 +206,7 @@ error[E0277]: the trait bound `{integer}: QueryId` is not satisfied
<() as QueryId>
<(T0, T1) as QueryId>
<(T0, T1, T2) as QueryId>
and 231 others
and 234 others
= note: required because of the requirements on the impl of `QueryId` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
= note: 3 redundant requirements hidden
= note: required because of the requirements on the impl of `QueryId` for `SelectStatement<NoFromClause, diesel::query_builder::select_clause::SelectClause<diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>>>`
Expand All @@ -228,6 +228,6 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
<(T0, T1) as diesel::Expression>
<(T0, T1, T2) as diesel::Expression>
<(T0, T1, T2, T3) as diesel::Expression>
and 120 others
and 123 others
= note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Double>` for `{integer}`
= note: required because of the requirements on the impl of `AsExpressionList<diesel::sql_types::Double>` for `({integer}, f64)`
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
<(T0, T1) as diesel::Expression>
<(T0, T1, T2) as diesel::Expression>
<(T0, T1, T2, T3) as diesel::Expression>
and 124 others
and 127 others
= note: required because of the requirements on the impl of `diesel::Expression` for `diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>`
= note: required because of the requirements on the impl of `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>>>` for `SelectStatement<FromClause<string_primary_key::table>>`

Expand All @@ -47,7 +47,7 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
<(T0, T1) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2, T3) as ValidGrouping<__GroupByClause>>
and 146 others
and 149 others
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>`
= note: required because of the requirements on the impl of `NonAggregate` for `diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>>`
= note: required because of the requirements on the impl of `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>>>` for `SelectStatement<FromClause<string_primary_key::table>>`
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
<(T0, T1) as diesel::Expression>
<(T0, T1, T2) as diesel::Expression>
<(T0, T1, T2, T3) as diesel::Expression>
and 124 others
and 127 others
= note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Text>` for `{integer}`
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
extern crate diesel;

use diesel::prelude::*;

table! {
users {
id -> Integer,
name -> Binary,
}
}

fn main() {
use self::users::dsl::*;

let mut connection = SqliteConnection::establish("").unwrap();

users
.select(name.concat(b"foo".to_vec()))
.filter(name.like(b"bar".to_vec()))
.filter(name.not_like(b"baz".to_vec()))
.get_result::<Vec<u8>>(&mut connection).unwrap();

let mut connection = MysqlConnection::establish("").unwrap();

users
.select(name.concat(b"foo".to_vec()))
.filter(name.like(b"bar".to_vec()))
.filter(name.not_like(b"baz".to_vec()))
.get_result::<Vec<u8>>(&mut connection).unwrap();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0271]: type mismatch resolving `<diesel::SqliteConnection as diesel::Connection>::Backend == Pg`
--> tests/fail/pg_specific_binary_expressions_only_usable_with_pg.rs:21:10
|
21 | .get_result::<Vec<u8>>(&mut connection).unwrap();
| ^^^^^^^^^^ expected struct `Sqlite`, found struct `Pg`
|
= note: required because of the requirements on the impl of `LoadQuery<'_, diesel::SqliteConnection, Vec<u8>>` for `SelectStatement<FromClause<users::table>, diesel::query_builder::select_clause::SelectClause<diesel::expression::grouped::Grouped<diesel::pg::expression::operators::ConcatBinary<columns::name, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Binary, Vec<u8>>>>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<diesel::expression::grouped::Grouped<diesel::expression::operators::And<diesel::expression::grouped::Grouped<diesel::pg::expression::operators::LikeBinary<columns::name, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Binary, Vec<u8>>>>, diesel::expression::grouped::Grouped<diesel::pg::expression::operators::NotLikeBinary<columns::name, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Binary, Vec<u8>>>>>>>>`

error[E0271]: type mismatch resolving `<diesel::MysqlConnection as diesel::Connection>::Backend == Pg`
--> tests/fail/pg_specific_binary_expressions_only_usable_with_pg.rs:29:10
|
29 | .get_result::<Vec<u8>>(&mut connection).unwrap();
| ^^^^^^^^^^ expected struct `Mysql`, found struct `Pg`
|
= note: required because of the requirements on the impl of `LoadQuery<'_, diesel::MysqlConnection, Vec<u8>>` for `SelectStatement<FromClause<users::table>, diesel::query_builder::select_clause::SelectClause<diesel::expression::grouped::Grouped<diesel::pg::expression::operators::ConcatBinary<columns::name, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Binary, Vec<u8>>>>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<diesel::expression::grouped::Grouped<diesel::expression::operators::And<diesel::expression::grouped::Grouped<diesel::pg::expression::operators::LikeBinary<columns::name, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Binary, Vec<u8>>>>, diesel::expression::grouped::Grouped<diesel::pg::expression::operators::NotLikeBinary<columns::name, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Binary, Vec<u8>>>>>>>>`