1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! Partition key digest computation for Index.db lookups
use crate::Result;
use super::super::key_digest::KeyDigestComputer;
use super::types::SSTableReader;
impl SSTableReader {
/// Compute partition key digest for Index.db lookup.
///
/// This method computes the digest from raw partition key bytes using the appropriate
/// byte-comparable encoding based on the table schema's partition key definition.
///
/// For modern formats (BIG v5, BTI), this MUST use schema-driven Murmur3 digest.
/// Simple digest is only allowed for legacy formats behind feature flag.
///
/// NOTE (Issue #553): This function is no longer called from the main
/// `lookup_partition_with_index` path, which now uses raw key bytes directly.
/// It is retained for potential future use.
#[allow(dead_code)]
pub(crate) async fn compute_partition_key_digest(
&self,
partition_key: &[u8],
) -> Result<Vec<u8>> {
let mut computer = KeyDigestComputer::new();
// For modern formats, schema-driven digest is MANDATORY
match self.header.cassandra_version {
crate::parser::header::CassandraVersion::V5_0NewBig
| crate::parser::header::CassandraVersion::V5_0Bti => {
// Modern formats require schema registry and must never use simple digest
if let Some(schema_registry) = &self.schema_registry {
// Acquire read lock to access SchemaRegistry methods
#[cfg(feature = "state_machine")]
let schema_guard = schema_registry.read().await;
#[cfg(not(feature = "state_machine"))]
let schema_guard = schema_registry.as_ref();
match schema_guard
.get_parsing_context(&self.header.keyspace, &self.header.table_name)
.await
{
Ok(parsing_context) => {
tracing::debug!(
"Using schema-driven Murmur3 digest for modern format {:?}",
self.header.cassandra_version
);
return computer
.compute_partition_key_digest(partition_key, &parsing_context);
}
Err(e) => {
return Err(crate::error::Error::schema(format!(
"Failed to get parsing context for schema-driven digest in modern format {:?}: {}. \
Schema-driven digest is mandatory for modern formats.",
self.header.cassandra_version, e
)));
}
}
} else {
return Err(crate::error::Error::schema(format!(
"Schema registry required for Index.db lookups in modern format {:?}. \
Modern formats cannot use simple digest fallback.",
self.header.cassandra_version
)));
}
}
_ => {
// Legacy formats: try schema-driven first, fallback to simple digest if needed
if let Some(schema_registry) = &self.schema_registry {
// Acquire read lock to access SchemaRegistry methods
#[cfg(feature = "state_machine")]
let schema_guard = schema_registry.read().await;
#[cfg(not(feature = "state_machine"))]
let schema_guard = schema_registry.as_ref();
match schema_guard
.get_parsing_context(&self.header.keyspace, &self.header.table_name)
.await
{
Ok(parsing_context) => {
tracing::debug!("Using schema-driven Murmur3 digest for legacy format");
return computer
.compute_partition_key_digest(partition_key, &parsing_context);
}
Err(e) => {
tracing::warn!(
"Failed to get parsing context for legacy format, falling back to simple digest: {}",
e
);
// Continue to legacy fallback below
}
}
}
}
}
// Legacy path: only allow simple digest when legacy-heuristics feature is enabled
#[cfg(feature = "legacy-heuristics")]
{
tracing::warn!(
"Falling back to simple digest - this may cause incorrect Index.db lookups"
);
computer.compute_simple_digest(partition_key)
}
#[cfg(not(feature = "legacy-heuristics"))]
{
// For modern builds, reject simple digest to force schema-driven approach
Err(crate::error::Error::invalid_operation(
"Schema-driven digest required for Index.db lookup in modern format. \
Enable 'legacy-heuristics' feature only for legacy compatibility testing.",
))
}
}
}