use rupg_catalog::ConKind;
use rupg_common::{Error, Result, SqlState};
use rupg_sql::nodes::{AlterTableCmd, Node};
use super::alter::Target;
use super::alter_type::Changed;
use super::table::SYSTEM_COLUMNS;
use super::{Definer, Fill, column_numbers};
fn no_column(column: &str, table: &str) -> Error {
if SYSTEM_COLUMNS.contains(&column) {
return Error::new(
SqlState::FEATURE_NOT_SUPPORTED,
format!("cannot alter system column \"{column}\""),
);
}
Error::new(
SqlState::UNDEFINED_COLUMN,
format!("column \"{column}\" of relation \"{table}\" does not exist"),
)
}
fn not_generated(column: &str, table: &str) -> Error {
Error::new(
SqlState::OBJECT_NOT_IN_PREREQUISITE_STATE,
format!("column \"{column}\" of relation \"{table}\" is not a generated column"),
)
}
impl Definer<'_, '_> {
pub(super) fn set_expression(
&mut self,
target: &Target,
column: &str,
node: &Node,
changed: &mut Changed,
) -> Result<()> {
let table = target.oid;
let rel = self.catalog.relation(table);
let Some(number) = rel.and_then(|r| r.column_number(column)) else {
return Err(no_column(column, &target.name));
};
let generated = rel.and_then(|r| r.column(number)).map_or(0, |c| c.generated);
if generated == 0 {
return Err(not_generated(column, &target.name));
}
let mut checks = Vec::new();
for con in self.catalog.constraints_of(table).filter(|c| c.kind == ConKind::Check) {
let expr = crate::node::read(con.expr.as_deref().unwrap_or_default())?;
let read = column_numbers(&[&expr]);
if read.contains(&number) || read.contains(&0) {
checks.push(con.oid);
}
}
changed.add_checks(checks);
self.catalog.remove_default(table, number);
self.add_generation_expression(table, number, node)?;
if target.kind != b'p' && generated == b's' {
self.check_row_type_users(target)?;
let default = self.catalog.default_of(table, number).map(|d| d.expr.clone());
self.added.push(Fill { table, column: number, default });
}
Ok(())
}
pub(super) fn prep_drop_expression(
&self,
target: &Target,
cmd: &AlterTableCmd,
recurse: bool,
recursing: bool,
) -> Result<()> {
if !recurse && !recursing && !self.catalog.children(target.oid).is_empty() {
return Err(Error::new(
SqlState::FEATURE_NOT_SUPPORTED,
"ALTER TABLE / DROP EXPRESSION must be applied to child tables too",
));
}
if recursing {
return Ok(());
}
let column = cmd.name.as_deref().unwrap_or_default();
let rel = self.catalog.relation(target.oid);
let found = rel.and_then(|r| r.column(r.column_number(column)?));
let Some(found) = found else {
return Err(Error::new(
SqlState::UNDEFINED_COLUMN,
format!("column \"{column}\" of relation \"{}\" does not exist", target.name),
));
};
if found.inhcount > 0 {
return Err(Error::new(
SqlState::INVALID_TABLE_DEFINITION,
"cannot drop generation expression from inherited column",
));
}
Ok(())
}
pub(super) fn drop_expression(
&mut self,
target: &Target,
column: &str,
missing_ok: bool,
) -> Result<()> {
let table = target.oid;
let rel = self.catalog.relation(table);
let Some(number) = rel.and_then(|r| r.column_number(column)) else {
return Err(no_column(column, &target.name));
};
if rel.and_then(|r| r.column(number)).is_none_or(|c| c.generated == 0) {
if !missing_ok {
return Err(not_generated(column, &target.name));
}
self.notice(
SqlState::SUCCESSFUL_COMPLETION,
format!(
"column \"{column}\" of relation \"{}\" is not a generated column, skipping",
target.name
),
);
return Ok(());
}
self.catalog.set_column_generated(table, number, 0)?;
self.catalog.remove_default(table, number);
Ok(())
}
}