use crate::config::UncascadedPolicy;
use crate::error::{TViewError, TViewResult};
use pgrx::pg_sys::{self, Oid};
use pgrx::prelude::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UncascadedTable {
pub oid: Oid,
pub name: String,
pub reason: String,
}
#[derive(Debug, Clone)]
pub(crate) struct Uncascaded {
pub tables: Vec<UncascadedTable>,
pub policy: UncascadedPolicy,
}
impl Uncascaded {
pub(crate) fn oids(&self) -> Vec<Oid> {
self.tables.iter().map(|t| t.oid).collect()
}
}
fn describe(tview: &str, tables: &[UncascadedTable], verb: &str) -> String {
let names = tables
.iter()
.map(|t| t.name.as_str())
.collect::<Vec<_>>()
.join(", ");
let reasons = match tables {
[one] => one.reason.clone(),
_ => tables
.iter()
.map(|t| format!("{}: {}", t.name, t.reason))
.collect::<Vec<_>>()
.join("; "),
};
format!("writes to {names} {verb} {tview} ({reasons})")
}
pub(crate) fn report(
tview: &str,
tables: &[UncascadedTable],
policy: UncascadedPolicy,
) -> TViewResult<()> {
if tables.is_empty() {
return Ok(());
}
match policy {
UncascadedPolicy::Warn => {
pg_sys::panic::ErrorReport::new(
PgSqlErrorCode::ERRCODE_WARNING,
describe(tview, tables, "will not refresh"),
function_name!(),
)
.set_hint(
"Set pg_tviews.uncascaded_policy to 'full_refresh' before creating the TVIEW \
to refresh it in full on such writes, or to 'error' to refuse it.",
)
.report(PgLogLevel::WARNING);
Ok(())
}
UncascadedPolicy::FullRefresh => {
notice!("{}", describe(tview, tables, "will refresh all rows of"));
Ok(())
}
UncascadedPolicy::Error => Err(TViewError::InvalidInput {
parameter: "tview definition".to_string(),
reason: format!(
"{}. Set pg_tviews.uncascaded_policy to 'warn' or 'full_refresh' to create it anyway",
describe(tview, tables, "would not refresh")
),
}),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn describe_lists_tables_and_reasons() {
let t = |n: &str, r: &str| UncascadedTable {
oid: Oid::INVALID,
name: n.to_string(),
reason: r.to_string(),
};
assert_eq!(
describe("public.tv_o", &[t("public.a", "x")], "will not refresh"),
"writes to public.a will not refresh public.tv_o (x)"
);
assert_eq!(
describe(
"public.tv_o",
&[t("public.a", "x"), t("public.b", "y")],
"will not refresh"
),
"writes to public.a, public.b will not refresh public.tv_o (public.a: x; public.b: y)"
);
}
}