-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor duplicated transaction management code
The code for transactions is duplicated between SQLite and PostgreSQL. MySQL would have also used identical code. However, the SQL being executed is not universal across all backends. Oracle appears to use the same SQL, but SQL Server has its own special syntax for this. As such, I'm not comfortable promoting this to a default impl on the trait. Instead I've moved the code out into a shared trait/struct, and operate on that instead. I had wanted to make `TransactionManager` be generic over the backend, not the connection itself, since constraints for it will always be about the backend, but I ran into rust-lang/rust#39532 when attempting to do so.
- Loading branch information
Showing
9 changed files
with
137 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
use backend::UsesAnsiSavepointSyntax; | ||
use connection::Connection; | ||
use result::QueryResult; | ||
|
||
/// Manages the internal transaction state for a connection. You should not | ||
/// interface with this trait unless you are implementing a new connection | ||
/// adapter. You should use [`Connection::transaction`][transaction], | ||
/// [`Connection::test_transaction`][test_transaction], or | ||
/// [`Connection::begin_test_transaction`][begin_test_transaction] instead. | ||
pub trait TransactionManager<Conn: Connection> { | ||
/// Begin a new transaction. If the transaction depth is greater than 0, | ||
/// this should create a savepoint instead. This function is expected to | ||
/// increment the transaction depth by 1. | ||
fn begin_transaction(&self, conn: &Conn) -> QueryResult<()>; | ||
|
||
/// Rollback the inner-most transcation. If the transaction depth is greater | ||
/// than 1, this should rollback to the most recent savepoint. This function | ||
/// is expected to decrement the transaction depth by 1. | ||
fn rollback_transaction(&self, conn: &Conn) -> QueryResult<()>; | ||
|
||
/// Commit the inner-most transcation. If the transaction depth is greater | ||
/// than 1, this should release the most recent savepoint. This function is | ||
/// expected to decrement the transaction depth by 1. | ||
fn commit_transaction(&self, conn: &Conn) -> QueryResult<()>; | ||
|
||
/// Fetch the current transaction depth. Used to ensure that | ||
/// `begin_test_transaction` is not called when already inside of a | ||
/// transaction. | ||
fn get_transaction_depth(&self) -> u32; | ||
} | ||
|
||
use std::cell::Cell; | ||
|
||
/// An implementation of TransactionManager which can be used for backends | ||
/// which use ANSI standard syntax for savepoints such as SQLite and PostgreSQL. | ||
#[allow(missing_debug_implementations)] | ||
pub struct AnsiTransactionManager { | ||
transaction_depth: Cell<i32>, | ||
} | ||
|
||
impl AnsiTransactionManager { | ||
pub fn new() -> Self { | ||
AnsiTransactionManager { | ||
transaction_depth: Cell::new(0), | ||
} | ||
} | ||
|
||
fn change_transaction_depth(&self, by: i32, query: QueryResult<()>) -> QueryResult<()> { | ||
if query.is_ok() { | ||
self.transaction_depth.set(self.transaction_depth.get() + by) | ||
} | ||
query | ||
} | ||
} | ||
|
||
impl<Conn> TransactionManager<Conn> for AnsiTransactionManager where | ||
Conn: Connection, | ||
Conn::Backend: UsesAnsiSavepointSyntax, | ||
{ | ||
fn begin_transaction(&self, conn: &Conn) -> QueryResult<()> { | ||
let transaction_depth = self.transaction_depth.get(); | ||
self.change_transaction_depth(1, if transaction_depth == 0 { | ||
conn.batch_execute("BEGIN") | ||
} else { | ||
conn.batch_execute(&format!("SAVEPOINT diesel_savepoint_{}", transaction_depth)) | ||
}) | ||
} | ||
|
||
fn rollback_transaction(&self, conn: &Conn) -> QueryResult<()> { | ||
let transaction_depth = self.transaction_depth.get(); | ||
self.change_transaction_depth(-1, if transaction_depth == 1 { | ||
conn.batch_execute("ROLLBACK") | ||
} else { | ||
conn.batch_execute(&format!("ROLLBACK TO SAVEPOINT diesel_savepoint_{}", | ||
transaction_depth - 1)) | ||
}) | ||
} | ||
|
||
fn commit_transaction(&self, conn: &Conn) -> QueryResult<()> { | ||
let transaction_depth = self.transaction_depth.get(); | ||
self.change_transaction_depth(-1, if transaction_depth <= 1 { | ||
conn.batch_execute("COMMIT") | ||
} else { | ||
conn.batch_execute(&format!("RELEASE SAVEPOINT diesel_savepoint_{}", | ||
transaction_depth - 1)) | ||
}) | ||
} | ||
|
||
fn get_transaction_depth(&self) -> u32 { | ||
self.transaction_depth.get() as u32 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.