Skip to main content

reifydb_core/key/
column.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_codec::key::encoded::EncodedKey;
5use reifydb_macro::KeyCodec;
6
7use super::KeyTag;
8use crate::{
9	interface::catalog::{
10		id::{ColumnId, ColumnSnapshotId, SeriesId, TableId},
11		object::ObjectId,
12	},
13	key::{
14		any::{Field, KeyFields, Width},
15		bound::{TaggedKeyBoundRange, object_fields},
16		catalog::{KeyDeserializerCatalogExt, KeySerializerCatalogExt},
17	},
18};
19
20#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
21#[key(tag = Column)]
22pub struct ColumnKey {
23	pub object: ObjectId,
24	pub column: ColumnId,
25}
26
27impl ColumnKey {
28	pub fn new(object: impl Into<ObjectId>, column: impl Into<ColumnId>) -> Self {
29		Self {
30			object: object.into(),
31			column: column.into(),
32		}
33	}
34
35	pub fn encoded(object: impl Into<ObjectId>, column: impl Into<ColumnId>) -> EncodedKey {
36		Self::new(object, column).encode()
37	}
38
39	pub fn full_scan(object: impl Into<ObjectId>) -> TaggedKeyBoundRange {
40		TaggedKeyBoundRange::prefix(Self::TAG, object_fields(object.into()))
41	}
42}
43
44#[cfg(test)]
45pub mod column_key_tests {
46	use crate::{
47		interface::catalog::{id::ColumnId, object::ObjectId},
48		key::column::ColumnKey,
49	};
50
51	#[test]
52	fn test_encode_decode() {
53		let key = ColumnKey {
54			object: ObjectId::table(0xABCD),
55			column: ColumnId(0x123456789ABCDEF0),
56		};
57		let encoded = key.encode();
58
59		let expected: Vec<u8> = vec![
60			0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x32, 0xED, 0xCB, 0xA9, 0x87, 0x65, 0x43,
61			0x21, 0x0F,
62		];
63
64		assert_eq!(encoded.as_slice(), expected);
65
66		let key = ColumnKey::decode(&encoded).unwrap();
67		assert_eq!(key.object, 0xABCD);
68		assert_eq!(key.column, 0x123456789ABCDEF0);
69	}
70
71	#[test]
72	fn test_order_preserving() {
73		let key1 = ColumnKey {
74			object: ObjectId::table(1),
75			column: ColumnId(100),
76		};
77		let key2 = ColumnKey {
78			object: ObjectId::table(1),
79			column: ColumnId(200),
80		};
81		let key3 = ColumnKey {
82			object: ObjectId::table(2),
83			column: ColumnId(0),
84		};
85
86		let encoded1 = key1.encode();
87		let encoded2 = key2.encode();
88		let encoded3 = key3.encode();
89
90		assert!(encoded3 < encoded2, "ordering not preserved");
91		assert!(encoded2 < encoded1, "ordering not preserved");
92	}
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, KeyCodec, Hash)]
96#[key(tag = ColumnSequence)]
97pub struct ColumnSequenceKey {
98	pub object: ObjectId,
99	pub column: ColumnId,
100}
101
102impl ColumnSequenceKey {
103	pub fn new(object: impl Into<ObjectId>, column: impl Into<ColumnId>) -> Self {
104		Self {
105			object: object.into(),
106			column: column.into(),
107		}
108	}
109
110	pub fn encoded(object: impl Into<ObjectId>, column: impl Into<ColumnId>) -> EncodedKey {
111		Self::new(object, column).encode()
112	}
113}
114
115#[cfg(test)]
116pub mod column_sequence_key_tests {
117	use reifydb_codec::key::encoded::EncodedKey;
118
119	use super::ColumnSequenceKey;
120	use crate::interface::catalog::{id::ColumnId, object::ObjectId};
121
122	#[test]
123	fn test_encode_decode() {
124		let key = ColumnSequenceKey {
125			object: ObjectId::table(0x1234),
126			column: ColumnId(0x5678),
127		};
128		let encoded = key.encode();
129
130		assert_eq!(encoded[0], 0xF1);
131
132		let decoded = ColumnSequenceKey::decode(&encoded).unwrap();
133		assert_eq!(decoded.object, ObjectId::table(0x1234));
134		assert_eq!(decoded.column, ColumnId(0x5678));
135	}
136
137	#[test]
138	fn test_decode_invalid_version() {
139		let mut encoded = vec![0xFF];
140		encoded.push(0x0E);
141		encoded.extend(&[0; 16]);
142
143		let decoded = ColumnSequenceKey::decode(&EncodedKey::new(encoded));
144		assert!(decoded.is_none());
145	}
146
147	#[test]
148	fn test_decode_invalid_kind() {
149		let mut encoded = vec![0x01];
150		encoded.push(0xFF);
151		encoded.extend(&[0; 16]);
152
153		let decoded = ColumnSequenceKey::decode(&EncodedKey::new(encoded));
154		assert!(decoded.is_none());
155	}
156
157	#[test]
158	fn test_decode_invalid_length() {
159		let encoded = vec![0x01, 0x0E];
160		let decoded = ColumnSequenceKey::decode(&EncodedKey::new(encoded));
161		assert!(decoded.is_none());
162	}
163}
164
165#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
166#[key(tag = ColumnSnapshot)]
167pub struct ColumnSnapshotKey {
168	pub snapshot: ColumnSnapshotId,
169}
170
171impl ColumnSnapshotKey {
172	pub fn new(snapshot: ColumnSnapshotId) -> Self {
173		Self {
174			snapshot,
175		}
176	}
177
178	pub fn encoded(snapshot: impl Into<ColumnSnapshotId>) -> EncodedKey {
179		Self::new(snapshot.into()).encode()
180	}
181
182	pub fn full_scan() -> TaggedKeyBoundRange {
183		TaggedKeyBoundRange::kind(Self::TAG)
184	}
185}
186
187#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
188#[key(tag = SeriesColumnSnapshot)]
189pub struct SeriesColumnSnapshotKey {
190	pub series: SeriesId,
191	pub snapshot: ColumnSnapshotId,
192}
193
194impl SeriesColumnSnapshotKey {
195	pub fn new(series: SeriesId, snapshot: ColumnSnapshotId) -> Self {
196		Self {
197			series,
198			snapshot,
199		}
200	}
201
202	pub fn encoded(series: impl Into<SeriesId>, snapshot: impl Into<ColumnSnapshotId>) -> EncodedKey {
203		Self::new(series.into(), snapshot.into()).encode()
204	}
205
206	pub fn full_scan(series: SeriesId) -> TaggedKeyBoundRange {
207		TaggedKeyBoundRange::prefix(Self::TAG, [Field::UDesc(Width::U64, series.0 as u128)])
208	}
209}
210
211#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
212#[key(tag = TableColumnSnapshot)]
213pub struct TableColumnSnapshotKey {
214	pub table: TableId,
215	pub snapshot: ColumnSnapshotId,
216}
217
218impl TableColumnSnapshotKey {
219	pub fn new(table: TableId, snapshot: ColumnSnapshotId) -> Self {
220		Self {
221			table,
222			snapshot,
223		}
224	}
225
226	pub fn encoded(table: impl Into<TableId>, snapshot: impl Into<ColumnSnapshotId>) -> EncodedKey {
227		Self::new(table.into(), snapshot.into()).encode()
228	}
229
230	pub fn full_scan(table: TableId) -> TaggedKeyBoundRange {
231		TaggedKeyBoundRange::prefix(Self::TAG, [Field::UDesc(Width::U64, table.0 as u128)])
232	}
233}
234
235#[cfg(test)]
236pub mod column_snapshot_key_tests {
237	use std::ops::Bound;
238
239	use super::*;
240
241	#[test]
242	fn a_snapshot_key_reports_its_own_kind_from_its_first_byte() {
243		// the leading version byte made KeyTag::of read every snapshot key as a namespace key, so the
244		// metrics parser and the entry classifier both routed them to an owner they never belonged to
245		assert_eq!(KeyTag::of(&ColumnSnapshotKey::encoded(ColumnSnapshotId(1))), Some(KeyTag::ColumnSnapshot));
246		assert_eq!(
247			KeyTag::of(&SeriesColumnSnapshotKey::encoded(SeriesId(1), ColumnSnapshotId(2))),
248			Some(KeyTag::SeriesColumnSnapshot)
249		);
250		assert_eq!(
251			KeyTag::of(&TableColumnSnapshotKey::encoded(TableId(1), ColumnSnapshotId(2))),
252			Some(KeyTag::TableColumnSnapshot)
253		);
254	}
255
256	#[test]
257	fn test_column_snapshot_key_encode_decode() {
258		let key = ColumnSnapshotKey {
259			snapshot: ColumnSnapshotId(0x1234),
260		};
261		let encoded = key.encode();
262		let decoded = ColumnSnapshotKey::decode(&encoded).unwrap();
263		assert_eq!(decoded.snapshot, key.snapshot);
264	}
265
266	#[test]
267	fn test_column_snapshot_key_full_scan() {
268		let range = ColumnSnapshotKey::full_scan();
269		assert!(matches!(range.start, Bound::Included(_) | Bound::Excluded(_)));
270		assert!(matches!(range.end, Bound::Included(_) | Bound::Excluded(_)));
271	}
272
273	#[test]
274	fn test_series_column_snapshot_key_encode_decode() {
275		let key = SeriesColumnSnapshotKey {
276			series: SeriesId(42),
277			snapshot: ColumnSnapshotId(99),
278		};
279		let encoded = key.encode();
280		let decoded = SeriesColumnSnapshotKey::decode(&encoded).unwrap();
281		assert_eq!(decoded.series, key.series);
282		assert_eq!(decoded.snapshot, key.snapshot);
283	}
284
285	#[test]
286	fn test_table_column_snapshot_key_encode_decode() {
287		let key = TableColumnSnapshotKey {
288			table: TableId(42),
289			snapshot: ColumnSnapshotId(99),
290		};
291		let encoded = key.encode();
292		let decoded = TableColumnSnapshotKey::decode(&encoded).unwrap();
293		assert_eq!(decoded.table, key.table);
294		assert_eq!(decoded.snapshot, key.snapshot);
295	}
296}
297
298#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
299#[key(tag = Columns)]
300pub struct ColumnsKey {
301	pub column: ColumnId,
302}
303
304impl ColumnsKey {
305	pub fn new(column: impl Into<ColumnId>) -> Self {
306		Self {
307			column: column.into(),
308		}
309	}
310
311	pub fn encoded(column: impl Into<ColumnId>) -> EncodedKey {
312		Self::new(column).encode()
313	}
314
315	pub fn full_scan() -> TaggedKeyBoundRange {
316		TaggedKeyBoundRange::kind(Self::TAG)
317	}
318}
319
320#[cfg(test)]
321pub mod columns_key_tests {
322	use super::ColumnsKey;
323	use crate::interface::catalog::id::ColumnId;
324
325	#[test]
326	fn test_encode_decode() {
327		let key = ColumnsKey {
328			column: ColumnId(0xABCD),
329		};
330		let encoded = key.encode();
331		let expected = vec![0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x32];
332		assert_eq!(encoded.as_slice(), expected);
333
334		let key = ColumnsKey::decode(&encoded).unwrap();
335		assert_eq!(key.column, 0xABCD);
336	}
337}