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
use ;
/// Metadata: "next_node_id" / "next_edge_id" / "schema_version" -> counter.
pub const META: = new;
/// Label string -> interned u32 id.
pub const LABEL_TO_ID: = new;
/// Interned u32 id -> label string.
pub const ID_TO_LABEL: = new;
/// node_id -> postcard-encoded NodeRecord.
pub const NODES: = new;
/// edge_id -> postcard-encoded EdgeRecord.
pub const EDGES: = new;
/// Outgoing adjacency as a composite-key plain table (v2 step 2):
/// `(src_node_id, label_id, edge_id)` -> dst node_id. redb tuple keys are
/// FIXED-WIDTH (mars-am7: erasing fixed-width keys to `&[u8]` measured
/// +34% file size on this exact codebase — a first cut of this change
/// repeated that mistake with byte-packed keys and doubled the db file;
/// tuples keep redb's fixed-slot packing) and order component-wise, so
/// one node's edges cluster together grouped by label: a typed expansion
/// (`-[:KNOWS]->`) is a `range()` over the `(node, label, *)` prefix,
/// touching only matching entries (`O(matching degree)`), and an untyped
/// expansion is the wider `(node, *, *)` prefix. Replaces a
/// `node_id -> {20-byte AdjEntry}` multimap whose per-node entries were
/// ordered by edge id, forcing every typed expansion to decode and
/// label-check the node's entire entry set (`O(total degree)`).
pub const ADJ_OUT: = new;
/// Incoming mirror of `ADJ_OUT`: `dst ++ label ++ edge` -> src node_id.
pub const ADJ_IN: = new;
/// label_id -> number of live edges of that relationship type. Planner
/// statistic only (the expand-cost half of start-point reversal — see
/// `plan_reversed_pattern`), never consulted to answer a query, so a
/// wrong value can cost a suboptimal plan but never a wrong result.
/// Maintained by the only two places an edge is born or dies
/// (`create_edge_ctx`/`delete_edge_ctx`), and rebuilt from a one-time
/// `EDGES` scan at open for files that predate the table
/// (`GraphStore::backfill_rel_type_counts`).
pub const REL_TYPE_COUNTS: = new;
/// label_id -> set of node_ids carrying that label. Secondary index so a
/// label-filtered scan (`NodeByLabelScan`) can look up matching nodes
/// directly instead of scanning every row in `NODES`.
pub const NODE_LABEL_INDEX: =
new;
/// Property-name string -> interned u32 id. Mirrors `LABEL_TO_ID` — property
/// names are interned globally (not per-label), since the same property
/// name (`name`, `id`, ...) is common across many labels and there's no
/// benefit to a separate namespace per label.
pub const PROP_TO_ID: = new;
/// Interned u32 id -> property-name string.
pub const ID_TO_PROP: = new;
/// `label_id(4 bytes BE) ++ property_id(4 bytes BE)` -> postcard-encoded
/// `IndexDef` (currently just a `unique` flag). Presence of a key here is
/// what "this (label, property) has a declared index" means — checked by
/// the planner before it can emit an `IndexSeek` and by every mutation path
/// before it needs to maintain `PROPERTY_INDEX`.
pub const INDEX_DEFS: = new;
/// `label_id(4 bytes BE) ++ property_id(4 bytes BE) ++ encoded_value` ->
/// set of node_ids. One shared table for every declared property index
/// (not one table per index) — keeps schema/table lifecycle management
/// simple; the label_id+property_id prefix keeps each index's entries
/// contiguous under redb's own key ordering, which is what a future range
/// scan (`WHERE n.prop > x`) would need anyway. `encoded_value` uses an
/// order-preserving byte encoding (see `marsdb-graph::index_key`) so
/// lexicographic byte comparison matches the real value ordering within one
/// type — cross-type ordering isn't meaningful and isn't relied on.
pub const PROPERTY_INDEX: =
new;