#[cfg(feature = "bench-internals")]
mod bench_shim;
mod block_entries;
pub(crate) mod byte_comparable; pub(crate) mod comparator_value_parsing; mod custom_scalar;
mod key_parsing;
pub(crate) mod row_decoder;
#[cfg(test)]
mod schema_fallback_stall_tests;
mod value_parsing;
#[cfg(test)]
mod value_parsing_schema_type_tests;
pub(in crate::storage::sstable::reader) use row_decoder::V5CompressedLegacyParser;
pub(in crate::storage::sstable::reader) use row_decoder::ParseStep;
pub(in crate::storage::sstable::reader) use row_decoder::{
CompactionPartitionState, PartitionStreamStep,
};
pub(in crate::storage::sstable::reader) use row_decoder::RowColumnResolution;
#[doc(hidden)]
pub use row_decoder::V5CompressedLegacyParser as PublicV5CompressedLegacyParser;
use std::collections::HashMap;
use std::path::Path;
use tracing::debug;
use crate::{
schema::{ClusteringColumn, Column, KeyColumn, TableSchema},
Error, Result,
};
use super::types::SSTableReader;
pub(in crate::storage::sstable::reader) fn extract_keyspace_table_from_path(
path: &Path,
) -> Result<(String, String)> {
use crate::storage::sstable::snapshot_path;
let keyspace = snapshot_path::extract_keyspace(path)
.ok_or_else(|| Error::schema("SSTable path has no keyspace directory (too shallow)"))?;
let table_name = snapshot_path::extract_table_name(path)
.ok_or_else(|| Error::schema("SSTable path has no table directory"))?;
Ok((keyspace, table_name))
}
impl SSTableReader {
pub(in crate::storage::sstable::reader) fn get_table_schema(
&self,
provided_schema: Option<&TableSchema>,
) -> Option<TableSchema> {
if let Some(schema) = provided_schema {
debug!(
"get_table_schema: Using provided schema for {}.{}",
schema.keyspace, schema.table
);
return Some(schema.clone());
}
if let Some(schema) = self.schema.as_deref() {
debug!(
"get_table_schema: Using schema from SSTable header for {}.{}",
self.header.keyspace, self.header.table_name
);
return Some(schema.clone());
}
#[cfg(feature = "state_machine")]
{
if let Some(schema) = self.registry_schema.as_deref() {
debug!(
"get_table_schema: Using pre-resolved registry schema for {}.{}",
schema.keyspace, schema.table
);
return Some(schema.clone());
}
}
#[cfg(not(feature = "state_machine"))]
{
if let Some(registry) = self.schema_registry.as_ref() {
let file_path = self.file_path();
let (keyspace, table_name) = match extract_keyspace_table_from_path(&file_path) {
Ok(names) => names,
Err(e) => {
debug!(
"get_table_schema: Failed to extract names from path {}: {}. Falling back to header names.",
file_path.display(), e
);
(self.header.keyspace.clone(), self.header.table_name.clone())
}
};
debug!("get_table_schema: Schema registry lookup not available in non-state_machine builds");
let _ = (registry, &keyspace, &table_name); }
}
debug!(
"get_table_schema: Falling back to header column construction for {}.{}",
self.header.keyspace, self.header.table_name
);
if self.header.columns.is_empty() {
return None;
}
let mut columns = Vec::new();
let mut partition_keys = Vec::new();
let mut clustering_keys = Vec::new();
for col_info in self.header.columns.iter() {
let column = Column {
name: col_info.name.clone(),
data_type: col_info.column_type.clone(), nullable: true,
default: None,
is_static: col_info.is_static,
};
if col_info.is_primary_key && !col_info.is_clustering {
partition_keys.push(KeyColumn {
name: col_info.name.clone(),
data_type: col_info.column_type.clone(),
position: partition_keys.len(),
});
} else if col_info.is_clustering {
clustering_keys.push(ClusteringColumn {
name: col_info.name.clone(),
data_type: col_info.column_type.clone(),
position: clustering_keys.len(),
order: crate::schema::ClusteringOrder::Asc,
});
}
columns.push(column);
}
Some(TableSchema {
keyspace: self.header.keyspace.clone(),
table: self.header.table_name.clone(),
partition_keys,
clustering_keys,
columns,
comments: HashMap::new(),
dropped_columns: HashMap::new(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn test_extract_keyspace_table_standard_format() {
let path = Path::new("/data/sstables/test_basic/simple_table-6b0425d0a25111f0a3fef1a551383fb9/nb-1-big-Data.db");
let (keyspace, table) = extract_keyspace_table_from_path(path).unwrap();
assert_eq!(keyspace, "test_basic");
assert_eq!(table, "simple_table");
}
#[test]
fn test_extract_keyspace_table_with_hyphens() {
let path = Path::new(
"/data/sstables/my_keyspace/user-profiles-00112233445566778899aabbccddeeff/nb-1-big-Data.db",
);
let (keyspace, table) = extract_keyspace_table_from_path(path).unwrap();
assert_eq!(keyspace, "my_keyspace");
assert_eq!(table, "user-profiles");
}
#[test]
fn test_extract_keyspace_table_snapshot_layout() {
let path = Path::new(
"/data/sstables/myks/mytable-9f3a1c2d4e5f6071829304a5b6c7d8e9/\
snapshots/cqlite-3b1e7f90/nb-1-big-Data.db",
);
let (keyspace, table) = extract_keyspace_table_from_path(path).unwrap();
assert_eq!(keyspace, "myks");
assert_eq!(table, "mytable");
}
#[test]
fn test_extract_keyspace_table_snapshots_named_keyspace() {
let path = Path::new(
"/data/sstables/snapshots/mytable-9f3a1c2d4e5f6071829304a5b6c7d8e9/nb-1-big-Data.db",
);
let (keyspace, table) = extract_keyspace_table_from_path(path).unwrap();
assert_eq!(keyspace, "snapshots");
assert_eq!(table, "mytable");
}
#[test]
fn test_extract_keyspace_table_real_test_data() {
let path = Path::new("/Users/patrick/local_projects/cqlite/test-data/datasets/sstables/test_basic/simple_table-6de93b70934a11f08d448925b7a9e804/nb-1-big-Data.db");
let (keyspace, table) = extract_keyspace_table_from_path(path).unwrap();
assert_eq!(keyspace, "test_basic");
assert_eq!(table, "simple_table");
}
#[test]
fn test_extract_keyspace_table_collections() {
let path = Path::new("test-data/datasets/sstables/test_collections/collection_table-6b8c8fb0a25111f0a3fef1a551383fb9/nb-1-big-Data.db");
let (keyspace, table) = extract_keyspace_table_from_path(path).unwrap();
assert_eq!(keyspace, "test_collections");
assert_eq!(table, "collection_table");
}
#[test]
fn test_extract_keyspace_table_invalid_no_parent() {
let path = Path::new("/Data.db");
let result = extract_keyspace_table_from_path(path);
assert!(result.is_err());
}
#[test]
fn test_extract_keyspace_table_no_id_suffix() {
let path = Path::new("/data/keyspace/tablename/Data.db");
let (keyspace, table) = extract_keyspace_table_from_path(path).unwrap();
assert_eq!(keyspace, "keyspace");
assert_eq!(table, "tablename");
}
#[test]
fn test_extract_keyspace_table_relative_path() {
let path =
Path::new("test_basic/simple_table-6b0425d0a25111f0a3fef1a551383fb9/nb-1-big-Data.db");
let (keyspace, table) = extract_keyspace_table_from_path(path).unwrap();
assert_eq!(keyspace, "test_basic");
assert_eq!(table, "simple_table");
}
}