use crate::types::StoragePropertyDef;
use linkrs_core::types::EdgeStrategy;
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default,
)]
pub enum RecordForm {
Pure,
Bundled,
#[default]
Columnar,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default,
)]
pub enum RecordFormPreference {
Auto,
#[default]
Columnar,
Bundled,
}
pub(crate) const SINGLE_REQUIRES_COLUMNAR_MSG: &str =
"single edge strategy requires the columnar record form; adjust the strategy or keep the columnar form, see migration_plan/migrate_record_form";
pub(crate) const INLINE_FORM_SCHEMA_CHANGE_MSG: &str =
"schema change on an inline-form table requires a record-form rebuild (migrate_record_form or switch_record_form_online)";
pub(crate) const PURE_REQUIRES_ZERO_PROPERTIES_MSG: &str =
"pure record form requires zero properties; drop properties or keep the columnar form, see migration_plan/migrate_record_form";
pub(crate) const SCHEMA_CHANGE_PENDING_MSG: &str = "another schema change is already pending";
pub(crate) const NO_EDGES_STORED_MSG: &str = "no edges stored for this edge type";
pub(crate) const ROW_POSITION_CROSS_VARIANT_MSG: &str =
"row position must not cross variants; re-resolve by edge id";
pub const BUNDLED_RANK_REQUIRES_COLUMNAR_MSG: &str =
"nonzero rank requires the columnar record form; keep the columnar form, see migration_plan/migrate_record_form/switch_record_form_online";
pub fn inline_form_accepts_rank(record_form: RecordForm, rank: i64) -> bool {
match record_form {
RecordForm::Pure | RecordForm::Bundled => rank == 0,
RecordForm::Columnar => true,
}
}
pub fn validate_strategy_form(
strategy: EdgeStrategy,
record_form: RecordForm,
) -> linkrs_core::StorageResult<()> {
if strategy == EdgeStrategy::Single && record_form != RecordForm::Columnar {
return Err(linkrs_core::StorageError::invalid_operation(
SINGLE_REQUIRES_COLUMNAR_MSG.to_string(),
));
}
Ok(())
}
pub fn is_scalar_encodable(dt: &linkrs_core::DataType) -> bool {
use linkrs_core::DataType;
matches!(
dt,
DataType::Bool
| DataType::SmallInt
| DataType::Int
| DataType::BigInt
| DataType::Float
| DataType::Double
| DataType::Date
| DataType::Time
| DataType::DateTime
)
}
pub fn is_bundled_eligible(
properties: &[StoragePropertyDef],
oe_strategy: EdgeStrategy,
ie_strategy: EdgeStrategy,
) -> bool {
bundled_ineligibility_reason(properties, oe_strategy, ie_strategy).is_none()
}
pub fn bundled_ineligibility_reason(
properties: &[StoragePropertyDef],
oe_strategy: EdgeStrategy,
ie_strategy: EdgeStrategy,
) -> Option<String> {
if oe_strategy == EdgeStrategy::Single || ie_strategy == EdgeStrategy::Single {
return Some(SINGLE_REQUIRES_COLUMNAR_MSG.to_string());
}
if properties.len() != 1 {
return Some(
"bundled record form requires exactly one property; adjust the schema or keep the columnar form, see migration_plan/migrate_record_form"
.to_string(),
);
}
if !is_scalar_encodable(&properties[0].data_type) {
return Some(format!(
"property type {:?} cannot inline into the bundled form; keep the columnar form, see migration_plan/migrate_record_form",
properties[0].data_type
));
}
None
}
pub fn validate_record_form_target(
properties: &[StoragePropertyDef],
oe_strategy: EdgeStrategy,
ie_strategy: EdgeStrategy,
target: RecordForm,
) -> linkrs_core::StorageResult<()> {
validate_strategy_form(oe_strategy, target)?;
validate_strategy_form(ie_strategy, target)?;
match target {
RecordForm::Pure if !properties.is_empty() => {
return Err(linkrs_core::StorageError::invalid_operation(
PURE_REQUIRES_ZERO_PROPERTIES_MSG.to_string(),
));
}
RecordForm::Bundled => {
if let Some(reason) = bundled_ineligibility_reason(properties, oe_strategy, ie_strategy)
{
return Err(linkrs_core::StorageError::invalid_operation(reason));
}
}
RecordForm::Pure | RecordForm::Columnar => {}
}
Ok(())
}