use super::aggregate::{self, StatsAccumulator};
use super::config as stats_config;
use super::output;
use crate::config::Config;
use crate::error::{Error, ErrorStats, ParserError, Result};
pub fn run_stats(cfg: &Config, top_n: u32) -> Result<()> {
debug_assert!(top_n >= 1, "top_n must be >= 1 (Phase 51 CLI validation)");
stats_config::validate_stats_time_range(&cfg.stats)?;
let log_files = crate::parser::SqllogParser::new(cfg.sqllog.inputs.clone()).log_files()?;
if log_files.is_empty() {
return Err(Error::Parser(ParserError::NoFilesFound {
inputs: cfg.sqllog.inputs.clone(),
}));
}
let mut accumulator =
StatsAccumulator::new(top_n, cfg.stats.from.clone(), cfg.stats.to.clone());
scan_files_into_accumulator(&log_files, &mut accumulator)?;
let (slow_rows, frequent_rows) = accumulator.into_results();
write_stats_output(cfg, &slow_rows, &frequent_rows)
}
fn scan_files_into_accumulator(
log_files: &[std::path::PathBuf],
accumulator: &mut StatsAccumulator,
) -> Result<()> {
let mut scan_stats = ErrorStats::default();
crate::scanner::scan_files(
log_files,
&mut |record| accumulator.update(record),
&mut scan_stats,
)?;
if scan_stats.has_errors() {
log::warn!(
"stats: {} parse error(s) encountered during scan",
scan_stats.parse_errors
);
}
Ok(())
}
fn write_stats_output(
cfg: &Config,
slow_rows: &[aggregate::SlowSqlRow],
frequent_rows: &[aggregate::FrequentSqlRow],
) -> Result<()> {
if let Some(csv_cfg) = cfg.exporter.csv.as_ref() {
let csv_dir = std::path::Path::new(&csv_cfg.file)
.parent()
.unwrap_or_else(|| std::path::Path::new("."));
output::write_csv_stats(slow_rows, frequent_rows, csv_dir)?;
log::info!(
"stats: wrote {} slow rows and {} frequent rows to {}",
slow_rows.len(),
frequent_rows.len(),
csv_dir.display()
);
return Ok(());
}
if let Some(sqlite_cfg) = cfg.exporter.sqlite.as_ref() {
output::write_sqlite_stats(slow_rows, frequent_rows, &sqlite_cfg.database_url)?;
log::info!(
"stats: wrote {} slow rows and {} frequent rows to {}",
slow_rows.len(),
frequent_rows.len(),
sqlite_cfg.database_url
);
return Ok(());
}
Err(Error::Config(crate::error::ConfigError::NoExporters))
}