pg_tviews 0.1.0-beta.31

Transactional materialized views with incremental refresh for PostgreSQL
//! Functions acting on every TVIEW: an operator's (`EXECUTE` revoked from
//! `PUBLIC`, `docs/user-guides/operators.md`).

use pgrx::pg_sys::panic::ErrorReport;
use pgrx::prelude::*;

use crate::error::TViewError;

/// Rebuild every TVIEW, dependencies first, and report how many were rebuilt,
/// in which order, and how long it took.
#[pg_extern]
fn pg_tviews_refresh_all() -> Result<pgrx::datum::JsonB, ErrorReport> {
    crate::revision::check();
    if crate::suspend::is_suspended() {
        return Err(TViewError::WrongState {
            reason: "Cannot refresh: triggers are suspended".to_string(),
        }
        .into());
    }
    let start = std::time::Instant::now();
    let order = crate::admin::refresh_all_in_dependency_order()?;
    Ok(pgrx::datum::JsonB(serde_json::json!({
        "refreshed_count": order.len(),
        "order": order,
        "duration_ms": start.elapsed().as_millis(),
    })))
}

/// Rebuild TVIEWs from their backing views, dependencies first, and return each
/// rebuilt entity with the rows it inserted (`rows_inserted`), in rebuild order.
/// With `only_empty` (the default) only the UNLOGGED TVIEWs PostgreSQL reset are
/// filled: run it after a promotion, a crash restart or a restore. With
/// `only_empty => false` every TVIEW is rebuilt: every row of its view is
/// inserted again.
#[pg_extern]
#[allow(clippy::type_complexity)] // Reason: pgrx TableIterator row type spells out the columns
fn pg_tviews_rebuild_all(
    only_empty: default!(bool, true),
) -> Result<TableIterator<'static, (name!(entity, String), name!(rows_inserted, i64))>, ErrorReport>
{
    crate::revision::check();
    Ok(TableIterator::new(crate::replication::rebuild_all(
        only_empty,
    )?))
}

/// Zero the refresh statistics of `tview`, or of every TVIEW of the database
/// (`tviews.stats`, ADR 0221).
#[pg_extern]
fn pg_tviews_stats_reset(tview: default!(Option<&str>, "NULL")) -> Result<(), ErrorReport> {
    crate::revision::check();
    if let Some(name) = tview {
        crate::stats::reset(&[crate::catalog::resolve::find(name)?.table], false)?;
    } else {
        let tables: Vec<_> = crate::catalog::row::relations()?
            .into_iter()
            .map(|listed| listed.table)
            .collect();
        crate::stats::reset(&tables, true)?;
    }
    Ok(())
}