Mímir v2 — tables and columns
Every column below is taken from crates/mimir-schema/src/lib.rs (the four payload tables), crates/mimir-schema/src/code_mir.rs (the three lossless tables of schema 8) and crates/mimir-ingest/src/schema.rs (the lake's event prefix and table names). utf8 is an Arrow string; "null" means nullable.
1. The driver's payload tables
code_nodes (*.nodes.arrow)
| column | type | meaning |
id |
utf8 | the moniker |
kind |
utf8 | node kind (list below) |
level |
utf8 | workspace · repo · crate · module · item · part |
name |
utf8 | the item's name |
parent |
utf8 null | the containing node's id |
repo |
utf8 null | from MIMIR_REPO |
crate |
utf8 | crate name |
version |
utf8 | crate version |
file |
utf8 null | path relative to the workspace root (members) or the manifest dir (dependencies); OUT_DIR files mapped |
start_line, end_line |
u32 null | the item's span |
cfg |
utf8 null | the cfg predicate |
origin |
utf8 | local · library · lang |
provenance |
utf8 | source_only · binary_only · both |
valid_from_sha, valid_to_sha |
utf8 null | from MIMIR_SHA; valid_to_sha is null in a dump |
ty |
utf8 null | printed type for params, fields, consts, statics, aliases (short paths: OnceLock<Option<Arc<T>>>; null on a reference stub); the MIR payload for mir_body |
is_test |
bool | a test fn |
defined_here |
bool | false = a reference stub of another crate's item |
visibility |
utf8 null | pub · pub(crate) · private |
effective_visibility |
utf8 null | direct · reexported · reachable · reachable_impl_trait · internal |
doc |
utf8 null | the first line of the doc comment |
has_doc |
bool null | the item carries a doc comment |
ignored |
bool null | #[ignore] on a test fn |
doc_hidden |
bool null | the item itself is #[doc(hidden)] |
link_name |
utf8 null | the symbol a foreign fn or static is linked as |
Node kinds (mimir_schema::kind):
crate mod fn method struct enum union trait trait_alias impl const static type macro
ffi foreign_type field variant param ret local generic assoc_type assoc_const
mono_instance ffi_import feature
plus the MIR part mir_body (one per fn), at level part. Its payload (the block fingerprints, terminator kinds and successors) is derived from the fn's code_mir rows. Since schema 8 the driver writes no mir_block node: a reader derives the blocks from the same rows (section 1.1).
code_edges (*.edges.arrow)
| column | type | meaning |
from, to |
utf8 | monikers |
kind |
utf8 | edge kind (list below) |
count |
u32 | distinct sites that carry the edge |
provenance |
utf8 | source_only · binary_only · both |
valid_from_sha, valid_to_sha |
utf8 null | commit range |
negated |
bool null | GATED_BY only: the feature sits under an odd number of not(…) |
partial |
bool null | GATED_BY only: the feature gates something inside the item |
access |
utf8 null | TOUCHES_TABLE only: read · write · both |
Edge kinds (mimir_schema::EdgeKind):
CONTAINS DEPENDS_ON CALLS IMPLEMENTS FOR_TYPE USES_TYPE READS_FIELD EXPANDS GATED_BY
TESTS TOUCHES_TABLE COMPATIBLE INSTANCE_OF ENABLES ENABLES_WEAK BUILT_FROM SYSCALLS CFG_SUCC
WRITES_FIELD
The order is EdgeKind::ALL: a new kind is appended (mimir-room keys its kind masks on the position), so WRITES_FIELD (schema 6) is index 18. CFG_SUCC keeps its position, but since schema 8 the driver writes no CFG_SUCC edge: the successors of a block are derived from a code_mir row (section 1.1).
The two field kinds, for a body owner b and a field f (a struct, union or enum-variant field):
READS_FIELD(b, f) ⇔ b has an expression `e.f` (struct / union): a MENTION, read or written
WRITES_FIELD(b, f) ⇔ f ∈ written(p) for a place p of b with p = v | p op= v | &mut p | &raw mut p
| p auto-borrowed &mut (a `&mut self` method on p)
∨ b has a literal `S { f: v, .. }` / `E::V { f: v, .. }` (not the fields `..base` fills)
written(p) = { g : `.g` on the path of p, outside in, up to the first deref }
count = |{ (file, line) of the field expression }| (as for every kind)
A read-only use is READS_FIELD(b, f) ∧ ¬WRITES_FIELD(b, f); a literal names a field without a field expression, so it is a WRITES_FIELD only. a.b.c = v writes c and b, and a too unless a is reached through a pointer; r.f = v with r: &mut S writes only f.
code_spans (*.spans.arrow)
| column | type | meaning |
target |
utf8 | node or edge |
key |
utf8 | the node id, or from ␞ to ␞ KIND for an edge site (U+001E separators) |
file |
utf8 | path, as in code_nodes.file |
start_line, end_line |
u32 | the span; an edge site has start_line = end_line |
code_symbols (*.symbols.arrow)
| column | type | meaning |
id |
utf8 | moniker |
symbol |
utf8 | the linked symbol |
demangled |
utf8 null | its demangled form |
role |
utf8 | the symbol's role (for example import) |
crate, version |
utf8 | owner crate |
binary |
utf8 null | artifact file name; null on a driver row |
section |
utf8 null | ELF section |
addr, size |
u64 null | address and size in the artifact |
provenance |
utf8 | as above |
valid_from_sha, valid_to_sha |
utf8 null | commit range |
1.1 The lossless tables (schema 8)
Three more files per unit hold the MIR facts in ONE lossless form (crates/mimir-schema/src/code_mir.rs; the plan is exec-graph-plan.md). A unit may lack any of them (MIMIR_MIR=off, a dump of an older driver). A program's --exec unit has only these files and <stem>.exec.json.
| file | table | one row per | identity |
<stem>.mir.arrow |
code_mir |
body (fn, method, closure, coroutine, compiler shim) | def |
<stem>.inst.arrow |
code_instances |
instance (a body with concrete generic arguments) | inst |
<stem>.data.arrow |
code_data |
a static the unit defines, or an unnamed allocation a constant or a static points at (the instance pass: the statics of every crate the program names) | id |
The files are Arrow IPC with zstd buffers, at most 2,048 rows per record batch. A row holds its lists as Arrow List columns (a struct of arrays). A string column inside a row holds indices into the row's own pool strs (0xFFFF_FFFF = none). Every table ends with content.
code_mir, columns in schema order:
| column | type | meaning |
def |
utf8 | the body's def key (std::fs::File::open, app[bin=app]@0.1.0::main::{closure#0}; a shim: its instance key): the same string in every compiler session, the key code_instances.body joins on |
owner |
utf8 | moniker of the nearest item with a node (the fn, the method, the static a closure sits in) |
sub |
utf8 | def path from the owner to the body: empty for the owner's own body, {closure#0}, {closure#0}::{closure#1}; a shim: {shim:…} |
kind |
utf8 | fn, closure, coroutine, shim |
legacy |
utf8 null | the block-name prefix of the schema-7 view (empty, c1., c2. …); null = that view never held this body |
arg_count |
u32 | the first arg_count locals after _0 are the parameters |
legacy_cap |
u32 | the schema-7 view keeps at most this many (non-cleanup) blocks (0xFFFF_FFFF = all: the default) |
base_line |
u32 | the owner's first line when the unit was dumped; the positions of the sites are relative to it; not part of content |
local_flags |
list<u8> | per local: bit 0 mutable, bit 1 user variable, bit 2 parameter |
blk_flags |
list<u8> | per block: bit 0 cleanup (unwind) block |
st_kind |
list<u8> | per site: its kind (code_mir::site) |
st_flags |
list<u8> | per site: bit 0 from a macro expansion, bit 1 the site has a position |
op_kind |
list<u8> | per operand: code_mir::opk (copy, move, constant, place) |
pj_kind |
list<u8> | per projection step: code_mir::pjk |
k_kind |
list<u8> | per constant: code_mir::kk |
tg_role |
list<u8> | per target: code_mir::role |
st_file |
list<u16> | per site: its source file (index into files; 0 = the owner's file) |
files |
list<u32> | source files of the positions (strs); entry 0 is the owner's file |
local_ty |
list<u32> | per local: its type, rustc's full print (strs) |
dbg_name, dbg_place |
list<u32> | every source name (strs) and the place it names (0xFFFF_FFFF = a constant) |
blk_end |
list<u32> | block b holds the sites [blk_end[b-1], blk_end[b]); its last site is the terminator |
st_word |
list<u32> | per site: the operation word (strs): Add, cast.IntToInt, adt~2, assert.bounds, use … |
st_dest |
list<u32> | per site: the written place, 0xFFFF_FFFF = none |
st_ty |
list<u32> | per site: a type (strs): a cast's target, the dropped type, an aggregate's type |
st_callee |
list<u32> | per site: the callee as the body's own text names it, a stable moniker (strs) |
st_op_end |
list<u32> | per site: its operands are [st_op_end[i-1], st_op_end[i]) |
st_tg_end |
list<u32> | per site: its targets are [st_tg_end[i-1], st_tg_end[i]) |
st_dline |
list<u32> | per site with a position: its line, zigzag-coded: relative to base_line in the owner's file, the absolute line in another file |
st_col |
list<u32> | per site: its column (1-based) |
op_ref |
list<u32> | per operand: index of its place or constant |
pl_local, pl_pj_end |
list<u32> | per place: its local, and its projection run [pl_pj_end[i-1], pl_pj_end[i]) |
pj_a, pj_b |
list<u32> | per projection step: its two arguments (code_mir::pjk) |
k_ty |
list<u32> | per constant: its type (strs) |
k_def, k_args |
list<u32> | per constant: see code_mir::kk (strs) |
k_bytes |
list<u32> | per constant: its bytes (index into bytes) |
tg_block |
list<u32> | per target: the block (0xFFFF_FFFF = no block: see tg_role) |
drop_key |
list<u32> | one per drop site, in site order: the dropped type's key (strs), written over the body's own generic parameters |
st_aux |
list<u64> | per site: variant index; an assert's expected value; a drop's state; a storage marker's local; asm flags; a call's flags (bit 0: the callee is a compiler intrinsic) |
k_lo, k_hi |
list<u64> | per constant: the value's low and high 64 bits |
tg_lo, tg_hi |
list<u64> | per target: the switch value |
strs |
list<utf8> | the row's string pool |
bytes |
list<binary> | the row's byte pool (k_bytes) |
content |
u64 | a hash of every other column but base_line (the lake's delta key) |
code_instances, columns in schema order:
| column | type | meaning |
inst |
utf8 | the instance key |
body |
utf8 | the code_mir row this instance runs (code_mir.def) |
args |
utf8 | the generic arguments as type keys (⟨a,b⟩, empty = none) |
symbol |
utf8 null | rustc's symbol name of the instance (v0 mangling) |
root |
u32 | 0 no; 1 main; 2 an exported fn; 3 a constructor (.init_array); 4 the process start (std::rt::lang_start) |
flags |
u32 | bit 0: the body is a compiler shim or an instance body the program's pass wrote; bit 1: rustc holds no MIR for it |
res_kind |
list<u8> | per resolved term: code_mir::res (instance, virtual, intrinsic, foreign item, fn pointer, nothing to do, not resolved) |
res_def, res_args |
list<u32> | a generic term of the body: the fn item (k_def, k_args of the code_mir row), or the dropped type (res_def = drop, res_args = the type's key) |
res_inst |
list<u32> | what it is in this instance: an instance key, a trait method + slot, a link name |
res_line, res_col |
list<u32> | where the term's site is in the source (line 1-based, 0 = none; column 1-based) |
kv_def, kv_args |
list<u32> | a generic constant of the body (k_def, k_args) |
vt_dyn, vt_ty |
list<u32> | every vtable this instance builds: the trait object type's key, the concrete type's key |
vt_end, vt_m |
list<u32> | its methods are vt_m[vt_end[i-1] .. vt_end[i]]: instance keys |
fp_sig, fp_inst |
list<u32> | every fn pointer this instance makes: the signature's key and the instance |
spawn |
list<u32> | thread / task marks: this instance starts the named instance as a new entry |
kv_lo, kv_hi |
list<u64> | the value of each generic constant in this instance |
strs |
list<utf8> | the row's string pool |
content |
u64 | a hash of every other column |
code_data, columns in schema order:
| column | type | meaning |
id |
utf8 | a static: its def key; an unnamed allocation: alloc:<16 hex digits> (code_mir::alloc_id, a hash of size, alignment, mutability, bytes and pointers) |
ty |
utf8 | a static: its type's key; an unnamed allocation: empty |
link_section |
utf8 null | the link section (.init_array makes a constructor) |
flags |
u32 | bit 0 mutable (a static mut, or the allocation is mutable), bit 1 thread local, bit 2 bytes is cut at 64 KiB, bit 3 the #[global_allocator] (not set yet), bit 4 #[used]; bits 8 to 13 log2 of the alignment |
size |
u64 | its size |
bytes |
list<u8> | its bytes (at most 64 KiB); an uninitialised byte and the bytes of a pointer are 0 |
ptr_to |
list<u32> | every pointer in the allocation, in offset order, never cut: its target (strs): fn:<instance key>, a static's def key, alloc:<hash>, `{vtable} |
ptr_sig |
list<u32> | for a fn pointer: the signature's key (strs), else 0xFFFF_FFFF |
ptr_off |
list<u64> | the pointer's offset in the allocation |
strs |
list<utf8> | the row's string pool |
content |
u64 | a hash of every other column |
The schema-7 view is derived, never stored. For a code_mir row with legacy not null, mimir_schema::code_mir::legacy gives:
view(row) the non-cleanup blocks: name, fingerprint, terminator word, successors
body_payload the `mir_body` node's `ty` (the driver writes it as the derived column it is)
block_nodes(row) per block: id `<fn>bb<name>:`, the schema-7 `mir_block` payload, the CFG_SUCC successor ids
Every batch carries schema metadata mimir.schema = "8" and mimir.table = <table name>.
Versions (mimir_schema::SCHEMA_VERSION): 1 the four tables; 2 the visibility / doc columns, negated, partial, the feature kinds; 3 ignored, doc_hidden; 4 access, link_name; 5 stable identities; 6 WRITES_FIELD and every MIR block of a body written (no 1,024 cap); 7 REFERS_FN; 8 the three lossless tables, no mir_block node and no CFG_SUCC edge. Migration: a version that only adds nullable columns is evolved in place; a lake stamped before the re-read epoch (8, mimir-ingest REREAD_SCHEMA) is wiped by EventLog::open_lake and the consumer re-reads every repo from its tip, because the facts of every unit changed and a dependency already in the ring is never re-dumped. The wipe keeps no backup: the lake is derived data.
2. The lake (skade namespace mimir_v2)
An event table is a payload table with eight prefix columns in front:
| column | meaning |
repo |
the member, or @universe for the dependency ring |
seq |
the repo's sequence number (its commit ordinal in the log) |
sha |
the commit |
op |
+ or - |
krate |
the crate the row came from |
payload_hash |
hash of the payload columns; for a lossless table the row's content (its 8 bytes, little endian, then zeros) |
key_dup |
non-null only when one key was dumped with different payloads |
rev |
binary stream only: the binary release ordinal per repo |
Tables:
| table | payload | written by |
code_node_events |
code_nodes |
the consumer (source stream) |
code_edge_events |
code_edges |
the consumer |
code_span_events |
code_spans |
the consumer |
code_mir_events |
code_mir |
the consumer (source stream), key def |
code_instance_events |
code_instances |
the consumer, key inst |
code_data_events |
code_data |
the consumer, key id |
universe_node_events |
code_nodes |
the consumer, repo = @universe, once the lake is split (mimir.ring = split) |
universe_edge_events |
code_edges |
same |
universe_span_events |
code_spans |
same |
universe_mir_events, universe_instance_events, universe_data_events |
code_mir, code_instances, code_data |
same: every dependency and toolchain crate carries its bodies, once per crate@version |
universe_manifest |
the ring's manifest (the dependency and toolchain crates dumped into it) | the consumer |
code_symbol_events |
code_symbols |
mimir-binary (binary stream) |
bin_node_events, bin_edge_events |
binary-layer nodes and edges | mimir-binary |
bin_ingest_runs |
one row per (artifact, binary release) | mimir-binary |
bin_offsets |
per artifact: the release and commit last written | mimir-binary |
consumer_offsets |
per repo: the seq and commit the lake is at |
the consumer |
ingest_runs |
the ledger of consumer runs | the consumer |
consumer_feed_runs |
one row per (repo, sha, feed) for the non-graph feeds that ride the offsets | the consumer |
backfill_runs |
requested / done rows of a history backfill |
the consumer |
Table properties: mimir.schema (the mimir-schema version the columns are at) and the comma-joined list of columns added in place by an evolution.
The lossless tables in the lake (crates/mimir-ingest/src/lossless.rs):
- Identity. The key columns are made stable by
mimir_schema::code_mir::stable_key: a moniker's version is*, and a<package>@<version>::inside a def, type or instance key loses its version. A version bump re-emits a row on the same identity. The stored columns keep the real versions. - Delta.
payload_hashis the row'scontent. A row whosecontentdid not change writes no event.base_lineis outsidecontent: moved code writes no event, and the storedbase_lineis the one of the last content change. The owner's span incode_span_eventsis the truth for the line. - Storage. skade's Iceberg bridge maps no nested type. A
list<T>column is stored as onebinarycolumn: the row's items back to back, an integer item as its little-endian bytes, autf8orbinaryitem behind its length (u32, little endian).u32is stored aslong,u64asdecimal(20, 0), as in every event table.mimir_ingest::dump_formgives a scanned batch its Arrow lists back. - Reading. No room folds these tables and the READ-FAST zone has no interval table for them. A reader asks for one owner:
EventLog::code_mir,code_instances,code_datatake a repo (with an optionalseq) or one crate of the ring, and a byte cap.EventLog::mir_block_payloadsgives the schema-7 block payloads of a repo, derived from itscode_mirrows. - Sizes.
mimir-lake <lake-dir> statprints files, bytes on disk and rows per table (EventLog::table_sizes).
Storage: Parquet, zstd level 3, 256 k-row row groups (log.rs write_props); every write of a release goes through one commit lock and one skade atomic commit.