use std::sync::Arc;
use serde_json::Value;
use tokio_postgres::{Client as PgClient, types::ToSql};
use super::fmt_db_error;
use crate::doctor::check::Check;
const REPORT_CAP: usize = 100;
const FETCH_CAP: usize = REPORT_CAP + 1;
fn wrap(sql: &str) -> String {
format!("SELECT to_jsonb(sub) AS row FROM ( {sql} ) sub LIMIT {FETCH_CAP}")
}
pub struct RowSet {
pub rows: Vec<Value>,
pub truncated: bool,
}
impl RowSet {
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
pub fn count(&self) -> Value {
if self.truncated {
Value::from(format!("{REPORT_CAP}+"))
} else {
Value::from(self.rows.len())
}
}
}
pub async fn fetch_rows(
client: &Arc<PgClient>,
sql: &str,
params: &[&(dyn ToSql + Sync)],
) -> Result<RowSet, tokio_postgres::Error> {
let wrapped = wrap(sql);
let raw = client.query(&wrapped, params).await?;
let truncated = raw.len() > REPORT_CAP;
let rows = raw
.into_iter()
.take(REPORT_CAP)
.map(|r| r.get::<_, Value>("row"))
.collect();
Ok(RowSet { rows, truncated })
}
#[expect(
clippy::too_many_arguments,
reason = "shared query helper; each parameter is a distinct knob the call sites set"
)]
pub async fn tiered_rows_check(
client: &Arc<PgClient>,
name: &'static str,
summary_pass: &str,
summary_prefix: &str,
sql: &str,
params: &[&(dyn ToSql + Sync)],
warn_min: usize,
fail_min: usize,
) -> Check {
match fetch_rows(client, sql, params).await {
Ok(set) => {
let n = if set.truncated {
REPORT_CAP + 1
} else {
set.rows.len()
};
let count = set.count();
if n < warn_min {
return Check::pass(name, summary_pass.to_string());
}
let summary = format!("{summary_prefix}{count}");
let reason = format!("{count} matching row(s)");
let check = if n >= fail_min {
Check::fail(name, summary, reason)
} else {
Check::warning(name, summary, reason)
};
check
.with_detail("rows", Value::Array(set.rows))
.with_detail("truncated", set.truncated)
.with_detail("count", count)
}
Err(err) => Check::fail(name, "query failed", fmt_db_error(&err)),
}
}
#[cfg(test)]
mod tests {
fn tier(n: usize, warn_min: usize, fail_min: usize) -> &'static str {
if n >= fail_min {
"fail"
} else if n >= warn_min {
"warning"
} else {
"pass"
}
}
#[test]
fn error_stream_boundaries() {
assert_eq!(tier(0, 1, 10), "pass");
assert_eq!(tier(1, 1, 10), "warning");
assert_eq!(tier(9, 1, 10), "warning");
assert_eq!(tier(10, 1, 10), "fail");
assert_eq!(tier(100, 1, 10), "fail");
}
}