sqlx icon indicating copy to clipboard operation
sqlx copied to clipboard

From sqlx 0.7 this code is not working anymore

Open frederikhors opened this issue 1 year ago • 5 comments

Hi people! Your work on sqlx is amazing! Congratulations and thank you very much! ♥

A few months ago - coming from Golang - I was looking for a way to use both direct queries and queries in transactions (I'm also using a clean code architecture, so I cannot call sqlx methods directly).

A very nice person on the rust Discord channel helped me put this code together and it works great: https://github.com/frederikhors/iss-sqlx-0.7.

This allows me to use code like this:

let mut tx = repo.start_transaction().await?;

let available_seats = tx.search_for_available_seats(stadium_id).await?;

let confirmed_seat = tx.confirm_seat(available_seats[0]).await?;

tx.commit().await?;

in addition to this:

let example = repo.clone_boxed().search_for_available_seats(stadium_id).await?;

Everything works perfectly with sqlx 0.6.3, but now with sqlx 0.7 I'm having this error:

error[E0277]: the trait bound `&'this mut Transaction<'static, Postgres>: sqlx::Executor<'this>` is not satisfied
  --> src\main.rs:99:28
   |
99 |     type Executor<'this> = &'this mut Self;
   |                            ^^^^^^^^^^^^^^^ the trait `sqlx::Executor<'this>` is not implemented for `&'this mut Transaction<'static, Postgres>`
   |
   = help: the following other types implement trait `sqlx::Executor<'c>`:
             <&'c mut PgConnection as sqlx::Executor<'c>>
             <&'c mut PgListener as sqlx::Executor<'c>>
             <&Pool<DB> as sqlx::Executor<'p>>
   = note: required for `<Transaction<'static, Postgres> as Executor>::Executor<'this>` to implement `PgExecutor<'this>`
note: required by a bound in `Executor::Executor`
  --> src\main.rs:78:41
   |
78 |     type Executor<'this>: Send + Sync + sqlx::PgExecutor<'this>;
   |                                         ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Executor::Executor`

For more information about this error, try `rustc --explain E0277`.

I saw the note in CHANGELOG about Executor trait but I don't know how to fix it in this case.

Could you suggest me a way to fix it?

Can you think of an alternative way to use instead of this Executor trait?

The custom trait

pub trait Executor: Send + Sync {
    type Executor<'this>: Send + Sync + sqlx::PgExecutor<'this>;

    // From https://users.rust-lang.org/t/why-does-this-impl-executor-does-not-live-long-enough/94241
    fn _disable_lint(e: Self::Executor<'_>) -> Self::Executor<'_>;

    fn as_executor(&mut self) -> Self::Executor<'_>;
}

impl Executor for sqlx::PgPool {
    type Executor<'this> = &'this Self;

    fn _disable_lint(e: Self::Executor<'_>) -> Self::Executor<'_> {
        e
    }

    fn as_executor(&mut self) -> Self::Executor<'_> {
        self
    }
}

impl Executor for sqlx::Transaction<'static, sqlx::Postgres> {
    type Executor<'this> = &'this mut Self;

    fn _disable_lint(e: Self::Executor<'_>) -> Self::Executor<'_> {
        e
    }

    fn as_executor(&mut self) -> Self::Executor<'_> {
        self
    }
}

frederikhors avatar Aug 01 '23 17:08 frederikhors

Just ran into this issue a minute ago. You need to add an extra deref to get the "concrete" PgConnection type that has Executor implemented.

From the change notes:

To fix this breakage, simply add a dereference where an impl Executor is expected, as they both dereference to the inner connection type which will still implement it:

  • &mut transaction -> &mut *transaction
  • &mut connection -> &mut *connection

You're going to need to change:

impl Executor for sqlx::Transaction<'static, sqlx::Postgres> {
    type Executor<'this> = &'this mut Self;

    fn _disable_lint(e: Self::Executor<'_>) -> Self::Executor<'_> {
        e
    }

    fn as_executor(&mut self) -> Self::Executor<'_> {
        self
    }
}

to something like,

impl Executor for sqlx::Transaction<'static, sqlx::Postgres> {
    type Executor<'this> = &'this mut PgConnection;

    fn _disable_lint(e: Self::Executor<'_>) -> Self::Executor<'_> {
        e
    }

    fn as_executor(&mut self) -> Self::Executor<'_> {
        &mut *self
    }
}

Or if you really want to, you could tell it to the use type that it derefs to, but I think this is less obvious that really you're turning it into a PgConnection.

use std::ops::Deref;

impl Executor for sqlx::Transaction<'static, sqlx::Postgres> {
    type Executor<'this> = &'this mut <Self as Deref>::Target;
    ...
}

jaxrtech avatar Aug 01 '23 18:08 jaxrtech

As an aside, since this might be helpful, I also had a lot of functions that had a tx argument:

async fn foo(tx: &mut Transaction<'tx, Postgres>) -> sqlx::Result<()> {
    sqlx::query!( ... ).execute(tx).await
}

This becomes either,

sqlx::query!( ... ).execute(&mut **tx).await

Or what seems a bit cleaner to me,

use std::ops::DerefMut;
sqlx::query!( ... ).execute(tx.deref_mut()).await

jaxrtech avatar Aug 01 '23 18:08 jaxrtech

@jaxrtech, amazing! Thank you very much!

Just one question: why are you using:

fn as_executor(&mut self) -> Self::Executor<'_> {
    &mut *self
}

instead of a simpler:

fn as_executor(&mut self) -> Self::Executor<'_> {
    self
}

The latter is working for me.

frederikhors avatar Aug 02 '23 12:08 frederikhors

Hi people! Your work on sqlx is amazing! Congratulations and thank you very much! ♥

A few months ago - coming from Golang - I was looking for a way to use both direct queries and queries in transactions (I'm also using a clean code architecture, so I cannot call sqlx methods directly).

A very nice person on the rust Discord channel helped me put this code together and it works great: https://github.com/frederikhors/iss-sqlx-0.7.

This allows me to use code like this:

let mut tx = repo.start_transaction().await?;

let available_seats = tx.search_for_available_seats(stadium_id).await?;

let confirmed_seat = tx.confirm_seat(available_seats[0]).await?;

tx.commit().await?;

in addition to this:

let example = repo.clone_boxed().search_for_available_seats(stadium_id).await?;

Everything works perfectly with sqlx 0.6.3, but now with sqlx 0.7 I'm having this error:

error[E0277]: the trait bound `&'this mut Transaction<'static, Postgres>: sqlx::Executor<'this>` is not satisfied
  --> src\main.rs:99:28
   |
99 |     type Executor<'this> = &'this mut Self;
   |                            ^^^^^^^^^^^^^^^ the trait `sqlx::Executor<'this>` is not implemented for `&'this mut Transaction<'static, Postgres>`
   |
   = help: the following other types implement trait `sqlx::Executor<'c>`:
             <&'c mut PgConnection as sqlx::Executor<'c>>
             <&'c mut PgListener as sqlx::Executor<'c>>
             <&Pool<DB> as sqlx::Executor<'p>>
   = note: required for `<Transaction<'static, Postgres> as Executor>::Executor<'this>` to implement `PgExecutor<'this>`
note: required by a bound in `Executor::Executor`
  --> src\main.rs:78:41
   |
78 |     type Executor<'this>: Send + Sync + sqlx::PgExecutor<'this>;
   |                                         ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Executor::Executor`

For more information about this error, try `rustc --explain E0277`.

I saw the note in CHANGELOG about Executor trait but I don't know how to fix it in this case.

Could you suggest me a way to fix it?

Can you think of an alternative way to use instead of this Executor trait?

The custom trait

pub trait Executor: Send + Sync {
    type Executor<'this>: Send + Sync + sqlx::PgExecutor<'this>;

    // From https://users.rust-lang.org/t/why-does-this-impl-executor-does-not-live-long-enough/94241
    fn _disable_lint(e: Self::Executor<'_>) -> Self::Executor<'_>;

    fn as_executor(&mut self) -> Self::Executor<'_>;
}

impl Executor for sqlx::PgPool {
    type Executor<'this> = &'this Self;

    fn _disable_lint(e: Self::Executor<'_>) -> Self::Executor<'_> {
        e
    }

    fn as_executor(&mut self) -> Self::Executor<'_> {
        self
    }
}

impl Executor for sqlx::Transaction<'static, sqlx::Postgres> {
    type Executor<'this> = &'this mut Self;

    fn _disable_lint(e: Self::Executor<'_>) -> Self::Executor<'_> {
        e
    }

    fn as_executor(&mut self) -> Self::Executor<'_> {
        self
    }
}

my code:

let sql = r#"insert into users (name,age,id_card,last_update) value(?,?,?,?)"#;
    let mut conn = pool.begin().await?; 
    let affect_rows = sqlx::query(sql)
        .bind("daheige")
        .bind(32)
        .bind("abc")
        .bind(chrono::NaiveDate::from_ymd(2022, 04, 13))
        .execute(&mut conn)
        .await?;
    conn.commit().await?; 
    let id = affect_rows.last_insert_id(); 
    println!("id = {}", id);

hi, I have this fatal problem too. 7.0.x version of the transaction operation, for mysql will not be compiled, error is as follows:

error[E0277]: the trait bound `&mut Transaction<'_, MySql>: Executor<'_>` is not satisfied
   --> src/main.rs:148:18
    |
148 |         .execute(&mut conn)
    |          ------- ^^^^^^^^^ the trait `Executor<'_>` is not implemented for `&mut Transaction<'_, MySql>`
    |          |
    |          required by a bound introduced by this call
    |
    = help: the following other types implement trait `Executor<'c>`:
              <&'c mut MySqlConnection as Executor<'c>>
              <&'c mut AnyConnection as Executor<'c>>
              <&Pool<DB> as Executor<'p>>
note: required by a bound in `Query::<'q, DB, A>::execute`
   --> /Users/heige/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-core-0.7.4/src/query.rs:155:12
    |
151 |     pub async fn execute<'e, 'c: 'e, E>(self, executor: E) -> Result<DB::QueryResult, Error>
    |                  ------- required by a bound in this associated function
...
155 |         E: Executor<'c, Database = DB>,
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Query::<'q, DB, A>::execute`

For more information about this error, try `rustc --explain E0277`.

I sincerely hope that sqlx developers can fix this bug.

daheige avatar Mar 21 '24 15:03 daheige

I corrected it in the following way fix my code, cargo check is normal.

let sql = r#"insert into users (name,age,id_card,last_update) value(?,?,?,?)"#;
let mut tx = pool.begin().await?; 
let conn = &mut tx; 
// https://github.com/launchbadge/sqlx/blob/main/examples/postgres/transaction/src/main.rs#L14
let affect_rows = sqlx::query(sql)
    .bind("daheige")
    .bind(32)
    .bind("abc")
    .bind(chrono::NaiveDate::from_ymd(2022, 04, 13))
    // In 0.7, `Transaction` can no longer implement `Executor` directly,
    // so it must be dereferenced to the internal connection type.
    .execute(&mut **conn)
    .await?;
tx.commit().await?; 
let id = affect_rows.last_insert_id(); 
println!("id = {}", id);

or

let sql = r#"insert into users (name,age,id_card,last_update) value(?,?,?,?)"#;
let mut tx = pool.begin().await?; 
// https://github.com/launchbadge/sqlx/blob/main/examples/postgres/transaction/src/main.rs#L14
let affect_rows = sqlx::query(sql)
    .bind("daheige")
    .bind(32)
    .bind("abc")
    .bind(chrono::NaiveDate::from_ymd(2022, 04, 13))
    // by calling the deref_mut method, the mutable reference inside tx is obtained after dereferencing mut tx.
    .execute(tx.deref_mut()) 
    .await?;
tx.commit().await?; 
let id = affect_rows.last_insert_id(); 
println!("id = {}", id);

The second method through deref_mut is relatively more readable. The second method is recommended. It is possible to run cargo check.

daheige avatar Mar 22 '24 02:03 daheige