1use crate::error::QuillSQLResult;
2use crate::storage::record::{RecordId, TupleMeta};
3use crate::storage::tuple::Tuple;
4use crate::storage::{IndexHandle, TableHandle};
5use sqlparser::ast::TransactionAccessMode;
6use std::str::FromStr;
7use std::sync::Arc;
8
9use super::TransactionSnapshot;
10
11pub type TransactionId = u64;
12pub type CommandId = u32;
13
14pub const INVALID_COMMAND_ID: CommandId = CommandId::MAX;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum IsolationLevel {
18 ReadUncommitted,
19 ReadCommitted,
20 RepeatableRead,
21 Serializable,
22}
23
24impl IsolationLevel {
25 pub fn as_str(&self) -> &'static str {
26 match self {
27 IsolationLevel::ReadUncommitted => "read-uncommitted",
28 IsolationLevel::ReadCommitted => "read-committed",
29 IsolationLevel::RepeatableRead => "repeatable-read",
30 IsolationLevel::Serializable => "serializable",
31 }
32 }
33}
34
35impl FromStr for IsolationLevel {
36 type Err = String;
37
38 fn from_str(s: &str) -> Result<Self, Self::Err> {
39 match s.trim().to_ascii_lowercase().as_str() {
40 "read-uncommitted" | "ru" => Ok(IsolationLevel::ReadUncommitted),
41 "read-committed" | "rc" => Ok(IsolationLevel::ReadCommitted),
42 "repeatable-read" | "rr" => Ok(IsolationLevel::RepeatableRead),
43 "serializable" | "sr" | "serial" => Ok(IsolationLevel::Serializable),
44 other => Err(format!("unknown isolation level '{}'", other)),
45 }
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum TransactionState {
51 Running,
52 Tainted,
53 Committed,
54 Aborted,
55}
56
57#[derive(Clone)]
58pub enum UndoAction {
59 Insert {
60 table: Arc<dyn TableHandle>,
61 rid: RecordId,
62 indexes: Vec<IndexUndoLink>,
63 },
64 Delete {
65 table: Arc<dyn TableHandle>,
66 rid: RecordId,
67 prev_meta: TupleMeta,
68 prev_tuple: Tuple,
69 indexes: Vec<IndexUndoLink>,
70 },
71}
72
73#[derive(Clone)]
74pub struct IndexUndoLink {
75 pub index: Arc<dyn IndexHandle>,
76 pub key: Tuple,
77 pub rid: RecordId,
78}
79
80pub(crate) type IndexUndoEntries = Vec<(Arc<dyn IndexHandle>, Tuple, RecordId)>;
81
82pub(crate) struct UpdateUndo {
83 pub old_rid: RecordId,
84 pub new_rid: RecordId,
85 pub prev_meta: TupleMeta,
86 pub prev_tuple: Tuple,
87 pub new_keys: IndexUndoEntries,
88 pub old_keys: IndexUndoEntries,
89}
90
91impl std::fmt::Debug for IndexUndoLink {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 f.debug_struct("IndexUndoLink")
94 .field("index", &self.index.name())
95 .field("key", &self.key)
96 .field("rid", &self.rid)
97 .finish()
98 }
99}
100
101impl std::fmt::Debug for UndoAction {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 Self::Insert {
105 table,
106 rid,
107 indexes,
108 } => f
109 .debug_struct("Insert")
110 .field("table", &table.table_ref())
111 .field("rid", rid)
112 .field("indexes", indexes)
113 .finish(),
114 Self::Delete {
115 table,
116 rid,
117 prev_meta,
118 prev_tuple,
119 indexes,
120 } => f
121 .debug_struct("Delete")
122 .field("table", &table.table_ref())
123 .field("rid", rid)
124 .field("prev_meta", prev_meta)
125 .field("prev_tuple", prev_tuple)
126 .field("indexes", indexes)
127 .finish(),
128 }
129 }
130}
131
132impl UndoAction {
133 pub fn undo(self, txn_id: TransactionId) -> QuillSQLResult<()> {
134 match self {
135 UndoAction::Insert {
136 table,
137 rid,
138 indexes,
139 } => {
140 for link in indexes.into_iter() {
141 link.index.delete(&link.key, link.rid, txn_id)?;
142 }
143 table.undo_insert(rid, txn_id)?;
144 Ok(())
145 }
146 UndoAction::Delete {
147 table,
148 rid,
149 prev_meta,
150 prev_tuple,
151 indexes,
152 } => {
153 for link in indexes {
154 link.index.insert(&link.key, link.rid, txn_id)?;
155 }
156 table.undo_delete(rid, prev_meta, prev_tuple)?;
157 Ok(())
158 }
159 }
160 }
161}
162
163pub struct Transaction {
164 id: TransactionId,
165 isolation_level: IsolationLevel,
166 access_mode: TransactionAccessMode,
167 state: TransactionState,
168 undo_actions: Vec<UndoAction>,
169 current_command_id: CommandId,
170 next_command_id: CommandId,
171 snapshot: Option<TransactionSnapshot>,
172}
173
174impl Transaction {
175 pub fn new(
176 id: TransactionId,
177 isolation_level: IsolationLevel,
178 access_mode: TransactionAccessMode,
179 ) -> Self {
180 Self {
181 id,
182 isolation_level,
183 access_mode,
184 state: TransactionState::Running,
185 undo_actions: Vec::new(),
186 current_command_id: INVALID_COMMAND_ID,
187 next_command_id: 0,
188 snapshot: None,
189 }
190 }
191
192 pub fn id(&self) -> TransactionId {
193 self.id
194 }
195
196 pub fn isolation_level(&self) -> IsolationLevel {
197 self.isolation_level
198 }
199
200 pub fn access_mode(&self) -> TransactionAccessMode {
201 self.access_mode
202 }
203
204 pub fn set_isolation_level(&mut self, isolation_level: IsolationLevel) {
205 self.isolation_level = isolation_level;
206 if matches!(
207 isolation_level,
208 IsolationLevel::ReadCommitted | IsolationLevel::ReadUncommitted
209 ) {
210 self.clear_snapshot();
211 }
212 }
213
214 pub fn state(&self) -> TransactionState {
215 self.state
216 }
217
218 pub fn begin_command(&mut self) -> CommandId {
219 let cid = self.next_command_id;
220 self.current_command_id = cid;
221 self.next_command_id = self.next_command_id.wrapping_add(1);
222 cid
223 }
224
225 pub fn current_command_id(&self) -> CommandId {
226 self.current_command_id
227 }
228
229 pub(crate) fn set_state(&mut self, state: TransactionState) {
230 self.state = state;
231 }
232
233 pub fn update_access_mode(&mut self, access_mode: TransactionAccessMode) {
234 self.access_mode = access_mode;
235 }
236
237 pub(crate) fn push_insert_undo(
238 &mut self,
239 table: Arc<dyn TableHandle>,
240 rid: RecordId,
241 indexes: IndexUndoEntries,
242 ) {
243 self.undo_actions.push(UndoAction::Insert {
244 table,
245 rid,
246 indexes: indexes
247 .into_iter()
248 .map(|(index, key, rid)| IndexUndoLink { index, key, rid })
249 .collect(),
250 });
251 }
252
253 pub(crate) fn push_update_undo(&mut self, table: Arc<dyn TableHandle>, undo: UpdateUndo) {
254 self.undo_actions.push(UndoAction::Insert {
255 table: table.clone(),
256 rid: undo.new_rid,
257 indexes: undo
258 .new_keys
259 .into_iter()
260 .map(|(index, key, rid)| IndexUndoLink { index, key, rid })
261 .collect(),
262 });
263 self.undo_actions.push(UndoAction::Delete {
264 table: table.clone(),
265 rid: undo.old_rid,
266 prev_meta: undo.prev_meta,
267 prev_tuple: undo.prev_tuple,
268 indexes: undo
269 .old_keys
270 .into_iter()
271 .map(|(index, key, rid)| IndexUndoLink { index, key, rid })
272 .collect(),
273 });
274 }
275
276 pub(crate) fn push_delete_undo(
277 &mut self,
278 table: Arc<dyn TableHandle>,
279 rid: RecordId,
280 prev_meta: TupleMeta,
281 prev_tuple: Tuple,
282 indexes: IndexUndoEntries,
283 ) {
284 self.undo_actions.push(UndoAction::Delete {
285 table,
286 rid,
287 prev_meta,
288 prev_tuple,
289 indexes: indexes
290 .into_iter()
291 .map(|(index, key, rid)| IndexUndoLink { index, key, rid })
292 .collect(),
293 });
294 }
295
296 pub(crate) fn pop_undo_action(&mut self) -> Option<UndoAction> {
297 self.undo_actions.pop()
298 }
299
300 pub(crate) fn clear_undo(&mut self) {
301 self.undo_actions.clear();
302 }
303
304 pub fn snapshot(&self) -> Option<&TransactionSnapshot> {
305 self.snapshot.as_ref()
306 }
307
308 pub fn set_snapshot(&mut self, snapshot: TransactionSnapshot) {
309 self.snapshot = Some(snapshot);
310 }
311
312 pub fn clear_snapshot(&mut self) {
313 self.snapshot = None;
314 }
315}