1use crate::be_tree::BeTree;
13use crate::epoch::Epoch;
14use crate::rowid::RowId;
15use serde::{Deserialize, Serialize};
16use std::collections::{BTreeMap, HashMap};
17
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21pub enum Value {
22 Null,
23 Bool(bool),
24 Int64(i64),
25 Float64(f64),
26 Bytes(Vec<u8>),
27 Embedding(Vec<f32>),
28 Decimal(i128),
31 Interval {
33 months: i64,
34 days: i32,
35 nanos: i64,
36 },
37}
38
39impl Value {
40 pub fn encode_key(&self) -> Vec<u8> {
43 match self {
44 Value::Null => Vec::new(),
45 Value::Bool(b) => vec![*b as u8],
46 Value::Int64(n) => n.to_be_bytes().to_vec(),
47 Value::Float64(f) => f.to_bits().to_be_bytes().to_vec(),
48 Value::Bytes(b) => b.clone(),
49 Value::Embedding(v) => {
50 let mut out = Vec::with_capacity(v.len() * 4);
51 for x in v {
52 out.extend_from_slice(&x.to_bits().to_be_bytes());
53 }
54 out
55 }
56 Value::Decimal(d) => d.to_be_bytes().to_vec(),
57 Value::Interval {
58 months,
59 days,
60 nanos,
61 } => {
62 let mut out = Vec::with_capacity(20);
63 out.extend_from_slice(&months.to_be_bytes());
64 out.extend_from_slice(&days.to_be_bytes());
65 out.extend_from_slice(&nanos.to_be_bytes());
66 out
67 }
68 }
69 }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct Row {
75 pub row_id: RowId,
76 pub committed_epoch: Epoch,
77 pub columns: HashMap<u16, Value>,
78 pub deleted: bool,
79}
80
81impl Row {
82 pub fn new(row_id: RowId, committed_epoch: Epoch) -> Self {
83 Self {
84 row_id,
85 committed_epoch,
86 columns: HashMap::new(),
87 deleted: false,
88 }
89 }
90
91 pub fn with_column(mut self, column_id: u16, value: Value) -> Self {
92 self.columns.insert(column_id, value);
93 self
94 }
95
96 pub fn estimated_bytes(&self) -> u64 {
98 let mut n = 32; for v in self.columns.values() {
100 n += match v {
101 Value::Null => 1,
102 Value::Bool(_) => 1,
103 Value::Int64(_) => 8,
104 Value::Float64(_) => 8,
105 Value::Bytes(b) => 16 + b.len() as u64,
106 Value::Embedding(v) => 16 + (v.len() as u64) * 4,
107 Value::Decimal(_) => 16,
108 Value::Interval { .. } => 20,
109 };
110 }
111 n
112 }
113}
114
115pub struct Memtable {
119 tree: BeTree,
120 byte_size: u64,
121}
122
123impl Default for Memtable {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl Memtable {
130 pub fn new() -> Self {
131 Self {
132 tree: BeTree::new(),
133 byte_size: 0,
134 }
135 }
136
137 pub fn upsert(&mut self, row: Row) {
140 self.byte_size += row.estimated_bytes();
141 self.tree.insert_row(row);
142 }
143
144 pub fn tombstone(&mut self, row_id: RowId, epoch: Epoch) {
148 let mut columns = HashMap::new();
149 if let Some(live) = self.get(row_id, Epoch(epoch.0.saturating_sub(1))) {
150 columns = live.columns;
151 }
152 let row = Row {
153 row_id,
154 committed_epoch: epoch,
155 columns,
156 deleted: true,
157 };
158 self.upsert(row);
159 }
160
161 pub fn get(&self, row_id: RowId, snapshot_epoch: Epoch) -> Option<Row> {
165 self.tree.get_visible(row_id, snapshot_epoch)
166 }
167
168 pub fn get_version(&self, row_id: RowId, snapshot_epoch: Epoch) -> Option<(Epoch, Row)> {
172 self.tree.get_version(row_id, snapshot_epoch)
173 }
174
175 pub fn len(&self) -> usize {
177 self.tree.mutations()
178 }
179
180 pub fn is_empty(&self) -> bool {
181 self.tree.is_empty()
182 }
183
184 pub fn approx_bytes(&self) -> u64 {
185 self.byte_size
186 }
187
188 pub fn visible_rows(&self, snapshot_epoch: Epoch) -> Vec<Row> {
191 self.visible_versions(snapshot_epoch)
192 .into_iter()
193 .filter(|r| !r.deleted)
194 .collect()
195 }
196
197 pub fn visible_versions(&self, snapshot_epoch: Epoch) -> Vec<Row> {
201 let mut by_row: BTreeMap<RowId, Row> = BTreeMap::new();
202 for row in self.tree.versions() {
203 if row.committed_epoch <= snapshot_epoch {
204 by_row
205 .entry(row.row_id)
206 .and_modify(|e| {
207 if row.committed_epoch > e.committed_epoch {
208 *e = row.clone();
209 }
210 })
211 .or_insert(row);
212 }
213 }
214 by_row.into_values().collect()
215 }
216
217 pub fn drain_sorted(&mut self) -> Vec<Row> {
220 let out = std::mem::take(&mut self.tree).into_sorted_rows();
221 self.byte_size = 0;
222 out
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 fn row(id: u64, epoch: u64) -> Row {
231 Row::new(RowId(id), Epoch(epoch)).with_column(1, Value::Int64(id as i64 * 10))
232 }
233
234 #[test]
235 fn upsert_get_and_visibility() {
236 let mut m = Memtable::new();
237 m.upsert(row(1, 5));
238 assert_eq!(m.len(), 1);
239 assert!(m.get(RowId(1), Epoch(5)).is_some());
240 assert!(m.get(RowId(1), Epoch(4)).is_none()); assert!(m.get(RowId(2), Epoch(9)).is_none()); }
243
244 #[test]
245 fn tombstone_supersedes_at_its_epoch() {
246 let mut m = Memtable::new();
247 m.upsert(row(1, 1));
248 assert!(m.get(RowId(1), Epoch(1)).is_some());
250 m.tombstone(RowId(1), Epoch(2));
251 assert!(m.get(RowId(1), Epoch(2)).is_none());
253 assert!(m.get(RowId(1), Epoch(9)).is_none());
254 assert!(m.get(RowId(1), Epoch(1)).is_some());
256 }
257
258 #[test]
259 fn drain_sorted_is_ascending_and_empties() {
260 let mut m = Memtable::new();
261 m.upsert(row(3, 1));
262 m.upsert(row(1, 1));
263 m.upsert(row(2, 1));
264 let out = m.drain_sorted();
265 let ids: Vec<u64> = out.iter().map(|r| r.row_id.0).collect();
266 assert_eq!(ids, vec![1, 2, 3]);
267 assert!(m.is_empty());
268 assert_eq!(m.approx_bytes(), 0);
269 }
270
271 #[test]
272 fn visible_rows_dedups_to_newest_version() {
273 let mut m = Memtable::new();
274 m.upsert(row(1, 1));
275 m.upsert(row(2, 9)); m.upsert(row(3, 1));
277 m.upsert(row(1, 3)); let ids: Vec<u64> = m
279 .visible_rows(Epoch(5))
280 .iter()
281 .map(|r| r.row_id.0)
282 .collect();
283 assert_eq!(ids, vec![1, 3]);
284 }
285}