use crate::error::{TViewError, TViewResult};
use pgrx::prelude::*;
use std::collections::HashSet;
pub fn find_base_tables(
view_name: &str,
schema_hint: Option<&str>,
) -> TViewResult<Vec<pg_sys::Oid>> {
let view_oid = get_view_oid(view_name, schema_hint)?;
let max_depth = crate::config::max_dependency_depth();
let depth = crate::catalog::reads::view_nesting(view_oid)?;
if depth > max_depth {
return Err(TViewError::DepthExceeded {
what: "dependency",
depth,
max_depth,
});
}
let tview_tables = load_tview_table_oids()?;
Ok(crate::catalog::reads::view_relations_read(view_oid)?
.into_iter()
.filter_map(|(oid, relkind)| match relkind {
b'r' | b'p' if !tview_tables.contains(&oid) => Some(oid),
b'm' => Some(oid),
_ => None,
})
.collect())
}
fn get_view_oid(view_name: &str, schema_hint: Option<&str>) -> TViewResult<pg_sys::Oid> {
let schema = schema_hint.map_or_else(
|| {
Spi::get_one::<String>("SELECT current_schema()::text")
.map_err(|e| TViewError::CatalogError {
operation: "Resolve current_schema()".to_string(),
pg_error: e.to_string(),
})
.and_then(|opt| {
opt.ok_or_else(|| TViewError::CatalogError {
operation: "Resolve current_schema()".to_string(),
pg_error: "current_schema() returned NULL".to_string(),
})
})
},
|s| Ok(s.to_string()),
)?;
let args = vec![
crate::utils::spi::text(view_name),
crate::utils::spi::text(schema.as_str()),
];
Spi::get_one_with_args::<pg_sys::Oid>(
"SELECT c.oid FROM pg_class c \
JOIN pg_namespace n ON c.relnamespace = n.oid \
WHERE c.relname = $1 \
AND n.nspname = $2 \
AND c.relkind IN ('v', 'm')",
&args,
)
.map_err(|e| TViewError::CatalogError {
operation: format!("Get OID for '{view_name}'"),
pg_error: e.to_string(),
})?
.ok_or_else(|| TViewError::CatalogError {
operation: format!("Get OID for '{view_name}'"),
pg_error: format!("not found in schema '{schema}'"),
})
}
fn load_tview_table_oids() -> TViewResult<HashSet<pg_sys::Oid>> {
Spi::connect(|client| -> crate::TViewResult<_> {
let rows = client.select(
&format!(
"SELECT table_oid::oid AS table_oid FROM {}",
crate::utils::meta_table()
),
None,
&[],
)?;
let mut oids = HashSet::new();
for row in rows {
if let Some(oid) =
row["table_oid"]
.value::<pg_sys::Oid>()
.map_err(|e| TViewError::CatalogError {
operation: "load_tview_table_oids".to_string(),
pg_error: e.to_string(),
})?
{
oids.insert(oid);
}
}
Ok(oids)
})
.map_err(|e: TViewError| TViewError::CatalogError {
operation: "load_tview_table_oids".to_string(),
pg_error: e.to_string(),
})
}