use super::SchemaMutationRequest;
use crate::db::schema::{PersistedFieldSnapshot, PersistedIndexSnapshot, PersistedSchemaSnapshot};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::schema) enum SchemaMutationDelta<'a> {
AppendOnlyFields(&'a [PersistedFieldSnapshot]),
AddFieldPathIndex(&'a PersistedIndexSnapshot),
AddExpressionIndex(&'a PersistedIndexSnapshot),
ExactMatch,
Incompatible,
}
pub(in crate::db::schema) fn classify_schema_mutation_delta<'a>(
actual: &PersistedSchemaSnapshot,
expected: &'a PersistedSchemaSnapshot,
) -> SchemaMutationDelta<'a> {
if actual == expected {
return SchemaMutationDelta::ExactMatch;
}
if let Some(fields) = append_only_additive_fields(actual, expected) {
return SchemaMutationDelta::AppendOnlyFields(fields);
}
if let Some(index) = single_added_index(actual, expected)
&& SchemaMutationRequest::from_accepted_field_path_index(index).is_ok()
{
return SchemaMutationDelta::AddFieldPathIndex(index);
}
if let Some(index) = single_added_index(actual, expected)
&& SchemaMutationRequest::from_accepted_expression_index(index).is_ok()
{
return SchemaMutationDelta::AddExpressionIndex(index);
}
SchemaMutationDelta::Incompatible
}
pub(in crate::db::schema) fn schema_mutation_request_for_snapshots<'a>(
actual: &PersistedSchemaSnapshot,
expected: &'a PersistedSchemaSnapshot,
) -> SchemaMutationRequest<'a> {
SchemaMutationRequest::from(classify_schema_mutation_delta(actual, expected))
}
impl<'a> From<SchemaMutationDelta<'a>> for SchemaMutationRequest<'a> {
fn from(delta: SchemaMutationDelta<'a>) -> Self {
match delta {
SchemaMutationDelta::AppendOnlyFields(fields) => Self::AppendOnlyFields(fields),
SchemaMutationDelta::AddFieldPathIndex(index) => {
Self::from_accepted_field_path_index(index).unwrap_or(Self::Incompatible)
}
SchemaMutationDelta::AddExpressionIndex(index) => {
Self::from_accepted_expression_index(index).unwrap_or(Self::Incompatible)
}
SchemaMutationDelta::ExactMatch => Self::ExactMatch,
SchemaMutationDelta::Incompatible => Self::Incompatible,
}
}
}
fn append_only_additive_fields<'a>(
actual: &PersistedSchemaSnapshot,
expected: &'a PersistedSchemaSnapshot,
) -> Option<&'a [PersistedFieldSnapshot]> {
if actual.fields().len() >= expected.fields().len()
|| actual.row_layout().field_to_slot().len() >= expected.row_layout().field_to_slot().len()
{
return None;
}
if !actual
.fields()
.iter()
.zip(expected.fields())
.all(|(actual_field, expected_field)| actual_field == expected_field)
{
return None;
}
if !actual
.row_layout()
.field_to_slot()
.iter()
.zip(expected.row_layout().field_to_slot())
.all(|(actual_pair, expected_pair)| actual_pair == expected_pair)
{
return None;
}
Some(&expected.fields()[actual.fields().len()..])
}
fn single_added_index<'a>(
actual: &PersistedSchemaSnapshot,
expected: &'a PersistedSchemaSnapshot,
) -> Option<&'a PersistedIndexSnapshot> {
if actual.entity_path() != expected.entity_path()
|| actual.entity_name() != expected.entity_name()
|| actual.primary_key_field_ids() != expected.primary_key_field_ids()
|| actual.row_layout().field_to_slot() != expected.row_layout().field_to_slot()
|| actual.fields() != expected.fields()
|| expected.indexes().len() != actual.indexes().len().saturating_add(1)
{
return None;
}
if !actual
.indexes()
.iter()
.zip(expected.indexes())
.all(|(actual_index, expected_index)| actual_index == expected_index)
{
return None;
}
expected.indexes().last()
}