SQLx reuse transactions in functions

Hi,
I'm creating a simple application to interact with a database using the SQLx library. Currently, I'm trying to reuse a transaction acquired from a pool with pool.begin(). However, I'm running into problems with re reuse of the transaction. The function truncate should deletes all entries from tables given in the config object. The config object looks like:

struct Config {
  locations: Vec<ConfigItem>,
}
struct ConfigItem {
  table_name: String,
  dir_path: String,
  upload_url: String,
}

Since the function should return the affected rows, I wanted to use fold to accumulate the affected rows of all the deleted tables. Because SQLx is async, I need to await the result so I decided to convert the config.locations into a stream. This is the Code I'm having issues with:

async fn truncate(config: &Config, tx: &mut Transaction<'_, MySql>) -> Result<u64> {
  let stream = stream::iter(&config.locations); 

  let rows_affected = stream.fold(0, |acc, item| async move {
    let query = format!("DELETE FROM {}", item.table_name);
    acc + sqlx::query(&query)
      .execute(tx)
      .await
      .unwrap()
      .rows_affected()
  }); 
  Ok(rows_affected)
}

The Error is:

error[E0507]: cannot move out of `tx`, a captured variable in an `FnMut` closure
   --> src/main.rs:103:63
    |
84  |   async fn truncate(config: &Config, tx: &mut Transaction<'_, MySql>) -> Result<u64> {
    |                                      -- captured outer variable
...
103 |       let rows_affected = stream.fold(0, |acc, item| async move {
    |  ________________________________________-----------____________^
    | |                                        |
    | |                                        captured by this `FnMut` closure
104 | |         let query = format!("DELETE FROM {}", item.table_name);
105 | |         acc + sqlx::query(&query)
106 | |             .execute(tx)
    | |                      --
    | |                      |
    | |                      variable moved due to use in generator
    | |                      move occurs because `tx` has type `&mut Transaction<'_, MySql>`, which does not implement the `Copy` trait
...   |
109 | |             .rows_affected()
110 | |     });
    | |_____^ move out of `tx` occurs here

I get that the issue comes from async move which forces the closure to take ownership of the transaction. I tried to remove the move which gives me the following error:

async fn truncate(config: &Config, tx: &mut Transaction<'_, MySql>) -> Result<u64> {
    |                                      -- captured outer variable
...
103 |       let rows_affected = stream.fold(0, |acc, item| async {
    |  ________________________________________-----------_______^
    | |                                        |
    | |                                        captured by this `FnMut` closure
104 | |         let query = format!("DELETE FROM {}", item.table_name);
105 | |         acc + sqlx::query(&query)
106 | |             .execute(tx)
    | |                      --
    | |                      |
    | |                      variable moved due to use in generator
    | |                      move occurs because `tx` has type `&mut Transaction<'_, MySql>`, which does not implement the `Copy` trait
...   |
109 | |             .rows_affected()
110 | |     });
error[E0373]: async block may outlive the current function, but it borrows `acc`, which is owned by the current function
   --> src/main.rs:103:58
    |
103 |       let rows_affected = stream.fold(0, |acc, item| async {
    |  __________________________________________________________^
104 | |         let query = format!("DELETE FROM {}", item.table_name);
105 | |         acc + sqlx::query(&query)
    | |         --- `acc` is borrowed here
106 | |             .execute(tx)
...   |
109 | |             .rows_affected()
110 | |     });
    | |_____^ may outlive borrowed value `acc`

I don't really understand this error. I thought about cloning the tx since it's a RC Reference, but clone is not implemented for tx. I want to use the same tx in other functions later on which change some other stuff in the database and then commit it all at once.

How can I achieve the functionality of counting all the affected rows of a query with fold and use the transaction later on to do more work and commit eventually?

1 Like

Did you ever find a solution?