pg-extras 0.7.2

PostgreSQL performance database insights
Documentation
use crate::queries::shared::Query as PgExtrasQuery;
use crate::web::routes::AppState;
use axum::{
    extract::{Query, State},
    response::IntoResponse,
};
use serde_json::Value;
use sqlx::{Pool, Postgres};

use crate::{
    all_locks, bloat, blocking, buffercache_stats, buffercache_usage, cache_hit, calls,
    connections, db_settings, duplicate_indexes, extensions, index_cache_hit, index_scans,
    index_size, index_usage, indexes, locks, long_running_queries, mandelbrot, null_indexes,
    outliers, records_rank, seq_scans, ssl_used, table_cache_hit, table_index_scans,
    table_indexes_size, table_size, tables, total_index_size, total_table_size, unused_indexes,
    vacuum_stats,
};
use askama_axum::Template;
use eyre::Result;
use reqwest::StatusCode;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
pub struct QueryName {
    pub query_name: String,
}

#[derive(Template)]
#[template(path = "query.html")]
pub struct QueryTemplate {
    pub alert: Option<String>,
    pub query_name: String,
    pub query_data: Vec<Value>,
    pub version: String,
}

pub async fn show(State(state): State<AppState>, query: Query<QueryName>) -> impl IntoResponse {
    let query_name = query.0.query_name;

    let query_data = match get_data(&state.pool, &query_name).await {
        Ok(rows) => rows,
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    };

    QueryTemplate {
        version: env!("CARGO_PKG_VERSION").to_string(),
        alert: state.alert.lock().unwrap().clone(),
        query_name,
        query_data,
    }
    .into_response()
}

async fn get_data(pool: &Pool<Postgres>, query_name: &str) -> Result<Vec<Value>> {
    Ok(match query_name {
        "db_settings" => db_settings(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "bloat" => bloat(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "all_locks" => all_locks(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "blocking" => blocking(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "buffercache_stats" => buffercache_stats(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "buffercache_usage" => buffercache_usage(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "cache_hit" => cache_hit(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "calls" => calls(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "locks" => locks(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "connections" => connections(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "duplicate_indexes" => duplicate_indexes(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "extensions" => extensions(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "index_cache_hit" => index_cache_hit(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "index_scans" => index_scans(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "index_size" => index_size(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "index_usage" => index_usage(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "indexes" => indexes(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "long_running_queries" => long_running_queries(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "mandelbrot" => mandelbrot(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "null_indexes" => null_indexes(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "outliers" => outliers(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "records_rank" => records_rank(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "seq_scans" => seq_scans(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "ssl_used" => ssl_used(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "table_cache_hit" => table_cache_hit(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "table_index_scans" => table_index_scans(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "table_indexes_size" => table_indexes_size(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "table_size" => table_size(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "tables" => tables(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "total_index_size" => total_index_size(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "total_table_size" => total_table_size(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "unused_indexes" => unused_indexes(None, pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        "vacuum_stats" => vacuum_stats(pool)
            .await?
            .into_iter()
            .map(|r| r.to_json())
            .collect(),
        _ => eyre::bail!("Invalid query name: {}", query_name),
    })
}