Skip to main content

reifydb_core/key/
row.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{cmp::Ordering, collections::Bound};
5
6use reifydb_codec::{
7	key::{
8		ByteSink,
9		deserializer::KeyDeserializer,
10		encoded::{EncodedKey, EncodedKeyRange},
11		serializer::KeySerializer,
12	},
13	row::shape::fingerprint::RowShapeFingerprint,
14};
15use reifydb_macro::KeyCodec;
16use reifydb_value::value::{partition::Partition, row_number::RowNumber};
17use serde::{Deserialize, Serialize};
18
19use super::{KeyRangeCodec, KeyTag};
20use crate::{
21	interface::catalog::{object::ObjectId, storage::StorageId},
22	key::{
23		any::{Field, KeyFields, RawEncoding, TaggedKey, Width},
24		bound::{TaggedKeyBoundRange, object_fields},
25		catalog::{KeyDeserializerCatalogExt, KeySerializerCatalogExt},
26		sort_run::SortRun,
27		typed::{
28			BoundedKey, DenseKey,
29			direction::{Asc, Desc},
30		},
31	},
32	metrics::heap::HeapSize,
33};
34
35#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
36#[key(tag = Row)]
37pub struct RowKey {
38	pub storage: StorageId,
39	pub row: RowNumber,
40}
41
42#[derive(Debug, Clone, PartialEq)]
43pub struct RowKeyRange {
44	pub storage: StorageId,
45}
46
47impl RowKeyRange {
48	fn decode_key(key: &EncodedKey) -> Option<Self> {
49		let mut de = KeyDeserializer::from_bytes(key.as_slice());
50
51		let kind: KeyTag = de.read_u8().ok()?.try_into().ok()?;
52		if kind != Self::TAG {
53			return None;
54		}
55
56		let storage = StorageId::from_object(de.read_object_id().ok()?)?;
57
58		Some(RowKeyRange {
59			storage,
60		})
61	}
62
63	pub fn storage_scan(storage: StorageId) -> TaggedKeyBoundRange {
64		TaggedKeyBoundRange::prefix(Self::TAG, object_fields(ObjectId::from(storage)))
65	}
66
67	pub fn scan_range(storage: StorageId, last: Option<&TaggedKey>) -> TaggedKeyBoundRange {
68		Self::storage_scan(storage).resume_after(last)
69	}
70
71	pub fn scan_range_rev(storage: StorageId, last: Option<&TaggedKey>) -> TaggedKeyBoundRange {
72		Self::storage_scan(storage).resume_before(last)
73	}
74}
75
76impl KeyRangeCodec for RowKeyRange {
77	const TAG: KeyTag = KeyTag::Row;
78
79	fn start(&self) -> Option<EncodedKey> {
80		let mut serializer = KeySerializer::with_capacity(10);
81		serializer.extend_u8(Self::TAG as u8).extend_object_id(self.storage);
82		Some(serializer.to_encoded_key())
83	}
84
85	fn end(&self) -> Option<EncodedKey> {
86		let mut serializer = KeySerializer::with_capacity(10);
87		serializer.extend_u8(Self::TAG as u8).extend_object_id(ObjectId::from(self.storage).prev());
88		Some(serializer.to_encoded_key())
89	}
90
91	fn decode(range: &EncodedKeyRange) -> (Option<Self>, Option<Self>)
92	where
93		Self: Sized,
94	{
95		let start_key = match &range.start {
96			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
97			Bound::Unbounded => None,
98		};
99
100		let end_key = match &range.end {
101			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
102			Bound::Unbounded => None,
103		};
104
105		(start_key, end_key)
106	}
107}
108
109impl RowKey {
110	pub fn new(storage: impl Into<StorageId>, row: impl Into<RowNumber>) -> Self {
111		Self {
112			storage: storage.into(),
113			row: row.into(),
114		}
115	}
116
117	pub fn encoded(storage: impl Into<StorageId>, row: impl Into<RowNumber>) -> EncodedKey {
118		Self {
119			storage: storage.into(),
120			row: row.into(),
121		}
122		.encode()
123	}
124
125	pub fn full_scan(storage: impl Into<StorageId>) -> TaggedKeyBoundRange {
126		TaggedKeyBoundRange::prefix(Self::TAG, object_fields(ObjectId::from(storage.into())))
127	}
128
129	pub fn storage_start(storage: impl Into<StorageId>) -> EncodedKey {
130		let mut serializer = KeySerializer::with_capacity(10);
131		serializer.extend_u8(RowKey::TAG as u8).extend_object_id(storage.into());
132		serializer.to_encoded_key()
133	}
134
135	pub fn storage_end(storage: impl Into<StorageId>) -> EncodedKey {
136		let mut serializer = KeySerializer::with_capacity(10);
137		serializer.extend_u8(RowKey::TAG as u8).extend_object_id(ObjectId::from(storage.into()).prev());
138		serializer.to_encoded_key()
139	}
140}
141
142#[derive(Debug, Clone, PartialEq, KeyCodec, Eq, Hash)]
143#[key(tag = SortedViewRow)]
144pub struct SortedViewRowKey {
145	pub storage: StorageId,
146	pub run: SortRun,
147	pub row: Asc<RowNumber>,
148}
149
150impl Ord for SortedViewRowKey {
151	fn cmp(&self, other: &Self) -> Ordering {
152		storage_order(self.storage)
153			.cmp(&storage_order(other.storage))
154			.then_with(|| self.run.cmp(&other.run))
155			.then_with(|| self.row.cmp(&other.row))
156	}
157}
158
159impl PartialOrd for SortedViewRowKey {
160	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
161		Some(self.cmp(other))
162	}
163}
164
165impl SortedViewRowKey {
166	pub fn new(storage: impl Into<StorageId>, run: SortRun, row: RowNumber) -> Self {
167		Self {
168			storage: storage.into(),
169			run,
170			row: Asc(row),
171		}
172	}
173
174	pub fn encoded(storage: impl Into<StorageId>, run: SortRun, row: RowNumber) -> EncodedKey {
175		Self::new(storage, run, row).encode()
176	}
177
178	pub fn storage_start(storage: impl Into<StorageId>) -> EncodedKey {
179		let mut serializer = KeySerializer::with_capacity(10);
180		serializer.extend_u8(Self::TAG as u8).extend_object_id(storage.into());
181		serializer.to_encoded_key()
182	}
183
184	pub fn storage_scan(storage: impl Into<StorageId>) -> TaggedKeyBoundRange {
185		TaggedKeyBoundRange::prefix(Self::TAG, object_fields(ObjectId::from(storage.into())))
186	}
187
188	pub fn scan_range(storage: impl Into<StorageId>, last: Option<&TaggedKey>) -> TaggedKeyBoundRange {
189		Self::storage_scan(storage).resume_after(last)
190	}
191
192	pub fn storage_of(key: &EncodedKey) -> Option<StorageId> {
193		let mut de = KeyDeserializer::from_bytes(key.as_slice());
194		let kind: KeyTag = de.read_u8().ok()?.try_into().ok()?;
195		if kind != Self::TAG {
196			return None;
197		}
198		StorageId::from_object(de.read_object_id().ok()?)
199	}
200
201	pub fn row_of(key: &EncodedKey) -> Option<RowNumber> {
202		let bytes = key.as_slice();
203		if KeyTag::of(bytes)? != Self::TAG {
204			return None;
205		}
206		let tail = bytes.len().checked_sub(8)?;
207		Some(RowNumber(u64::from_be_bytes(bytes[tail..].try_into().ok()?)))
208	}
209
210	pub fn range_storage_of(range: &EncodedKeyRange) -> Option<StorageId> {
211		let start = bound_storage_of(&range.start, Self::TAG)?;
212		bound_storage_of(&range.end, Self::TAG)?;
213		Some(start)
214	}
215}
216
217const SORT_RUN_MARKER: u8 = 0x00;
218const SORT_RUN_ZERO: u8 = 0xff;
219const SORT_RUN_END: u8 = 0x00;
220
221pub(crate) fn encode_sort_run<B: ByteSink>(run: &[u8], out: &mut B) {
222	for &byte in run {
223		if byte == SORT_RUN_MARKER {
224			out.extend_from_slice(&[SORT_RUN_MARKER, SORT_RUN_ZERO]);
225		} else {
226			out.push(byte);
227		}
228	}
229	out.extend_from_slice(&[SORT_RUN_MARKER, SORT_RUN_END]);
230}
231
232fn extend_sort_run(serializer: &mut KeySerializer, run: &SortRun) {
233	encode_sort_run(run.as_slice(), serializer);
234}
235
236fn read_sort_run(de: &mut KeyDeserializer) -> Option<SortRun> {
237	let mut run: Vec<u8> = Vec::new();
238	loop {
239		let byte = de.read_raw(1).ok()?[0];
240		if byte != SORT_RUN_MARKER {
241			run.push(byte);
242			continue;
243		}
244		match de.read_raw(1).ok()?[0] {
245			SORT_RUN_END => return Some(SortRun::new(run)),
246			SORT_RUN_ZERO => run.push(0x00),
247			_ => return None,
248		}
249	}
250}
251
252fn read_row_tail(de: &mut KeyDeserializer) -> Option<Asc<RowNumber>> {
253	let bytes: [u8; 8] = de.read_raw(8).ok()?.try_into().ok()?;
254	Some(Asc(RowNumber(u64::from_be_bytes(bytes))))
255}
256
257fn storage_order(storage: StorageId) -> (u8, Desc<u64>) {
258	(ObjectId::from(storage).type_tag(), Desc(storage.as_u64()))
259}
260
261fn bound_storage_of(bound: &Bound<EncodedKey>, kind: KeyTag) -> Option<StorageId> {
262	let key = match bound {
263		Bound::Included(key) | Bound::Excluded(key) => key,
264		Bound::Unbounded => return None,
265	};
266	let mut de = KeyDeserializer::from_bytes(key.as_slice());
267	if KeyTag::try_from(de.read_u8().ok()?).ok()? != kind {
268		return None;
269	}
270	StorageId::from_object(de.read_object_id().ok()?)
271}
272
273#[derive(Debug, Clone, PartialEq, KeyCodec, Eq, Hash)]
274#[key(tag = PartitionedSortedViewRow)]
275pub struct PartitionedSortedViewRowKey {
276	pub storage: StorageId,
277	pub partition: Partition,
278	pub run: SortRun,
279	pub row: Asc<RowNumber>,
280}
281
282impl Ord for PartitionedSortedViewRowKey {
283	fn cmp(&self, other: &Self) -> Ordering {
284		storage_order(self.storage)
285			.cmp(&storage_order(other.storage))
286			.then_with(|| Desc(self.partition).cmp(&Desc(other.partition)))
287			.then_with(|| self.run.cmp(&other.run))
288			.then_with(|| self.row.cmp(&other.row))
289	}
290}
291
292impl PartialOrd for PartitionedSortedViewRowKey {
293	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
294		Some(self.cmp(other))
295	}
296}
297
298impl PartitionedSortedViewRowKey {
299	pub fn new(storage: impl Into<StorageId>, partition: Partition, run: SortRun, row: RowNumber) -> Self {
300		Self {
301			storage: storage.into(),
302			partition,
303			run,
304			row: Asc(row),
305		}
306	}
307
308	pub fn encoded(
309		storage: impl Into<StorageId>,
310		partition: Partition,
311		run: SortRun,
312		row: RowNumber,
313	) -> EncodedKey {
314		Self::new(storage, partition, run, row).encode()
315	}
316
317	pub fn storage_start(storage: impl Into<StorageId>) -> EncodedKey {
318		let mut serializer = KeySerializer::with_capacity(10);
319		serializer.extend_u8(Self::TAG as u8).extend_object_id(storage.into());
320		serializer.to_encoded_key()
321	}
322
323	pub fn storage_scan(storage: impl Into<StorageId>) -> TaggedKeyBoundRange {
324		TaggedKeyBoundRange::prefix(Self::TAG, object_fields(ObjectId::from(storage.into())))
325	}
326
327	pub fn scan_range(storage: impl Into<StorageId>, last: Option<&TaggedKey>) -> TaggedKeyBoundRange {
328		Self::storage_scan(storage).resume_after(last)
329	}
330
331	pub fn partition_range(storage: impl Into<StorageId>, partition: Partition) -> TaggedKeyBoundRange {
332		TaggedKeyBoundRange::prefix(
333			Self::TAG,
334			object_fields(ObjectId::from(storage.into()))
335				.into_iter()
336				.chain([Field::UDesc(Width::U128, partition.0)])
337				.collect::<Vec<_>>(),
338		)
339	}
340
341	pub fn partition_scan_range(
342		storage: impl Into<StorageId>,
343		partition: Partition,
344		last: Option<&TaggedKey>,
345	) -> TaggedKeyBoundRange {
346		Self::partition_range(storage, partition).resume_after(last)
347	}
348
349	pub fn storage_of(key: &EncodedKey) -> Option<StorageId> {
350		let mut de = KeyDeserializer::from_bytes(key.as_slice());
351		let kind: KeyTag = de.read_u8().ok()?.try_into().ok()?;
352		if kind != Self::TAG {
353			return None;
354		}
355		StorageId::from_object(de.read_object_id().ok()?)
356	}
357
358	pub fn row_of(key: &EncodedKey) -> Option<RowNumber> {
359		let bytes = key.as_slice();
360		if KeyTag::of(bytes)? != Self::TAG {
361			return None;
362		}
363		let tail = bytes.len().checked_sub(8)?;
364		Some(RowNumber(u64::from_be_bytes(bytes[tail..].try_into().ok()?)))
365	}
366
367	pub fn range_storage_of(range: &EncodedKeyRange) -> Option<StorageId> {
368		let start = bound_storage_of(&range.start, Self::TAG)?;
369		bound_storage_of(&range.end, Self::TAG)?;
370		Some(start)
371	}
372}
373
374#[cfg(test)]
375#[allow(clippy::disallowed_methods)]
376mod sorted_view_row_key_tests {
377	use std::ops::RangeBounds;
378
379	use reifydb_codec::key::encoded::EncodedKey;
380	use reifydb_value::value::{Value, partition::Partition, row_number::RowNumber};
381
382	use super::{PartitionedSortedViewRowKey, RowKey, SortedViewRowKey, TaggedKey};
383	use crate::{interface::catalog::storage::StorageId, key::sort_run::SortRun};
384
385	fn part(v: &str) -> Partition {
386		Partition::of(&[Value::Utf8(v.to_string())])
387	}
388
389	fn sorted_view(storage: StorageId, sort: &[u8], row: RowNumber) -> EncodedKey {
390		SortedViewRowKey::encoded(storage, SortRun::new(sort), row)
391	}
392
393	fn sorted_view_cursor(storage: StorageId, sort: &[u8], row: RowNumber) -> TaggedKey {
394		TaggedKey::from(SortedViewRowKey::new(storage, SortRun::new(sort), row))
395	}
396
397	fn partitioned(storage: StorageId, partition: Partition, sort: &[u8], row: RowNumber) -> EncodedKey {
398		PartitionedSortedViewRowKey::encoded(storage, partition, SortRun::new(sort), row)
399	}
400
401	#[test]
402	fn test_row_comes_from_the_tail_not_the_sort_prefix() {
403		// The sort payload sits between the storage and the row, so reading a fixed offset picks up
404		// sort bytes instead: two rows that sort differently would report the same row number.
405		let storage = StorageId::view(3);
406		let a = sorted_view(storage, &[0xAA; 8], RowNumber(7));
407		let b = sorted_view(storage, &[0xBB; 24], RowNumber(9));
408
409		assert_eq!(SortedViewRowKey::row_of(&a), Some(RowNumber(7)));
410		assert_eq!(SortedViewRowKey::row_of(&b), Some(RowNumber(9)));
411		assert_eq!(SortedViewRowKey::storage_of(&a), Some(storage));
412	}
413
414	#[test]
415	fn test_row_of_rejects_a_plain_row_key() {
416		// A plain row key ends in the keycode-inverted row, so reading its tail raw yields a
417		// different number entirely; the kind check is what keeps the two keyspaces apart.
418		let plain = RowKey::encoded(StorageId::view(3), RowNumber(7));
419		assert_eq!(SortedViewRowKey::row_of(&plain), None);
420		assert_eq!(PartitionedSortedViewRowKey::row_of(&plain), None);
421	}
422
423	#[test]
424	fn test_scan_range_covers_its_storage_and_nothing_else() {
425		let storage = StorageId::view(3);
426		let range = SortedViewRowKey::scan_range(storage, None).encode();
427
428		assert!(range.contains(&sorted_view(storage, &[0x00; 8], RowNumber(1))));
429		assert!(range.contains(&sorted_view(storage, &[0xFF; 8], RowNumber(u64::MAX))));
430		assert!(!range.contains(&sorted_view(StorageId::view(4), &[0x00; 8], RowNumber(1))));
431	}
432
433	#[test]
434	fn test_scan_range_resumes_strictly_after_the_last_key() {
435		// Resuming inclusively re-serves the last row of the previous chunk, duplicating it in the view.
436		let storage = StorageId::view(3);
437		let last = sorted_view_cursor(storage, &[0x40; 8], RowNumber(5));
438		let range = SortedViewRowKey::scan_range(storage, Some(&last)).encode();
439
440		assert!(!range.contains(&last.encode()));
441		assert!(range.contains(&sorted_view(storage, &[0x41; 8], RowNumber(1))));
442	}
443
444	#[test]
445	fn test_sort_prefix_orders_the_keyspace() {
446		// The sorted view key exists so a plain forward scan returns the view already sorted; if the
447		// row bytes outranked the sort prefix the scan would come back in insertion order.
448		let storage = StorageId::view(3);
449		let early_sort_late_row = sorted_view(storage, &[0x10; 8], RowNumber(999));
450		let late_sort_early_row = sorted_view(storage, &[0x20; 8], RowNumber(1));
451
452		assert!(early_sort_late_row < late_sort_early_row);
453	}
454
455	#[test]
456	fn test_partition_range_contains_only_its_partition() {
457		let storage = StorageId::view(3);
458		let range = PartitionedSortedViewRowKey::partition_range(storage, part("us")).encode();
459
460		assert!(range.contains(&partitioned(storage, part("us"), &[0x10; 8], RowNumber(1))));
461		assert!(!range.contains(&partitioned(storage, part("eu"), &[0x10; 8], RowNumber(1))));
462	}
463
464	#[test]
465	fn test_partitioned_row_comes_from_the_tail() {
466		let storage = StorageId::view(3);
467		let key = partitioned(storage, part("us"), &[0xAA; 8], RowNumber(42));
468
469		assert_eq!(PartitionedSortedViewRowKey::row_of(&key), Some(RowNumber(42)));
470		assert_eq!(PartitionedSortedViewRowKey::storage_of(&key), Some(storage));
471		assert_eq!(SortedViewRowKey::row_of(&key), None);
472	}
473}
474
475#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
476pub struct StorageRowKey(pub Desc<RowNumber>);
477
478impl From<RowKey> for StorageRowKey {
479	fn from(key: RowKey) -> Self {
480		StorageRowKey::new(key.row)
481	}
482}
483
484impl StorageRowKey {
485	pub fn new(row: RowNumber) -> Self {
486		StorageRowKey(Desc(row))
487	}
488
489	pub fn row(self) -> RowNumber {
490		self.0.0
491	}
492
493	pub fn with_storage(self, storage: StorageId) -> RowKey {
494		RowKey {
495			storage,
496			row: self.row(),
497		}
498	}
499}
500
501impl HeapSize for StorageRowKey {
502	fn heap_size(&self) -> usize {
503		0
504	}
505}
506
507impl BoundedKey for StorageRowKey {
508	fn low() -> Self {
509		StorageRowKey(<Desc<RowNumber> as BoundedKey>::low())
510	}
511}
512
513impl DenseKey for StorageRowKey {
514	fn successor(&self) -> Option<Self> {
515		self.0.successor().map(StorageRowKey)
516	}
517}
518
519#[cfg(test)]
520pub mod row_key_tests {
521	use reifydb_value::value::row_number::RowNumber;
522
523	use super::{RowKey, StorageRowKey};
524	use crate::{
525		interface::catalog::storage::StorageId,
526		key::typed::{BoundedKey, DenseKey},
527	};
528
529	#[test]
530	fn test_encode_decode() {
531		let key = RowKey {
532			storage: StorageId::table(0xABCD),
533			row: RowNumber(0x123456789ABCDEF0),
534		};
535		let encoded = key.encode();
536
537		let expected: Vec<u8> = vec![
538			0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x32, 0xED, 0xCB, 0xA9, 0x87, 0x65, 0x43,
539			0x21, 0x0F,
540		];
541
542		assert_eq!(encoded.as_slice(), expected);
543
544		let key = RowKey::decode(&encoded).unwrap();
545		assert_eq!(key.storage, StorageId::table(0xABCD));
546		assert_eq!(key.row, 0x123456789ABCDEF0);
547	}
548
549	#[test]
550	fn test_encode_decode_view() {
551		// Without the view tag narrowing back through `from_object`, a view's row key decodes to `None`.
552		let key = RowKey {
553			storage: StorageId::view(0xABCD),
554			row: RowNumber(0x123456789ABCDEF0),
555		};
556		let encoded = key.encode();
557
558		let expected: Vec<u8> = vec![
559			0xFC, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x32, 0xED, 0xCB, 0xA9, 0x87, 0x65, 0x43,
560			0x21, 0x0F,
561		];
562
563		assert_eq!(encoded.as_slice(), expected);
564
565		let key = RowKey::decode(&encoded).unwrap();
566		assert_eq!(key.storage, StorageId::view(0xABCD));
567		assert_eq!(key.row, 0x123456789ABCDEF0);
568	}
569
570	#[test]
571	fn test_order_preserving() {
572		let key1 = RowKey {
573			storage: StorageId::table(1),
574			row: RowNumber(100),
575		};
576		let key2 = RowKey {
577			storage: StorageId::table(1),
578			row: RowNumber(200),
579		};
580		let key3 = RowKey {
581			storage: StorageId::table(2),
582			row: RowNumber(1),
583		};
584
585		let encoded1 = key1.encode();
586		let encoded2 = key2.encode();
587		let encoded3 = key3.encode();
588
589		assert!(encoded3 < encoded2, "ordering not preserved");
590		assert!(encoded2 < encoded1, "ordering not preserved");
591	}
592
593	#[test]
594	fn test_row_ident_roundtrip() {
595		let key = RowKey {
596			storage: StorageId::table(7),
597			row: RowNumber(42),
598		};
599
600		// dropping storage and re-supplying the same value must recover the original key
601		let ident: StorageRowKey = key.clone().into();
602		let restored = ident.with_storage(key.storage);
603		assert_eq!(restored, key);
604	}
605
606	#[test]
607	fn test_row_ident_ordering_matches_the_encoded_key() {
608		let storage = StorageId::table(1);
609		let one = StorageRowKey::new(RowNumber(1));
610		let two = StorageRowKey::new(RowNumber(2));
611
612		// the narrow identity stands in for the encoded key in the tiers, so it must sort the same way:
613		// descending by row number, not ascending
614		assert!(two < one);
615		assert_eq!(
616			two < one,
617			RowKey::encoded(storage, RowNumber(2)).as_slice()
618				< RowKey::encoded(storage, RowNumber(1)).as_slice()
619		);
620	}
621
622	#[test]
623	fn test_row_ident_low_is_the_greatest_row() {
624		// low() names the first key a scan meets. Under descending order that is the highest row,
625		// and a scan seeded from the lowest row would start past every key it meant to cover.
626		assert_eq!(<StorageRowKey as BoundedKey>::low(), StorageRowKey::new(RowNumber(u64::MAX)));
627	}
628
629	#[test]
630	fn test_row_ident_successor_is_the_next_key_in_scan_order() {
631		// nothing may sort between a key and its successor, or an exclusive upper end drops a row
632		let ident = StorageRowKey::new(RowNumber(5));
633		let next = ident.successor().unwrap();
634		assert_eq!(next, StorageRowKey::new(RowNumber(4)));
635		assert!(next > ident);
636		assert!(StorageRowKey::new(RowNumber(3)) > next);
637	}
638
639	#[test]
640	fn test_row_ident_successor_runs_out_at_row_zero() {
641		// row zero is the last key in descending order, so it has no successor to hand back
642		assert_eq!(StorageRowKey::new(RowNumber(0)).successor(), None);
643	}
644}
645
646#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
647#[key(tag = RowSequence)]
648pub struct RowSequenceKey {
649	pub storage: StorageId,
650}
651
652impl RowSequenceKey {
653	pub fn new(storage: StorageId) -> Self {
654		Self {
655			storage,
656		}
657	}
658
659	pub fn encoded(storage: impl Into<StorageId>) -> EncodedKey {
660		Self::new(storage.into()).encode()
661	}
662
663	pub fn full_scan() -> TaggedKeyBoundRange {
664		TaggedKeyBoundRange::kind(Self::TAG)
665	}
666}
667
668#[cfg(test)]
669pub mod row_sequence_key_tests {
670	use super::RowSequenceKey;
671	use crate::interface::catalog::storage::StorageId;
672
673	#[test]
674	fn test_encode_decode() {
675		let key = RowSequenceKey {
676			storage: StorageId::table(0xABCD),
677		};
678		let encoded = key.encode();
679		let expected = vec![0xF7, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x32];
680		assert_eq!(encoded.as_slice(), expected);
681
682		let key = RowSequenceKey::decode(&encoded).unwrap();
683		assert_eq!(key.storage, StorageId::table(0xABCD));
684	}
685
686	#[test]
687	fn test_encode_decode_view() {
688		// A view owns its row numbering; the view tag must survive the narrowing back through decode.
689		let key = RowSequenceKey {
690			storage: StorageId::view(0xABCD),
691		};
692		let encoded = key.encode();
693		let expected = vec![0xF7, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x32];
694		assert_eq!(encoded.as_slice(), expected);
695
696		let key = RowSequenceKey::decode(&encoded).unwrap();
697		assert_eq!(key.storage, StorageId::view(0xABCD));
698	}
699}
700
701#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, KeyCodec, Hash)]
702#[key(tag = RowSettings)]
703pub struct RowSettingsKey {
704	pub storage: StorageId,
705}
706
707impl RowSettingsKey {
708	pub fn new(storage: StorageId) -> Self {
709		Self {
710			storage,
711		}
712	}
713
714	pub fn encoded(storage: StorageId) -> EncodedKey {
715		Self::new(storage).encode()
716	}
717
718	pub fn full_scan() -> TaggedKeyBoundRange {
719		TaggedKeyBoundRange::kind(Self::TAG)
720	}
721}
722
723#[cfg(test)]
724pub mod row_settings_key_tests {
725	use super::*;
726	use crate::interface::catalog::id::{RingBufferId, SeriesId, TableId, ViewId};
727
728	#[test]
729	fn test_row_settings_key_encoding() {
730		let key = RowSettingsKey {
731			storage: StorageId::Table(TableId(42)),
732		};
733
734		let encoded = key.encode();
735		let decoded = RowSettingsKey::decode(&encoded).unwrap();
736		assert_eq!(key, decoded);
737	}
738
739	#[test]
740	fn test_row_settings_key_roundtrip_view() {
741		// A view owns its rows, so its settings key must survive the tag round trip like any other storage.
742		let key = RowSettingsKey {
743			storage: StorageId::View(ViewId(13)),
744		};
745
746		let encoded = key.encode();
747		let decoded = RowSettingsKey::decode(&encoded).unwrap();
748		assert_eq!(key, decoded);
749	}
750
751	#[test]
752	fn test_row_settings_key_roundtrip_ringbuffer() {
753		let key = RowSettingsKey {
754			storage: StorageId::RingBuffer(RingBufferId(99)),
755		};
756
757		let encoded = key.encode();
758		let decoded = RowSettingsKey::decode(&encoded).unwrap();
759		assert_eq!(key, decoded);
760	}
761
762	#[test]
763	fn test_row_settings_key_roundtrip_series() {
764		let key = RowSettingsKey {
765			storage: StorageId::Series(SeriesId(7)),
766		};
767
768		let encoded = key.encode();
769		let decoded = RowSettingsKey::decode(&encoded).unwrap();
770		assert_eq!(key, decoded);
771	}
772}
773
774#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
775#[key(tag = RowShape)]
776pub struct RowShapeKey {
777	pub fingerprint: RowShapeFingerprint,
778}
779
780impl RowShapeKey {
781	pub fn new(fingerprint: RowShapeFingerprint) -> Self {
782		Self {
783			fingerprint,
784		}
785	}
786
787	pub fn encoded(fingerprint: RowShapeFingerprint) -> EncodedKey {
788		Self {
789			fingerprint,
790		}
791		.encode()
792	}
793
794	pub fn full_scan() -> TaggedKeyBoundRange {
795		TaggedKeyBoundRange::kind(Self::TAG)
796	}
797}
798
799#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
800#[key(tag = RowShapeField)]
801pub struct RowShapeFieldKey {
802	pub shape_fingerprint: RowShapeFingerprint,
803	pub field_index: u16,
804}
805
806impl RowShapeFieldKey {
807	pub fn new(shape_fingerprint: RowShapeFingerprint, field_index: u16) -> Self {
808		Self {
809			shape_fingerprint,
810			field_index,
811		}
812	}
813
814	pub fn encoded(shape_fingerprint: RowShapeFingerprint, field_index: u16) -> EncodedKey {
815		Self::new(shape_fingerprint, field_index).encode()
816	}
817}
818
819#[cfg(test)]
820mod row_shape_key_tests {
821	use super::*;
822
823	#[test]
824	fn test_shape_key_encode_decode() {
825		let key = RowShapeKey {
826			fingerprint: RowShapeFingerprint::new(0xDEADBEEFCAFEBABE),
827		};
828		let encoded = key.encode();
829		let decoded = RowShapeKey::decode(&encoded).unwrap();
830		assert_eq!(decoded.fingerprint, RowShapeFingerprint::new(0xDEADBEEFCAFEBABE));
831	}
832
833	#[test]
834	fn test_shape_field_key_encode_decode() {
835		let key = RowShapeFieldKey {
836			shape_fingerprint: RowShapeFingerprint::new(0x1234567890ABCDEF),
837			field_index: 42,
838		};
839		let encoded = key.encode();
840		let decoded = RowShapeFieldKey::decode(&encoded).unwrap();
841		assert_eq!(decoded.shape_fingerprint, RowShapeFingerprint::new(0x1234567890ABCDEF));
842		assert_eq!(decoded.field_index, 42);
843	}
844}
845
846#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
847#[key(tag = PartitionedRow)]
848pub struct PartitionedRowKey {
849	pub storage: StorageId,
850	pub partition: Partition,
851	pub row: RowNumber,
852}
853
854impl PartitionedRowKey {
855	pub fn new(storage: impl Into<StorageId>, partition: Partition, row: RowNumber) -> Self {
856		Self {
857			storage: storage.into(),
858			partition,
859			row,
860		}
861	}
862
863	pub fn encoded(storage: impl Into<StorageId>, partition: Partition, row: RowNumber) -> EncodedKey {
864		Self::new(storage, partition, row).encode()
865	}
866
867	pub fn storage_start(storage: impl Into<StorageId>) -> EncodedKey {
868		let mut serializer = KeySerializer::with_capacity(10);
869		serializer.extend_u8(PartitionedRowKey::TAG as u8).extend_object_id(storage.into());
870		serializer.to_encoded_key()
871	}
872
873	pub fn storage_end(storage: impl Into<StorageId>) -> EncodedKey {
874		let mut serializer = KeySerializer::with_capacity(10);
875		serializer
876			.extend_u8(PartitionedRowKey::TAG as u8)
877			.extend_object_id(ObjectId::from(storage.into()).prev());
878		serializer.to_encoded_key()
879	}
880
881	pub fn storage_of(key: &EncodedKey) -> Option<StorageId> {
882		let mut de = KeyDeserializer::from_bytes(key.as_slice());
883		let kind: KeyTag = de.read_u8().ok()?.try_into().ok()?;
884		if kind != Self::TAG {
885			return None;
886		}
887		StorageId::from_object(de.read_object_id().ok()?)
888	}
889
890	pub fn full_scan(storage: impl Into<StorageId>) -> TaggedKeyBoundRange {
891		TaggedKeyBoundRange::prefix(Self::TAG, object_fields(ObjectId::from(storage.into())))
892	}
893
894	pub fn scan_range(storage: impl Into<StorageId>, last: Option<&TaggedKey>) -> TaggedKeyBoundRange {
895		Self::full_scan(storage).resume_after(last)
896	}
897
898	pub fn partition_range(storage: impl Into<StorageId>, partition: Partition) -> TaggedKeyBoundRange {
899		TaggedKeyBoundRange::prefix(
900			Self::TAG,
901			object_fields(ObjectId::from(storage.into()))
902				.into_iter()
903				.chain([Field::UDesc(Width::U128, partition.0)])
904				.collect::<Vec<_>>(),
905		)
906	}
907
908	pub fn partition_scan_range(
909		storage: impl Into<StorageId>,
910		partition: Partition,
911		last: Option<&TaggedKey>,
912	) -> TaggedKeyBoundRange {
913		Self::partition_range(storage, partition).resume_after(last)
914	}
915}
916
917#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
918pub struct StoragePartitionedRowKey {
919	pub partition: Desc<Partition>,
920	pub row: Desc<RowNumber>,
921}
922
923impl StoragePartitionedRowKey {
924	pub fn new(partition: Partition, row: RowNumber) -> Self {
925		Self {
926			partition: Desc(partition),
927			row: Desc(row),
928		}
929	}
930
931	pub fn partition(self) -> Partition {
932		self.partition.0
933	}
934
935	pub fn row(self) -> RowNumber {
936		self.row.0
937	}
938
939	pub fn partition_hi(self) -> u64 {
940		(self.partition().0 >> 64) as u64
941	}
942
943	pub fn partition_lo(self) -> u64 {
944		self.partition().0 as u64
945	}
946
947	pub fn from_halves(partition_hi: u64, partition_lo: u64, row: RowNumber) -> Self {
948		Self::new(Partition(((partition_hi as u128) << 64) | partition_lo as u128), row)
949	}
950
951	pub fn with_storage(self, storage: StorageId) -> PartitionedRowKey {
952		PartitionedRowKey {
953			storage,
954			partition: self.partition(),
955			row: self.row(),
956		}
957	}
958}
959
960impl From<PartitionedRowKey> for StoragePartitionedRowKey {
961	fn from(key: PartitionedRowKey) -> Self {
962		StoragePartitionedRowKey::new(key.partition, key.row)
963	}
964}
965
966impl HeapSize for StoragePartitionedRowKey {
967	fn heap_size(&self) -> usize {
968		0
969	}
970}
971
972impl BoundedKey for StoragePartitionedRowKey {
973	fn low() -> Self {
974		Self {
975			partition: <Desc<Partition> as BoundedKey>::low(),
976			row: <Desc<RowNumber> as BoundedKey>::low(),
977		}
978	}
979}
980
981impl DenseKey for StoragePartitionedRowKey {
982	fn successor(&self) -> Option<Self> {
983		if let Some(row) = self.row.successor() {
984			return Some(Self {
985				partition: self.partition,
986				row,
987			});
988		}
989		Some(Self {
990			partition: self.partition.successor()?,
991			row: <Desc<RowNumber> as BoundedKey>::low(),
992		})
993	}
994}
995
996#[derive(Debug, Clone, PartialEq)]
997pub struct PartitionedRowKeyRange {
998	pub storage: StorageId,
999}
1000
1001impl PartitionedRowKeyRange {
1002	fn decode_key(key: &EncodedKey) -> Option<Self> {
1003		let mut de = KeyDeserializer::from_bytes(key.as_slice());
1004
1005		let kind: KeyTag = de.read_u8().ok()?.try_into().ok()?;
1006		if kind != Self::TAG {
1007			return None;
1008		}
1009
1010		let storage = StorageId::from_object(de.read_object_id().ok()?)?;
1011
1012		Some(PartitionedRowKeyRange {
1013			storage,
1014		})
1015	}
1016}
1017
1018impl KeyRangeCodec for PartitionedRowKeyRange {
1019	const TAG: KeyTag = KeyTag::PartitionedRow;
1020
1021	fn start(&self) -> Option<EncodedKey> {
1022		let mut serializer = KeySerializer::with_capacity(10);
1023		serializer.extend_u8(Self::TAG as u8).extend_object_id(self.storage);
1024		Some(serializer.to_encoded_key())
1025	}
1026
1027	fn end(&self) -> Option<EncodedKey> {
1028		let mut serializer = KeySerializer::with_capacity(10);
1029		serializer.extend_u8(Self::TAG as u8).extend_object_id(ObjectId::from(self.storage).prev());
1030		Some(serializer.to_encoded_key())
1031	}
1032
1033	fn decode(range: &EncodedKeyRange) -> (Option<Self>, Option<Self>)
1034	where
1035		Self: Sized,
1036	{
1037		let start_key = match &range.start {
1038			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
1039			Bound::Unbounded => None,
1040		};
1041
1042		let end_key = match &range.end {
1043			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
1044			Bound::Unbounded => None,
1045		};
1046
1047		(start_key, end_key)
1048	}
1049}
1050
1051#[cfg(test)]
1052#[allow(clippy::disallowed_methods)]
1053mod partitioned_row_key_tests {
1054	use std::ops::RangeBounds;
1055
1056	use reifydb_codec::key::{encoded::EncodedKey, serializer::KeySerializer};
1057	use reifydb_value::value::{Value, partition::Partition, row_number::RowNumber};
1058
1059	use super::{PartitionedRowKey, RowKey, StoragePartitionedRowKey};
1060	use crate::{
1061		interface::catalog::{
1062			id::{TableId, ViewId},
1063			object::ObjectId,
1064			storage::StorageId,
1065		},
1066		key::{
1067			catalog::KeySerializerCatalogExt,
1068			typed::{BoundedKey, DenseKey},
1069		},
1070	};
1071
1072	fn part(v: &str) -> Partition {
1073		Partition::of(&[Value::Utf8(v.to_string())])
1074	}
1075
1076	#[test]
1077	fn test_table_roundtrip() {
1078		let key = PartitionedRowKey {
1079			storage: StorageId::Table(TableId(7)),
1080			partition: part("us"),
1081			row: RowNumber(42),
1082		};
1083		let decoded = PartitionedRowKey::decode(&key.encode()).unwrap();
1084		assert_eq!(decoded, key);
1085	}
1086
1087	#[test]
1088	fn test_view_roundtrip() {
1089		// A view owns its rows directly; without the view tag narrowing back the key decodes to `None`.
1090		let key = PartitionedRowKey {
1091			storage: StorageId::View(ViewId(11)),
1092			partition: part("us"),
1093			row: RowNumber(42),
1094		};
1095		let decoded = PartitionedRowKey::decode(&key.encode()).unwrap();
1096		assert_eq!(decoded, key);
1097	}
1098
1099	#[test]
1100	fn test_storage_of() {
1101		let key = PartitionedRowKey::encoded(StorageId::Table(TableId(42)), part("us"), RowNumber(1));
1102		assert_eq!(PartitionedRowKey::storage_of(&key), Some(StorageId::Table(TableId(42))));
1103	}
1104
1105	#[test]
1106	fn test_storage_of_rejects_a_rowless_object() {
1107		// A vtable owns no rows, so its tag must not narrow into a table of the same numeric id.
1108		let mut serializer = KeySerializer::with_capacity(10);
1109		serializer.extend_u8(PartitionedRowKey::TAG as u8).extend_object_id(ObjectId::vtable(42));
1110		assert_eq!(PartitionedRowKey::storage_of(&serializer.to_encoded_key()), None);
1111	}
1112
1113	#[test]
1114	fn test_partition_rows_cluster_together() {
1115		let storage = StorageId::Table(TableId(1));
1116		let us_a = PartitionedRowKey::encoded(storage, part("us"), RowNumber(1));
1117		let us_b = PartitionedRowKey::encoded(storage, part("us"), RowNumber(2));
1118		let eu = PartitionedRowKey::encoded(storage, part("eu"), RowNumber(1));
1119
1120		let mut keys = [us_a.clone(), us_b.clone(), eu.clone()];
1121		keys.sort();
1122		let us_positions: Vec<usize> =
1123			keys.iter().enumerate().filter(|(_, k)| **k == us_a || **k == us_b).map(|(i, _)| i).collect();
1124		assert_eq!(us_positions[1] - us_positions[0], 1, "us partition rows must be contiguous");
1125	}
1126
1127	#[test]
1128	fn test_partition_range_contains_only_its_partition() {
1129		let storage = StorageId::Table(TableId(1));
1130		let range = PartitionedRowKey::partition_range(storage, part("us")).encode();
1131		let us = PartitionedRowKey::encoded(storage, part("us"), RowNumber(500));
1132		let eu = PartitionedRowKey::encoded(storage, part("eu"), RowNumber(1));
1133		assert!(range.contains(&us), "us row must be inside the us partition range");
1134		assert!(!range.contains(&eu), "eu row must be outside the us partition range");
1135	}
1136
1137	#[test]
1138	fn test_partitioned_row_ident_roundtrip() {
1139		let key = PartitionedRowKey {
1140			storage: StorageId::Table(TableId(7)),
1141			partition: part("us"),
1142			row: RowNumber(42),
1143		};
1144
1145		// dropping storage and re-supplying it must recover the original key, halves included
1146		let ident: StoragePartitionedRowKey = key.clone().into();
1147		let restored = ident.with_storage(key.storage);
1148		assert_eq!(restored, key);
1149	}
1150
1151	#[test]
1152	fn test_partitioned_row_ident_halves_split_correctly() {
1153		let partition = Partition(0x1122334455667788_99AABBCCDDEEFF00);
1154		let ident = StoragePartitionedRowKey::new(partition, RowNumber(1));
1155
1156		// the two native halves must reassemble into the exact original 128-bit value
1157		assert_eq!(ident.partition_hi(), 0x1122334455667788);
1158		assert_eq!(ident.partition_lo(), 0x99AABBCCDDEEFF00);
1159		assert_eq!(ident.partition(), partition);
1160		assert_eq!(
1161			StoragePartitionedRowKey::from_halves(ident.partition_hi(), ident.partition_lo(), RowNumber(1)),
1162			ident
1163		);
1164	}
1165
1166	#[test]
1167	fn test_partitioned_row_ident_ordering_matches_field_order() {
1168		let lower_partition = StoragePartitionedRowKey::new(Partition(1), RowNumber(999));
1169		let higher_partition = StoragePartitionedRowKey::new(Partition(2), RowNumber(1));
1170		let same_partition_lower_row = StoragePartitionedRowKey::new(Partition(2), RowNumber(1));
1171		let same_partition_higher_row = StoragePartitionedRowKey::new(Partition(2), RowNumber(2));
1172
1173		// partition must dominate row in ordering, matching PartitionedRowKey's field order, and both
1174		// run descending so the identity sorts exactly as the encoded key does
1175		assert!(higher_partition < lower_partition);
1176		assert!(same_partition_higher_row < same_partition_lower_row);
1177	}
1178
1179	#[test]
1180	fn test_partitioned_row_ident_successor_carries_into_the_partition() {
1181		// row zero ends a partition. Without the carry the scan stops at that boundary and never
1182		// reaches the next partition, which is the whole point of an ordered walk across many.
1183		let last_row = StoragePartitionedRowKey::new(Partition(5), RowNumber(0));
1184		let next = last_row.successor().unwrap();
1185
1186		assert_eq!(next, StoragePartitionedRowKey::new(Partition(4), RowNumber(u64::MAX)));
1187		assert!(next > last_row);
1188	}
1189
1190	#[test]
1191	fn test_partitioned_row_ident_low_is_the_greatest_partition_and_row() {
1192		assert_eq!(
1193			<StoragePartitionedRowKey as BoundedKey>::low(),
1194			StoragePartitionedRowKey::new(Partition(u128::MAX), RowNumber(u64::MAX))
1195		);
1196	}
1197
1198	#[test]
1199	fn test_decode_rejects_trailing_bytes() {
1200		// A key that is longer than the fixed layout must not decode as this type, even when its
1201		// first byte is a matching kind: without the check the extra bytes are ignored and the
1202		// field offsets read a different key's payload as a valid row.
1203		let exact = RowKey::encoded(StorageId::table(7), RowNumber(42));
1204		assert_eq!(exact.as_slice().len(), 18);
1205		assert_eq!(
1206			RowKey::decode(&exact),
1207			Some(RowKey {
1208				storage: StorageId::table(7),
1209				row: RowNumber(42)
1210			})
1211		);
1212
1213		let mut longer = exact.as_slice().to_vec();
1214		longer.push(0x00);
1215		assert_eq!(RowKey::decode(&EncodedKey::new(longer)), None);
1216	}
1217
1218	#[test]
1219	fn test_decode_rejects_a_longer_key_that_shares_the_kind_byte() {
1220		// The live shape: a sorted view writes kind ++ storage ++ sort values ++ row. It carries
1221		// KeyTag::Row, so a length-blind decode claims it and reads sort payload as the row
1222		// number, silently aliasing two distinct rows onto one storage row key.
1223		let mut sorted_view = RowKey::encoded(StorageId::view(3), RowNumber(1)).as_slice().to_vec();
1224		sorted_view.extend_from_slice(&[0xAA; 8]);
1225		sorted_view.extend_from_slice(&99u64.to_be_bytes());
1226
1227		assert_eq!(RowKey::decode(&EncodedKey::new(sorted_view)), None);
1228	}
1229}
1230
1231#[cfg(test)]
1232mod sorted_view_run_tests {
1233	use reifydb_codec::key::encoded::EncodedKey;
1234	use reifydb_value::value::{partition::Partition, row_number::RowNumber};
1235
1236	use super::{PartitionedSortedViewRowKey, SortRun, SortedViewRowKey};
1237	use crate::interface::catalog::storage::StorageId;
1238
1239	fn run(bytes: &[u8]) -> SortRun {
1240		SortRun::new(bytes)
1241	}
1242
1243	#[test]
1244	fn test_a_run_round_trips_through_the_terminator() {
1245		// The run is spliced raw today and cannot be decoded; without the terminator the row tail and
1246		// the run cannot be told apart and decode has to guess where one ends.
1247		let key = SortedViewRowKey::new(StorageId::view(3), run(&[0x10, 0x00, 0xff, 0x00]), RowNumber(42));
1248		let decoded = SortedViewRowKey::decode(&key.encode()).unwrap();
1249		assert_eq!(decoded, key);
1250		assert_eq!(decoded.run.as_slice(), &[0x10, 0x00, 0xff, 0x00]);
1251	}
1252
1253	#[test]
1254	fn test_an_empty_run_round_trips() {
1255		let key = SortedViewRowKey::new(StorageId::view(3), run(&[]), RowNumber(1));
1256		assert_eq!(SortedViewRowKey::decode(&key.encode()), Some(key));
1257	}
1258
1259	#[test]
1260	fn test_a_partitioned_run_round_trips() {
1261		let key = PartitionedSortedViewRowKey::new(
1262			StorageId::view(3),
1263			Partition(0x1122334455667788_99AABBCCDDEEFF00),
1264			run(&[0x00, 0x00, 0x01]),
1265			RowNumber(7),
1266		);
1267		assert_eq!(PartitionedSortedViewRowKey::decode(&key.encode()), Some(key));
1268	}
1269
1270	#[test]
1271	fn test_decode_refuses_the_other_kind_and_trailing_bytes() {
1272		// Both kinds end in a raw eight byte row, so a length blind decode would read one as the other.
1273		let partitioned = PartitionedSortedViewRowKey::encoded(
1274			StorageId::view(3),
1275			Partition(1),
1276			run(&[0x10]),
1277			RowNumber(1),
1278		);
1279		assert_eq!(SortedViewRowKey::decode(&partitioned), None);
1280
1281		let mut longer = SortedViewRowKey::encoded(StorageId::view(3), run(&[0x10]), RowNumber(1)).to_vec();
1282		longer.push(0x00);
1283		assert_eq!(SortedViewRowKey::decode(&EncodedKey::new(longer)), None);
1284	}
1285
1286	#[test]
1287	fn test_ord_matches_the_encoded_byte_order() {
1288		// A view is read by a plain forward scan, so the struct order and the byte order must agree
1289		// everywhere or the scan returns rows in an order the planner never asked for.
1290		let mut keys = [
1291			SortedViewRowKey::new(StorageId::table(3), run(&[0x10]), RowNumber(1)),
1292			SortedViewRowKey::new(StorageId::view(3), run(&[0x10]), RowNumber(1)),
1293			SortedViewRowKey::new(StorageId::view(4), run(&[0x10]), RowNumber(1)),
1294			SortedViewRowKey::new(StorageId::view(3), run(&[0x10]), RowNumber(2)),
1295			SortedViewRowKey::new(StorageId::view(3), run(&[0x10, 0x00]), RowNumber(0)),
1296			SortedViewRowKey::new(StorageId::view(3), run(&[0x10, 0x01]), RowNumber(0)),
1297			SortedViewRowKey::new(StorageId::view(3), run(&[0x00]), RowNumber(0)),
1298			SortedViewRowKey::new(StorageId::view(3), run(&[]), RowNumber(u64::MAX)),
1299		];
1300		keys.sort();
1301
1302		let encoded: Vec<EncodedKey> = keys.iter().map(|key| key.encode()).collect();
1303		let mut sorted_bytes = encoded.clone();
1304		sorted_bytes.sort();
1305		assert_eq!(encoded, sorted_bytes);
1306	}
1307
1308	#[test]
1309	fn test_partitioned_ord_matches_the_encoded_byte_order() {
1310		let mut keys = [
1311			PartitionedSortedViewRowKey::new(StorageId::view(3), Partition(1), run(&[0x10]), RowNumber(1)),
1312			PartitionedSortedViewRowKey::new(StorageId::view(3), Partition(2), run(&[0x10]), RowNumber(1)),
1313			PartitionedSortedViewRowKey::new(StorageId::view(3), Partition(2), run(&[0x00]), RowNumber(1)),
1314			PartitionedSortedViewRowKey::new(StorageId::view(3), Partition(2), run(&[0x10]), RowNumber(0)),
1315			PartitionedSortedViewRowKey::new(StorageId::view(4), Partition(2), run(&[0x10]), RowNumber(0)),
1316		];
1317		keys.sort();
1318
1319		let encoded: Vec<EncodedKey> = keys.iter().map(|key| key.encode()).collect();
1320		let mut sorted_bytes = encoded.clone();
1321		sorted_bytes.sort();
1322		assert_eq!(encoded, sorted_bytes);
1323	}
1324
1325	#[test]
1326	fn test_a_shorter_run_sorts_before_the_run_that_extends_it() {
1327		// Without a terminator the row tail of the shorter key lands against the extra run bytes of the
1328		// longer one, so a high row number could drag a row past the whole group that follows it.
1329		let short = SortedViewRowKey::new(StorageId::view(3), run(&[0x10]), RowNumber(u64::MAX));
1330		let long = SortedViewRowKey::new(StorageId::view(3), run(&[0x10, 0x00]), RowNumber(0));
1331
1332		assert!(short < long);
1333		assert!(short.encode() < long.encode());
1334	}
1335
1336	#[test]
1337	fn test_the_helpers_still_read_the_new_layout() {
1338		let storage = StorageId::view(3);
1339		let key = SortedViewRowKey::encoded(storage, run(&[0x00, 0xAA]), RowNumber(9));
1340		assert_eq!(SortedViewRowKey::storage_of(&key), Some(storage));
1341		assert_eq!(SortedViewRowKey::row_of(&key), Some(RowNumber(9)));
1342
1343		let partitioned =
1344			PartitionedSortedViewRowKey::encoded(storage, Partition(5), run(&[0x00, 0xAA]), RowNumber(9));
1345		assert_eq!(PartitionedSortedViewRowKey::storage_of(&partitioned), Some(storage));
1346		assert_eq!(PartitionedSortedViewRowKey::row_of(&partitioned), Some(RowNumber(9)));
1347		assert_eq!(SortedViewRowKey::row_of(&partitioned), None);
1348	}
1349}