Skip to main content

quill_sql/transaction/
state.rs

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}