use crate::error::{TViewError, TViewResult};
use pgrx::pg_sys::Oid;
use pgrx::prelude::*;
use std::collections::HashSet;
fn read_error(what: &str, entity: &str, e: &impl std::fmt::Display) -> TViewError {
TViewError::CatalogError {
operation: format!("Read the {what} of TVIEW {entity}"),
pg_error: e.to_string(),
}
}
pub fn exists(entity: &str) -> TViewResult<bool> {
Spi::get_one_with_args::<bool>(
&format!(
"SELECT EXISTS (SELECT 1 FROM {} WHERE entity = $1)",
super::meta_table()
),
&[crate::utils::spi::text(entity)],
)
.map(|e| e == Some(true))
.map_err(|e| read_error("registration", entity, &e))
}
pub fn definition(entity: &str) -> TViewResult<Option<String>> {
Spi::get_one_with_args::<String>(
&format!(
"SELECT definition FROM {} WHERE entity = $1",
super::meta_table()
),
&[crate::utils::spi::text(entity)],
)
.map_err(|e| read_error("definition", entity, &e))
}
pub fn definition_and_schema(entity: &str) -> TViewResult<(Option<String>, Option<String>)> {
Spi::connect(|client| {
let args = [crate::utils::spi::text(entity)];
client
.select(
&format!(
"SELECT m.definition, n.nspname::text \
FROM {} m \
JOIN pg_catalog.pg_class c ON c.oid = m.table_oid \
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \
WHERE m.entity = $1",
super::meta_table()
),
None,
&args,
)?
.first()
.get_two::<String, String>()
})
.map_err(|e| read_error("definition", entity, &e))
}
pub fn group_keys(entity: &str) -> TViewResult<Option<serde_json::Value>> {
Spi::get_one_with_args::<pgrx::JsonB>(
&format!(
"SELECT group_keys FROM {} WHERE entity = $1",
super::meta_table()
),
&[crate::utils::spi::text(entity)],
)
.map(|j| j.map(|j| j.0))
.map_err(|e| read_error("group keys", entity, &e))
}
pub fn typename(entity: &str) -> TViewResult<Option<String>> {
Spi::get_one_with_args::<String>(
&format!(
"SELECT graphql_typename FROM {} WHERE entity = $1",
super::meta_table()
),
&[crate::utils::spi::text(entity)],
)
.map_err(|e| read_error("GraphQL type name", entity, &e))
}
pub fn typenames() -> TViewResult<Vec<(String, String)>> {
let sql = format!(
"SELECT entity, graphql_typename FROM {} ORDER BY entity",
super::meta_table()
);
crate::utils::spi::rows(&sql, &[], |row| {
let entity: String = row
.get(1)
.map_err(|e| crate::utils::spi::error(&sql, &e))?
.unwrap_or_default();
let declared: Option<String> =
row.get(2).map_err(|e| crate::utils::spi::error(&sql, &e))?;
let typename = super::typename(&entity, declared);
Ok((entity, typename))
})
}
pub fn set_typename(entity: &str, typename: Option<&str>) -> TViewResult<()> {
let _owner = crate::owner::AsOwner::of_extension()?;
Spi::run_with_args(
&format!(
"UPDATE {} SET graphql_typename = $2 WHERE entity = $1",
super::meta_table()
),
&[
crate::utils::spi::text(entity),
crate::utils::spi::text(typename),
],
)
.map_err(|e| crate::utils::spi::catalog_error("Store the GraphQL type name", &e))
}
pub fn time_dependent(entity: &str) -> TViewResult<bool> {
Spi::get_one_with_args::<bool>(
&format!(
"SELECT time_dependent FROM {} WHERE entity = $1",
super::meta_table()
),
&[crate::utils::spi::text(entity)],
)
.map(|t| t == Some(true))
.map_err(|e| read_error("time dependence", entity, &e))
}
pub fn clear_needs_reregister(entity: &str) -> TViewResult<()> {
let _owner = crate::owner::AsOwner::of_extension()?;
Spi::run_with_args(
&format!(
"UPDATE {} SET needs_reregister = false WHERE entity = $1",
super::meta_table()
),
&[crate::utils::spi::text(entity)],
)
.map_err(|e| TViewError::CatalogError {
operation: format!("Clear needs_reregister of TVIEW {entity}"),
pg_error: e.to_string(),
})
}
pub fn table_oids() -> TViewResult<HashSet<Oid>> {
Ok(crate::utils::spi::oids(
&format!(
"SELECT table_oid::pg_catalog.oid FROM {}",
super::meta_table()
),
&[],
)?
.into_iter()
.collect())
}
pub fn view_of_table(table: Oid) -> TViewResult<Option<Oid>> {
crate::utils::spi::one::<Oid>(
&format!(
"SELECT m.view_oid::pg_catalog.oid FROM {} m \
WHERE m.table_oid::pg_catalog.oid = $1",
super::meta_table()
),
&[crate::utils::spi::oid(table)],
)
}
pub fn counts() -> TViewResult<(i64, i64, i64)> {
let (total, stale, orphaned) = Spi::get_three::<i64, i64, i64>(&format!(
"SELECT pg_catalog.count(*), \
pg_catalog.count(*) FILTER (WHERE m.needs_reregister), \
pg_catalog.count(*) FILTER (WHERE NOT EXISTS \
(SELECT 1 FROM pg_catalog.pg_class c WHERE c.oid = m.table_oid)) \
FROM {} m",
super::meta_table()
))
.map_err(|e| crate::utils::spi::catalog_error("Count the TVIEWs", &e))?;
Ok((
total.unwrap_or(0),
stale.unwrap_or(0),
orphaned.unwrap_or(0),
))
}
pub struct Listed {
pub entity: String,
pub table: Oid,
pub schema: Option<String>,
pub name: Option<String>,
}
pub fn relations() -> TViewResult<Vec<Listed>> {
Spi::connect(|client| {
let mut out = Vec::new();
for row in client.select(
&format!(
"SELECT m.entity::pg_catalog.text, m.table_oid::pg_catalog.oid, \
n.nspname::pg_catalog.text, c.relname::pg_catalog.text \
FROM {} m \
LEFT JOIN pg_catalog.pg_class c ON c.oid = m.table_oid \
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \
ORDER BY m.entity",
super::meta_table()
),
None,
&[],
)? {
out.push(Listed {
entity: row.get::<String>(1)?.unwrap_or_default(),
table: row.get::<Oid>(2)?.unwrap_or(Oid::INVALID),
schema: row.get::<String>(3)?,
name: row.get::<String>(4)?,
});
}
Ok::<_, pgrx::spi::Error>(out)
})
.map_err(|e| crate::utils::spi::catalog_error("List the TVIEWs", &e))
}