High-level tables encode entities with BinPack. PackedTable stores one encoded entity as the engine value and builds keys from table prefixes, primary-key bytes, index values, and row-id metadata.
Supported Shapes
Section titled “Supported Shapes”| Shape | Notes |
|---|---|
bool | Encoded as one byte. |
| signed/unsigned integers | Fixed-width adapters. Primary keys and indexes use sortable encodings where needed. |
float, double | Fixed-width adapters; index values use sortable floating-point bytes. |
enum | Encoded through the underlying integer type. |
std::string | Length-prefixed bytes. |
std::string_view | Write adapter only. Do not model persisted fields as string_view if they must be decoded. |
std::vector<T>, std::array<T, N> | Element type must also be supported. |
std::vector<uint8_t> | Length-prefixed byte payload. |
std::map<K, V>, std::unordered_map<K, V> | Key and value types must be supported. |
std::optional<T> | Presence byte plus encoded value when present. |
std::pair<A, B>, std::tuple<Ts...> | Encoded element-by-element. |
| aggregate structs | Trivially copyable aggregates use a memcpy fast path; other aggregates use Boost.PFR field order. |
akkaradb::Ref<T> | Encodes the referenced row id. |
akkaradb::Immutable<T> | Encodes the wrapped value and decodes as sealed. |
Practical Rule
Section titled “Practical Rule”Treat entity layout as storage format. Renaming a table, changing a primary-key type, reordering fields, or changing field types should be handled as a migration.
Advanced
Section titled “Advanced”Compatibility Rules
Section titled “Compatibility Rules”BinPack is compact and direct. It is not a self-describing schema migration format.
Treat these as storage migrations:
- table rename
- primary-key type change
- field reorder in an aggregate
- field type change
- removing a field from an existing aggregate
- changing a
Ref<T>target's primary-key model - changing optional/null semantics used by foreign-key actions
Aggregate Layout
Section titled “Aggregate Layout”For non-trivial aggregate structs, Boost.PFR walks fields in declaration order.
struct UserV1 { uint64_t id; std::string name; uint32_t age;};Adding a field in the middle changes the byte layout for everything after it. For persisted data, prefer explicit versioning or a controlled rewrite.
Index Encoding
Section titled “Index Encoding”Field indexes encode the indexed field value into the index key. Numeric and floating-point fields use sortable bytes so ordered scans can work. Immutable<T> indexes the wrapped value. Ref<T> indexes the referenced row id.
This means index compatibility follows the field's encoded representation. Rebuilding indexes is required after incompatible field changes.
Practical Migration Pattern
Section titled “Practical Migration Pattern”For a controlled change:
- Open the old table with the old struct.
- Scan rows.
- Convert each row to the new struct.
- Write into a new table name or a temporary database.
- Register indexes before writing the converted rows.
- Switch application code after verification.
For small embedded datasets, a full rewrite is often simpler and safer than trying to patch bytes in place.