linkrs_server/client/
transaction_context.rs1use 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}