use crate::connection::Session;
use crate::error::{Error, Result};
use graphlite::QueryResult;
pub struct Transaction<'conn> {
session: &'conn Session,
committed: bool,
drop_behavior: DropBehavior,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DropBehavior {
Rollback,
Commit,
Panic,
Ignore,
}
impl<'conn> Transaction<'conn> {
pub(crate) fn begin(session: &'conn Session) -> Result<Self> {
session
.coordinator()
.process_query("BEGIN TRANSACTION", session.id())
.map_err(|e| Error::Transaction(format!("Failed to begin transaction: {}", e)))?;
Ok(Transaction {
session,
committed: false,
drop_behavior: DropBehavior::Rollback,
})
}
pub fn execute(&mut self, statement: &str) -> Result<()> {
if self.committed {
return Err(Error::Transaction(
"Transaction already committed".to_string(),
));
}
self.session
.coordinator()
.process_query(statement, self.session.id())
.map_err(|e| Error::Transaction(format!("Execute failed: {}", e)))?;
Ok(())
}
pub fn query(&mut self, query: &str) -> Result<QueryResult> {
if self.committed {
return Err(Error::Transaction(
"Transaction already committed".to_string(),
));
}
self.session
.coordinator()
.process_query(query, self.session.id())
.map_err(|e| Error::Transaction(format!("Query failed: {}", e)))
}
pub fn commit(mut self) -> Result<()> {
self.commit_internal()
}
pub fn rollback(mut self) -> Result<()> {
self.rollback_internal()
}
pub fn set_drop_behavior(&mut self, behavior: DropBehavior) {
self.drop_behavior = behavior;
}
fn commit_internal(&mut self) -> Result<()> {
if self.committed {
return Err(Error::Transaction(
"Transaction already committed".to_string(),
));
}
self.session
.coordinator()
.process_query("COMMIT", self.session.id())
.map_err(|e| Error::Transaction(format!("Failed to commit: {}", e)))?;
self.committed = true;
Ok(())
}
fn rollback_internal(&mut self) -> Result<()> {
if self.committed {
return Ok(()); }
self.session
.coordinator()
.process_query("ROLLBACK", self.session.id())
.map_err(|e| Error::Transaction(format!("Failed to rollback: {}", e)))?;
self.committed = true; Ok(())
}
}
impl<'conn> Drop for Transaction<'conn> {
fn drop(&mut self) {
if self.committed {
return; }
match self.drop_behavior {
DropBehavior::Rollback => {
if let Err(e) = self.rollback_internal() {
eprintln!("Warning: Failed to rollback transaction on drop: {}", e);
}
}
DropBehavior::Commit => {
if let Err(e) = self.commit_internal() {
eprintln!("Warning: Failed to commit transaction on drop: {}", e);
}
}
DropBehavior::Panic => {
if !std::thread::panicking() {
panic!("Transaction dropped without explicit commit or rollback");
}
}
DropBehavior::Ignore => {
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_drop_behavior() {
assert_eq!(DropBehavior::Rollback, DropBehavior::Rollback);
assert_ne!(DropBehavior::Rollback, DropBehavior::Commit);
}
}