use super::RelationRemovalContext;
use crate::row_locks::{
binding::{bind_relation, lock_descendants, RelationBinding},
RelationLockMode,
};
use std::collections::BTreeSet;
use uqa_sql::{
ast::{DropKind, DropStmt},
schema::removal::bind_relation_drop_target,
SQLError,
};
pub(super) fn bind_drop_targets(
context: &RelationRemovalContext<'_>,
statement: &DropStmt,
notice: &mut dyn FnMut(&str),
) -> Result<Vec<String>, SQLError> {
let mut targets = Vec::new();
let mut seen = BTreeSet::new();
for name in &statement.names {
let bound = bind_relation(
context.locks,
RelationLockMode::AccessExclusive,
false,
|| {
let Some(canonical) = bind_relation_drop_target(
context.catalog,
name,
statement.kind,
statement.if_exists,
notice,
)?
else {
return Ok(None);
};
let object_id = context.identities.relation_object_id(&canonical)?;
Ok(Some(RelationBinding {
name: canonical,
object_id,
value: (),
}))
},
|binding| match statement.kind {
DropKind::Table => context
.privileges
.ensure_table_drop_authority(&binding.name),
DropKind::ForeignTable => context
.privileges
.ensure_foreign_table_drop_authority(&binding.name),
DropKind::View | DropKind::MaterializedView => {
context.views.ensure_view_drop_authority(&binding.name)
}
DropKind::Sequence => {
let relation = uqa_core::RelationIdentity::from_legacy_name(&binding.name)
.map_err(SQLError::Internal)?;
context
.sequences
.sequence_removal_context()
.privileges
.ensure_sequence_owner(&binding.name, &relation)
.map(|_| ())
}
_ => unreachable!("relation DROP binding requires a table-shaped target"),
},
)?;
if let Some(bound) = bound {
if seen.insert(bound.name.clone()) {
targets.push(bound.name);
}
}
}
if statement.kind == DropKind::Table {
let (descendants, _) = context
.tables
.hierarchy_drop_targets(&targets, statement.cascade);
lock_descendants(
context.identities,
context.locks,
descendants.into_iter().filter(|name| !seen.contains(name)),
RelationLockMode::AccessExclusive,
false,
)?;
let (tables, _) = context
.tables
.hierarchy_drop_targets(&targets, statement.cascade);
context.views.lock_dependent_views(&tables)?;
} else if statement.kind == DropKind::ForeignTable {
context.views.lock_dependent_views(&targets)?;
}
Ok(targets)
}