use rupg_catalog::{IndexInfo, RelKind};
use rupg_common::{Error, Result, SqlState};
use rupg_sql::nodes::RefreshMatViewStmt;
use super::comment::not_owner;
use super::drop::{owner_word, quote_identifier};
use super::{Definer, Found, not_yet, relname};
fn usable_unique(index: &IndexInfo) -> bool {
index.unique
&& index.immediate
&& index.valid
&& index.predicate.is_none()
&& index.keys.iter().take(usize::try_from(index.key_count).unwrap_or(0)).all(|&k| k > 0)
}
impl Definer<'_, '_> {
pub(super) fn refresh_matview(&mut self, stmt: &RefreshMatViewStmt) -> Result<Option<u32>> {
let rv = stmt.relation.as_deref().cloned().unwrap_or_default();
let name = relname(&rv);
let found = self.lookup_relation(&rv).map_err(|e| e.with_position(None))?;
let kind = self.kind_of(found);
let Found::User(oid) = found else {
return Err(not_yet("REFRESH MATERIALIZED VIEW of a system catalog", None));
};
let Some(rel) = self.catalog.relation(oid).cloned() else {
return Err(Error::internal(format!("cache lookup failed for relation {oid}")));
};
if !self.is_superuser() && rel.owner != self.user {
return Err(not_owner(owner_word(kind), name));
}
if rel.kind != RelKind::MatView {
return Err(Error::new(
SqlState::FEATURE_NOT_SUPPORTED,
format!("\"{}\" is not a materialized view", rel.name),
));
}
let populated = rel.view.as_ref().is_some_and(|v| v.populated);
if stmt.concurrent && !populated {
return Err(Error::new(
SqlState::FEATURE_NOT_SUPPORTED,
"CONCURRENTLY cannot be used when the materialized view is not populated",
));
}
if stmt.concurrent && stmt.skipData {
return Err(Error::new(
SqlState::SYNTAX_ERROR,
"CONCURRENTLY and WITH NO DATA options cannot be used together",
));
}
if stmt.concurrent {
let found = self
.catalog
.relations()
.filter_map(|r| r.index.as_ref())
.any(|index| index.table == oid && usable_unique(index));
if !found {
let schema = self.schema_name(rel.namespace);
return Err(Error::new(
SqlState::OBJECT_NOT_IN_PREREQUISITE_STATE,
format!(
"cannot refresh materialized view \"{}.{}\" concurrently",
quote_identifier(&schema),
quote_identifier(&rel.name)
),
)
.with_hint(
"Create a unique index with no WHERE clause on one or more columns of the materialized view.",
));
}
}
self.truncated.push(oid);
self.catalog.set_populated(oid, !stmt.skipData)?;
Ok((!stmt.skipData).then_some(oid))
}
}