walletkit_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 query_row_optional<T>(
90 &self,
91 sql: &str,
92 params: &[Value],
93 mapper: impl FnOnce(&Row<'_, '_>) -> DbResult<T>,
94 ) -> DbResult<Option<T>> {
95 self.conn.query_row_optional(sql, params, mapper)
96 }
97
98 pub fn prepare(&self, sql: &str) -> DbResult<Statement<'_>> {
104 self.conn.prepare(sql)
105 }
106}
107
108impl Drop for Transaction<'_> {
109 fn drop(&mut self) {
110 if !self.committed {
111 let _ = self.conn.execute_batch("ROLLBACK");
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::Connection;
119 use crate::params;
120 use crate::test_utils::init_sqlite;
121
122 #[test]
123 fn test_transaction_commit() {
124 init_sqlite();
125 let conn = Connection::open_in_memory().expect("open in-memory db");
126 conn.execute_batch("CREATE TABLE t (id INTEGER PRIMARY KEY);")
127 .expect("create table");
128 {
129 let tx = conn.transaction().expect("begin tx");
130 tx.execute("INSERT INTO t (id) VALUES (?1)", params![42_i64])
131 .expect("insert");
132 tx.commit().expect("commit");
133 }
134 let result = conn
135 .query_row("SELECT id FROM t WHERE id = 42", &[], |stmt| {
136 Ok(stmt.column_i64(0))
137 })
138 .expect("query");
139 assert_eq!(result, 42);
140 }
141
142 #[test]
143 fn test_transaction_rollback_on_drop() {
144 init_sqlite();
145 let conn = Connection::open_in_memory().expect("open in-memory db");
146 conn.execute_batch("CREATE TABLE t (id INTEGER PRIMARY KEY);")
147 .expect("create table");
148 {
149 let tx = conn.transaction().expect("begin tx");
150 tx.execute("INSERT INTO t (id) VALUES (?1)", params![99_i64])
151 .expect("insert");
152 }
154 let result = conn
155 .query_row_optional("SELECT id FROM t WHERE id = 99", &[], |stmt| {
156 Ok(stmt.column_i64(0))
157 })
158 .expect("query");
159 assert!(result.is_none());
160 }
161}