use super::{
SchemaDdlAcceptedSnapshotDerivation, SchemaDdlMutationAdmission,
SchemaDdlMutationAdmissionError, SchemaDdlMutationTarget,
};
use crate::db::schema::{
AcceptedSchemaSnapshot, FieldId, PersistedFieldSnapshot, PersistedSchemaSnapshot,
SchemaFieldDefault, SchemaFieldSlot, SchemaRowLayout,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct SchemaFieldAdditionTarget {
field_id: FieldId,
name: String,
slot: SchemaFieldSlot,
}
impl SchemaFieldAdditionTarget {
#[must_use]
fn from_field(field: &PersistedFieldSnapshot) -> Self {
Self {
field_id: field.id(),
name: field.name().to_string(),
slot: field.slot(),
}
}
#[must_use]
pub(in crate::db) const fn field_id(&self) -> FieldId {
self.field_id
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub(in crate::db) const fn slot(&self) -> SchemaFieldSlot {
self.slot
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct SchemaFieldDropTarget {
field_id: FieldId,
name: String,
slot: SchemaFieldSlot,
}
impl SchemaFieldDropTarget {
#[must_use]
fn from_field(field: &PersistedFieldSnapshot) -> Self {
Self {
field_id: field.id(),
name: field.name().to_string(),
slot: field.slot(),
}
}
#[must_use]
pub(in crate::db) const fn field_id(&self) -> FieldId {
self.field_id
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub(in crate::db) const fn slot(&self) -> SchemaFieldSlot {
self.slot
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct SchemaFieldDefaultTarget {
field_id: FieldId,
name: String,
}
impl SchemaFieldDefaultTarget {
#[must_use]
fn from_field(field: &PersistedFieldSnapshot) -> Self {
Self {
field_id: field.id(),
name: field.name().to_string(),
}
}
#[must_use]
pub(in crate::db) const fn field_id(&self) -> FieldId {
self.field_id
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct SchemaFieldNullabilityTarget {
field_id: FieldId,
name: String,
}
impl SchemaFieldNullabilityTarget {
#[must_use]
fn from_field(field: &PersistedFieldSnapshot) -> Self {
Self {
field_id: field.id(),
name: field.name().to_string(),
}
}
#[must_use]
pub(in crate::db) const fn field_id(&self) -> FieldId {
self.field_id
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct SchemaFieldRenameTarget {
field_id: FieldId,
old_name: String,
new_name: String,
}
impl SchemaFieldRenameTarget {
#[must_use]
fn from_fields(before: &PersistedFieldSnapshot, after: &PersistedFieldSnapshot) -> Self {
Self {
field_id: before.id(),
old_name: before.name().to_string(),
new_name: after.name().to_string(),
}
}
#[must_use]
pub(in crate::db) const fn field_id(&self) -> FieldId {
self.field_id
}
#[must_use]
pub(in crate::db) const fn old_name(&self) -> &str {
self.old_name.as_str()
}
#[must_use]
pub(in crate::db) const fn new_name(&self) -> &str {
self.new_name.as_str()
}
}
pub(in crate::db) fn admit_sql_ddl_field_addition_candidate(
field: &PersistedFieldSnapshot,
) -> SchemaDdlMutationAdmission {
SchemaDdlMutationAdmission {
target: SchemaDdlMutationTarget::FieldAddition(SchemaFieldAdditionTarget::from_field(
field,
)),
}
}
#[must_use]
pub(in crate::db) fn admit_sql_ddl_field_drop_candidate(
field: &PersistedFieldSnapshot,
) -> SchemaDdlMutationAdmission {
SchemaDdlMutationAdmission {
target: SchemaDdlMutationTarget::FieldDrop(SchemaFieldDropTarget::from_field(field)),
}
}
pub(in crate::db) fn admit_sql_ddl_field_default_candidate(
field: &PersistedFieldSnapshot,
) -> SchemaDdlMutationAdmission {
SchemaDdlMutationAdmission {
target: SchemaDdlMutationTarget::FieldDefaultChange(SchemaFieldDefaultTarget::from_field(
field,
)),
}
}
#[must_use]
pub(in crate::db) fn admit_sql_ddl_field_nullability_candidate(
field: &PersistedFieldSnapshot,
) -> SchemaDdlMutationAdmission {
SchemaDdlMutationAdmission {
target: SchemaDdlMutationTarget::FieldNullabilityChange(
SchemaFieldNullabilityTarget::from_field(field),
),
}
}
#[must_use]
pub(in crate::db) fn admit_sql_ddl_field_rename_candidate(
before: &PersistedFieldSnapshot,
after: &PersistedFieldSnapshot,
) -> SchemaDdlMutationAdmission {
SchemaDdlMutationAdmission {
target: SchemaDdlMutationTarget::FieldRename(SchemaFieldRenameTarget::from_fields(
before, after,
)),
}
}
pub(in crate::db) fn resolve_sql_ddl_field_drop_dependent_index(
accepted_before: &AcceptedSchemaSnapshot,
field_id: FieldId,
) -> Option<String> {
accepted_before
.persisted_snapshot()
.indexes()
.iter()
.find(|index| index.key().references_field(field_id))
.map(|index| index.name().to_string())
}
pub(in crate::db) fn derive_sql_ddl_field_addition_accepted_after(
accepted_before: &AcceptedSchemaSnapshot,
field: PersistedFieldSnapshot,
) -> Result<SchemaDdlAcceptedSnapshotDerivation, SchemaDdlMutationAdmissionError> {
let before = accepted_before.persisted_snapshot();
let mut fields = before.fields().to_vec();
fields.push(field.clone());
let mut field_to_slot = before.row_layout().field_to_slot().to_vec();
field_to_slot.push((field.id(), field.slot()));
let persisted_after = PersistedSchemaSnapshot::new_with_primary_key_fields_and_indexes(
before.version(),
before.entity_path().to_string(),
before.entity_name().to_string(),
before.primary_key_field_ids().to_vec(),
SchemaRowLayout::new_with_retired_slots(
before.row_layout().version(),
field_to_slot,
before.row_layout().retired_field_slots().to_vec(),
),
fields,
before.indexes().to_vec(),
);
let accepted_after = AcceptedSchemaSnapshot::try_new(persisted_after)
.map_err(|_| SchemaDdlMutationAdmissionError::AcceptedAfterRejected)?;
let admission = admit_sql_ddl_field_addition_candidate(&field);
Ok(SchemaDdlAcceptedSnapshotDerivation {
accepted_after,
admission,
})
}
pub(in crate::db) fn derive_sql_ddl_field_drop_accepted_after(
accepted_before: &AcceptedSchemaSnapshot,
field_name: &str,
) -> Result<SchemaDdlAcceptedSnapshotDerivation, SchemaDdlMutationAdmissionError> {
let before = accepted_before.persisted_snapshot();
let before_field = before
.fields()
.iter()
.find(|field| field.name() == field_name)
.ok_or(SchemaDdlMutationAdmissionError::UnsupportedExecutionPath)?;
let fields = before
.fields()
.iter()
.filter(|field| field.id() != before_field.id())
.cloned()
.collect();
let row_layout = before
.row_layout()
.clone_retiring_field(before_field.id())
.ok_or(SchemaDdlMutationAdmissionError::UnsupportedExecutionPath)?;
let persisted_after = PersistedSchemaSnapshot::new_with_primary_key_fields_and_indexes(
before.version(),
before.entity_path().to_string(),
before.entity_name().to_string(),
before.primary_key_field_ids().to_vec(),
row_layout,
fields,
before.indexes().to_vec(),
);
let accepted_after = AcceptedSchemaSnapshot::try_new(persisted_after)
.map_err(|_| SchemaDdlMutationAdmissionError::AcceptedAfterRejected)?;
let admission = admit_sql_ddl_field_drop_candidate(before_field);
Ok(SchemaDdlAcceptedSnapshotDerivation {
accepted_after,
admission,
})
}
pub(in crate::db) fn derive_sql_ddl_field_default_accepted_after(
accepted_before: &AcceptedSchemaSnapshot,
field_name: &str,
default: SchemaFieldDefault,
) -> Result<SchemaDdlAcceptedSnapshotDerivation, SchemaDdlMutationAdmissionError> {
let before = accepted_before.persisted_snapshot();
let before_field = before
.fields()
.iter()
.find(|field| field.name() == field_name)
.ok_or(SchemaDdlMutationAdmissionError::UnsupportedExecutionPath)?;
let fields = before
.fields()
.iter()
.map(|field| {
if field.id() == before_field.id() {
field.clone_with_default(default.clone())
} else {
field.clone()
}
})
.collect();
let persisted_after = PersistedSchemaSnapshot::new_with_primary_key_fields_and_indexes(
before.version(),
before.entity_path().to_string(),
before.entity_name().to_string(),
before.primary_key_field_ids().to_vec(),
before.row_layout().clone(),
fields,
before.indexes().to_vec(),
);
let accepted_after = AcceptedSchemaSnapshot::try_new(persisted_after)
.map_err(|_| SchemaDdlMutationAdmissionError::AcceptedAfterRejected)?;
let after_field = accepted_after
.persisted_snapshot()
.fields()
.iter()
.find(|field| field.id() == before_field.id())
.ok_or(SchemaDdlMutationAdmissionError::AcceptedAfterRejected)?;
let admission = admit_sql_ddl_field_default_candidate(after_field);
Ok(SchemaDdlAcceptedSnapshotDerivation {
accepted_after,
admission,
})
}
pub(in crate::db) fn derive_sql_ddl_field_nullability_accepted_after(
accepted_before: &AcceptedSchemaSnapshot,
field_name: &str,
nullable: bool,
) -> Result<SchemaDdlAcceptedSnapshotDerivation, SchemaDdlMutationAdmissionError> {
let before = accepted_before.persisted_snapshot();
let before_field = before
.fields()
.iter()
.find(|field| field.name() == field_name)
.ok_or(SchemaDdlMutationAdmissionError::UnsupportedExecutionPath)?;
let fields = before
.fields()
.iter()
.map(|field| {
if field.id() == before_field.id() {
field.clone_with_nullable(nullable)
} else {
field.clone()
}
})
.collect();
let persisted_after = PersistedSchemaSnapshot::new_with_primary_key_fields_and_indexes(
before.version(),
before.entity_path().to_string(),
before.entity_name().to_string(),
before.primary_key_field_ids().to_vec(),
before.row_layout().clone(),
fields,
before.indexes().to_vec(),
);
let accepted_after = AcceptedSchemaSnapshot::try_new(persisted_after)
.map_err(|_| SchemaDdlMutationAdmissionError::AcceptedAfterRejected)?;
let after_field = accepted_after
.persisted_snapshot()
.fields()
.iter()
.find(|field| field.id() == before_field.id())
.ok_or(SchemaDdlMutationAdmissionError::AcceptedAfterRejected)?;
let admission = admit_sql_ddl_field_nullability_candidate(after_field);
Ok(SchemaDdlAcceptedSnapshotDerivation {
accepted_after,
admission,
})
}
pub(in crate::db) fn derive_sql_ddl_field_rename_accepted_after(
accepted_before: &AcceptedSchemaSnapshot,
old_name: &str,
new_name: &str,
) -> Result<SchemaDdlAcceptedSnapshotDerivation, SchemaDdlMutationAdmissionError> {
let before = accepted_before.persisted_snapshot();
let before_field = before
.fields()
.iter()
.find(|field| field.name() == old_name)
.ok_or(SchemaDdlMutationAdmissionError::UnsupportedExecutionPath)?;
let fields = before
.fields()
.iter()
.map(|field| {
if field.id() == before_field.id() {
field.clone_with_name(new_name.to_string())
} else {
field.clone()
}
})
.collect();
let indexes = before
.indexes()
.iter()
.map(|index| {
index.clone_with_renamed_field_path_root(
before_field.id(),
before_field.name(),
new_name,
)
})
.collect();
let persisted_after = PersistedSchemaSnapshot::new_with_primary_key_fields_and_indexes(
before.version(),
before.entity_path().to_string(),
before.entity_name().to_string(),
before.primary_key_field_ids().to_vec(),
before.row_layout().clone(),
fields,
indexes,
);
let accepted_after = AcceptedSchemaSnapshot::try_new(persisted_after)
.map_err(|_| SchemaDdlMutationAdmissionError::AcceptedAfterRejected)?;
let after_field = accepted_after
.persisted_snapshot()
.fields()
.iter()
.find(|field| field.id() == before_field.id())
.ok_or(SchemaDdlMutationAdmissionError::AcceptedAfterRejected)?;
let admission = admit_sql_ddl_field_rename_candidate(before_field, after_field);
Ok(SchemaDdlAcceptedSnapshotDerivation {
accepted_after,
admission,
})
}