| JSON Plan |
| • schema: name -> arity |
| • facts: name -> rows |
| • query: { root, nodes } |
| • expected_bindings (optional oracle) |
| build_tables(plan) |
| --backend memory |
| direct from plan.facts |
| build_tables_via_storage<S: Storage> |
| --backend memory-storage |
| --backend lmdb / redb / fjall |
| --backend sqlite / geomerge |
| create_relation → tx.insert → scan_as_table |
| HashMap<String, Table> |
| positional rows per relation |
| execute(tables, query) |
| Action::Scan → scan_atom |
| Action::Join Left → semijoin(l, r) |
| Action::Join Right → semijoin(r, l) |
| Action::Join Natural → natural_join(l, r) |
| cache per-node Relation; return root |
| Relation |
| columns: variables + wildcards |
| rows: bindings |
| verify(plan, relation) |
| project to expected.columns |
| multiset compare against expected.rows |
| stdout JSON |
| { columns, rows } |
| Table (struct) |
| Table (struct, from storage) |
| arity: usize |
| rows: Vec<Vec<Value>> |
| Value (enum) |
| Value (enum, from storage) |
| Int(i64) |
| Str(String) |
| Id(RowId) |