1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! Module: db::schema::layout
//! Responsibility: schema-owned row-layout identity contracts.
//! Does not own: executor row decoding or persisted schema reconciliation.
//! Boundary: maps durable schema field identity to physical row slots.
use crate::db::schema::FieldId;
///
/// SchemaVersion
///
/// Monotonic version for one entity's live schema snapshot.
/// It is intentionally schema-owned rather than executor-owned so layout
/// changes can be reconciled before any row decode path consumes them.
///
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(in crate::db) struct SchemaVersion(u32);
impl SchemaVersion {
/// Build one schema version from trusted persisted metadata.
#[must_use]
pub(in crate::db) const fn new(raw: u32) -> Self {
Self(raw)
}
/// Return the first live schema version for a newly initialized entity.
#[must_use]
pub(in crate::db) const fn initial() -> Self {
Self(1)
}
/// Return the raw persisted version value.
#[must_use]
pub(in crate::db) const fn get(self) -> u32 {
self.0
}
}
///
/// SchemaFieldSlot
///
/// Physical row slot assigned to one live schema field.
/// This wrapper keeps slot order separate from durable `FieldId` identity and
/// prevents later reconciliation code from passing bare `usize` values around.
///
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(in crate::db) struct SchemaFieldSlot(u16);
impl SchemaFieldSlot {
/// Build one schema field slot from trusted persisted metadata.
#[must_use]
pub(in crate::db) const fn new(raw: u16) -> Self {
Self(raw)
}
/// Build one schema field slot from a generated field index.
#[must_use]
pub(in crate::db) fn from_generated_index(index: usize) -> Self {
let slot = u16::try_from(index).expect("schema layout invariant");
Self(slot)
}
/// Return the raw slot value used by the persisted row layout.
#[must_use]
pub(in crate::db) const fn get(self) -> u16 {
self.0
}
}
///
/// SchemaRowLayout
///
/// Schema-owned mapping from durable field IDs to physical row slots.
/// This is the persisted slot authority projected into the accepted runtime
/// decode and write contract.
///
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct SchemaRowLayout {
version: SchemaVersion,
field_to_slot: Vec<(FieldId, SchemaFieldSlot)>,
}
impl SchemaRowLayout {
/// Build one schema row layout from already-validated field-to-slot pairs.
#[must_use]
pub(in crate::db) const fn new(
version: SchemaVersion,
field_to_slot: Vec<(FieldId, SchemaFieldSlot)>,
) -> Self {
Self {
version,
field_to_slot,
}
}
/// Return the schema version associated with this layout.
#[must_use]
pub(in crate::db) const fn version(&self) -> SchemaVersion {
self.version
}
/// Return the durable field-ID to physical-slot mapping.
#[must_use]
pub(in crate::db) const fn field_to_slot(&self) -> &[(FieldId, SchemaFieldSlot)] {
self.field_to_slot.as_slice()
}
/// Clone this row layout with a new declared schema version while
/// preserving the dense active slot allocation.
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn clone_with_version(&self, version: SchemaVersion) -> Self {
Self::new(version, self.field_to_slot.clone())
}
/// Return the next dense physical slot index for additive field DDL.
#[must_use]
#[cfg(any(test, feature = "sql"))]
pub(in crate::db) fn next_unallocated_slot(&self) -> SchemaFieldSlot {
SchemaFieldSlot::from_generated_index(self.field_to_slot.len())
}
/// Return the current dense physical slot count.
#[must_use]
pub(in crate::db) const fn allocated_slot_count(&self) -> usize {
self.field_to_slot.len()
}
/// Resolve one durable field identity into its accepted physical row slot.
#[must_use]
pub(in crate::db) fn slot_for_field(&self, field_id: FieldId) -> Option<SchemaFieldSlot> {
self.field_to_slot
.iter()
.find_map(|(id, slot)| (*id == field_id).then_some(*slot))
}
}