Repository: diesel-rs/diesel (language: rust)

Issue:
Regression: LIKE queries on nullable fields in MySQL
<!--
If you want to report a bug, we added some points below which help us track down the problem faster.
-->

## Setup

Install the latest version of diesel for MySQL from GitHub, i.e. 
```toml
diesel = { git = "https://github.com/diesel-rs/diesel", features = ["mysql", "extras", "chrono", "r2d2"] }
```


### Versions

- **Rust:** 1.76.0
- **Diesel:** unstable, latest
- **Database:** MySQL
- **Operating System** MacOS

### Feature Flags

- **diesel:** ["mysql", "extras", "chrono", "r2d2"] 

## Problem Description

A previously compiling query (see below) now causes the following error:

the trait `expression::operators::LikeIsAllowedForType<diesel::sql_types::Nullable<diesel::sql_types::Text>>` is not implemented for `Mysql`

```rs

#[derive(AsChangeset, Clone, Debug, Default, Deserialize, Insertable, Queryable, Serialize)]
pub struct User {
  pub id: String,
  pub name: Option<String>,
  pub email: String,
  pub email_verified: Option<chrono::NaiveDateTime>,
  pub image: Option<String>,
  pub created_at: chrono::NaiveDateTime,
  pub state: String,
}

  pub fn search_by_name(connection: &mut MysqlConnection, name: &str) -> Vec<User> {
    users::table
      .filter(
        users::name
          .like(format!("%{}%", name))
          .or(users::email.like(format!("%{}%", name))),
      )
      .load::<User>(connection)
      .expect("Error reading user")
  }

```


### What are you trying to accomplish?

Just want to give a heads-up ahead of the next release

### What is the expected output?

Should compile

### What is the actual output?
```
error[E0277]: the trait bound `Mysql: expression::operators::LikeIsAllowedForType<diesel::sql_types::Nullable<diesel::sql_types::Text>>` is not satisfied
    --> bdata/src/dal/user.rs:227:21
     |
227  |       .load::<User>(connection)
     |        ----         ^^^^^^^^^^ the trait `expression::operators::LikeIsAllowedForType<diesel::sql_types::Nullable<diesel::sql_types::Text>>` is not implemented for `Mysql`
     |        |
     |        required by a bound introduced by this call
```

### Are you seeing any additional errors?

No

### Steps to reproduce

see above

## Checklist

- [x] I have already looked over the [issue tracker](https://github.com/diesel-rs/diesel/issues) and the [discussion forum](https://github.com/diesel-rs/diesel/discussions) for similar possible closed issues.
<!--
If you are unsure if your issue is a duplicate of an existing issue please link the issue in question here
--> 
- [x] This issue can be reproduced on Rust's stable channel. (Your issue will be
  closed if this is not the case)
- [x] This issue can be reproduced without requiring a third party crate

<!--
Thank you for your submission!  You're helping make Diesel more robust 🎉

We'll try to respond as quickly as possible.
-->



Discussion / hints:
Thanks for reporting and good job for noticing that ~2h after the relevant change was merged :+1:

I think we just miss the relevant impl for nullable here: https://github.com/diesel-rs/diesel/blob/6ef23bd9496f14e735372c88047ed69bf08c0910/diesel/src/expression/operators.rs#L749 

I think I would likely write something like that:

```rust
impl<T, DB> LikeIsAllowedForType<crate::sql_types::Nullable<T>> for DB 
where 
    DB: Backend + LikeIsAllowedForType<T>,  
{} 
```

That also should cover `Nullable<Binary>` for the postgres backend. 

Mind sumitting a PR that adds this impl + adds a test so that we don't break it again?
Would be happy to :) 
@weiznich I think I can figure out how to write the test, but can you point me to the workspace member library where the test should go?
https://github.com/diesel-rs/diesel/blob/master/diesel_tests/tests/filter.rs would be a good place for the test. I think the users table there already has a nullable text field. 
Thank you!

Fix patch:
diff --git a/diesel/src/expression/operators.rs b/diesel/src/expression/operators.rs
index 718cd0d149d1..961e80d037f8 100644
--- a/diesel/src/expression/operators.rs
+++ b/diesel/src/expression/operators.rs
@@ -747,3 +747,8 @@ where
 pub trait LikeIsAllowedForType<ST>: Backend {}
 
 impl<DB> LikeIsAllowedForType<crate::sql_types::Text> for DB where DB: Backend {}
+
+impl<T, DB> LikeIsAllowedForType<crate::sql_types::Nullable<T>> for DB where
+    DB: Backend + LikeIsAllowedForType<T>
+{
+}


Test patch:
diff --git a/diesel_tests/tests/filter.rs b/diesel_tests/tests/filter.rs
index a962b143a45f..f499f90cc393 100644
--- a/diesel_tests/tests/filter.rs
+++ b/diesel_tests/tests/filter.rs
@@ -432,6 +432,19 @@ fn filter_by_boxed_predicate() {
     assert_eq!(Ok(tess), queried_tess);
 }
 
+#[test]
+fn filter_like_nullable_column() {
+    use crate::schema::users::dsl::*;
+
+    let conn = &mut connection_with_gilbert_and_jonathan_in_users_table();
+    let jonathan = find_user_by_name("Jonathan", conn);
+
+    let data = users.filter(hair_color.like("%blue%")).load(conn);
+
+    let expected = Ok(vec![jonathan]);
+    assert_eq!(expected, data);
+}
+
 #[test]
 fn filter_subselect_referencing_outer_table() {
     use diesel::dsl::exists;
diff --git a/diesel_tests/tests/schema/mod.rs b/diesel_tests/tests/schema/mod.rs
index 8584e1b67971..bf586de6458f 100644
--- a/diesel_tests/tests/schema/mod.rs
+++ b/diesel_tests/tests/schema/mod.rs
@@ -333,6 +333,19 @@ pub fn insert_sean_and_tess_into_users_table(connection: &mut TestConnection) {
     ensure_primary_key_seq_greater_than(2, connection);
 }
 
+pub fn connection_with_gilbert_and_jonathan_in_users_table() -> TestConnection {
+    let mut connection = connection();
+    insert_gilbert_and_jonathan_into_users_table(&mut connection);
+    connection
+}
+
+pub fn insert_gilbert_and_jonathan_into_users_table(connection: &mut TestConnection) {
+    diesel::sql_query("INSERT INTO users (id, name, hair_color) VALUES (1, 'Gilbert', 'brown'), (2, 'Jonathan', 'electric-blue')")
+        .execute(connection)
+        .unwrap();
+    ensure_primary_key_seq_greater_than(2, connection);
+}
+
 pub fn connection_with_nullable_table_data() -> TestConnection {
     let mut connection = connection();