use crate::connection::{
Connection, IsolationLevel, TransactionMode, TransactionOps, TransactionOptions,
};
use crate::{Cx, Error, Outcome, PreparedStatement, Row, Value};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CallRecord {
pub call: &'static str,
pub sql: Option<String>,
pub cancelled_before: bool,
}
#[derive(Debug, Default)]
struct SweepState {
calls: AtomicU64,
injected: AtomicBool,
log: Mutex<Vec<CallRecord>>,
}
impl SweepState {
fn lock(&self) -> MutexGuard<'_, Vec<CallRecord>> {
self.log.lock().unwrap_or_else(PoisonError::into_inner)
}
fn arm(&self, cx: &Cx, call: &'static str, sql: Option<&str>, cancel_from_call: u64) {
let ordinal = self.calls.fetch_add(1, Ordering::SeqCst) + 1;
let cancel_here = cancel_from_call != 0 && ordinal == cancel_from_call;
if cancel_here {
cx.set_cancel_requested(true);
self.injected.store(true, Ordering::SeqCst);
}
self.lock().push(CallRecord {
call,
sql: sql.map(str::to_owned),
cancelled_before: cancel_here,
});
}
}
#[derive(Debug)]
pub struct CancelAt<C> {
inner: C,
cancel_from_call: u64,
state: Arc<SweepState>,
}
impl<C> CancelAt<C> {
pub fn new(inner: C, cancel_from_call: u64) -> Self {
Self {
inner,
cancel_from_call,
state: Arc::new(SweepState::default()),
}
}
pub fn calls_made(&self) -> u64 {
self.state.calls.load(Ordering::SeqCst)
}
pub fn cancellation_injected(&self) -> bool {
self.state.injected.load(Ordering::SeqCst)
}
pub fn log(&self) -> Vec<CallRecord> {
self.state.lock().clone()
}
pub fn inner(&self) -> &C {
&self.inner
}
fn tx_wrapper<T>(&self, tx: T) -> CancelAtTx<T> {
CancelAtTx {
inner: tx,
cancel_from_call: self.cancel_from_call,
state: Arc::clone(&self.state),
}
}
}
impl<C> Connection for CancelAt<C>
where
C: Connection,
for<'conn> C::Tx<'conn>: Sync,
{
type Tx<'conn>
= CancelAtTx<C::Tx<'conn>>
where
Self: 'conn;
fn dialect(&self) -> crate::Dialect {
self.inner.dialect()
}
async fn query(&self, cx: &Cx, sql: &str, params: &[Value]) -> Outcome<Vec<Row>, Error> {
self.state
.arm(cx, "query", Some(sql), self.cancel_from_call);
self.inner.query(cx, sql, params).await
}
async fn query_one(&self, cx: &Cx, sql: &str, params: &[Value]) -> Outcome<Option<Row>, Error> {
self.state
.arm(cx, "query_one", Some(sql), self.cancel_from_call);
self.inner.query_one(cx, sql, params).await
}
async fn execute(&self, cx: &Cx, sql: &str, params: &[Value]) -> Outcome<u64, Error> {
self.state
.arm(cx, "execute", Some(sql), self.cancel_from_call);
self.inner.execute(cx, sql, params).await
}
async fn insert(&self, cx: &Cx, sql: &str, params: &[Value]) -> Outcome<i64, Error> {
self.state
.arm(cx, "insert", Some(sql), self.cancel_from_call);
self.inner.insert(cx, sql, params).await
}
async fn batch(
&self,
cx: &Cx,
statements: &[(String, Vec<Value>)],
) -> Outcome<Vec<u64>, Error> {
self.state.arm(
cx,
"batch",
statements.first().map(|(sql, _)| sql.as_str()),
self.cancel_from_call,
);
self.inner.batch(cx, statements).await
}
async fn begin(&self, cx: &Cx) -> Outcome<Self::Tx<'_>, Error> {
self.state.arm(cx, "begin", None, self.cancel_from_call);
self.inner.begin(cx).await.map(|tx| self.tx_wrapper(tx))
}
async fn begin_with(&self, cx: &Cx, isolation: IsolationLevel) -> Outcome<Self::Tx<'_>, Error> {
self.state
.arm(cx, "begin_with", None, self.cancel_from_call);
self.inner
.begin_with(cx, isolation)
.await
.map(|tx| self.tx_wrapper(tx))
}
fn supports_transaction_mode(&self, mode: TransactionMode) -> bool {
self.inner.supports_transaction_mode(mode)
}
async fn begin_with_options(
&self,
cx: &Cx,
options: TransactionOptions,
) -> Outcome<Self::Tx<'_>, Error> {
self.state
.arm(cx, "begin_with_options", None, self.cancel_from_call);
self.inner
.begin_with_options(cx, options)
.await
.map(|tx| self.tx_wrapper(tx))
}
async fn prepare(&self, cx: &Cx, sql: &str) -> Outcome<PreparedStatement, Error> {
self.state
.arm(cx, "prepare", Some(sql), self.cancel_from_call);
self.inner.prepare(cx, sql).await
}
async fn query_prepared(
&self,
cx: &Cx,
stmt: &PreparedStatement,
params: &[Value],
) -> Outcome<Vec<Row>, Error> {
self.state.arm(
cx,
"query_prepared",
Some(stmt.sql()),
self.cancel_from_call,
);
self.inner.query_prepared(cx, stmt, params).await
}
async fn execute_prepared(
&self,
cx: &Cx,
stmt: &PreparedStatement,
params: &[Value],
) -> Outcome<u64, Error> {
self.state.arm(
cx,
"execute_prepared",
Some(stmt.sql()),
self.cancel_from_call,
);
self.inner.execute_prepared(cx, stmt, params).await
}
async fn ping(&self, cx: &Cx) -> Outcome<(), Error> {
self.state.arm(cx, "ping", None, self.cancel_from_call);
self.inner.ping(cx).await
}
async fn close(self, cx: &Cx) -> Result<(), Error> {
self.state.arm(cx, "close", None, self.cancel_from_call);
self.inner.close(cx).await
}
async fn close_for_pool(self, cx: &Cx) -> Result<(), Error>
where
Self: Sized,
{
self.state
.arm(cx, "close_for_pool", None, self.cancel_from_call);
self.inner.close_for_pool(cx).await
}
}
#[derive(Debug)]
pub struct CancelAtTx<T> {
inner: T,
cancel_from_call: u64,
state: Arc<SweepState>,
}
impl<T: TransactionOps + Sync> TransactionOps for CancelAtTx<T> {
async fn query(&self, cx: &Cx, sql: &str, params: &[Value]) -> Outcome<Vec<Row>, Error> {
self.state
.arm(cx, "tx.query", Some(sql), self.cancel_from_call);
self.inner.query(cx, sql, params).await
}
async fn query_one(&self, cx: &Cx, sql: &str, params: &[Value]) -> Outcome<Option<Row>, Error> {
self.state
.arm(cx, "tx.query_one", Some(sql), self.cancel_from_call);
self.inner.query_one(cx, sql, params).await
}
async fn execute(&self, cx: &Cx, sql: &str, params: &[Value]) -> Outcome<u64, Error> {
self.state
.arm(cx, "tx.execute", Some(sql), self.cancel_from_call);
self.inner.execute(cx, sql, params).await
}
async fn savepoint(&self, cx: &Cx, name: &str) -> Outcome<(), Error> {
self.state
.arm(cx, "tx.savepoint", Some(name), self.cancel_from_call);
self.inner.savepoint(cx, name).await
}
async fn rollback_to(&self, cx: &Cx, name: &str) -> Outcome<(), Error> {
self.state
.arm(cx, "tx.rollback_to", Some(name), self.cancel_from_call);
self.inner.rollback_to(cx, name).await
}
async fn release(&self, cx: &Cx, name: &str) -> Outcome<(), Error> {
self.state
.arm(cx, "tx.release", Some(name), self.cancel_from_call);
self.inner.release(cx, name).await
}
async fn commit(self, cx: &Cx) -> Outcome<(), Error> {
self.state.arm(cx, "tx.commit", None, self.cancel_from_call);
self.inner.commit(cx).await
}
async fn rollback(self, cx: &Cx) -> Outcome<(), Error> {
self.state
.arm(cx, "tx.rollback", None, self.cancel_from_call);
self.inner.rollback(cx).await
}
}