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