redb/tree_store/page_store/
savepoint.rs
1use crate::transaction_tracker::{SavepointId, TransactionId, TransactionTracker};
2use crate::tree_store::page_store::page_manager::FILE_FORMAT_VERSION2;
3use crate::tree_store::{BtreeHeader, TransactionalMemory};
4use crate::{TypeName, Value};
5use std::fmt::Debug;
6use std::mem::size_of;
7use std::sync::Arc;
8
9pub struct Savepoint {
26 version: u8,
27 id: SavepointId,
28 transaction_id: TransactionId,
31 user_root: Option<BtreeHeader>,
32 system_root: Option<BtreeHeader>,
33 freed_root: Option<BtreeHeader>,
34 regional_allocators: Vec<Vec<u8>>,
35 transaction_tracker: Arc<TransactionTracker>,
36 ephemeral: bool,
37}
38
39impl Savepoint {
40 #[allow(clippy::too_many_arguments)]
41 pub(crate) fn new_ephemeral(
42 mem: &TransactionalMemory,
43 transaction_tracker: Arc<TransactionTracker>,
44 id: SavepointId,
45 transaction_id: TransactionId,
46 user_root: Option<BtreeHeader>,
47 system_root: Option<BtreeHeader>,
48 freed_root: Option<BtreeHeader>,
49 regional_allocators: Vec<Vec<u8>>,
50 ) -> Self {
51 Self {
52 id,
53 transaction_id,
54 version: mem.get_version(),
55 user_root,
56 system_root,
57 freed_root,
58 regional_allocators,
59 transaction_tracker,
60 ephemeral: true,
61 }
62 }
63
64 pub(crate) fn get_version(&self) -> u8 {
65 self.version
66 }
67
68 pub(crate) fn get_id(&self) -> SavepointId {
69 self.id
70 }
71
72 pub(crate) fn get_transaction_id(&self) -> TransactionId {
73 self.transaction_id
74 }
75
76 pub(crate) fn get_user_root(&self) -> Option<BtreeHeader> {
77 self.user_root
78 }
79
80 pub(crate) fn get_system_root(&self) -> Option<BtreeHeader> {
81 self.system_root
82 }
83
84 pub(crate) fn get_freed_root(&self) -> Option<BtreeHeader> {
85 self.freed_root
86 }
87
88 pub(crate) fn get_regional_allocators(&self) -> &Vec<Vec<u8>> {
89 &self.regional_allocators
90 }
91
92 pub(crate) fn db_address(&self) -> *const TransactionTracker {
93 std::ptr::from_ref(self.transaction_tracker.as_ref())
94 }
95
96 pub(crate) fn set_persistent(&mut self) {
97 self.ephemeral = false;
98 }
99}
100
101impl Drop for Savepoint {
102 fn drop(&mut self) {
103 if self.ephemeral {
104 self.transaction_tracker
105 .deallocate_savepoint(self.get_id(), self.get_transaction_id());
106 }
107 }
108}
109
110#[derive(Debug)]
111pub(crate) enum SerializedSavepoint<'a> {
112 Ref(&'a [u8]),
113 Owned(Vec<u8>),
114}
115
116impl SerializedSavepoint<'_> {
117 pub(crate) fn from_savepoint(savepoint: &Savepoint) -> Self {
118 assert_eq!(savepoint.version, FILE_FORMAT_VERSION2);
119 let mut result = vec![savepoint.version];
120 result.extend(savepoint.id.0.to_le_bytes());
121 result.extend(savepoint.transaction_id.raw_id().to_le_bytes());
122
123 if let Some(header) = savepoint.user_root {
124 result.push(1);
125 result.extend(header.to_le_bytes());
126 } else {
127 result.push(0);
128 result.extend([0; BtreeHeader::serialized_size()]);
129 }
130
131 if let Some(header) = savepoint.system_root {
132 result.push(1);
133 result.extend(header.to_le_bytes());
134 } else {
135 result.push(0);
136 result.extend([0; BtreeHeader::serialized_size()]);
137 }
138
139 if let Some(header) = savepoint.freed_root {
140 result.push(1);
141 result.extend(header.to_le_bytes());
142 } else {
143 result.push(0);
144 result.extend([0; BtreeHeader::serialized_size()]);
145 }
146
147 result.extend(
148 u32::try_from(savepoint.regional_allocators.len())
149 .unwrap()
150 .to_le_bytes(),
151 );
152 for region in &savepoint.regional_allocators {
153 assert_eq!(savepoint.regional_allocators[0].len(), region.len());
154 }
155 result.extend(
156 u32::try_from(savepoint.regional_allocators[0].len())
157 .unwrap()
158 .to_le_bytes(),
159 );
160
161 for region in &savepoint.regional_allocators {
164 result.extend(region);
165 }
166 Self::Owned(result)
167 }
168
169 fn data(&self) -> &[u8] {
170 match self {
171 SerializedSavepoint::Ref(x) => x,
172 SerializedSavepoint::Owned(x) => x.as_slice(),
173 }
174 }
175
176 pub(crate) fn to_savepoint(&self, transaction_tracker: Arc<TransactionTracker>) -> Savepoint {
177 let data = self.data();
178 let mut offset = 0;
179 let version = data[offset];
180 assert_eq!(version, FILE_FORMAT_VERSION2);
181 offset += size_of::<u8>();
182
183 let id = u64::from_le_bytes(
184 data[offset..(offset + size_of::<u64>())]
185 .try_into()
186 .unwrap(),
187 );
188 offset += size_of::<u64>();
189
190 let transaction_id = u64::from_le_bytes(
191 data[offset..(offset + size_of::<u64>())]
192 .try_into()
193 .unwrap(),
194 );
195 offset += size_of::<u64>();
196
197 let not_null = data[offset];
198 assert!(not_null == 0 || not_null == 1);
199 offset += 1;
200 let user_root = if not_null == 1 {
201 Some(BtreeHeader::from_le_bytes(
202 data[offset..(offset + BtreeHeader::serialized_size())]
203 .try_into()
204 .unwrap(),
205 ))
206 } else {
207 None
208 };
209 offset += BtreeHeader::serialized_size();
210
211 let not_null = data[offset];
212 assert!(not_null == 0 || not_null == 1);
213 offset += 1;
214 let system_root = if not_null == 1 {
215 Some(BtreeHeader::from_le_bytes(
216 data[offset..(offset + BtreeHeader::serialized_size())]
217 .try_into()
218 .unwrap(),
219 ))
220 } else {
221 None
222 };
223 offset += BtreeHeader::serialized_size();
224
225 let not_null = data[offset];
226 assert!(not_null == 0 || not_null == 1);
227 offset += 1;
228 let freed_root = if not_null == 1 {
229 Some(BtreeHeader::from_le_bytes(
230 data[offset..(offset + BtreeHeader::serialized_size())]
231 .try_into()
232 .unwrap(),
233 ))
234 } else {
235 None
236 };
237 offset += BtreeHeader::serialized_size();
238
239 let regions = u32::from_le_bytes(
240 data[offset..(offset + size_of::<u32>())]
241 .try_into()
242 .unwrap(),
243 ) as usize;
244 offset += size_of::<u32>();
245 let allocator_len = u32::from_le_bytes(
246 data[offset..(offset + size_of::<u32>())]
247 .try_into()
248 .unwrap(),
249 ) as usize;
250 offset += size_of::<u32>();
251
252 let mut regional_allocators = vec![];
253
254 for _ in 0..regions {
255 regional_allocators.push(data[offset..(offset + allocator_len)].to_vec());
256 offset += allocator_len;
257 }
258
259 assert_eq!(offset, data.len());
260
261 Savepoint {
262 version,
263 id: SavepointId(id),
264 transaction_id: TransactionId::new(transaction_id),
265 user_root,
266 system_root,
267 freed_root,
268 regional_allocators,
269 transaction_tracker,
270 ephemeral: false,
271 }
272 }
273}
274
275impl Value for SerializedSavepoint<'_> {
276 type SelfType<'a>
277 = SerializedSavepoint<'a>
278 where
279 Self: 'a;
280 type AsBytes<'a>
281 = &'a [u8]
282 where
283 Self: 'a;
284
285 fn fixed_width() -> Option<usize> {
286 None
287 }
288
289 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
290 where
291 Self: 'a,
292 {
293 SerializedSavepoint::Ref(data)
294 }
295
296 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
297 where
298 Self: 'b,
299 {
300 value.data()
301 }
302
303 fn type_name() -> TypeName {
304 TypeName::internal("redb::SerializedSavepoint")
305 }
306}