use pgrx::pg_sys;
use pgrx::prelude::*;
use std::cell::Cell;
pub const CATALOG_REVISION: i32 = 2;
thread_local! {
static MATCHED: Cell<bool> = const { Cell::new(false) };
}
pub enum Installed {
Matches,
Differs(i32),
Unversioned,
}
pub fn check() {
if MATCHED.get() || in_own_script() {
return;
}
match installed() {
Installed::Matches => {}
Installed::Differs(revision) => pg_sys::panic::ErrorReport::new(
PgSqlErrorCode::ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE,
format!(
"pg_tviews library catalog revision {CATALOG_REVISION} does not match the \
installed extension ({revision})"
),
function_name!(),
)
.set_hint(remedy(revision))
.report(PgLogLevel::ERROR),
Installed::Unversioned => pg_sys::panic::ErrorReport::new(
PgSqlErrorCode::ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE,
format!(
"pg_tviews library catalog revision {CATALOG_REVISION} does not match the \
installed extension (0.1.0, no revision)"
),
function_name!(),
)
.set_hint(
"a 0.1.0 install cannot be updated in place: run scripts/migrate-from-0.1.0.sql \
from the pg_tviews release",
)
.report(PgLogLevel::ERROR),
}
}
pub fn is_current() -> bool {
MATCHED.get() || in_own_script() || matches!(installed(), Installed::Matches)
}
#[must_use]
pub fn remedy(revision: i32) -> &'static str {
if revision > CATALOG_REVISION {
"the installed extension is newer than this library: install the pg_tviews package \
that matches it"
} else {
"run ALTER EXTENSION pg_tviews UPDATE"
}
}
pub fn reset() {
MATCHED.set(false);
}
pub fn installed() -> Installed {
if MATCHED.get() {
return Installed::Matches;
}
let revision = Spi::connect(|client| {
let schema = client
.select(
"SELECT pg_catalog.quote_ident(n.nspname) \
FROM pg_catalog.pg_extension e \
JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace \
JOIN pg_catalog.pg_proc p ON p.pronamespace = e.extnamespace \
AND p.proname = 'pg_tviews_catalog_revision' AND p.pronargs = 0 \
WHERE e.extname = 'pg_tviews'",
None,
&[],
)?
.first()
.get_one::<String>()?;
match schema {
Some(schema) => client
.select(
&format!("SELECT {schema}.pg_tviews_catalog_revision()"),
None,
&[],
)?
.first()
.get_one::<i32>(),
None => Ok(None),
}
})
.ok()
.flatten();
match revision {
Some(CATALOG_REVISION) => {
MATCHED.set(true);
Installed::Matches
}
Some(other) => Installed::Differs(other),
None => Installed::Unversioned,
}
}
fn in_own_script() -> bool {
unsafe {
pg_sys::creating_extension
&& pg_sys::CurrentExtensionObject
== pg_sys::get_extension_oid(c"pg_tviews".as_ptr(), true)
}
}