1use std::collections::Bound;
5
6use reifydb_codec::key::{
7 deserializer::KeyDeserializer,
8 encoded::{EncodedKey, EncodedKeyRange},
9 serializer::KeySerializer,
10};
11use reifydb_value::value::{partition::Partition, row_number::RowNumber};
12
13use super::{EncodableKey, EncodableKeyRange, KeyKind};
14use crate::{
15 interface::catalog::shape::ShapeId,
16 key::catalog::{KeyDeserializerCatalogExt, KeySerializerCatalogExt},
17};
18
19#[derive(Debug, Clone, PartialEq)]
20pub enum RowLocator {
21 Row(RowNumber),
22
23 Series {
24 variant_tag: Option<u8>,
25 key: u64,
26 sequence: u64,
27 },
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub struct PartitionedRowKey {
32 pub shape: ShapeId,
33 pub partition: Partition,
34 pub locator: RowLocator,
35}
36
37impl PartitionedRowKey {
38 pub fn new(shape: impl Into<ShapeId>, partition: Partition, locator: RowLocator) -> Self {
39 Self {
40 shape: shape.into(),
41 partition,
42 locator,
43 }
44 }
45
46 pub fn encoded(shape: impl Into<ShapeId>, partition: Partition, locator: RowLocator) -> EncodedKey {
47 Self::new(shape, partition, locator).encode()
48 }
49
50 pub fn shape_of(key: &EncodedKey) -> Option<ShapeId> {
51 let mut de = KeyDeserializer::from_bytes(key.as_slice());
52 let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
53 if kind != Self::KIND {
54 return None;
55 }
56 de.read_shape_id().ok()
57 }
58
59 pub fn full_scan(shape: impl Into<ShapeId>) -> EncodedKeyRange {
60 let shape = shape.into();
61 let mut start = KeySerializer::with_capacity(10);
62 start.extend_u8(Self::KIND as u8).extend_shape_id(shape);
63 let mut end = KeySerializer::with_capacity(10);
64 end.extend_u8(Self::KIND as u8).extend_shape_id(shape.prev());
65 EncodedKeyRange::start_end(Some(start.to_encoded_key()), Some(end.to_encoded_key()))
66 }
67
68 pub fn scan_range(shape: impl Into<ShapeId>, last_key: Option<&EncodedKey>) -> EncodedKeyRange {
69 let shape = shape.into();
70 let start = match last_key {
71 Some(last) => Bound::Excluded(last.clone()),
72 None => {
73 let mut start = KeySerializer::with_capacity(10);
74 start.extend_u8(Self::KIND as u8).extend_shape_id(shape);
75 Bound::Included(start.to_encoded_key())
76 }
77 };
78 let mut end = KeySerializer::with_capacity(10);
79 end.extend_u8(Self::KIND as u8).extend_shape_id(shape.prev());
80 EncodedKeyRange::new(start, Bound::Included(end.to_encoded_key()))
81 }
82
83 pub fn partition_range(shape: impl Into<ShapeId>, partition: Partition) -> EncodedKeyRange {
84 let shape = shape.into();
85 let mut prefix = KeySerializer::with_capacity(26);
86 prefix.extend_u8(Self::KIND as u8).extend_shape_id(shape).extend_u128(partition.0);
87 let start = prefix.to_encoded_key();
88 let end = prefix_successor(start.as_slice());
89 EncodedKeyRange::new(Bound::Included(start), end)
90 }
91
92 pub fn partition_scan_range(
93 shape: impl Into<ShapeId>,
94 partition: Partition,
95 last_key: Option<&EncodedKey>,
96 ) -> EncodedKeyRange {
97 let base = Self::partition_range(shape, partition);
98 match last_key {
99 Some(last) => EncodedKeyRange::new(Bound::Excluded(last.clone()), base.end),
100 None => base,
101 }
102 }
103}
104
105fn prefix_successor(prefix: &[u8]) -> Bound<EncodedKey> {
106 let mut end = prefix.to_vec();
107 while let Some(&last) = end.last() {
108 if last == 0xFF {
109 end.pop();
110 } else {
111 *end.last_mut().unwrap() = last + 1;
112 return Bound::Excluded(EncodedKey::new(end));
113 }
114 }
115 Bound::Unbounded
116}
117
118impl EncodableKey for PartitionedRowKey {
119 const KIND: KeyKind = KeyKind::PartitionedRow;
120
121 fn encode(&self) -> EncodedKey {
122 let mut serializer = KeySerializer::with_capacity(32);
123 serializer.extend_u8(Self::KIND as u8).extend_shape_id(self.shape).extend_u128(self.partition.0);
124 match &self.locator {
125 RowLocator::Row(row) => {
126 serializer.extend_u64(row.0);
127 }
128 RowLocator::Series {
129 variant_tag,
130 key,
131 sequence,
132 } => {
133 match variant_tag {
134 Some(tag) => {
135 serializer.extend_u8(1u8).extend_u8(*tag);
136 }
137 None => {
138 serializer.extend_u8(0u8);
139 }
140 }
141 serializer.extend_u64(*key).extend_u64(*sequence);
142 }
143 }
144 serializer.to_encoded_key()
145 }
146
147 fn decode(key: &EncodedKey) -> Option<Self> {
148 let mut de = KeyDeserializer::from_bytes(key.as_slice());
149
150 let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
151 if kind != Self::KIND {
152 return None;
153 }
154
155 let shape = de.read_shape_id().ok()?;
156 let partition = Partition(de.read_u128().ok()?);
157
158 let locator = match shape {
159 ShapeId::Series(_) => {
160 let has_tag = de.read_u8().ok()?;
161 let variant_tag = if has_tag == 1 {
162 Some(de.read_u8().ok()?)
163 } else {
164 None
165 };
166 let key = de.read_u64().ok()?;
167 let sequence = de.read_u64().ok()?;
168 RowLocator::Series {
169 variant_tag,
170 key,
171 sequence,
172 }
173 }
174 _ => RowLocator::Row(RowNumber(de.read_u64().ok()?)),
175 };
176
177 Some(Self {
178 shape,
179 partition,
180 locator,
181 })
182 }
183}
184
185#[derive(Debug, Clone, PartialEq)]
186pub struct PartitionedRowKeyRange {
187 pub shape: ShapeId,
188}
189
190impl PartitionedRowKeyRange {
191 fn decode_key(key: &EncodedKey) -> Option<Self> {
192 let mut de = KeyDeserializer::from_bytes(key.as_slice());
193
194 let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
195 if kind != Self::KIND {
196 return None;
197 }
198
199 let shape = de.read_shape_id().ok()?;
200
201 Some(PartitionedRowKeyRange {
202 shape,
203 })
204 }
205}
206
207impl EncodableKeyRange for PartitionedRowKeyRange {
208 const KIND: KeyKind = KeyKind::PartitionedRow;
209
210 fn start(&self) -> Option<EncodedKey> {
211 let mut serializer = KeySerializer::with_capacity(10);
212 serializer.extend_u8(Self::KIND as u8).extend_shape_id(self.shape);
213 Some(serializer.to_encoded_key())
214 }
215
216 fn end(&self) -> Option<EncodedKey> {
217 let mut serializer = KeySerializer::with_capacity(10);
218 serializer.extend_u8(Self::KIND as u8).extend_shape_id(self.shape.prev());
219 Some(serializer.to_encoded_key())
220 }
221
222 fn decode(range: &EncodedKeyRange) -> (Option<Self>, Option<Self>)
223 where
224 Self: Sized,
225 {
226 let start_key = match &range.start {
227 Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
228 Bound::Unbounded => None,
229 };
230
231 let end_key = match &range.end {
232 Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
233 Bound::Unbounded => None,
234 };
235
236 (start_key, end_key)
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use std::ops::RangeBounds;
243
244 use reifydb_value::value::{Value, partition::Partition, row_number::RowNumber};
245
246 use super::{EncodableKey, PartitionedRowKey, RowLocator};
247 use crate::interface::catalog::{
248 id::{SeriesId, TableId},
249 shape::ShapeId,
250 };
251
252 fn part(v: &str) -> Partition {
253 Partition::of(&[Value::Utf8(v.to_string())])
254 }
255
256 #[test]
257 fn test_table_roundtrip() {
258 let key = PartitionedRowKey {
259 shape: ShapeId::Table(TableId(7)),
260 partition: part("us"),
261 locator: RowLocator::Row(RowNumber(42)),
262 };
263 let decoded = PartitionedRowKey::decode(&key.encode()).unwrap();
264 assert_eq!(decoded, key);
265 }
266
267 #[test]
268 fn test_series_roundtrip_with_tag() {
269 let key = PartitionedRowKey {
270 shape: ShapeId::Series(SeriesId(3)),
271 partition: part("btc"),
272 locator: RowLocator::Series {
273 variant_tag: Some(5),
274 key: 1_700_000_000,
275 sequence: 9,
276 },
277 };
278 let decoded = PartitionedRowKey::decode(&key.encode()).unwrap();
279 assert_eq!(decoded, key);
280 }
281
282 #[test]
283 fn test_series_roundtrip_without_tag() {
284 let key = PartitionedRowKey {
285 shape: ShapeId::Series(SeriesId(3)),
286 partition: part("eth"),
287 locator: RowLocator::Series {
288 variant_tag: None,
289 key: 100,
290 sequence: 0,
291 },
292 };
293 let decoded = PartitionedRowKey::decode(&key.encode()).unwrap();
294 assert_eq!(decoded, key);
295 }
296
297 #[test]
298 fn test_shape_of() {
299 let key = PartitionedRowKey::encoded(
300 ShapeId::Table(TableId(42)),
301 part("us"),
302 RowLocator::Row(RowNumber(1)),
303 );
304 assert_eq!(PartitionedRowKey::shape_of(&key), Some(ShapeId::Table(TableId(42))));
305 }
306
307 #[test]
308 fn test_partition_rows_cluster_together() {
309 let shape = ShapeId::Table(TableId(1));
310 let us_a = PartitionedRowKey::encoded(shape, part("us"), RowLocator::Row(RowNumber(1)));
311 let us_b = PartitionedRowKey::encoded(shape, part("us"), RowLocator::Row(RowNumber(2)));
312 let eu = PartitionedRowKey::encoded(shape, part("eu"), RowLocator::Row(RowNumber(1)));
313
314 let mut keys = [us_a.clone(), us_b.clone(), eu.clone()];
315 keys.sort();
316 let us_positions: Vec<usize> =
317 keys.iter().enumerate().filter(|(_, k)| **k == us_a || **k == us_b).map(|(i, _)| i).collect();
318 assert_eq!(us_positions[1] - us_positions[0], 1, "us partition rows must be contiguous");
319 }
320
321 #[test]
322 fn test_partition_range_contains_only_its_partition() {
323 let shape = ShapeId::Table(TableId(1));
324 let range = PartitionedRowKey::partition_range(shape, part("us"));
325 let us = PartitionedRowKey::encoded(shape, part("us"), RowLocator::Row(RowNumber(500)));
326 let eu = PartitionedRowKey::encoded(shape, part("eu"), RowLocator::Row(RowNumber(1)));
327 assert!(range.contains(&us), "us row must be inside the us partition range");
328 assert!(!range.contains(&eu), "eu row must be outside the us partition range");
329 }
330}