Skip to main content

core_crypto_keystore/transaction/
finalize.rs

1//! Methods which terminate the lifecycle of a [`Transaction`]
2
3use rusqlite::Connection;
4
5use super::{GUARD_EXPECTATION, Transaction};
6use crate::{CryptoKeystoreError, CryptoKeystoreResult, UniqueArc};
7
8impl Transaction {
9    /// Execute rollback. Abstracted here because there are two paths to this destination.
10    fn execute_rollback(conn: &Connection) -> CryptoKeystoreResult<()> {
11        if !Self::is_active(conn) {
12            return Ok(());
13        }
14
15        let mut stmt = conn.prepare_cached("ROLLBACK TRANSACTION")?;
16        // rollback doesn't tell us how many rows were changed, so ignore it
17        stmt.execute([])?;
18
19        Ok(())
20    }
21}
22
23impl UniqueArc<Transaction> {
24    /// Consume this arc-wrapper and drop the internal transaction, first performing an operation
25    /// on the connection.
26    async fn consume(
27        self,
28        operation: impl FnOnce(&Connection) -> CryptoKeystoreResult<()>,
29    ) -> CryptoKeystoreResult<()> {
30        // We're doing a little dance here, which is tricky but legal: this statement
31        // _consumes_ the `UniqueArc` which is `self`, but
32        // _borrows_ the `Transaction` contained in there. That struct impls `Drop`,
33        // which means it can't be consumably destructured like this, but because we know
34        // we have the only arc reference, we know it's going to run its destructor as soon
35        // as this function ends.
36        let Transaction {
37            ref database,
38            ref mut conn,
39            ..
40        } = UniqueArc::into_inner(self).await;
41
42        // clear the weak reference to this transaction
43        *database.transaction.lock().await = None;
44
45        // locking here is guaranteed not to block because we consumed the unique arc; there are no other
46        // live references to the data
47        let _sqlite = crate::connection::SqliteGuard::lock();
48        let mut guard = conn.lock();
49
50        // we need to ensure we only actually clear the connection (preventing rollback)
51        // after we know the operation succeeded. Otherwise if something in this block errors,
52        // the transaction doesn't get rolled back and no new transaction can ever be created
53        {
54            let conn = guard.as_ref().expect(GUARD_EXPECTATION);
55            operation(conn)?;
56        }
57        let _ = guard.take();
58
59        Ok(())
60    }
61
62    /// Persists all the operations in the database.
63    pub async fn commit(self) -> Result<(), CryptoKeystoreError> {
64        self.consume(|conn| {
65            let mut stmt = conn.prepare_cached("COMMIT TRANSACTION")?;
66            stmt.execute([])?;
67            Ok(())
68        })
69        .await
70    }
71
72    /// Roll back this transaction in the database.
73    ///
74    /// There are two differences between manual rollback and implicit:
75    ///
76    /// - manual rollback clears the database's weak transaction ref
77    /// - manual rollback permits propagation of db-level errors
78    pub async fn rollback(self) -> CryptoKeystoreResult<()> {
79        self.consume(Transaction::execute_rollback).await
80    }
81}
82
83impl Drop for Transaction {
84    fn drop(&mut self) {
85        // a dropped wrapper has rollback behavior, but we can't clear the weak arc
86        // in the database. that's fine, that's why it was weak in the first place.
87
88        // locking here is guaranteed not to wait because this is `Drop`; the strong count is 0
89        if let Some(conn) = self.conn.lock().take() {
90            #[cfg(target_os = "unknown")]
91            crate::connection::managed_connection::defer(move || {
92                if let Err(error) = Self::execute_rollback(&conn) {
93                    log::warn!(err:err = error; "failed to roll back dropped SQLite transaction");
94                }
95            });
96            #[cfg(not(target_os = "unknown"))]
97            let _ = Self::execute_rollback(&conn);
98        }
99    }
100}