use super::implicit::stored_owner_names_current;
use crate::ast::ColumnDef;
use uqa_core::RelationIdentity;
pub trait StoredSequenceNames {
fn stored_sequence_name(&self, reference: &str) -> Result<String, String>;
}
pub struct BoundImplicitSequenceOwner {
pub sequence: String,
pub table_object_id: [u8; 16],
pub column_object_id: [u8; 16],
pub identity: bool,
}
pub fn bind_implicit_sequence_owners(
catalog: &dyn StoredSequenceNames,
table_name: &str,
table_object_id: [u8; 16],
columns: &[ColumnDef],
) -> Result<Vec<BoundImplicitSequenceOwner>, String> {
let relation = RelationIdentity::from_legacy_name(table_name)?;
let mut bindings = Vec::new();
for column in columns {
let Some(provenance) = column.auto_increment.as_ref() else {
continue;
};
let Some(named_owner) = provenance.owner.as_ref() else {
continue;
};
if !stored_owner_names_current(&relation, column, named_owner) {
continue;
}
let Some(sequence) = provenance.sequence.as_deref() else {
continue;
};
let sequence = catalog.stored_sequence_name(sequence)?;
let column_object_id = column.object_id.ok_or_else(|| {
format!(
"column `{table_name}`.`{}` has no object identity",
column.name
)
})?;
bindings.push(BoundImplicitSequenceOwner {
sequence,
table_object_id,
column_object_id,
identity: provenance.is_identity(),
});
}
Ok(bindings)
}
pub fn clear_auto_increment_owner_markers(
columns: &mut [crate::ast::ColumnDef],
target: &uqa_core::RelationIdentity,
) -> bool {
let mut changed = false;
for column in columns {
let Some(provenance) = column.auto_increment.as_mut() else {
continue;
};
if provenance.owner.is_some()
&& provenance.sequence.as_deref().is_some_and(|reference| {
crate::schema::dependencies::rewrites::stored_relation_reference_matches(
reference, target,
)
})
{
provenance.owner = None;
changed = true;
}
}
changed
}
pub fn bind_declared_sequence_owners(
names: &dyn StoredSequenceNames,
catalog: &dyn super::ownership::SequenceOwnerCatalog,
table_name: &str,
table_object_id: [u8; 16],
columns: &[ColumnDef],
) -> Result<Vec<BoundImplicitSequenceOwner>, crate::SQLError> {
let mut bindings = bind_implicit_sequence_owners(names, table_name, table_object_id, columns)
.map_err(crate::SQLError::Internal)?;
let relation =
RelationIdentity::from_legacy_name(table_name).map_err(crate::SQLError::Internal)?;
for column in columns {
let Some(provenance) = column.auto_increment.as_ref() else {
continue;
};
let (Some(owner), Some(sequence)) = (&provenance.owner, &provenance.sequence) else {
continue;
};
if stored_owner_names_current(&relation, column, owner) {
continue;
}
let sequence = names
.stored_sequence_name(sequence)
.map_err(crate::SQLError::Internal)?;
let ownership = crate::ast::SequenceOwnership::Column {
table: owner.table.clone(),
column: owner.column.clone(),
};
let Some(identity) = super::ownership::bind_sequence_owner(catalog, &sequence, &ownership)?
else {
return Err(crate::SQLError::Internal(
"declared sequence owner disappeared".into(),
));
};
bindings.push(BoundImplicitSequenceOwner {
sequence,
table_object_id: identity.table_object_id,
column_object_id: identity.column_object_id,
identity: provenance.is_identity(),
});
}
Ok(bindings)
}
pub fn identity_column_sequence(
catalog: &dyn crate::semantics::partition::PartitionCatalog,
table: &str,
column: &ColumnDef,
) -> Result<Option<String>, crate::SQLError> {
use crate::SQLError;
let Some(provenance) = column
.auto_increment
.as_ref()
.filter(|provenance| provenance.is_identity())
else {
return Ok(None);
};
let missing = || SQLError::Routine {
sqlstate: "XX000".into(),
message: "no owned sequence found".into(),
};
let owner = provenance.owner.as_ref().ok_or_else(missing)?;
let mut current = table.to_string();
let mut visited = std::collections::BTreeSet::new();
loop {
let relation = RelationIdentity::from_legacy_name(¤t).map_err(SQLError::Internal)?;
if stored_owner_names_current(&relation, column, owner) {
break;
}
if !visited.insert(current.clone()) {
return Err(SQLError::Internal(
"cycle in identity sequence ancestry".into(),
));
}
let hierarchy = catalog
.try_table_hierarchy(¤t)
.map_err(SQLError::Internal)?;
if !hierarchy.is_partition() {
return Err(missing());
}
current.clone_from(hierarchy.parents.first().ok_or_else(missing)?);
}
provenance.sequence.clone().map(Some).ok_or_else(missing)
}
#[cfg(test)]
mod tests;