linkrs-server 0.1.0

Linkrs network service layer (HTTP/gRPC)
Documentation
use linkrs_transaction::{SavepointId, TransactionId, TransactionOptions};
use log::info;
use parking_lot::RwLock;
use std::sync::Arc;

#[derive(Debug)]
pub struct TransactionContext {
    current_transaction: Arc<RwLock<Option<TransactionId>>>,
    savepoint_stack: Arc<RwLock<Vec<SavepointId>>>,
    transaction_options: Arc<RwLock<TransactionOptions>>,
    auto_commit: Arc<RwLock<bool>>,
}

impl Default for TransactionContext {
    fn default() -> Self {
        Self::new()
    }
}

impl TransactionContext {
    pub fn new() -> Self {
        Self {
            current_transaction: Arc::new(RwLock::new(None)),
            savepoint_stack: Arc::new(RwLock::new(Vec::new())),
            transaction_options: Arc::new(RwLock::new(TransactionOptions::default())),
            auto_commit: Arc::new(RwLock::new(true)),
        }
    }

    pub fn current_transaction(&self) -> Option<TransactionId> {
        *self.current_transaction.read()
    }

    pub fn bind_transaction(&self, txn_id: TransactionId, session_id: i64) {
        info!("Binding transaction {} to session {}", txn_id, session_id);
        *self.current_transaction.write() = Some(txn_id);
    }

    pub fn unbind_transaction(&self, session_id: i64) {
        if let Some(txn_id) = self.current_transaction() {
            info!(
                "Unbinding transaction {} from session {}",
                txn_id, session_id
            );
            *self.current_transaction.write() = None;
            self.savepoint_stack.write().clear();
        }
    }

    pub fn has_active_transaction(&self) -> bool {
        self.current_transaction().is_some()
    }

    pub fn is_auto_commit(&self) -> bool {
        *self.auto_commit.read()
    }

    pub fn set_auto_commit(&self, auto_commit: bool, session_id: i64) {
        info!(
            "Setting auto_commit to {} for session {}",
            auto_commit, session_id
        );
        *self.auto_commit.write() = auto_commit;
    }

    pub fn transaction_options(&self) -> TransactionOptions {
        self.transaction_options.read().clone()
    }

    pub fn set_transaction_options(&self, options: TransactionOptions) {
        *self.transaction_options.write() = options;
    }

    pub fn push_savepoint(&self, savepoint_id: SavepointId, session_id: i64) {
        info!(
            "Pushing savepoint {} to session {}",
            savepoint_id, session_id
        );
        self.savepoint_stack.write().push(savepoint_id);
    }

    pub fn savepoint_stack(&self) -> Vec<SavepointId> {
        self.savepoint_stack.read().clone()
    }

    pub fn clear_savepoints(&self, session_id: i64) {
        info!("Clearing savepoint stack for session {}", session_id);
        self.savepoint_stack.write().clear();
    }

    pub fn savepoint_count(&self) -> usize {
        self.savepoint_stack.read().len()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_transaction_context() {
        let context = TransactionContext::new();
        assert!(context.current_transaction().is_none());
        assert!(!context.has_active_transaction());
        assert!(context.is_auto_commit());

        context.bind_transaction(TransactionId::from(1001u64), 123);
        assert_eq!(
            context.current_transaction(),
            Some(TransactionId::from(1001u64))
        );
        assert!(context.has_active_transaction());

        context.unbind_transaction(123);
        assert!(context.current_transaction().is_none());
    }

    #[test]
    fn test_auto_commit() {
        let context = TransactionContext::new();
        assert!(context.is_auto_commit());

        context.set_auto_commit(false, 123);
        assert!(!context.is_auto_commit());
    }

    #[test]
    fn test_savepoint_stack() {
        let context = TransactionContext::new();
        assert_eq!(context.savepoint_count(), 0);
        assert!(context.savepoint_stack().is_empty());

        context.push_savepoint(1, 123);
        context.push_savepoint(2, 123);
        assert_eq!(context.savepoint_count(), 2);
        assert_eq!(context.savepoint_stack(), vec![1, 2]);

        context.clear_savepoints(123);
        assert_eq!(context.savepoint_count(), 0);
    }
}