use mysql_common::packets::OkPacket;
use std::{borrow::Cow, fmt};
use crate::{
conn::{
query_result::{Binary, Text},
ConnMut,
},
prelude::*,
LocalInfileHandler, Params, QueryResult, Result, Statement,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct TxOpts {
with_consistent_snapshot: bool,
isolation_level: Option<IsolationLevel>,
access_mode: Option<AccessMode>,
}
impl TxOpts {
pub fn with_consistent_snapshot(&self) -> bool {
self.with_consistent_snapshot
}
pub fn access_mode(&self) -> Option<AccessMode> {
self.access_mode
}
pub fn isolation_level(&self) -> Option<IsolationLevel> {
self.isolation_level
}
pub fn set_with_consistent_snapshot(mut self, val: bool) -> Self {
self.with_consistent_snapshot = val;
self
}
pub fn set_access_mode(mut self, access_mode: Option<AccessMode>) -> Self {
self.access_mode = access_mode;
self
}
pub fn set_isolation_level(mut self, level: Option<IsolationLevel>) -> Self {
self.isolation_level = level;
self
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
#[repr(u8)]
pub enum AccessMode {
ReadOnly,
ReadWrite,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
#[repr(u8)]
pub enum IsolationLevel {
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
}
impl fmt::Display for IsolationLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
IsolationLevel::ReadUncommitted => write!(f, "READ UNCOMMITTED"),
IsolationLevel::ReadCommitted => write!(f, "READ COMMITTED"),
IsolationLevel::RepeatableRead => write!(f, "REPEATABLE READ"),
IsolationLevel::Serializable => write!(f, "SERIALIZABLE"),
}
}
}
#[derive(Debug)]
pub struct Transaction<'a> {
pub(crate) conn: ConnMut<'a, 'static, 'static>,
committed: bool,
rolled_back: bool,
restore_local_infile_handler: Option<LocalInfileHandler>,
}
impl Transaction<'_> {
pub(crate) fn new<'a>(conn: ConnMut<'a, 'static, 'static>) -> Transaction<'a> {
let handler = conn.0.local_infile_handler.clone();
Transaction {
conn,
committed: false,
rolled_back: false,
restore_local_infile_handler: handler,
}
}
pub fn commit(mut self) -> Result<()> {
self.conn.query_drop("COMMIT")?;
self.committed = true;
Ok(())
}
pub fn rollback(mut self) -> Result<()> {
self.conn.query_drop("ROLLBACK")?;
self.rolled_back = true;
Ok(())
}
pub fn set_local_infile_handler(&mut self, handler: Option<LocalInfileHandler>) {
self.conn.set_local_infile_handler(handler);
}
pub fn affected_rows(&self) -> u64 {
self.conn.affected_rows()
}
pub fn last_insert_id(&self) -> Option<u64> {
self.conn
.0
.ok_packet
.as_ref()
.and_then(OkPacket::last_insert_id)
}
pub fn warnings(&self) -> u16 {
self.conn.warnings()
}
pub fn info_ref(&self) -> &[u8] {
self.conn.info_ref()
}
pub fn info_str(&self) -> Cow<'_, str> {
self.conn.info_str()
}
}
impl<'a> Queryable for Transaction<'a> {
fn query_iter<T: AsRef<str>>(&mut self, query: T) -> Result<QueryResult<'_, '_, '_, Text>> {
self.conn.query_iter(query)
}
fn prep<T: AsRef<str>>(&mut self, query: T) -> Result<Statement> {
self.conn.prep(query)
}
fn close(&mut self, stmt: Statement) -> Result<()> {
self.conn.close(stmt)
}
fn exec_iter<S, P>(&mut self, stmt: S, params: P) -> Result<QueryResult<'_, '_, '_, Binary>>
where
S: AsStatement,
P: Into<Params>,
{
self.conn.exec_iter(stmt, params)
}
fn exec_batch<S, P, I>(&mut self, stmt: S, params: I) -> Result<()>
where
Self: Sized,
S: AsStatement,
P: Into<Params>,
I: IntoIterator<Item = P>,
{
self.conn.exec_batch(stmt, params)
}
}
impl<'a> Drop for Transaction<'a> {
fn drop(&mut self) {
if !self.committed && !self.rolled_back {
let _ = self.conn.query_drop("ROLLBACK");
}
self.conn.0.local_infile_handler = self.restore_local_infile_handler.take();
}
}