use crate::{
db::schema::{
AcceptedEnumCatalog, AcceptedFieldKind, AcceptedSchemaRevision, AcceptedSchemaSnapshot,
AcceptedValueAdmissionContract, AcceptedValueCatalogHandle, AcceptedValueContract, FieldId,
PersistedNestedLeafSnapshot, PersistedRelationEdgeSnapshot, RowLayoutVersion,
SchemaFieldSlot, SchemaFieldWritePolicy, SchemaHistoricalFill, SchemaInsertDefault,
enum_catalog::EnumCatalogBuildError,
},
db::schema::{FieldStorageDecode, LeafCodec},
error::InternalError,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum AcceptedInsertOmissionPolicy {
NullIfMissing,
DefaultIfMissing,
Required,
}
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) const fn accepted_insert_field_is_omittable(
omission_policy: AcceptedInsertOmissionPolicy,
write_policy: SchemaFieldWritePolicy,
) -> bool {
!matches!(omission_policy, AcceptedInsertOmissionPolicy::Required)
|| write_policy.insert_generation().is_some()
|| write_policy.write_management().is_some()
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedRowLayoutRuntimeField<'a> {
field_id: FieldId,
name: &'a str,
slot: SchemaFieldSlot,
kind: &'a AcceptedFieldKind,
nested_leaves: &'a [PersistedNestedLeafSnapshot],
nullable: bool,
introduced_in_layout: RowLayoutVersion,
insert_default: &'a SchemaInsertDefault,
historical_fill: &'a SchemaHistoricalFill,
write_policy: SchemaFieldWritePolicy,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
insert_omission_policy: AcceptedInsertOmissionPolicy,
generated: bool,
}
impl<'a> AcceptedRowLayoutRuntimeField<'a> {
#[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) -> &'a str {
self.name
}
#[must_use]
pub(in crate::db) const fn slot(&self) -> SchemaFieldSlot {
self.slot
}
#[cfg(any(test, feature = "sql"))]
#[must_use]
pub(in crate::db) const fn kind(&self) -> &'a AcceptedFieldKind {
self.kind
}
#[must_use]
pub(in crate::db) const fn introduced_in_layout(&self) -> RowLayoutVersion {
self.introduced_in_layout
}
#[must_use]
pub(in crate::db) const fn insert_default(&self) -> &'a SchemaInsertDefault {
self.insert_default
}
#[must_use]
pub(in crate::db) const fn historical_fill(&self) -> &'a SchemaHistoricalFill {
self.historical_fill
}
#[must_use]
pub(in crate::db) const fn write_policy(&self) -> SchemaFieldWritePolicy {
self.write_policy
}
#[must_use]
pub(in crate::db) const fn insert_omission_policy(&self) -> AcceptedInsertOmissionPolicy {
self.insert_omission_policy
}
#[must_use]
pub(in crate::db) const fn generated(&self) -> bool {
self.generated
}
#[must_use]
pub(in crate::db) const fn decode_contract(&self) -> AcceptedFieldDecodeContract<'a> {
AcceptedFieldDecodeContract::new(
self.name,
self.kind,
self.nullable,
self.storage_decode,
self.leaf_codec,
)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedFieldDecodeContract<'a> {
field_name: &'a str,
kind: &'a AcceptedFieldKind,
nullable: bool,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
}
impl<'a> AcceptedFieldDecodeContract<'a> {
#[must_use]
pub(in crate::db) const fn new(
field_name: &'a str,
kind: &'a AcceptedFieldKind,
nullable: bool,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
) -> Self {
Self {
field_name,
kind,
nullable,
storage_decode,
leaf_codec,
}
}
#[must_use]
pub(in crate::db) const fn field_name(&self) -> &'a str {
self.field_name
}
#[must_use]
pub(in crate::db) const fn kind(&self) -> &'a AcceptedFieldKind {
self.kind
}
#[must_use]
pub(in crate::db) const fn nullable(&self) -> bool {
self.nullable
}
#[must_use]
pub(in crate::db) const fn storage_decode(&self) -> FieldStorageDecode {
self.storage_decode
}
#[must_use]
pub(in crate::db) const fn leaf_codec(&self) -> LeafCodec {
self.leaf_codec
}
#[must_use]
pub(in crate::db) fn uses_canonical_value_wire(&self) -> bool {
self.kind.requires_canonical_value_wire()
|| matches!(self.storage_decode, FieldStorageDecode::CatalogValue)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedFieldPersistenceContract<'a> {
field: AcceptedFieldDecodeContract<'a>,
admission_contract: AcceptedValueAdmissionContract<'a>,
}
impl<'a> AcceptedFieldPersistenceContract<'a> {
pub(in crate::db) fn new(
value_catalog: &'a AcceptedValueCatalogHandle,
field: AcceptedFieldDecodeContract<'a>,
) -> Result<Self, EnumCatalogBuildError> {
let value_contract = AcceptedValueContract::from_accepted_field(
value_catalog,
field.kind(),
field.storage_decode(),
)?;
Ok(Self {
field,
admission_contract: AcceptedValueAdmissionContract::owned(
value_catalog,
value_contract,
field.nullable(),
),
})
}
#[cfg(test)]
pub(in crate::db) fn new_for_tests(
value_catalog: &'a AcceptedValueCatalogHandle,
field: AcceptedFieldDecodeContract<'a>,
) -> Result<Self, EnumCatalogBuildError> {
Self::new(value_catalog, field)
}
#[must_use]
pub(in crate::db) const fn field(&self) -> AcceptedFieldDecodeContract<'a> {
self.field
}
#[must_use]
pub(in crate::db) const fn admission_contract(&self) -> &AcceptedValueAdmissionContract<'a> {
&self.admission_contract
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct OwnedAcceptedFieldDecodeContract {
field_name: String,
kind: AcceptedFieldKind,
nullable: bool,
storage_decode: FieldStorageDecode,
leaf_codec: LeafCodec,
write_policy: SchemaFieldWritePolicy,
insert_omission_policy: AcceptedInsertOmissionPolicy,
introduced_in_layout: RowLayoutVersion,
insert_default: SchemaInsertDefault,
historical_fill: SchemaHistoricalFill,
generated: bool,
}
impl OwnedAcceptedFieldDecodeContract {
#[must_use]
fn from_runtime_field(field: &AcceptedRowLayoutRuntimeField<'_>) -> Self {
let contract = field.decode_contract();
Self {
field_name: contract.field_name().to_string(),
kind: contract.kind().clone(),
nullable: contract.nullable(),
storage_decode: contract.storage_decode(),
leaf_codec: contract.leaf_codec(),
write_policy: field.write_policy(),
insert_omission_policy: field.insert_omission_policy(),
introduced_in_layout: field.introduced_in_layout(),
insert_default: field.insert_default().clone(),
historical_fill: field.historical_fill().clone(),
generated: field.generated(),
}
}
#[must_use]
pub(in crate::db) const fn decode_contract(&self) -> AcceptedFieldDecodeContract<'_> {
AcceptedFieldDecodeContract::new(
self.field_name.as_str(),
&self.kind,
self.nullable,
self.storage_decode,
self.leaf_codec,
)
}
#[must_use]
pub(in crate::db) const fn insert_omission_policy(&self) -> AcceptedInsertOmissionPolicy {
self.insert_omission_policy
}
#[must_use]
pub(in crate::db) const fn write_policy(&self) -> SchemaFieldWritePolicy {
self.write_policy
}
#[must_use]
pub(in crate::db) const fn introduced_in_layout(&self) -> RowLayoutVersion {
self.introduced_in_layout
}
#[must_use]
pub(in crate::db) const fn insert_default(&self) -> &SchemaInsertDefault {
&self.insert_default
}
#[must_use]
pub(in crate::db) const fn historical_fill(&self) -> &SchemaHistoricalFill {
&self.historical_fill
}
#[must_use]
pub(in crate::db) const fn field_name(&self) -> &str {
self.field_name.as_str()
}
#[must_use]
pub(in crate::db) const fn kind(&self) -> &AcceptedFieldKind {
&self.kind
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct OwnedAcceptedRelationEdgeContract {
name: String,
physical_generation: u64,
target_path: String,
local_field_slots: Vec<usize>,
}
impl OwnedAcceptedRelationEdgeContract {
fn from_runtime_relation_edge(
relation: &PersistedRelationEdgeSnapshot,
fields: &[AcceptedRowLayoutRuntimeField<'_>],
) -> Result<Self, InternalError> {
let mut local_field_slots = Vec::with_capacity(relation.local_field_ids().len());
for field_id in relation.local_field_ids() {
let Some(field) = fields.iter().find(|field| field.field_id() == *field_id) else {
return Err(InternalError::store_invariant());
};
local_field_slots.push(usize::from(field.slot().get()));
}
Ok(Self {
name: relation.name().to_string(),
physical_generation: relation.physical_generation(),
target_path: relation.target_path().to_string(),
local_field_slots,
})
}
#[must_use]
pub(in crate::db) const fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub(in crate::db) const fn physical_generation(&self) -> u64 {
self.physical_generation
}
#[must_use]
pub(in crate::db) const fn target_path(&self) -> &str {
self.target_path.as_str()
}
#[must_use]
pub(in crate::db) const fn local_field_slots(&self) -> &[usize] {
self.local_field_slots.as_slice()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedRowDecodeContract {
current_layout_version: RowLayoutVersion,
history_floor: RowLayoutVersion,
required_slot_count: usize,
primary_key_slot_index: usize,
primary_key_slot_indices: Vec<usize>,
fields_by_slot: Vec<Option<OwnedAcceptedFieldDecodeContract>>,
relation_edges: Vec<OwnedAcceptedRelationEdgeContract>,
value_catalog: AcceptedValueCatalogHandle,
}
impl AcceptedRowDecodeContract {
fn from_runtime_contract(
descriptor: &AcceptedRowLayoutRuntimeContract<'_>,
value_catalog: AcceptedValueCatalogHandle,
) -> Self {
let mut fields_by_slot = vec![None; descriptor.required_slot_count()];
for field in descriptor.fields() {
fields_by_slot[usize::from(field.slot().get())] =
Some(OwnedAcceptedFieldDecodeContract::from_runtime_field(field));
}
Self {
current_layout_version: descriptor.current_layout_version(),
history_floor: descriptor.history_floor(),
required_slot_count: descriptor.required_slot_count(),
primary_key_slot_index: descriptor.first_primary_key_slot_index(),
primary_key_slot_indices: descriptor.primary_key_slot_indices().to_vec(),
fields_by_slot,
relation_edges: descriptor.relation_edges().to_vec(),
value_catalog,
}
}
#[must_use]
pub(in crate::db) const fn required_slot_count(&self) -> usize {
self.required_slot_count
}
#[must_use]
pub(in crate::db) const fn current_layout_version(&self) -> RowLayoutVersion {
self.current_layout_version
}
pub(in crate::db) fn expected_slot_count(
&self,
version: RowLayoutVersion,
) -> Result<usize, InternalError> {
if version < self.history_floor || version > self.current_layout_version {
return Err(InternalError::persisted_row_layout_outside_accepted_window());
}
Ok(self
.fields_by_slot
.iter()
.filter_map(Option::as_ref)
.filter(|field| field.introduced_in_layout() <= version)
.count())
}
#[must_use]
pub(in crate::db) const fn first_primary_key_slot_index(&self) -> usize {
self.primary_key_slot_index
}
#[must_use]
pub(in crate::db) const fn primary_key_slot_indices(&self) -> &[usize] {
self.primary_key_slot_indices.as_slice()
}
#[must_use]
pub(in crate::db) const fn relation_edges(&self) -> &[OwnedAcceptedRelationEdgeContract] {
self.relation_edges.as_slice()
}
#[must_use]
pub(in crate::db) fn enum_catalog(&self) -> &AcceptedEnumCatalog {
self.value_catalog.enum_catalog()
}
#[must_use]
pub(in crate::db) const fn value_catalog_handle(&self) -> &AcceptedValueCatalogHandle {
&self.value_catalog
}
#[must_use]
pub(in crate::db) const fn accepted_schema_revision(&self) -> AcceptedSchemaRevision {
self.value_catalog.revision()
}
#[must_use]
pub(in crate::db) fn field_for_slot(
&self,
slot: usize,
) -> Option<&OwnedAcceptedFieldDecodeContract> {
self.fields_by_slot.get(slot)?.as_ref()
}
pub(in crate::db) fn required_field_for_slot(
&self,
entity_path: &str,
slot: usize,
) -> Result<&OwnedAcceptedFieldDecodeContract, InternalError> {
self.field_for_slot(slot).ok_or_else(|| {
InternalError::persisted_row_slot_lookup_out_of_bounds(entity_path, slot)
})
}
pub(in crate::db) fn required_field_persistence_contract(
&self,
entity_path: &str,
slot: usize,
) -> Result<AcceptedFieldPersistenceContract<'_>, InternalError> {
let field = self.required_field_for_slot(entity_path, slot)?;
AcceptedFieldPersistenceContract::new(self.value_catalog_handle(), field.decode_contract())
.map_err(|_| InternalError::persisted_row_field_encode_internal(field.field_name()))
}
}
#[derive(Debug, Eq, PartialEq)]
pub(in crate::db) struct AcceptedRowLayoutRuntimeContract<'a> {
current_layout_version: RowLayoutVersion,
history_floor: RowLayoutVersion,
required_slot_count: usize,
primary_key_names: Vec<&'a str>,
primary_key_slot_indices: Vec<usize>,
fields: Vec<AcceptedRowLayoutRuntimeField<'a>>,
relation_edges: Vec<OwnedAcceptedRelationEdgeContract>,
}
impl<'a> AcceptedRowLayoutRuntimeContract<'a> {
pub(in crate::db) fn from_accepted_schema(
accepted: &'a AcceptedSchemaSnapshot,
) -> Result<Self, InternalError> {
let snapshot = accepted.persisted_snapshot();
let row_layout = snapshot.row_layout();
let mut required_slot_count = 0usize;
let mut fields = Vec::with_capacity(snapshot.fields().len());
for field in snapshot.fields() {
let Some(slot) = row_layout.slot_for_field(field.id()) else {
return Err(InternalError::store_invariant());
};
let slot_end = usize::from(slot.get()).saturating_add(1);
required_slot_count = required_slot_count.max(slot_end);
fields.push(AcceptedRowLayoutRuntimeField {
field_id: field.id(),
name: field.name(),
slot,
kind: field.kind(),
nested_leaves: field.nested_leaves(),
nullable: field.nullable(),
introduced_in_layout: field.introduced_in_layout(),
insert_default: field.insert_default(),
historical_fill: field.historical_fill(),
write_policy: field.write_policy(),
storage_decode: field.storage_decode(),
leaf_codec: field.leaf_codec(),
insert_omission_policy: accepted_insert_omission_policy(
field.nullable(),
field.insert_default(),
),
generated: field.generated(),
});
}
let mut primary_key_names = Vec::with_capacity(snapshot.primary_key_field_ids().len());
let mut primary_key_slot_indices =
Vec::with_capacity(snapshot.primary_key_field_ids().len());
for primary_key_field_id in snapshot.primary_key_field_ids() {
let Some(primary_key_field) = fields
.iter()
.find(|field| field.field_id() == *primary_key_field_id)
else {
return Err(InternalError::store_invariant());
};
primary_key_names.push(primary_key_field.name());
primary_key_slot_indices.push(usize::from(primary_key_field.slot().get()));
}
let relation_edges = snapshot
.relations()
.iter()
.map(|relation| {
OwnedAcceptedRelationEdgeContract::from_runtime_relation_edge(relation, &fields)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self {
current_layout_version: row_layout.current_version(),
history_floor: row_layout.history_floor(),
required_slot_count,
primary_key_names,
primary_key_slot_indices,
fields,
relation_edges,
})
}
#[must_use]
pub(in crate::db) const fn current_layout_version(&self) -> RowLayoutVersion {
self.current_layout_version
}
#[must_use]
pub(in crate::db) const fn history_floor(&self) -> RowLayoutVersion {
self.history_floor
}
#[must_use]
pub(in crate::db) const fn required_slot_count(&self) -> usize {
self.required_slot_count
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) const fn primary_key_names(&self) -> &[&'a str] {
self.primary_key_names.as_slice()
}
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn is_primary_key_field_name(&self, field_name: &str) -> bool {
self.primary_key_names.contains(&field_name)
}
#[must_use]
pub(in crate::db) fn first_primary_key_slot_index(&self) -> usize {
self.primary_key_slot_indices[0]
}
#[must_use]
pub(in crate::db) const fn primary_key_slot_indices(&self) -> &[usize] {
self.primary_key_slot_indices.as_slice()
}
#[must_use]
pub(in crate::db) const fn relation_edges(&self) -> &[OwnedAcceptedRelationEdgeContract] {
self.relation_edges.as_slice()
}
#[must_use]
pub(in crate::db) const fn fields(&self) -> &[AcceptedRowLayoutRuntimeField<'a>] {
self.fields.as_slice()
}
#[must_use]
pub(in crate::db) fn field_for_slot_index(
&self,
slot: usize,
) -> Option<&AcceptedRowLayoutRuntimeField<'a>> {
self.fields
.iter()
.find(|field| usize::from(field.slot().get()) == slot)
}
#[must_use]
pub(in crate::db) fn field_by_name(
&self,
name: &str,
) -> Option<&AcceptedRowLayoutRuntimeField<'a>> {
self.fields.iter().find(|field| field.name() == name)
}
#[must_use]
pub(in crate::db) fn field_slot_index_by_name(&self, name: &str) -> Option<usize> {
self.field_by_name(name)
.map(|field| usize::from(field.slot().get()))
}
#[must_use]
pub(in crate::db) fn row_decode_contract(
&self,
value_catalog: AcceptedValueCatalogHandle,
) -> AcceptedRowDecodeContract {
AcceptedRowDecodeContract::from_runtime_contract(self, value_catalog)
}
}
const fn accepted_insert_omission_policy(
nullable: bool,
default: &SchemaInsertDefault,
) -> AcceptedInsertOmissionPolicy {
match (nullable, default) {
(true, SchemaInsertDefault::None) => AcceptedInsertOmissionPolicy::NullIfMissing,
(false, SchemaInsertDefault::None) => AcceptedInsertOmissionPolicy::Required,
(_, SchemaInsertDefault::SlotPayload(_)) => AcceptedInsertOmissionPolicy::DefaultIfMissing,
}
}