use pgrx::pg_sys::panic::ErrorReport;
use pgrx::prelude::*;
#[pg_extern]
#[allow(clippy::missing_const_for_fn)] fn pg_tviews_version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
#[pg_extern(stable, parallel_safe)]
fn pg_tviews_entity_of(tview: &str) -> Result<String, ErrorReport> {
Ok(crate::catalog::resolve::find(tview)?.entity)
}
#[pg_extern]
fn pg_tviews_check_jsonb_delta() -> bool {
crate::jsonb_delta::check_jsonb_delta_available()
}
#[pg_extern]
#[allow(clippy::type_complexity)] fn pg_tviews_health_check() -> TableIterator<
'static,
(
name!(status, String),
name!(component, String),
name!(message, String),
name!(severity, String),
),
> {
TableIterator::new(crate::health::health_check())
}
#[pg_extern]
#[allow(clippy::type_complexity)] fn pg_tviews_replication_status() -> Result<
TableIterator<
'static,
(
name!(entity, String),
name!(persistence, String),
name!(replica_readable, bool),
name!(is_empty, Option<bool>),
name!(needs_rebuild, Option<bool>),
),
>,
ErrorReport,
> {
Ok(TableIterator::new(crate::replication::replication_status()?))
}
#[pg_extern]
#[allow(clippy::type_complexity)] fn pg_tviews_stats_rows() -> Result<
TableIterator<
'static,
(
name!(schema, Option<String>),
name!(name, String),
name!(entity, String),
name!(view_recomputes, Option<i64>),
name!(noop_skipped, Option<i64>),
name!(patch_captured, Option<i64>),
name!(patch_applied, Option<i64>),
name!(patch_fallbacks, Option<i64>),
name!(propagation_pruned, Option<i64>),
name!(rows_written, Option<i64>),
name!(rows_deleted, Option<i64>),
name!(full_refreshes, Option<i64>),
name!(refresh_ms, Option<f64>),
name!(stats_reset, Option<pgrx::datum::TimestampWithTimeZone>),
name!(untracked, bool),
),
>,
ErrorReport,
> {
use crate::stats::Counter;
let (counts, overflowed) = crate::stats::read()?;
let rows = crate::catalog::row::relations()?
.into_iter()
.map(|listed| {
let crate::catalog::row::Listed {
entity,
table,
schema,
name,
} = listed;
let name = name.unwrap_or_else(|| format!("tv_{entity}"));
let found = counts.get(&table);
let value = |counter: Counter| match found {
Some(c) => Some(i64::try_from(c.get(counter)).unwrap_or(i64::MAX)),
None if overflowed => None,
None => Some(0),
};
#[allow(clippy::cast_precision_loss)]
let refresh_ms = value(Counter::RefreshMicros).map(|us| us as f64 / 1000.0);
(
schema,
name,
entity,
value(Counter::ViewRecomputes),
value(Counter::NoopSkipped),
value(Counter::PatchCaptured),
value(Counter::PatchApplied),
value(Counter::PatchFallbacks),
value(Counter::PropagationPruned),
value(Counter::RowsWritten),
value(Counter::RowsDeleted),
value(Counter::FullRefreshes),
refresh_ms,
found
.map(|c| c.since)
.or_else(|| (!overflowed).then(crate::stats::started))
.and_then(|since| pgrx::datum::TimestampWithTimeZone::try_from(since).ok()),
found.is_none() && overflowed,
)
})
.collect::<Vec<_>>();
Ok(TableIterator::new(rows))
}
extension_sql!(
r"
-- Per-TVIEW refresh statistics, readable from any session (ADR 0221). Counts,
-- no data: readable by every role.
CREATE VIEW @extschema@.stats AS SELECT * FROM @extschema@.pg_tviews_stats_rows();
COMMENT ON VIEW @extschema@.stats IS
'Refresh statistics of each TVIEW since the server started or pg_tviews_stats_reset()';
GRANT SELECT ON @extschema@.stats TO PUBLIC;
",
name = "stats_view",
requires = [pg_tviews_stats_rows],
);