blackbeam / rust-mysql-simple

Mysql client library implemented in rust.
Apache License 2.0
661 stars 144 forks source link

Can't continue with a Transaction after it is commit/rollback due to mutable borrow #366

Open darksylinc opened 9 months ago

darksylinc commented 9 months ago

Hi!

I have the following situation (dummy commands to explain the point):

    let mut tx = conn.start_transaction(TxOpts::default())?;
    tx.query_drop("SELECT 1");
    tx.commit();

    // All ok! I continue to perform operations after the transaction is committed.
    conn.query_drop("SELECT 1");

Now the problem is that I wanted to encapsulate some common functionality, so I need to pass the tx and conn variables and ended up with:

let mut tx = conn.start_transaction(TxOpts::default())?;
common_func( tx, conn ) ;

fn common_func( tx : mut Transaction, conn : mut PooledConn ) {
    tx.query_drop("SELECT 1");
    tx.commit();
    conn.query_drop("SELECT 1");
}

The problem is that this does not work because conn is already mutably borrowed in tx.

Is there a solution to this problem?

AFAIK it is not possible to reuse the Transaction tx after commit/rollback. From what I can tell, this could be solved if I could retrieve the connection again after transaction is dropped.

Something like the following would fix my problem:

let mut tx = conn.start_transaction(TxOpts::default())?;
common_func( tx ) ;

fn common_func( tx : mut Transaction ) {
    tx.query_drop("SELECT 1");
    let mut conn = tx.commit_with_connection();
    conn.query_drop("SELECT 1");
}

Is this already possible? Am I trying to do something that I shouldn't do? Is this is a simply an API oversight?

Of course rust-mysql would need to implement both commit_with_connection and rollback_with_connection.

Cheers

blackbeam commented 9 months ago

Hi. There is no such an API because Transaction doesn't owns the Conn, but borrows it, i.e. there is nothing to unwrap 🤷

Regarding your example - it looks really unnatural to me. It's like someone, for whatever reason, wants to write this HTML:

<div>
  <h1>Hello<h2></h1>World</h2>
</div>

Instead of writing correct HTML:

<div>
  <h1>Hello</h1><h2>World</h2>
</div>

Anyway. As mentioned in the docs, Transaction is just a convenient wrapper, so, if you really need to write it that way, then your options are:

  1. Commit tx by hand (you can also set autocommit=1):
    fn common_func( tx : mut Transaction) {
        tx.query_drop("SELECT 1");
        tx.query_drop("COMMIT");
        tx.query_drop("SELECT 1");
    }
  2. Do not use the wrapper but manage the Tx yourself using START TRANSACTION, COMMIT, ROLLBACK and other relevant directives.