Skip to main content

mongreldb_core/
rowid.rs

1use serde::{Deserialize, Serialize};
2
3/// A stable, dense row identifier shared by *every* index in a table.
4///
5/// Row IDs are allocated monotonically and **never reused**. Deletes record a
6/// tombstone at the row id; updates allocate a *new* row id and tombstone the
7/// old one. All indexes (primary HOT, learned PGM, secondary bitmaps, ANN,
8/// FM-index) resolve to or from `RowId`, so multi-condition queries intersect
9/// in a single id space.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
11pub struct RowId(pub u64);
12
13impl RowId {
14    pub const MIN: RowId = RowId(0);
15    pub const NULL_SORT_KEY: u16 = 0xFFFF;
16
17    #[inline]
18    pub fn next(self) -> RowId {
19        RowId(self.0.wrapping_add(1))
20    }
21}
22
23impl std::fmt::Display for RowId {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        write!(f, "RowId({})", self.0)
26    }
27}
28
29impl From<u64> for RowId {
30    fn from(v: u64) -> Self {
31        RowId(v)
32    }
33}
34
35/// Monotonic allocator for [`RowId`]s.
36#[derive(Debug, Default)]
37pub struct RowIdAllocator {
38    next: u64,
39}
40
41impl RowIdAllocator {
42    pub fn new(start: u64) -> Self {
43        Self { next: start }
44    }
45
46    /// Allocate a single new row id.
47    #[inline]
48    pub fn alloc(&mut self) -> RowId {
49        let id = self.next;
50        self.next += 1;
51        RowId(id)
52    }
53
54    /// Allocate a contiguous range of `n` row ids, returning the inclusive start.
55    pub fn alloc_range(&mut self, n: u64) -> RowId {
56        let start = self.next;
57        self.next = self.next.saturating_add(n);
58        RowId(start)
59    }
60
61    #[inline]
62    pub fn current(&self) -> RowId {
63        RowId(self.next)
64    }
65
66    /// Advance the allocator past `id` if it is ahead. Used during recovery.
67    pub fn advance_to(&mut self, id: RowId) {
68        if id.0 >= self.next {
69            self.next = id.0 + 1;
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn allocates_monotonically() {
80        let mut a = RowIdAllocator::default();
81        assert_eq!(a.alloc(), RowId(0));
82        assert_eq!(a.alloc(), RowId(1));
83        let start = a.alloc_range(3);
84        assert_eq!(start, RowId(2));
85        assert_eq!(a.current(), RowId(5));
86        assert_eq!(a.alloc(), RowId(5));
87    }
88
89    #[test]
90    fn advance_to_moves_head() {
91        let mut a = RowIdAllocator::default();
92        a.advance_to(RowId(100));
93        assert_eq!(a.alloc(), RowId(101));
94    }
95}