quill_sql/session/
mod.rs

1use crate::error::{QuillSQLError, QuillSQLResult};
2use crate::transaction::{IsolationLevel, Transaction};
3use sqlparser::ast::TransactionAccessMode;
4/// Session-level state used to manage transactions and defaults for a client connection.
5pub struct SessionContext {
6    default_isolation: IsolationLevel,
7    default_access_mode: TransactionAccessMode,
8    autocommit: bool,
9    active_txn: Option<Transaction>,
10    pending_session_isolation: Option<IsolationLevel>,
11    pending_session_access: Option<TransactionAccessMode>,
12}
13
14impl SessionContext {
15    pub fn new(default_isolation: IsolationLevel) -> Self {
16        Self {
17            default_isolation,
18            default_access_mode: TransactionAccessMode::ReadWrite,
19            autocommit: true,
20            active_txn: None,
21            pending_session_isolation: None,
22            pending_session_access: None,
23        }
24    }
25
26    pub fn default_isolation(&self) -> IsolationLevel {
27        self.default_isolation
28    }
29
30    pub fn set_default_isolation(&mut self, isolation: IsolationLevel) {
31        self.default_isolation = isolation;
32    }
33
34    pub fn default_access_mode(&self) -> TransactionAccessMode {
35        self.default_access_mode
36    }
37
38    pub fn set_default_access_mode(&mut self, mode: TransactionAccessMode) {
39        self.default_access_mode = mode;
40    }
41
42    /// Apply a new isolation level to the currently active transaction, if one exists.
43    pub fn set_active_isolation(&mut self, isolation: IsolationLevel) {
44        if let Some(txn) = self.active_txn.as_mut() {
45            txn.set_isolation_level(isolation);
46        }
47    }
48
49    pub fn autocommit(&self) -> bool {
50        self.autocommit
51    }
52
53    pub fn set_autocommit(&mut self, enabled: bool) {
54        self.autocommit = enabled;
55    }
56
57    /// Retrieve the pending session isolation override that will apply to the next transaction.
58    pub fn pending_session_isolation(&self) -> Option<IsolationLevel> {
59        self.pending_session_isolation
60    }
61
62    /// Record a pending session isolation override that should apply to the next transaction.
63    pub fn set_pending_session_isolation(&mut self, isolation: Option<IsolationLevel>) {
64        self.pending_session_isolation = isolation;
65    }
66
67    pub fn pending_session_access(&self) -> Option<TransactionAccessMode> {
68        self.pending_session_access
69    }
70
71    pub fn set_pending_session_access(&mut self, mode: Option<TransactionAccessMode>) {
72        self.pending_session_access = mode;
73    }
74
75    pub fn has_active_transaction(&self) -> bool {
76        self.active_txn.is_some()
77    }
78
79    pub fn active_txn(&self) -> Option<&Transaction> {
80        self.active_txn.as_ref()
81    }
82
83    pub fn active_txn_mut(&mut self) -> Option<&mut Transaction> {
84        self.active_txn.as_mut()
85    }
86
87    pub fn set_active_transaction(&mut self, mut txn: Transaction) -> QuillSQLResult<()> {
88        if self.active_txn.is_some() {
89            return Err(QuillSQLError::Execution(
90                "transaction already active in session".to_string(),
91            ));
92        }
93        if let Some(isolation) = self.pending_session_isolation.take() {
94            self.default_isolation = isolation;
95            txn.set_isolation_level(isolation);
96        } else {
97            self.default_isolation = txn.isolation_level();
98        }
99
100        if let Some(mode) = self.pending_session_access.take() {
101            self.default_access_mode = mode;
102            txn.update_access_mode(mode);
103        } else {
104            self.default_access_mode = txn.access_mode();
105        }
106
107        self.active_txn = Some(txn);
108        Ok(())
109    }
110
111    pub fn take_active_transaction(&mut self) -> Option<Transaction> {
112        self.active_txn.take()
113    }
114
115    pub fn clear_active_transaction(&mut self) {
116        self.active_txn = None;
117    }
118
119    /// Apply isolation override when a transaction-scoped SET TRANSACTION is issued.
120    pub fn apply_transaction_modes(&mut self, modes: &crate::plan::logical_plan::TransactionModes) {
121        if let Some(level) = modes.isolation_level {
122            if let Some(txn) = self.active_txn.as_mut() {
123                txn.set_isolation_level(level);
124            }
125        }
126        if let Some(mode) = modes.access_mode {
127            if let Some(txn) = self.active_txn.as_mut() {
128                txn.update_access_mode(mode);
129            }
130        }
131    }
132
133    /// Merge `SET SESSION TRANSACTION` modes into session defaults.
134    pub fn apply_session_modes(&mut self, modes: &crate::plan::logical_plan::TransactionModes) {
135        if let Some(level) = modes.isolation_level {
136            self.default_isolation = level;
137            self.pending_session_isolation = Some(level);
138        }
139        if let Some(mode) = modes.access_mode {
140            self.default_access_mode = mode;
141            self.pending_session_access = Some(mode);
142        }
143    }
144}