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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! Pre-resolved column index — Phase 5 / PLAN.md backlog 3.2.
//!
//! Replaces string-keyed lookups in `UnifiedRecord` /
//! aggregation hot paths with a fixed `u16` column index
//! resolved once per query against the schema. The slot
//! itself is a tiny wrapper that owns the `(values, schema)`
//! pair and exposes O(1) get-by-index plus a fall-through
//! get-by-name for legacy callers.
//!
//! Mirrors PG's heap tuple `t_attrs` — every column is at a
//! known offset within the row, so attribute access compiles
//! to a single pointer arithmetic op.
//!
//! ## Why it matters
//!
//! Today every WHERE clause walker, every projection, every
//! aggregation key build calls `record.values.get(&col_name)`
//! which is an `HashMap<String, Value>` probe. Hash + string
//! compare + Value clone per column per row.
//!
//! With pre-resolved indexes:
//!
//! ```text
//! // Once per query, at plan time:
//! let idx_age = schema.position_of("age");
//! let idx_name = schema.position_of("name");
//!
//! // Per row:
//! let age = slot.get(idx_age);
//! let name = slot.get(idx_name);
//! ```
//!
//! The hot loop becomes a couple of array indexes — typically
//! 4-10× faster than the HashMap path on filter-heavy queries.
//!
//! ## Wiring
//!
//! Phase 5 wiring adds:
//! 1. `Schema::position_of(name) -> Option<u16>` lookup,
//! populated at table-creation time.
//! 2. `RowSlot::from_record(record, schema)` builder that
//! walks the legacy `UnifiedRecord::values: HashMap` once
//! and writes the `Vec<Value>` in schema order.
//! 3. Filter / projection / aggregation hot loops switch from
//! `record.values.get(name)` to `slot.get(idx)`.
//!
//! Step 3 is the cascade — it touches every executor. Phase 5
//! ships steps 1+2 only; the hot-loop migration lives in a
//! follow-up commit per executor.
use Value;
/// Pre-resolved column index. Wraps a u16 to make the AST
/// ergonomic but stay one machine word.
;
/// A row stored as a flat Vec indexed by `ColumnIndex`. The
/// schema (column names) is held alongside as a parallel Vec
/// so `get_by_name` can fall back when callers haven't been
/// migrated yet.
/// Resolver that walks a column-name list once and produces
/// a `Vec<ColumnIndex>` parallel to the input. Used by the
/// planner / analyze pass to convert string-typed projections
/// into index-typed projections at plan-build time.
///
/// Returns `None` on the first column that's not in the schema —
/// callers should treat this as a "schema mismatch" error.