use std::collections::BTreeMap;
use crate::mutation::constraints::context::MutationRead;
use crate::schema::columns::ColumnRewritePublication;
use crate::schema::view_dependencies::ViewDependencyContext;
use uqa_sql::ast::ColumnDef;
use uqa_sql::expr::composites::{
apply_attribute_change, type_contains_composite, AttributeChange, CompositeTypeCatalog,
};
use uqa_sql::SQLError;
pub trait CompositeValueTables {
fn composite_value_tables(&self) -> Result<Vec<(String, Vec<ColumnDef>)>, SQLError>;
}
pub struct CompositeValueContext<'a> {
pub memory: &'a dyn crate::query::runtime::QueryMemorySettings,
pub cancellation: &'a uqa_core::CancellationToken,
pub tables: &'a dyn CompositeValueTables,
pub reads: &'a dyn MutationRead,
pub writes: &'a dyn ColumnRewritePublication,
pub views: ViewDependencyContext<'a>,
pub types: &'a dyn CompositeTypeCatalog,
pub catalogs: super::catalog_values::CompositeCatalogValueContext<'a>,
}
pub fn rewrite_composite_values(
context: &CompositeValueContext<'_>,
target: u32,
change: &AttributeChange,
) -> Result<std::collections::BTreeSet<String>, SQLError> {
let memory = uqa_core::memory::MemoryBudget::new(context.memory.work_mem_bytes()?);
let control = uqa_storage::read_control::StorageReadControl::new(&memory, context.cancellation);
let mut rebuild = std::collections::BTreeSet::new();
for (table, columns) in context.tables.composite_value_tables()? {
let mut affected = Vec::new();
for column in columns {
if type_contains_composite(&column.ty, target, context.types)? {
affected.push(column);
}
}
if affected.is_empty() {
continue;
}
rebuild.insert(table.clone());
crate::schema::columns::rows::visit_document_ids(
context.reads,
&table,
&control,
&mut |doc_id| {
let Some(document) = context.reads.get_document(&table, doc_id)? else {
return Ok(());
};
let mut updates = BTreeMap::new();
for column in &affected {
let Some(value) = document.get(&column.name).cloned() else {
continue;
};
updates.insert(
column.name.clone(),
apply_attribute_change(value, &column.ty, target, change, context.types)?,
);
}
if !updates.is_empty() {
context
.writes
.update_fields(&table, doc_id, updates, BTreeMap::new())?;
}
Ok(())
},
)?;
}
let constants = uqa_sql::expr::composites::constants::CompositeConstantChange {
target,
change,
catalog: context.types,
types: context.catalogs.types,
};
rebuild.extend(super::catalog_values::rewrite(
&context.catalogs,
&constants,
)?);
crate::schema::view_dependencies::rewrite_materialized_composite_values(
&context.views,
target,
change,
context.types,
&constants,
)?;
Ok(rebuild)
}
pub(super) fn rebuild_indexes(
context: &CompositeValueContext<'_>,
tables: std::collections::BTreeSet<String>,
) -> Result<(), SQLError> {
for table in tables {
context
.catalogs
.index_publication
.refresh_index_table(&table)
.map_err(|error| {
uqa_sql::catalog::errors::storage_error("rebuild composite index values", &error)
})?;
}
Ok(())
}