use rupg_catalog::RelKind;
use rupg_common::{Error, Result, SqlState};
use rupg_sql::nodes::{AlterTableCmd, Node, RangeVar};
use super::alter::Target;
use super::{Definer, relname};
impl Definer<'_, '_> {
pub(super) fn prep_inherit(&self, target: &Target) -> Result<()> {
self.check_not_typed(target.oid, "cannot change inheritance of typed table")?;
if self.catalog.relation(target.oid).is_some_and(|r| r.partition_of.is_some()) {
return Err(Error::new(
SqlState::WRONG_OBJECT_TYPE,
"cannot change inheritance of a partition",
));
}
Ok(())
}
pub(super) fn add_inherit(&mut self, target: &Target, cmd: &AlterTableCmd) -> Result<()> {
let Some(Node::RangeVar(rv)) = &cmd.def else {
return Err(Error::internal("INHERIT with no parent"));
};
let parent = self.open_attached(rv, cmd)?;
let lookup = |oid: u32| Error::internal(format!("cache lookup failed for relation {oid}"));
let rel = self.catalog.relation(target.oid).cloned().ok_or_else(|| lookup(target.oid))?;
let parent_rel = self.catalog.relation(parent).cloned().ok_or_else(|| lookup(parent))?;
if self.catalog.is_temp_schema(parent_rel.namespace)
&& !self.catalog.is_temp_schema(rel.namespace)
{
return Err(Error::new(
SqlState::WRONG_OBJECT_TYPE,
format!("cannot inherit from temporary relation \"{}\"", parent_rel.name),
));
}
if parent_rel.kind == RelKind::Partitioned {
return Err(Error::new(
SqlState::WRONG_OBJECT_TYPE,
format!("cannot inherit from partitioned table \"{}\"", relname(rv)),
));
}
if parent_rel.partition_of.is_some() {
return Err(Error::new(SqlState::WRONG_OBJECT_TYPE, "cannot inherit from a partition"));
}
if parent == rel.oid || self.catalog.all_inheritors(rel.oid).contains(&parent) {
return Err(Error::new(SqlState::DUPLICATE_TABLE, "circular inheritance not allowed")
.with_detail(format!(
"\"{}\" is already a child of \"{}\".",
relname(rv),
rel.name
)));
}
self.check_transition_triggers(&rel, false)?;
if rel.inherits.contains(&parent) {
return Err(Error::new(
SqlState::DUPLICATE_TABLE,
format!("relation \"{}\" would be inherited from more than once", parent_rel.name),
));
}
self.merge_attributes(&rel, &parent_rel)?;
self.merge_constraints(&rel, &parent_rel)?;
self.catalog.add_inherit(rel.oid, parent)
}
pub(super) fn drop_inherit(&mut self, target: &Target, rv: &RangeVar) -> Result<()> {
let parent = self.open_table(rv)?;
let inherits =
self.catalog.relation(target.oid).is_some_and(|r| r.inherits.contains(&parent));
if !inherits {
let name =
self.catalog.relation(parent).map_or_else(|| relname(rv), |r| r.name.as_str());
return Err(Error::new(
SqlState::UNDEFINED_TABLE,
format!("relation \"{name}\" is not a parent of relation \"{}\"", target.name),
));
}
self.remove_inheritance(target.oid, parent)
}
}