quill_sql/buffer/
page.rs

1use crate::buffer::buffer_pool::{BufferPool, FrameMeta};
2use crate::buffer::{BufferManager, FrameId};
3use crate::recovery::Lsn;
4use derive_with::With;
5use parking_lot::{RwLockReadGuard, RwLockWriteGuard};
6use std::mem::{self, ManuallyDrop};
7use std::sync::atomic::{AtomicU32, Ordering};
8use std::sync::Arc;
9
10pub type PageId = u32;
11pub type AtomicPageId = AtomicU32;
12
13pub const INVALID_PAGE_ID: PageId = 0;
14pub const PAGE_SIZE: usize = 4096;
15
16#[derive(Debug, With)]
17pub struct PageMeta {
18    pub page_id: PageId,
19    pub pin_count: AtomicU32,
20    pub is_dirty: bool,
21    pub page_lsn: Lsn,
22}
23
24impl PageMeta {
25    pub fn empty() -> Self {
26        Self {
27            page_id: INVALID_PAGE_ID,
28            pin_count: AtomicU32::new(0),
29            is_dirty: false,
30            page_lsn: 0,
31        }
32    }
33
34    pub fn new(page_id: PageId) -> Self {
35        Self {
36            page_id,
37            pin_count: AtomicU32::new(0),
38            is_dirty: false,
39            page_lsn: 0,
40        }
41    }
42
43    pub fn destroy(&mut self) {
44        self.page_id = INVALID_PAGE_ID;
45        self.pin_count.store(0, Ordering::Relaxed);
46        self.is_dirty = false;
47        self.page_lsn = 0;
48    }
49}
50
51#[derive(Debug)]
52pub struct ReadPageGuard {
53    bpm: Arc<BufferManager>,
54    pool: Arc<BufferPool>,
55    frame_id: FrameId,
56    guard: ManuallyDrop<RwLockReadGuard<'static, ()>>,
57}
58
59impl ReadPageGuard {
60    pub fn pin_count(&self) -> u32 {
61        self.meta_snapshot().pin_count
62    }
63
64    pub fn data(&self) -> &[u8] {
65        unsafe { self.pool.frame_slice(self.frame_id) }
66    }
67
68    pub fn is_dirty(&self) -> bool {
69        self.meta_snapshot().is_dirty
70    }
71
72    pub fn page_id(&self) -> PageId {
73        self.meta_snapshot().page_id
74    }
75
76    pub fn lsn(&self) -> Lsn {
77        self.meta_snapshot().lsn
78    }
79
80    pub fn meta_snapshot(&self) -> FrameMeta {
81        self.pool.frame_meta(self.frame_id).clone()
82    }
83
84    pub fn frame_id(&self) -> FrameId {
85        self.frame_id
86    }
87}
88
89impl Drop for ReadPageGuard {
90    fn drop(&mut self) {
91        let snapshot = self.meta_snapshot();
92        let page_id = snapshot.page_id;
93        let is_dirty = snapshot.is_dirty;
94        unsafe {
95            ManuallyDrop::drop(&mut self.guard);
96        }
97        if let Err(e) = self.bpm.complete_unpin(page_id, is_dirty, None) {
98            eprintln!("Warning: Failed to complete_unpin page {}: {}", page_id, e);
99        }
100    }
101}
102
103#[derive(Debug)]
104pub struct WritePageGuard {
105    bpm: Arc<BufferManager>,
106    pool: Arc<BufferPool>,
107    frame_id: FrameId,
108    guard: ManuallyDrop<RwLockWriteGuard<'static, ()>>,
109    first_dirty_lsn: Option<Lsn>,
110}
111
112impl WritePageGuard {
113    pub fn pin_count(&self) -> u32 {
114        self.meta_snapshot().pin_count
115    }
116
117    pub fn data(&self) -> &[u8] {
118        unsafe { self.pool.frame_slice(self.frame_id) }
119    }
120
121    pub fn data_mut(&mut self) -> &mut [u8] {
122        unsafe { self.pool.frame_slice_mut(self.frame_id) }
123    }
124
125    pub fn is_dirty(&self) -> bool {
126        self.meta_snapshot().is_dirty
127    }
128
129    pub fn page_id(&self) -> PageId {
130        self.meta_snapshot().page_id
131    }
132
133    pub fn lsn(&self) -> Lsn {
134        self.meta_snapshot().lsn
135    }
136
137    pub fn set_lsn(&mut self, lsn: Lsn) {
138        let mut meta = self.pool.frame_meta(self.frame_id);
139        meta.lsn = lsn;
140        if self.first_dirty_lsn.is_none() {
141            self.first_dirty_lsn = Some(lsn);
142        }
143    }
144
145    pub fn mark_dirty(&mut self) {
146        let mut meta = self.pool.frame_meta(self.frame_id);
147        meta.is_dirty = true;
148        if self.first_dirty_lsn.is_none() {
149            self.first_dirty_lsn = Some(meta.lsn);
150        }
151    }
152
153    pub fn overwrite(&mut self, data: &[u8], new_lsn: Option<Lsn>) {
154        debug_assert_eq!(data.len(), PAGE_SIZE);
155        let slice = unsafe { self.pool.frame_slice_mut(self.frame_id) };
156        slice.copy_from_slice(data);
157        if let Some(lsn) = new_lsn {
158            self.set_lsn(lsn);
159        }
160        self.mark_dirty();
161    }
162
163    pub fn meta_snapshot(&self) -> FrameMeta {
164        self.pool.frame_meta(self.frame_id).clone()
165    }
166
167    pub fn frame_id(&self) -> FrameId {
168        self.frame_id
169    }
170}
171
172impl Drop for WritePageGuard {
173    fn drop(&mut self) {
174        let snapshot = self.meta_snapshot();
175        let page_id = snapshot.page_id;
176        let is_dirty = snapshot.is_dirty;
177        let lsn = snapshot.lsn;
178        unsafe {
179            ManuallyDrop::drop(&mut self.guard);
180        }
181        let rec_lsn_hint = if let Some(first) = self.first_dirty_lsn {
182            Some(first)
183        } else if is_dirty {
184            Some(lsn)
185        } else {
186            None
187        };
188        if let Err(e) = self.bpm.complete_unpin(page_id, is_dirty, rec_lsn_hint) {
189            eprintln!("Warning: Failed to complete_unpin page {}: {}", page_id, e);
190        }
191    }
192}
193
194pub(crate) fn new_read_guard(bpm: Arc<BufferManager>, frame_id: FrameId) -> ReadPageGuard {
195    let pool = bpm.buffer_pool();
196    let lock = pool.frame_lock(frame_id).read();
197    let static_guard =
198        unsafe { mem::transmute::<RwLockReadGuard<'_, ()>, RwLockReadGuard<'static, ()>>(lock) };
199    ReadPageGuard {
200        bpm,
201        pool,
202        frame_id,
203        guard: ManuallyDrop::new(static_guard),
204    }
205}
206
207pub(crate) fn new_write_guard(bpm: Arc<BufferManager>, frame_id: FrameId) -> WritePageGuard {
208    let pool = bpm.buffer_pool();
209    let lock = pool.frame_lock(frame_id).write();
210    let static_guard =
211        unsafe { mem::transmute::<RwLockWriteGuard<'_, ()>, RwLockWriteGuard<'static, ()>>(lock) };
212    WritePageGuard {
213        bpm,
214        pool,
215        frame_id,
216        guard: ManuallyDrop::new(static_guard),
217        first_dirty_lsn: None,
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use std::sync::atomic::Ordering;
224    use std::sync::Arc;
225    use tempfile::TempDir;
226
227    use crate::buffer::BufferManager;
228    use crate::storage::disk_manager::DiskManager;
229    use crate::storage::disk_scheduler::DiskScheduler;
230
231    use super::{PageMeta, INVALID_PAGE_ID, PAGE_SIZE};
232
233    fn setup_real_bpm_environment(num_pages: usize) -> (TempDir, Arc<BufferManager>) {
234        let temp_dir = TempDir::new().unwrap();
235        let db_path = temp_dir.path().join("test.db");
236
237        let disk_manager = Arc::new(DiskManager::try_new(db_path).unwrap());
238        let disk_scheduler = Arc::new(DiskScheduler::new(disk_manager));
239        let buffer_pool_manager = Arc::new(BufferManager::new(num_pages, disk_scheduler));
240
241        (temp_dir, buffer_pool_manager)
242    }
243
244    #[test]
245    fn test_page_struct_creation() {
246        let page = PageMeta::new(1);
247        assert_eq!(page.page_id, 1);
248        assert!(!page.is_dirty);
249        assert_eq!(page.pin_count.load(Ordering::Acquire), 0);
250
251        let empty_page = PageMeta::empty();
252        assert_eq!(empty_page.page_id, INVALID_PAGE_ID);
253    }
254
255    #[test]
256    fn test_read_guard_deref_and_drop() {
257        let (_temp_dir, bpm) = setup_real_bpm_environment(10);
258
259        let (page_id, frame_id) = {
260            let guard = bpm.new_page().unwrap();
261            let frame_id = guard.frame_id();
262            (guard.page_id(), frame_id)
263        };
264
265        {
266            let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
267            assert_eq!(meta.pin_count, 0);
268        }
269
270        let read_guard = bpm.fetch_page_read(page_id).unwrap();
271        assert_eq!(read_guard.page_id(), page_id);
272        assert_eq!(read_guard.pin_count(), 1);
273        assert_eq!(read_guard.data().len(), PAGE_SIZE);
274        let snapshot = read_guard.meta_snapshot();
275        assert_eq!(snapshot.pin_count, 1);
276        drop(read_guard);
277
278        let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
279        assert_eq!(meta.pin_count, 0);
280    }
281
282    #[test]
283    fn test_write_guard_deref_mut_and_drop() {
284        let (_temp_dir, bpm) = setup_real_bpm_environment(10);
285        let (page_id, frame_id) = {
286            let mut write_guard = bpm.new_page().unwrap();
287            write_guard.data_mut()[0] = 123;
288            write_guard.set_lsn(42);
289            write_guard.mark_dirty();
290            (write_guard.page_id(), write_guard.frame_id())
291        };
292
293        let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
294        assert!(meta.is_dirty);
295        assert_eq!(meta.lsn, 42);
296        assert_eq!(meta.pin_count, 0);
297
298        let read_guard = bpm.fetch_page_read(page_id).unwrap();
299        assert_eq!(read_guard.data()[0], 123);
300        assert!(read_guard.is_dirty());
301        assert_eq!(read_guard.lsn(), 42);
302        let snapshot = read_guard.meta_snapshot();
303        assert_eq!(snapshot.lsn, 42);
304        assert!(snapshot.is_dirty);
305        assert_eq!(snapshot.pin_count, 1);
306        drop(read_guard);
307
308        let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
309        assert!(meta.is_dirty);
310        assert_eq!(meta.lsn, 42);
311        assert_eq!(meta.pin_count, 0);
312    }
313
314    #[test]
315    fn test_write_guard_without_mutation_is_not_dirty() {
316        let (_temp_dir, bpm) = setup_real_bpm_environment(10);
317        let (page_id, frame_id) = {
318            let guard = bpm.new_page().unwrap();
319            (guard.page_id(), guard.frame_id())
320        };
321
322        {
323            let _write_guard = bpm.fetch_page_write(page_id).unwrap();
324        }
325
326        let read_guard = bpm.fetch_page_read(page_id).unwrap();
327        let snapshot = read_guard.meta_snapshot();
328        assert!(!snapshot.is_dirty);
329        assert_eq!(snapshot.lsn, 0);
330        assert_eq!(snapshot.pin_count, 1);
331        drop(read_guard);
332
333        let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
334        assert!(!meta.is_dirty);
335        assert_eq!(meta.pin_count, 0);
336    }
337}