use reifydb_core::common::CommitVersion;
use reifydb_type::{Result, util::cowvec::CowVec};
use crate::tier::{EntryKind, TierStorage};
#[derive(Debug, Clone)]
pub enum VersionedGetResult {
Value {
value: CowVec<u8>,
version: CommitVersion,
},
Tombstone,
NotFound,
}
pub fn get_at_version<S: TierStorage>(
storage: &S,
table: EntryKind,
key: &[u8],
version: CommitVersion,
) -> Result<VersionedGetResult> {
match storage.get(table, key, version)? {
Some(value) => Ok(VersionedGetResult::Value {
value,
version,
}),
None => {
let all_versions = storage.get_all_versions(table, key)?;
for (v, value) in all_versions {
if v <= version {
return match value {
Some(val) => Ok(VersionedGetResult::Value {
value: val,
version: v,
}),
None => Ok(VersionedGetResult::Tombstone),
};
}
}
Ok(VersionedGetResult::NotFound)
}
}
}
#[cfg(test)]
pub mod tests {
use std::collections::HashMap;
use super::*;
use crate::hot::{memory::storage::MemoryPrimitiveStorage, storage::HotStorage};
#[test]
fn test_get_at_version_basic() {
let storage = MemoryPrimitiveStorage::new();
let key = CowVec::new(b"test_key".to_vec());
let version = CommitVersion(42);
storage.set(
version,
HashMap::from([(EntryKind::Multi, vec![(key.clone(), Some(CowVec::new(b"value".to_vec())))])]),
)
.unwrap();
match get_at_version(&storage, EntryKind::Multi, &key, version).unwrap() {
VersionedGetResult::Value {
value,
..
} => {
assert_eq!(value.as_slice(), b"value");
}
_ => panic!("Expected Value result"),
}
match get_at_version(&storage, EntryKind::Multi, &key, CommitVersion(100)).unwrap() {
VersionedGetResult::Value {
value,
..
} => {
assert_eq!(value.as_slice(), b"value");
}
_ => panic!("Expected Value result"),
}
}
#[test]
fn test_get_at_version_not_found() {
let storage = MemoryPrimitiveStorage::new();
let result = get_at_version(&storage, EntryKind::Multi, b"nonexistent", CommitVersion(100)).unwrap();
assert!(matches!(result, VersionedGetResult::NotFound));
}
#[test]
fn test_get_at_version_tombstone() {
let storage = MemoryPrimitiveStorage::new();
let key = CowVec::new(b"test_key".to_vec());
storage.set(CommitVersion(1), HashMap::from([(EntryKind::Multi, vec![(key.clone(), None)])])).unwrap();
let result = get_at_version(&storage, EntryKind::Multi, &key, CommitVersion(1)).unwrap();
assert!(matches!(result, VersionedGetResult::Tombstone));
}
#[test]
fn test_get_at_version_multiple_versions() {
let storage = HotStorage::memory();
let key = CowVec::new(b"test_key".to_vec());
storage.set(
CommitVersion(1),
HashMap::from([(EntryKind::Multi, vec![(key.clone(), Some(CowVec::new(b"v1".to_vec())))])]),
)
.unwrap();
storage.set(
CommitVersion(5),
HashMap::from([(EntryKind::Multi, vec![(key.clone(), Some(CowVec::new(b"v5".to_vec())))])]),
)
.unwrap();
storage.set(
CommitVersion(10),
HashMap::from([(EntryKind::Multi, vec![(key.clone(), Some(CowVec::new(b"v10".to_vec())))])]),
)
.unwrap();
match get_at_version(&storage, EntryKind::Multi, &key, CommitVersion(3)).unwrap() {
VersionedGetResult::Value {
value,
..
} => {
assert_eq!(value.as_slice(), b"v1");
}
_ => panic!("Expected Value result"),
}
match get_at_version(&storage, EntryKind::Multi, &key, CommitVersion(7)).unwrap() {
VersionedGetResult::Value {
value,
..
} => {
assert_eq!(value.as_slice(), b"v5");
}
_ => panic!("Expected Value result"),
}
match get_at_version(&storage, EntryKind::Multi, &key, CommitVersion(15)).unwrap() {
VersionedGetResult::Value {
value,
..
} => {
assert_eq!(value.as_slice(), b"v10");
}
_ => panic!("Expected Value result"),
}
}
#[test]
fn test_get_at_version_before_any_version() {
let storage = HotStorage::memory();
let key = CowVec::new(b"test_key".to_vec());
storage.set(
CommitVersion(10),
HashMap::from([(EntryKind::Multi, vec![(key.clone(), Some(CowVec::new(b"value".to_vec())))])]),
)
.unwrap();
let result = get_at_version(&storage, EntryKind::Multi, &key, CommitVersion(5)).unwrap();
assert!(matches!(result, VersionedGetResult::NotFound));
}
}