use sqlite_wasm_reader::{Database, Error, Value, LogLevel, init_default_logger, set_log_level, log_info, log_debug, log_warn};
use std::env;
fn main() -> Result<(), Error> {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <database.db> [log_level]", args[0]);
eprintln!("Example: {} test.db debug", args[0]);
eprintln!("Log levels: error, warn, info, debug, trace");
std::process::exit(1);
}
let db_path = &args[1];
if args.len() > 2 {
if let Some(log_level) = LogLevel::from_str(&args[2]) {
init_default_logger();
set_log_level(log_level);
log_info(&format!("Starting database analysis with log level: {:?}", log_level));
} else {
eprintln!("Invalid log level: {}. Using default (info)", args[2]);
init_default_logger();
}
} else {
init_default_logger();
log_info("Starting database analysis with default log level (info)");
}
println!("Opening database: {}", db_path);
let mut db = Database::open(db_path)?;
log_debug("Database opened successfully");
println!("\nListing tables:");
let tables = db.tables()?;
log_info(&format!("Found {} tables in database", tables.len()));
for table in &tables {
println!(" - {}", table);
}
for table in tables {
println!("\n--- Analyzing table: {} ---", table);
log_debug(&format!("Starting analysis of table: {}", table));
match db.count_table_rows(&table) {
Ok(count) => {
println!(" Total rows: {}", count);
log_debug(&format!("Table {} has {} rows", table, count));
}
Err(e) => {
log_warn(&format!("Failed to count rows in table {}: {}", table, e));
println!(" Error counting rows: {}", e);
continue;
}
}
match db.execute_query(&sqlite_wasm_reader::SelectQuery::parse(&format!("SELECT * FROM {} LIMIT 10", table))?) {
Ok(rows) => {
if rows.is_empty() {
println!(" Empty table");
log_debug(&format!("Table {} is empty", table));
continue;
}
println!(" Found {} rows (showing first 10)", rows.len());
log_info(&format!("Successfully queried {} rows from table {}", rows.len(), table));
if let Some(first_row) = rows.first() {
println!(" Columns and data types:");
log_debug(&format!("Analyzing schema for table {}", table));
for (column, value) in first_row {
let data_type = match value {
Value::Null => "NULL",
Value::Integer(_) => "INTEGER",
Value::Real(_) => "REAL",
Value::Text(_) => "TEXT",
Value::Blob(b) => &format!("BLOB({} bytes)", b.len()),
};
println!(" {}: {}", column, data_type);
}
}
for (i, row) in rows.iter().enumerate() {
println!(" Row {}: {:?}", i + 1, row);
}
analyze_data_distribution(&rows);
}
Err(e) => {
log_warn(&format!("Failed to query table {}: {} (table may not have suitable indexes)", table, e));
eprintln!(" Error querying table: {} (table may not have suitable indexes)", e);
}
}
}
log_info("Database analysis completed");
Ok(())
}
fn analyze_data_distribution(rows: &[sqlite_wasm_reader::Row]) {
if rows.is_empty() {
return;
}
let mut type_counts = std::collections::HashMap::new();
for row in rows {
for (column, value) in row {
let data_type = match value {
Value::Null => "NULL",
Value::Integer(_) => "INTEGER",
Value::Real(_) => "REAL",
Value::Text(_) => "TEXT",
Value::Blob(b) => &format!("BLOB({} bytes)", b.len()),
};
let key = format!("{}:{}", column, data_type);
*type_counts.entry(key).or_insert(0) += 1;
}
}
println!(" Data type distribution:");
for (key, count) in type_counts {
let percentage = (count as f64 / rows.len() as f64) * 100.0;
println!(" {}: {} ({:.1}%)", key, count, percentage);
}
}