Record encoding v2¶
Status: default for new inserts when all schema field definitions are single-segment top-level paths. Use v3 when the schema includes multi-segment FieldPaths.
Record payloads live in SegmentType::Record segment bodies. All integers are little-endian.
Wire layout¶
| Field | Type | Notes |
|---|---|---|
payload_version |
u16 |
2 |
collection_id |
u32 |
|
schema_version |
u32 |
Must match catalog |
op |
u8 |
1 = insert, 2 = replace, 3 = delete |
pk |
tagged primitive | Same tags as v1 |
field_count |
u32 |
Non-PK fields in schema order (excluding PK) |
values |
repeated | Each value: RowValue encoding per field Type |
For op = 3, field_count must be 0.
Primary key¶
The primary key remains a primitive type (bool through timestamp), encoded as a tagged scalar (v1 tags 0–7).
Optional semantics¶
- Non-optional fields: must be present;
RowValue::Noneis rejected for non-optional scalars. Optional<T>: field may be omitted from the row map, or encoded as absent:u80 = absent (no following bytes)u81 = present, thenRowValueforT
There is no SQL NULL vs omitted distinction in v2.
RowValue encoding¶
Type-driven recursive encoding (encode_row_value / decode_row_value):
Primitives¶
Same tagged scalar layout as v1 (tags 0–7).
Optional(inner)¶
| Field | Type |
|---|---|
presence |
u8 (0 = absent, 1 = present) |
value |
present only: RowValue for inner |
List(inner)¶
| Field | Type |
|---|---|
count |
u32 |
elements |
count × RowValue for inner |
Object(nested fields)¶
For each nested FieldDef in declaration order:
| Field | Type |
|---|---|
value |
RowValue for that field’s Type |
Nested object keys are the first path segment of each nested FieldDef (top-level keys inside the object map).
Enum(variants)¶
Encoded as a string tagged scalar (tag 4 + UTF-8 bytes). Content must be one of variants.
Implementation: crates/modelvault-core/src/record/row_value.rs.
Compatibility¶
Replay accepts v1, v2, and v3 in one file. The decoder branches on the leading u16 payload version.