1use crate::index::BitmapIndex;
15use crate::memtable::{Row, Value};
16use crate::rowid::RowId;
17use crate::schema::{IndexKind, Schema};
18use std::collections::HashMap;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum BitmapIndexDelta {
23 Repoint { column_id: u16, key: Vec<u8> },
26 Move {
29 column_id: u16,
30 old_key: Option<Vec<u8>>,
31 new_key: Option<Vec<u8>>,
32 },
33}
34
35pub fn bitmap_key_for_column(row: &Row, column_id: u16) -> Option<Vec<u8>> {
40 row.columns.get(&column_id).map(Value::encode_key)
41}
42
43pub fn plan_bitmap_secondary_deltas(
49 schema: &Schema,
50 old: &Row,
51 new: &Row,
52) -> Vec<BitmapIndexDelta> {
53 let mut out = Vec::new();
54 for idef in &schema.indexes {
55 if idef.kind != IndexKind::Bitmap {
56 continue;
57 }
58 let old_key = bitmap_key_for_column(old, idef.column_id);
59 let new_key = bitmap_key_for_column(new, idef.column_id);
60 match (old_key, new_key) {
61 (Some(o), Some(n)) if o == n => {
62 out.push(BitmapIndexDelta::Repoint {
63 column_id: idef.column_id,
64 key: o,
65 });
66 }
67 (old_key, new_key) => {
68 out.push(BitmapIndexDelta::Move {
69 column_id: idef.column_id,
70 old_key,
71 new_key,
72 });
73 }
74 }
75 }
76 out
77}
78
79pub fn apply_bitmap_secondary_delta(
84 bitmap: &mut HashMap<u16, BitmapIndex>,
85 deltas: &[BitmapIndexDelta],
86 old_row_id: RowId,
87 new_row_id: RowId,
88) {
89 for delta in deltas {
90 match delta {
91 BitmapIndexDelta::Repoint { column_id, key } => {
92 let Some(b) = bitmap.get_mut(column_id) else {
93 continue;
94 };
95 if old_row_id != new_row_id {
96 b.remove(key, old_row_id);
97 b.insert(key.clone(), new_row_id);
98 }
99 }
101 BitmapIndexDelta::Move {
102 column_id,
103 old_key,
104 new_key,
105 } => {
106 let Some(b) = bitmap.get_mut(column_id) else {
107 continue;
108 };
109 if let Some(k) = old_key {
110 b.remove(k, old_row_id);
111 }
112 if let Some(k) = new_key {
113 b.insert(k.clone(), new_row_id);
114 }
115 }
116 }
117 }
118}
119
120pub fn maintain_bitmap_secondary_on_replace(
122 schema: &Schema,
123 bitmap: &mut HashMap<u16, BitmapIndex>,
124 old: &Row,
125 new: &Row,
126) {
127 let deltas = plan_bitmap_secondary_deltas(schema, old, new);
128 apply_bitmap_secondary_delta(bitmap, &deltas, old.row_id, new.row_id);
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::schema::{ColumnDef, ColumnFlags, IndexDef, TypeId};
135
136 fn schema_trip_bitmap() -> Schema {
137 Schema {
138 schema_id: 1,
139 columns: vec![
140 ColumnDef {
141 id: 1,
142 name: "id".into(),
143 ty: TypeId::Int64,
144 flags: ColumnFlags::empty().with(ColumnFlags::PRIMARY_KEY),
145 default_value: None,
146 embedding_source: None,
147 },
148 ColumnDef {
149 id: 2,
150 name: "trip_id".into(),
151 ty: TypeId::Int64,
152 flags: ColumnFlags::empty(),
153 default_value: None,
154 embedding_source: None,
155 },
156 ColumnDef {
157 id: 3,
158 name: "title".into(),
159 ty: TypeId::Bytes,
160 flags: ColumnFlags::empty(),
161 default_value: None,
162 embedding_source: None,
163 },
164 ],
165 indexes: vec![IndexDef {
166 name: "segments_trip_idx".into(),
167 column_id: 2,
168 kind: IndexKind::Bitmap,
169 predicate: None,
170 options: Default::default(),
171 }],
172 colocation: vec![],
173 constraints: Default::default(),
174 clustered: false,
175 }
176 }
177
178 fn row(rid: u64, id: i64, trip: i64, title: &[u8]) -> Row {
179 let mut r = Row::new(RowId(rid), crate::epoch::Epoch(1));
180 r.columns.insert(1, Value::Int64(id));
181 r.columns.insert(2, Value::Int64(trip));
182 r.columns.insert(3, Value::Bytes(title.to_vec()));
183 r
184 }
185
186 #[test]
187 fn plan_title_only_change_is_repoint_not_move() {
188 let schema = schema_trip_bitmap();
189 let old = row(0, 10, 42, b"before");
190 let new = row(1, 10, 42, b"after");
191 let deltas = plan_bitmap_secondary_deltas(&schema, &old, &new);
192 assert_eq!(
193 deltas,
194 vec![BitmapIndexDelta::Repoint {
195 column_id: 2,
196 key: Value::Int64(42).encode_key(),
197 }]
198 );
199 }
200
201 #[test]
202 fn plan_trip_id_change_is_move() {
203 let schema = schema_trip_bitmap();
204 let old = row(0, 10, 42, b"x");
205 let new = row(1, 10, 99, b"x");
206 let deltas = plan_bitmap_secondary_deltas(&schema, &old, &new);
207 assert_eq!(
208 deltas,
209 vec![BitmapIndexDelta::Move {
210 column_id: 2,
211 old_key: Some(Value::Int64(42).encode_key()),
212 new_key: Some(Value::Int64(99).encode_key()),
213 }]
214 );
215 }
216
217 #[test]
218 fn apply_repoint_removes_old_rid_under_same_key() {
219 let schema = schema_trip_bitmap();
220 let mut bitmap = HashMap::new();
221 bitmap.insert(2, BitmapIndex::new());
222 let old = row(0, 10, 42, b"before");
223 let new = row(1, 10, 42, b"after");
224 bitmap
225 .get_mut(&2)
226 .unwrap()
227 .insert(Value::Int64(42).encode_key(), old.row_id);
228 maintain_bitmap_secondary_on_replace(&schema, &mut bitmap, &old, &new);
229 let b = bitmap.get(&2).unwrap();
230 assert!(!b.contains(&Value::Int64(42).encode_key(), old.row_id));
231 assert!(b.contains(&Value::Int64(42).encode_key(), new.row_id));
232 }
233
234 #[test]
235 fn apply_move_switches_keys() {
236 let schema = schema_trip_bitmap();
237 let mut bitmap = HashMap::new();
238 bitmap.insert(2, BitmapIndex::new());
239 let old = row(0, 10, 42, b"x");
240 let new = row(1, 10, 99, b"x");
241 bitmap
242 .get_mut(&2)
243 .unwrap()
244 .insert(Value::Int64(42).encode_key(), old.row_id);
245 maintain_bitmap_secondary_on_replace(&schema, &mut bitmap, &old, &new);
246 let b = bitmap.get(&2).unwrap();
247 assert!(!b.contains(&Value::Int64(42).encode_key(), old.row_id));
248 assert!(b.contains(&Value::Int64(99).encode_key(), new.row_id));
249 }
250}