sql-cli 1.67.2

SQL query tool for CSV/JSON with both interactive TUI and non-interactive CLI modes - perfect for exploration and automation
Documentation
use std::time::{Duration, Instant};

#[derive(Debug, Clone)]
pub struct BenchmarkMetrics {
    pub query_parse_time: Duration,
    pub query_plan_time: Duration,
    pub query_execute_time: Duration,
    pub total_time: Duration,

    pub rows_processed: usize,
    pub rows_returned: usize,
    pub rows_per_second: f64,

    pub peak_memory_bytes: Option<usize>,
    pub allocations_count: Option<usize>,

    pub table_scans: usize,
    pub expressions_evaluated: usize,
    pub functions_called: usize,
}

impl BenchmarkMetrics {
    pub fn new() -> Self {
        BenchmarkMetrics {
            query_parse_time: Duration::ZERO,
            query_plan_time: Duration::ZERO,
            query_execute_time: Duration::ZERO,
            total_time: Duration::ZERO,
            rows_processed: 0,
            rows_returned: 0,
            rows_per_second: 0.0,
            peak_memory_bytes: None,
            allocations_count: None,
            table_scans: 0,
            expressions_evaluated: 0,
            functions_called: 0,
        }
    }

    pub fn calculate_throughput(&mut self) {
        if self.total_time.as_secs_f64() > 0.0 {
            self.rows_per_second = self.rows_processed as f64 / self.total_time.as_secs_f64();
        }
    }

    pub fn to_csv_row(&self) -> String {
        format!(
            "{:.3},{:.3},{:.3},{:.3},{},{},{:.0}",
            self.query_parse_time.as_secs_f64() * 1000.0,
            self.query_plan_time.as_secs_f64() * 1000.0,
            self.query_execute_time.as_secs_f64() * 1000.0,
            self.total_time.as_secs_f64() * 1000.0,
            self.rows_processed,
            self.rows_returned,
            self.rows_per_second
        )
    }
}

#[derive(Debug)]
pub struct BenchmarkResult {
    pub query_name: String,
    pub query_category: String,
    pub table_name: String,
    pub row_count: usize,
    pub metrics: BenchmarkMetrics,
    pub error: Option<String>,
}

impl BenchmarkResult {
    pub fn new(
        query_name: String,
        query_category: String,
        table_name: String,
        row_count: usize,
    ) -> Self {
        BenchmarkResult {
            query_name,
            query_category,
            table_name,
            row_count,
            metrics: BenchmarkMetrics::new(),
            error: None,
        }
    }

    pub fn to_csv_row(&self) -> String {
        format!(
            "{},{},{},{},{}",
            self.query_name,
            self.table_name,
            self.row_count,
            self.metrics.to_csv_row(),
            self.error.as_ref().unwrap_or(&"OK".to_string())
        )
    }
}

pub struct MetricsCollector {
    start: Option<Instant>,
    phase_start: Option<Instant>,
    current_metrics: BenchmarkMetrics,
}

impl MetricsCollector {
    pub fn new() -> Self {
        MetricsCollector {
            start: None,
            phase_start: None,
            current_metrics: BenchmarkMetrics::new(),
        }
    }

    pub fn start_total(&mut self) {
        self.start = Some(Instant::now());
    }

    pub fn start_phase(&mut self) {
        self.phase_start = Some(Instant::now());
    }

    pub fn end_parse_phase(&mut self) {
        if let Some(start) = self.phase_start {
            self.current_metrics.query_parse_time = start.elapsed();
        }
    }

    pub fn end_plan_phase(&mut self) {
        if let Some(start) = self.phase_start {
            self.current_metrics.query_plan_time = start.elapsed();
        }
    }

    pub fn end_execute_phase(&mut self) {
        if let Some(start) = self.phase_start {
            self.current_metrics.query_execute_time = start.elapsed();
        }
    }

    pub fn end_total(&mut self) {
        if let Some(start) = self.start {
            self.current_metrics.total_time = start.elapsed();
        }
    }

    pub fn set_rows(&mut self, processed: usize, returned: usize) {
        self.current_metrics.rows_processed = processed;
        self.current_metrics.rows_returned = returned;
        self.current_metrics.calculate_throughput();
    }

    pub fn get_metrics(&self) -> BenchmarkMetrics {
        self.current_metrics.clone()
    }
}