walletkit_db/sqlite/
transaction.rs1use super::connection::Connection;
6use super::error::DbResult;
7use super::statement::{Row, Statement};
8use super::value::Value;
9
10pub struct Transaction<'conn> {
16 conn: &'conn Connection,
17 committed: bool,
18}
19
20impl<'conn> Transaction<'conn> {
21 pub(super) fn begin(conn: &'conn Connection, immediate: bool) -> DbResult<Self> {
26 let sql = if immediate {
27 "BEGIN IMMEDIATE"
28 } else {
29 "BEGIN DEFERRED"
30 };
31 conn.execute_batch(sql)?;
32 Ok(Self {
33 conn,
34 committed: false,
35 })
36 }
37
38 pub fn commit(mut self) -> DbResult<()> {
44 self.conn.execute_batch("COMMIT")?;
45 self.committed = true;
46 Ok(())
47 }
48
49 #[allow(dead_code)]
57 pub fn execute_batch(&self, sql: &str) -> DbResult<()> {
58 self.conn.execute_batch(sql)
59 }
60
61 pub fn execute(&self, sql: &str, params: &[Value]) -> DbResult<usize> {
67 self.conn.execute(sql, params)
68 }
69
70 pub fn query_row<T>(
76 &self,
77 sql: &str,
78 params: &[Value],
79 mapper: impl FnOnce(&Row<'_, '_>) -> DbResult<T>,
80 ) -> DbResult<T> {
81 self.conn.query_row(sql, params, mapper)
82 }
83
84 pub fn prepare(&self, sql: &str) -> DbResult<Statement<'_>> {
90 self.conn.prepare(sql)
91 }
92}
93
94impl Drop for Transaction<'_> {
95 fn drop(&mut self) {
96 if !self.committed {
97 let _ = self.conn.execute_batch("ROLLBACK");
98 }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::Connection;
105 use crate::params;
106 use crate::test_utils::init_sqlite;
107
108 #[test]
109 fn test_transaction_commit() {
110 init_sqlite();
111 let conn = Connection::open_in_memory().expect("open in-memory db");
112 conn.execute_batch("CREATE TABLE t (id INTEGER PRIMARY KEY);")
113 .expect("create table");
114 {
115 let tx = conn.transaction().expect("begin tx");
116 tx.execute("INSERT INTO t (id) VALUES (?1)", params![42_i64])
117 .expect("insert");
118 tx.commit().expect("commit");
119 }
120 let result = conn
121 .query_row("SELECT id FROM t WHERE id = 42", &[], |stmt| {
122 Ok(stmt.column_i64(0))
123 })
124 .expect("query");
125 assert_eq!(result, 42);
126 }
127
128 #[test]
129 fn test_transaction_rollback_on_drop() {
130 init_sqlite();
131 let conn = Connection::open_in_memory().expect("open in-memory db");
132 conn.execute_batch("CREATE TABLE t (id INTEGER PRIMARY KEY);")
133 .expect("create table");
134 {
135 let tx = conn.transaction().expect("begin tx");
136 tx.execute("INSERT INTO t (id) VALUES (?1)", params![99_i64])
137 .expect("insert");
138 }
140 let result = conn
141 .query_row_optional("SELECT id FROM t WHERE id = 99", &[], |stmt| {
142 Ok(stmt.column_i64(0))
143 })
144 .expect("query");
145 assert!(result.is_none());
146 }
147}