mod labels;
mod locking;
mod plan;
mod removal;
pub use labels::drop_graph_label_dependents;
use crate::catalog::context::CatalogContext;
use crate::catalog::projection::CatalogDependencies;
use crate::row_locks::binding::RelationDefinitionSession;
use crate::schema::domains::dependencies::DomainDependencyContext;
use crate::schema::foreign_removal::ForeignTableRemovalContext;
use crate::schema::indexes::removal::IndexRemovalContext;
use crate::schema::namespaces::removal::EmptySchemaRemovalContext;
use crate::schema::table_removal::context::TableRemovalContext;
use plan::DeletionPlan;
use uqa_sql::catalog::dependencies::{DeletionTargets, ObjectAddress};
use uqa_sql::{SQLError, SQLNotice};
pub struct CatalogRemovalContext<'a> {
pub catalog: CatalogContext<'a>,
pub locks: &'a dyn RelationDefinitionSession,
pub shared_locks: &'a dyn crate::row_locks::shared_objects::SharedObjectLockSession,
pub identities: &'a dyn crate::row_locks::binding::RelationLockCatalog,
pub tables: TableRemovalContext<'a>,
pub foreign_tables: ForeignTableRemovalContext<'a>,
pub foreign_servers: crate::schema::foreign_server_removal::ForeignServerRemovalPublication<'a>,
pub foreign_wrappers:
crate::schema::foreign_wrapper_removal::ForeignWrapperRemovalPublication<'a>,
pub indexes: IndexRemovalContext<'a>,
pub domains: DomainDependencyContext<'a>,
pub composites: crate::schema::composites::attributes::CompositeAttributeContext<'a>,
pub schemas: EmptySchemaRemovalContext<'a>,
pub events: &'a dyn crate::schema::removal::RelationRemovalEvents,
pub notices: &'a crate::query::NoticeQueue,
}
pub trait CatalogRemovalInputs {
fn catalog_removal_context(&self) -> CatalogRemovalContext<'_>;
}
pub fn perform_deletion(
context: &CatalogRemovalContext<'_>,
originals: impl Fn(&CatalogDependencies) -> Result<Vec<ObjectAddress>, SQLError>,
cascade: bool,
) -> Result<(), SQLError> {
delete_objects(context, originals, cascade, false)
}
pub fn perform_quiet_cascade(
context: &CatalogRemovalContext<'_>,
originals: impl Fn(&CatalogDependencies) -> Result<Vec<ObjectAddress>, SQLError>,
) -> Result<(), SQLError> {
delete_objects(context, originals, true, true)
}
fn delete_objects(
context: &CatalogRemovalContext<'_>,
originals: impl Fn(&CatalogDependencies) -> Result<Vec<ObjectAddress>, SQLError>,
cascade: bool,
quiet: bool,
) -> Result<(), SQLError> {
let mut locked = std::collections::BTreeSet::new();
let mut locked_foreign = std::collections::BTreeSet::new();
loop {
let dependencies = catalog_dependencies(&context.catalog)?;
let originals = originals(&dependencies)?;
if originals.is_empty() {
return Ok(());
}
let original = match originals.as_slice() {
[original] => Some(*original),
_ => None,
};
let describe = |object| dependencies.describe(&context.catalog, object);
let targets = DeletionTargets::collect(dependencies.graph(), &originals, &describe)?;
if locking::lock_foreign_definitions(context, &targets, &mut locked_foreign)? {
continue;
}
let plan = DeletionPlan::new(&dependencies, &targets)?;
if locking::lock_relations(context, &plan, &mut locked)? {
continue;
}
let notice = targets.report(cascade, original, &describe)?;
plan.execute(context)?;
if let Some(notice) = notice.filter(|_| !quiet) {
let report = SQLNotice::notice(notice.message);
context.notices.push(match notice.detail {
Some(detail) => report.with_detail(detail),
None => report,
});
}
return Ok(());
}
}
pub fn drop_table_on_commit(
context: &CatalogRemovalContext<'_>,
name: &str,
) -> Result<(), SQLError> {
let relation =
uqa_core::RelationIdentity::from_legacy_name(name).map_err(SQLError::Internal)?;
perform_quiet_cascade(context, |dependencies| {
Ok(vec![required_address(
dependencies.relation_address(&relation, None),
|| format!("table {name}"),
)?])
})
}
pub fn catalog_dependencies(context: &CatalogContext<'_>) -> Result<CatalogDependencies, SQLError> {
context.catalog.refreshed_catalog_snapshot()?;
let catalog = context.catalog.current_catalog_snapshot();
let mut resolution = context.session_execution_view().relation_name_resolution();
resolution.set_lookup_mode(crate::catalog::RelationLookupMode::Bound);
CatalogDependencies::build(context, &catalog, &resolution)
}
pub fn required_address(
address: Option<ObjectAddress>,
object: impl FnOnce() -> String,
) -> Result<ObjectAddress, SQLError> {
address.ok_or_else(|| {
SQLError::Internal(format!(
"{} is missing from the catalog dependencies",
object()
))
})
}