places/db/tx/
mod.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5mod coop_transaction;
6
7use crate::api::places_api::ConnectionType;
8use crate::error::*;
9use coop_transaction::ChunkedCoopTransaction;
10use rusqlite::Connection;
11use sql_support::{ConnExt, UncheckedTransaction};
12
13/// High level transaction type which "does the right thing" for you.
14/// Construct one with `PlacesDb::begin_transaction()`.
15pub struct PlacesTransaction<'conn>(PlacesTransactionRepr<'conn>);
16
17/// Only separated from PlacesTransaction so that the internals of the former
18/// are private (so that it can't be `matched` on, for example)
19enum PlacesTransactionRepr<'conn> {
20    ChunkedWrite(ChunkedCoopTransaction<'conn>),
21    UnchunkedWrite(UncheckedTransaction<'conn>),
22    // Note: these might seem pointless, but can allow us to ensure consistency
23    // between separate reads.
24    ReadOnly(UncheckedTransaction<'conn>),
25}
26
27impl PlacesTransaction<'_> {
28    /// Returns `true` if the current transaction should be committed at the
29    /// earliest opportunity.
30    #[inline]
31    pub fn should_commit(&self) -> bool {
32        match &self.0 {
33            PlacesTransactionRepr::ChunkedWrite(tx) => tx.should_commit(),
34            _ => true,
35        }
36    }
37
38    /// - For transactions on sync connections: Checks to see if we have held a
39    ///   transaction for longer than the requested time, and if so, commits the
40    ///   current transaction and opens another.
41    /// - For transactions on other connections: `debug_assert!`s, or logs a
42    ///   warning and does nothing.
43    #[inline]
44    pub fn maybe_commit(&mut self) -> Result<()> {
45        if let PlacesTransactionRepr::ChunkedWrite(tx) = &mut self.0 {
46            tx.maybe_commit()?;
47        } else {
48            error_support::report_error!(
49                "places-nonchunked-maybe-commit",
50                "maybe_commit called on a non-chunked transaction"
51            );
52            if cfg!(debug_assertions) {
53                panic!("maybe_commit called on a non-chunked transaction");
54            }
55        }
56        Ok(())
57    }
58
59    /// Consumes and commits a PlacesTransaction transaction.
60    pub fn commit(self) -> Result<()> {
61        match self.0 {
62            PlacesTransactionRepr::ChunkedWrite(t) => t.commit()?,
63            PlacesTransactionRepr::UnchunkedWrite(t) => t.commit()?,
64            PlacesTransactionRepr::ReadOnly(t) => t.commit()?,
65        };
66        Ok(())
67    }
68
69    /// Consumes and attempst to roll back a PlacesTransaction. Note that if
70    /// maybe_commit has been called, this may only roll back as far as that
71    /// call.
72    pub fn rollback(self) -> Result<()> {
73        match self.0 {
74            PlacesTransactionRepr::ChunkedWrite(t) => t.rollback()?,
75            PlacesTransactionRepr::UnchunkedWrite(t) => t.rollback()?,
76            PlacesTransactionRepr::ReadOnly(t) => t.rollback()?,
77        };
78        Ok(())
79    }
80}
81
82impl super::PlacesDb {
83    /// Begin the "correct" transaction type for this connection.
84    ///
85    /// - For Sync connections, begins a chunked coop transaction.
86    /// - for ReadWrite connections, begins a normal coop transaction
87    /// - for ReadOnly connections, begins an unchecked transaction.
88    pub fn begin_transaction(&self) -> Result<PlacesTransaction<'_>> {
89        Ok(PlacesTransaction(match self.conn_type() {
90            ConnectionType::Sync => {
91                PlacesTransactionRepr::ChunkedWrite(self.chunked_coop_trransaction()?)
92            }
93            ConnectionType::ReadWrite => {
94                PlacesTransactionRepr::UnchunkedWrite(self.coop_transaction()?)
95            }
96            ConnectionType::ReadOnly => {
97                // Use an unchecked transaction with no locking.
98                PlacesTransactionRepr::ReadOnly(self.unchecked_transaction()?)
99            }
100        }))
101    }
102}
103
104impl std::ops::Deref for PlacesTransaction<'_> {
105    type Target = Connection;
106
107    fn deref(&self) -> &Connection {
108        match &self.0 {
109            PlacesTransactionRepr::ChunkedWrite(t) => t,
110            PlacesTransactionRepr::UnchunkedWrite(t) => t,
111            PlacesTransactionRepr::ReadOnly(t) => t,
112        }
113    }
114}
115
116impl ConnExt for PlacesTransaction<'_> {
117    #[inline]
118    fn conn(&self) -> &Connection {
119        self
120    }
121}