use super::{
index_definition, invalid, BTreeMap, CatalogIndexRow, ColumnDef, IndexKey, RelationIdentity,
StorageBackendResult, TableConstraintSet,
};
pub(crate) fn bind(
rows: &BTreeMap<RelationIdentity, CatalogIndexRow>,
columns: &mut [ColumnDef],
constraints: &mut TableConstraintSet,
allow_new: bool,
) -> StorageBackendResult<bool> {
let mut changed = false;
for column in columns {
if let Some(reference) = &mut column.references {
let names = reference.column.iter().cloned().collect::<Vec<_>>();
changed |= bind_one(
rows,
&reference.table,
&names,
&mut reference.referenced_index,
&mut reference.referenced_key,
allow_new,
)?;
}
}
for key in &mut constraints.foreign_keys {
changed |= bind_one(
rows,
&key.ref_table,
&key.ref_columns,
&mut key.referenced_index,
&mut key.referenced_key,
allow_new,
)?;
}
for key in &mut constraints.hierarchy.partition_inherited_foreign_keys {
changed |= bind_one(
rows,
&key.ref_table,
&key.ref_columns,
&mut key.referenced_index,
&mut key.referenced_key,
allow_new,
)?;
}
Ok(changed)
}
fn bind_one(
rows: &BTreeMap<RelationIdentity, CatalogIndexRow>,
table: &str,
columns: &[String],
identity: &mut Option<[u8; 16]>,
name: &mut Option<String>,
allow_new: bool,
) -> StorageBackendResult<bool> {
if identity.is_none() && !allow_new {
return Err(invalid("foreign key has no referenced index incarnation"));
}
for row in rows.values().filter(|row| row.table_name == table) {
let definition = index_definition(row)?;
let index = definition
.catalog
.as_ref()
.ok_or_else(|| invalid("referenced index has no catalog address"))?;
if let Some(id) = identity {
if *id != index.identity.object_id {
continue;
}
} else if name.as_ref().is_some_and(|name| name != &row.relation.name) {
continue;
}
let keys = serde_json::from_str::<Vec<IndexKey>>(&row.columns_json)?;
if !definition.unique
|| definition.predicate.is_some()
|| keys.len() != columns.len()
|| !keys.iter().all(|key| {
key.column()
.is_some_and(|key| columns.iter().any(|column| column == key))
})
{
if identity.is_some() {
return Err(invalid("foreign key references an ineligible index"));
}
continue;
}
let changed = identity.is_none();
*identity = Some(index.identity.object_id);
if changed {
*name = Some(row.relation.name.clone());
}
return Ok(changed);
}
Err(invalid(format!(
"foreign key references a missing unique index on `{table}`"
)))
}