redb/tree_store/page_store/
savepoint.rs1use crate::transaction_tracker::{SavepointId, TransactionId, TransactionTracker};
2use crate::tree_store::page_store::page_manager::{FILE_FORMAT_VERSION2, FILE_FORMAT_VERSION3};
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!(
119 savepoint.version == FILE_FORMAT_VERSION2 || savepoint.version == FILE_FORMAT_VERSION3
120 );
121 let mut result = vec![savepoint.version];
122 result.extend(savepoint.id.0.to_le_bytes());
123 result.extend(savepoint.transaction_id.raw_id().to_le_bytes());
124
125 if let Some(header) = savepoint.user_root {
126 result.push(1);
127 result.extend(header.to_le_bytes());
128 } else {
129 result.push(0);
130 result.extend([0; BtreeHeader::serialized_size()]);
131 }
132
133 if savepoint.version <= FILE_FORMAT_VERSION2 {
134 if let Some(header) = savepoint.system_root {
135 result.push(1);
136 result.extend(header.to_le_bytes());
137 } else {
138 result.push(0);
139 result.extend([0; BtreeHeader::serialized_size()]);
140 }
141
142 if let Some(header) = savepoint.freed_root {
143 result.push(1);
144 result.extend(header.to_le_bytes());
145 } else {
146 result.push(0);
147 result.extend([0; BtreeHeader::serialized_size()]);
148 }
149
150 result.extend(
151 u32::try_from(savepoint.regional_allocators.len())
152 .unwrap()
153 .to_le_bytes(),
154 );
155 for region in &savepoint.regional_allocators {
156 assert_eq!(savepoint.regional_allocators[0].len(), region.len());
157 }
158 result.extend(
159 u32::try_from(savepoint.regional_allocators[0].len())
160 .unwrap()
161 .to_le_bytes(),
162 );
163
164 for region in &savepoint.regional_allocators {
167 result.extend(region);
168 }
169 }
170
171 Self::Owned(result)
172 }
173
174 fn data(&self) -> &[u8] {
175 match self {
176 SerializedSavepoint::Ref(x) => x,
177 SerializedSavepoint::Owned(x) => x.as_slice(),
178 }
179 }
180
181 pub(crate) fn to_savepoint(&self, transaction_tracker: Arc<TransactionTracker>) -> Savepoint {
182 let data = self.data();
183 let mut offset = 0;
184 let version = data[offset];
185 assert!(version == FILE_FORMAT_VERSION2 || version == FILE_FORMAT_VERSION3);
186 offset += size_of::<u8>();
187
188 let id = u64::from_le_bytes(
189 data[offset..(offset + size_of::<u64>())]
190 .try_into()
191 .unwrap(),
192 );
193 offset += size_of::<u64>();
194
195 let transaction_id = u64::from_le_bytes(
196 data[offset..(offset + size_of::<u64>())]
197 .try_into()
198 .unwrap(),
199 );
200 offset += size_of::<u64>();
201
202 let not_null = data[offset];
203 assert!(not_null == 0 || not_null == 1);
204 offset += 1;
205 let user_root = if not_null == 1 {
206 Some(BtreeHeader::from_le_bytes(
207 data[offset..(offset + BtreeHeader::serialized_size())]
208 .try_into()
209 .unwrap(),
210 ))
211 } else {
212 None
213 };
214 offset += BtreeHeader::serialized_size();
215
216 let (system_root, freed_root, regional_allocators) = if version >= FILE_FORMAT_VERSION3 {
217 (None, None, vec![])
218 } else {
219 let not_null = data[offset];
220 assert!(not_null == 0 || not_null == 1);
221 offset += 1;
222 let system_root = if not_null == 1 {
223 Some(BtreeHeader::from_le_bytes(
224 data[offset..(offset + BtreeHeader::serialized_size())]
225 .try_into()
226 .unwrap(),
227 ))
228 } else {
229 None
230 };
231 offset += BtreeHeader::serialized_size();
232
233 let not_null = data[offset];
234 assert!(not_null == 0 || not_null == 1);
235 offset += 1;
236 let freed_root = if not_null == 1 {
237 Some(BtreeHeader::from_le_bytes(
238 data[offset..(offset + BtreeHeader::serialized_size())]
239 .try_into()
240 .unwrap(),
241 ))
242 } else {
243 None
244 };
245 offset += BtreeHeader::serialized_size();
246
247 let regions = u32::from_le_bytes(
248 data[offset..(offset + size_of::<u32>())]
249 .try_into()
250 .unwrap(),
251 ) as usize;
252 offset += size_of::<u32>();
253 let allocator_len = u32::from_le_bytes(
254 data[offset..(offset + size_of::<u32>())]
255 .try_into()
256 .unwrap(),
257 ) as usize;
258 offset += size_of::<u32>();
259
260 let mut regional_allocators = vec![];
261
262 for _ in 0..regions {
263 regional_allocators.push(data[offset..(offset + allocator_len)].to_vec());
264 offset += allocator_len;
265 }
266
267 (system_root, freed_root, regional_allocators)
268 };
269
270 assert_eq!(offset, data.len());
271
272 Savepoint {
273 version,
274 id: SavepointId(id),
275 transaction_id: TransactionId::new(transaction_id),
276 user_root,
277 system_root,
278 freed_root,
279 regional_allocators,
280 transaction_tracker,
281 ephemeral: false,
282 }
283 }
284}
285
286impl Value for SerializedSavepoint<'_> {
287 type SelfType<'a>
288 = SerializedSavepoint<'a>
289 where
290 Self: 'a;
291 type AsBytes<'a>
292 = &'a [u8]
293 where
294 Self: 'a;
295
296 fn fixed_width() -> Option<usize> {
297 None
298 }
299
300 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
301 where
302 Self: 'a,
303 {
304 SerializedSavepoint::Ref(data)
305 }
306
307 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
308 where
309 Self: 'b,
310 {
311 value.data()
312 }
313
314 fn type_name() -> TypeName {
315 TypeName::internal("redb::SerializedSavepoint")
316 }
317}