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