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