Skip to main content

linkrs_server/client/
transaction_context.rs

1use linkrs_transaction::{SavepointId, TransactionId, TransactionOptions};
2use log::info;
3use parking_lot::RwLock;
4use std::sync::Arc;
5
6#[derive(Debug)]
7pub struct TransactionContext {
8    current_transaction: Arc<RwLock<Option<TransactionId>>>,
9    savepoint_stack: Arc<RwLock<Vec<SavepointId>>>,
10    transaction_options: Arc<RwLock<TransactionOptions>>,
11    auto_commit: Arc<RwLock<bool>>,
12}
13
14impl Default for TransactionContext {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20impl TransactionContext {
21    pub fn new() -> Self {
22        Self {
23            current_transaction: Arc::new(RwLock::new(None)),
24            savepoint_stack: Arc::new(RwLock::new(Vec::new())),
25            transaction_options: Arc::new(RwLock::new(TransactionOptions::default())),
26            auto_commit: Arc::new(RwLock::new(true)),
27        }
28    }
29
30    pub fn current_transaction(&self) -> Option<TransactionId> {
31        *self.current_transaction.read()
32    }
33
34    pub fn bind_transaction(&self, txn_id: TransactionId, session_id: i64) {
35        info!("Binding transaction {} to session {}", txn_id, session_id);
36        *self.current_transaction.write() = Some(txn_id);
37    }
38
39    pub fn unbind_transaction(&self, session_id: i64) {
40        if let Some(txn_id) = self.current_transaction() {
41            info!(
42                "Unbinding transaction {} from session {}",
43                txn_id, session_id
44            );
45            *self.current_transaction.write() = None;
46            self.savepoint_stack.write().clear();
47        }
48    }
49
50    pub fn has_active_transaction(&self) -> bool {
51        self.current_transaction().is_some()
52    }
53
54    pub fn is_auto_commit(&self) -> bool {
55        *self.auto_commit.read()
56    }
57
58    pub fn set_auto_commit(&self, auto_commit: bool, session_id: i64) {
59        info!(
60            "Setting auto_commit to {} for session {}",
61            auto_commit, session_id
62        );
63        *self.auto_commit.write() = auto_commit;
64    }
65
66    pub fn transaction_options(&self) -> TransactionOptions {
67        self.transaction_options.read().clone()
68    }
69
70    pub fn set_transaction_options(&self, options: TransactionOptions) {
71        *self.transaction_options.write() = options;
72    }
73
74    pub fn push_savepoint(&self, savepoint_id: SavepointId, session_id: i64) {
75        info!(
76            "Pushing savepoint {} to session {}",
77            savepoint_id, session_id
78        );
79        self.savepoint_stack.write().push(savepoint_id);
80    }
81
82    pub fn savepoint_stack(&self) -> Vec<SavepointId> {
83        self.savepoint_stack.read().clone()
84    }
85
86    pub fn clear_savepoints(&self, session_id: i64) {
87        info!("Clearing savepoint stack for session {}", session_id);
88        self.savepoint_stack.write().clear();
89    }
90
91    pub fn savepoint_count(&self) -> usize {
92        self.savepoint_stack.read().len()
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_transaction_context() {
102        let context = TransactionContext::new();
103        assert!(context.current_transaction().is_none());
104        assert!(!context.has_active_transaction());
105        assert!(context.is_auto_commit());
106
107        context.bind_transaction(TransactionId::from(1001u64), 123);
108        assert_eq!(
109            context.current_transaction(),
110            Some(TransactionId::from(1001u64))
111        );
112        assert!(context.has_active_transaction());
113
114        context.unbind_transaction(123);
115        assert!(context.current_transaction().is_none());
116    }
117
118    #[test]
119    fn test_auto_commit() {
120        let context = TransactionContext::new();
121        assert!(context.is_auto_commit());
122
123        context.set_auto_commit(false, 123);
124        assert!(!context.is_auto_commit());
125    }
126
127    #[test]
128    fn test_savepoint_stack() {
129        let context = TransactionContext::new();
130        assert_eq!(context.savepoint_count(), 0);
131        assert!(context.savepoint_stack().is_empty());
132
133        context.push_savepoint(1, 123);
134        context.push_savepoint(2, 123);
135        assert_eq!(context.savepoint_count(), 2);
136        assert_eq!(context.savepoint_stack(), vec![1, 2]);
137
138        context.clear_savepoints(123);
139        assert_eq!(context.savepoint_count(), 0);
140    }
141}