mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 09:09:19 +08:00
Make the creation of transactions and savepoints take &mut self.
Transactions in SQLite are nested, but the previous API allowed rusqlite transaction wrappers to be created as "siblings". This resulted in unexpected (and usually wrong) behavior.
This commit is contained in:
parent
63e5570ca9
commit
92834951e3
@ -237,7 +237,7 @@ impl Connection {
|
|||||||
/// # Failure
|
/// # Failure
|
||||||
///
|
///
|
||||||
/// Will return `Err` if the underlying SQLite call fails.
|
/// Will return `Err` if the underlying SQLite call fails.
|
||||||
pub fn transaction(&self) -> Result<Transaction> {
|
pub fn transaction(&mut self) -> Result<Transaction> {
|
||||||
Transaction::new(self, TransactionBehavior::Deferred)
|
Transaction::new(self, TransactionBehavior::Deferred)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ impl Connection {
|
|||||||
/// # Failure
|
/// # Failure
|
||||||
///
|
///
|
||||||
/// Will return `Err` if the underlying SQLite call fails.
|
/// Will return `Err` if the underlying SQLite call fails.
|
||||||
pub fn transaction_with_behavior(&self, behavior: TransactionBehavior) -> Result<Transaction> {
|
pub fn transaction_with_behavior(&mut self, behavior: TransactionBehavior) -> Result<Transaction> {
|
||||||
Transaction::new(self, behavior)
|
Transaction::new(self, behavior)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
use {Result, Connection};
|
use {Result, Connection};
|
||||||
|
|
||||||
/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is deprecated.
|
/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is deprecated.
|
||||||
@ -47,13 +48,13 @@ pub struct Transaction<'conn> {
|
|||||||
|
|
||||||
impl<'conn> Transaction<'conn> {
|
impl<'conn> Transaction<'conn> {
|
||||||
/// Begin a new transaction. Cannot be nested; see `savepoint` for nested transactions.
|
/// Begin a new transaction. Cannot be nested; see `savepoint` for nested transactions.
|
||||||
pub fn new(conn: &Connection, behavior: TransactionBehavior) -> Result<Transaction> {
|
pub fn new(conn: &mut Connection, behavior: TransactionBehavior) -> Result<Transaction> {
|
||||||
let query = match behavior {
|
let query = match behavior {
|
||||||
TransactionBehavior::Deferred => "BEGIN DEFERRED",
|
TransactionBehavior::Deferred => "BEGIN DEFERRED",
|
||||||
TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
|
TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
|
||||||
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
|
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
|
||||||
};
|
};
|
||||||
conn.execute_batch(query).map(|_| {
|
conn.execute_batch(query).map(move |_| {
|
||||||
Transaction {
|
Transaction {
|
||||||
conn: conn,
|
conn: conn,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
@ -89,7 +90,7 @@ impl<'conn> Transaction<'conn> {
|
|||||||
/// tx.commit()
|
/// tx.commit()
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn savepoint(&self) -> Result<Transaction> {
|
pub fn savepoint(&mut self) -> Result<Transaction> {
|
||||||
self.conn.execute_batch("SAVEPOINT sp").map(|_| {
|
self.conn.execute_batch("SAVEPOINT sp").map(|_| {
|
||||||
Transaction {
|
Transaction {
|
||||||
conn: self.conn,
|
conn: self.conn,
|
||||||
@ -166,6 +167,14 @@ impl<'conn> Transaction<'conn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'conn> Deref for Transaction<'conn> {
|
||||||
|
type Target = Connection;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Connection {
|
||||||
|
self.conn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
impl<'conn> Drop for Transaction<'conn> {
|
impl<'conn> Drop for Transaction<'conn> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@ -186,62 +195,62 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_drop() {
|
fn test_drop() {
|
||||||
let db = checked_memory_handle();
|
let mut db = checked_memory_handle();
|
||||||
{
|
{
|
||||||
let _tx = db.transaction().unwrap();
|
let tx = db.transaction().unwrap();
|
||||||
db.execute_batch("INSERT INTO foo VALUES(1)").unwrap();
|
tx.execute_batch("INSERT INTO foo VALUES(1)").unwrap();
|
||||||
// default: rollback
|
// default: rollback
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut tx = db.transaction().unwrap();
|
let mut tx = db.transaction().unwrap();
|
||||||
db.execute_batch("INSERT INTO foo VALUES(2)").unwrap();
|
tx.execute_batch("INSERT INTO foo VALUES(2)").unwrap();
|
||||||
tx.set_commit()
|
tx.set_commit()
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let _tx = db.transaction().unwrap();
|
let tx = db.transaction().unwrap();
|
||||||
assert_eq!(2i32,
|
assert_eq!(2i32,
|
||||||
db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
|
tx.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_explicit_rollback_commit() {
|
fn test_explicit_rollback_commit() {
|
||||||
let db = checked_memory_handle();
|
let mut db = checked_memory_handle();
|
||||||
{
|
{
|
||||||
let tx = db.transaction().unwrap();
|
let tx = db.transaction().unwrap();
|
||||||
db.execute_batch("INSERT INTO foo VALUES(1)").unwrap();
|
tx.execute_batch("INSERT INTO foo VALUES(1)").unwrap();
|
||||||
tx.rollback().unwrap();
|
tx.rollback().unwrap();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let tx = db.transaction().unwrap();
|
let tx = db.transaction().unwrap();
|
||||||
db.execute_batch("INSERT INTO foo VALUES(2)").unwrap();
|
tx.execute_batch("INSERT INTO foo VALUES(2)").unwrap();
|
||||||
tx.commit().unwrap();
|
tx.commit().unwrap();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let _tx = db.transaction().unwrap();
|
let tx = db.transaction().unwrap();
|
||||||
assert_eq!(2i32,
|
assert_eq!(2i32,
|
||||||
db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
|
tx.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_savepoint() {
|
fn test_savepoint() {
|
||||||
let db = checked_memory_handle();
|
let mut db = checked_memory_handle();
|
||||||
{
|
{
|
||||||
let mut tx = db.transaction().unwrap();
|
let mut tx = db.transaction().unwrap();
|
||||||
db.execute_batch("INSERT INTO foo VALUES(1)").unwrap();
|
tx.execute_batch("INSERT INTO foo VALUES(1)").unwrap();
|
||||||
tx.set_commit();
|
tx.set_commit();
|
||||||
{
|
{
|
||||||
let mut sp1 = tx.savepoint().unwrap();
|
let mut sp1 = tx.savepoint().unwrap();
|
||||||
db.execute_batch("INSERT INTO foo VALUES(2)").unwrap();
|
sp1.execute_batch("INSERT INTO foo VALUES(2)").unwrap();
|
||||||
sp1.set_commit();
|
sp1.set_commit();
|
||||||
{
|
{
|
||||||
let sp2 = sp1.savepoint().unwrap();
|
let mut sp2 = sp1.savepoint().unwrap();
|
||||||
db.execute_batch("INSERT INTO foo VALUES(4)").unwrap();
|
sp2.execute_batch("INSERT INTO foo VALUES(4)").unwrap();
|
||||||
// will rollback sp2
|
// will rollback sp2
|
||||||
{
|
{
|
||||||
let sp3 = sp2.savepoint().unwrap();
|
let sp3 = sp2.savepoint().unwrap();
|
||||||
db.execute_batch("INSERT INTO foo VALUES(8)").unwrap();
|
sp3.execute_batch("INSERT INTO foo VALUES(8)").unwrap();
|
||||||
sp3.commit().unwrap();
|
sp3.commit().unwrap();
|
||||||
// committed sp3, but will be erased by sp2 rollback
|
// committed sp3, but will be erased by sp2 rollback
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user