use crate::error::Result;
use crate::types::Value;
pub use crate::parser::vint::{parse_vint, parse_vint_length, parse_vuint};
pub use crate::schema::cql_parser::{cql_type_to_type_id, parse_create_table};
pub const FUZZ_VALUE_TYPES: &[&str] = &[
"boolean",
"tinyint",
"smallint",
"int",
"bigint",
"float",
"double",
"text",
"blob",
"uuid",
"timeuuid",
"timestamp",
"date",
"time",
"inet",
"decimal",
"varint",
"ascii",
"counter",
"duration",
"list<int>",
"set<text>",
"map<text,int>",
"tuple<int,text>",
"frozen<list<list<int>>>",
];
pub fn fuzz_decode_value(type_str: &str, bytes: &[u8]) -> Result<Value> {
let comparator = crate::types::ComparatorType::from_type_string(type_str)?;
crate::storage::sstable::reader::parsing::comparator_value_parsing::parse_value_with_comparator(
bytes,
&comparator,
)
}
pub fn fuzz_cql_type(s: &str) -> Result<()> {
let _ = crate::schema::cql_parser::cql_type_fuzz(s);
let _ = cql_type_to_type_id(s);
Ok(())
}
pub fn fuzz_bti_traverse(bytes: &[u8]) -> Result<()> {
let mut cursor = std::io::Cursor::new(bytes);
let _entries =
crate::storage::sstable::bti::parser::iterate_partitions_in_bti_file(&mut cursor)?;
Ok(())
}
pub fn fuzz_block_emit(block: &[u8]) -> Result<()> {
let ctx = block_emit_context()
.ok_or_else(|| crate::error::Error::schema("fuzz_block_emit: no simple_table fixture"))?;
let parser =
crate::storage::sstable::reader::parsing::row_decoder::V5CompressedLegacyParser::new(
"test_basic".to_string(),
"simple_table".to_string(),
0,
0,
None,
);
parser.parse_block_emit(block, Some(&ctx.schema), &ctx.reader, |_entry| {
Ok(std::ops::ControlFlow::Continue(()))
})
}
struct BlockEmitContext {
reader: crate::storage::sstable::SSTableReader,
schema: crate::schema::TableSchema,
}
const SIMPLE_TABLE_DDL: &str = "CREATE TABLE test_basic.simple_table (\
id UUID PRIMARY KEY, name TEXT, age INT, salary BIGINT, height FLOAT, \
weight DOUBLE, active BOOLEAN, created TIMESTAMP, birth_date DATE, \
work_time TIME, description BLOB, account_balance DECIMAL, \
session_id TIMEUUID, ip_address INET, small_number TINYINT, \
medium_number SMALLINT, duration_val DURATION, varchar_field VARCHAR, \
ascii_field ASCII)";
fn block_emit_context() -> Option<&'static BlockEmitContext> {
use std::sync::OnceLock;
static CTX: OnceLock<Option<BlockEmitContext>> = OnceLock::new();
CTX.get_or_init(build_block_emit_context).as_ref()
}
fn build_block_emit_context() -> Option<BlockEmitContext> {
let data_file = simple_table_data_file()?;
let schema = match parse_create_table(SIMPLE_TABLE_DDL) {
Ok((_, schema)) => schema,
Err(_) => return None,
};
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.ok()?;
let reader = runtime.block_on(async {
let config = crate::Config::default();
let platform = crate::platform::Platform::new(&config).await.ok()?;
crate::storage::sstable::SSTableReader::open(
&data_file,
&config,
std::sync::Arc::new(platform),
)
.await
.ok()
})?;
Some(BlockEmitContext { reader, schema })
}
fn simple_table_data_file() -> Option<std::path::PathBuf> {
let root = std::env::var("CQLITE_DATASETS_ROOT").ok()?;
let keyspace_dir = std::path::PathBuf::from(&root)
.join("sstables")
.join("test_basic");
let entries = std::fs::read_dir(&keyspace_dir).ok()?;
for entry in entries.flatten() {
let path = entry.path();
let is_simple_table = path
.file_name()
.and_then(|n| n.to_str())
.map(|n| n.starts_with("simple_table-"))
.unwrap_or(false);
if !is_simple_table {
continue;
}
if let Ok(inner) = std::fs::read_dir(&path) {
for df in inner.flatten() {
let is_data_db = df
.file_name()
.to_str()
.map(|s| s.ends_with("-Data.db"))
.unwrap_or(false);
if is_data_db {
return Some(df.path());
}
}
}
}
None
}