use crate::db::schema::{
PersistedFieldSnapshot, PersistedSchemaSnapshot, SchemaHistoricalFill, SchemaRowLayout,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::schema) enum GeneratedAcceptedCandidateError {
RowLayoutVersionExhausted,
}
pub(in crate::db::schema) fn derive_generated_accepted_candidate(
accepted: &PersistedSchemaSnapshot,
generated: &PersistedSchemaSnapshot,
) -> Result<Option<PersistedSchemaSnapshot>, GeneratedAcceptedCandidateError> {
if let Some(candidate) = derive_generated_default_candidate(accepted, generated) {
return Ok(Some(candidate));
}
if generated.fields().len() <= accepted.fields().len()
|| generated.row_layout().field_to_slot().len()
<= accepted.row_layout().field_to_slot().len()
|| accepted.fields().len() != accepted.row_layout().field_to_slot().len()
|| generated.fields().len() != generated.row_layout().field_to_slot().len()
{
return Ok(None);
}
if !accepted
.row_layout()
.field_to_slot()
.iter()
.zip(generated.row_layout().field_to_slot())
.all(|(accepted_entry, generated_entry)| accepted_entry == generated_entry)
{
return Ok(None);
}
let mut fields = Vec::with_capacity(generated.fields().len());
for (accepted_field, generated_field) in accepted.fields().iter().zip(generated.fields()) {
let candidate = field_with_temporal_contract(
generated_field,
accepted_field.introduced_in_layout(),
accepted_field.historical_fill().clone(),
);
if &candidate != accepted_field {
return Ok(None);
}
fields.push(candidate);
}
let current_layout = accepted
.row_layout()
.current_version()
.checked_next()
.ok_or(GeneratedAcceptedCandidateError::RowLayoutVersionExhausted)?;
for generated_field in &generated.fields()[accepted.fields().len()..] {
let historical_fill = match generated_field.insert_default().slot_payload() {
Some(payload) => SchemaHistoricalFill::SlotPayload(payload.to_vec()),
None if generated_field.nullable() => SchemaHistoricalFill::Null,
None => return Ok(None),
};
fields.push(field_with_temporal_contract(
generated_field,
current_layout,
historical_fill,
));
}
Ok(Some(
PersistedSchemaSnapshot::new_with_primary_key_fields_and_indexes(
generated.version(),
generated.entity_path().to_string(),
generated.entity_name().to_string(),
generated.primary_key_field_ids().to_vec(),
SchemaRowLayout::new(
current_layout,
accepted.row_layout().history_floor(),
generated.row_layout().field_to_slot().to_vec(),
),
fields,
generated.indexes().to_vec(),
)
.with_constraint_id_allocator(accepted.constraint_id_allocator())
.with_relations(generated.relations().to_vec()),
))
}
fn derive_generated_default_candidate(
accepted: &PersistedSchemaSnapshot,
generated: &PersistedSchemaSnapshot,
) -> Option<PersistedSchemaSnapshot> {
if generated.fields().is_empty()
|| generated.fields().len() > accepted.fields().len()
|| accepted.entity_path() != generated.entity_path()
|| accepted.entity_name() != generated.entity_name()
|| accepted.primary_key_field_ids() != generated.primary_key_field_ids()
|| generated.row_layout().field_to_slot().len() != generated.fields().len()
|| !accepted
.row_layout()
.field_to_slot()
.iter()
.zip(generated.row_layout().field_to_slot())
.all(|(accepted_entry, generated_entry)| accepted_entry == generated_entry)
|| accepted.fields()[generated.fields().len()..]
.iter()
.any(PersistedFieldSnapshot::generated)
|| !generated
.indexes()
.iter()
.all(|index| accepted.indexes().contains(index))
|| !generated
.relations()
.iter()
.all(|relation| accepted.relations().contains(relation))
{
return None;
}
let mut fields = accepted.fields().to_vec();
let mut changed = false;
for (index, (accepted_field, generated_field)) in
accepted.fields().iter().zip(generated.fields()).enumerate()
{
if !accepted_field.generated() {
return None;
}
let candidate = field_with_temporal_contract(
generated_field,
accepted_field.introduced_in_layout(),
accepted_field.historical_fill().clone(),
);
if candidate.clone_with_insert_default(accepted_field.insert_default().clone())
!= *accepted_field
{
return None;
}
if candidate.insert_default() != accepted_field.insert_default() {
fields[index] = candidate;
changed = true;
}
}
changed.then(|| {
PersistedSchemaSnapshot::new_with_primary_key_fields_and_indexes(
generated.version(),
accepted.entity_path().to_string(),
accepted.entity_name().to_string(),
accepted.primary_key_field_ids().to_vec(),
accepted.row_layout().clone(),
fields,
accepted.indexes().to_vec(),
)
.with_constraint_id_allocator(accepted.constraint_id_allocator())
.with_relations(accepted.relations().to_vec())
})
}
fn field_with_temporal_contract(
field: &PersistedFieldSnapshot,
introduced_in_layout: crate::db::schema::RowLayoutVersion,
historical_fill: SchemaHistoricalFill,
) -> PersistedFieldSnapshot {
PersistedFieldSnapshot::new_with_write_policy_and_origin(
field.id(),
field.name().to_string(),
field.slot(),
field.kind().clone(),
field.nested_leaves().to_vec(),
field.nullable(),
introduced_in_layout,
field.insert_default().clone(),
historical_fill,
field.write_policy(),
field.origin(),
field.storage_decode(),
field.leaf_codec(),
)
}