Skip to content

Commit 7137f29

Browse files
MagicalTuxclaude
andcommitted
docs: defer B9b (window-function EQP) by design after investigation
Probed window EQP vs sqlite3 3.50.4 and confirmed it can't be made byte-exact: the `CO-ROUTINE (subquery-N)` label is only predictable for a single top-level window (nests for multiple windows, shifts inside a derived table), and — the real blocker — the co-routine's body is exactly the deferred B9h cost-model index choice (SQLite picks the index that both covers the windowed input's columns and serves the PARTITION/window-ORDER-BY sort; graphite can't reproduce that on a multi-index table without solving B9h). Rows are already correct; only the plan differs. Moved B9b from "Remaining" to "Blocked / deferred by design" with the precise findings, and updated §7. No code change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent fc01f90 commit 7137f29

1 file changed

Lines changed: 26 additions & 9 deletions

File tree

ROADMAP.md

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,9 +1190,10 @@ correct for all of these — perf/EQP-fidelity, not correctness):
11901190
an *indexed* column — SQLite renders `USING INDEX <ix> FOR IN-OPERATOR` rather than
11911191
`LIST SUBQUERY`+bloom; graphite still emits the bloom node there. Correlated / compound
11921192
bodies stay deferred (`CORRELATED LIST SUBQUERY`).
1193-
- **B9b — window-function EQP.** `… OVER (…)` renders `CO-ROUTINE (subquery-N)` over the
1194-
windowed input; the `(subquery-N)` label is codegen-order-fragile, so this needs a
1195-
deterministic-numbering model before it can be byte-exact.
1193+
1194+
With B9a-seek shipped, the only open EQP-fidelity threads are the `FOR IN-OPERATOR`
1195+
render residual above and **B9b** (window EQP), now confirmed deferred by design —
1196+
see below.
11961197

11971198
**Blocked / deferred by design:**
11981199
- **B9h — cost-model single-table index *choice*.** SQLite prefers, among indexes
@@ -1219,6 +1220,22 @@ correct for all of these — perf/EQP-fidelity, not correctness):
12191220
current column-collation gate is itself an earlier ORDER-BY-ordering correctness fix,
12201221
so relocating it risks the whole collation/seek/order suite for a niche pattern.
12211222
Deferred; a careful cross-cutting refactor, not a quick slice.
1223+
- **B9b — window-function EQP.** `SELECT …, <win>() OVER (…) FROM t` renders in SQLite
1224+
as `CO-ROUTINE (subquery-N)#<windowed-input plan>#SCAN (subquery-N)`. **Investigated
1225+
2026-07-04 and confirmed deferred by design** for two compounding reasons:
1226+
(1) the `(subquery-N)` label — while consistently `(subquery-2)` for a *single
1227+
top-level* window — nests for multiple windows (`(subquery-2)` wrapping
1228+
`(subquery-3)`) and shifts when the window sits inside a derived table
1229+
(`(subquery-1)` outer + `(subquery-3)` inner), i.e. it depends on SQLite's codegen
1230+
order (the standing "don't chase `(subquery-N)`" rule); and (2) more fundamentally,
1231+
the co-routine's *body* — the windowed-input scan — is exactly the **B9h** cost-model
1232+
index choice: SQLite picks the index that both covers the input's referenced columns
1233+
and serves the `PARTITION BY`/window-`ORDER BY` sort (`SELECT a, row_number() OVER
1234+
(ORDER BY b)``COVERING INDEX tb`; `SELECT b, sum(c) OVER (ORDER BY b)` →
1235+
`COVERING INDEX tbc`; `SELECT d, … ORDER BY b` → non-covering `INDEX tb`), which
1236+
graphite can't reproduce on a multi-index table without solving B9h. Rows are already
1237+
correct; only the plan differs. Blocked on B9h (+ a deterministic-numbering model for
1238+
the multi-window/nested cases).
12221239
- **B1b — cost-based join reordering.** graphite's per-cursor seek/bloom-filter
12231240
choices diverge from sqlite's cost-reordered plain scans *by design*; matching
12241241
the EQP would mean abandoning often-cheaper access paths. Results already correct.
@@ -1431,12 +1448,12 @@ reasonable order:
14311448
5. **Track A leftovers** — the `Expr::Column` enrichment (source span + schema
14321449
field) that unblocks both **A-rn3-edge** and the 3-part-qualifier check, plus
14331450
the statement-level prepare pass for the lazy-validation gaps.
1434-
6. **B9a-seek / B9b — the last `EXPLAIN QUERY PLAN` fidelity slices** (Track B). The
1435-
rest of the B9 cluster (B9c–B9g, the B9d subset, the B9a EQP nodes) shipped in
1436-
2026-07; what's left is the positive-`IN`-on-indexed-column executor seek
1437-
(**B9a-seek**) and the fragile-numbering window EQP (**B9b**). The cost-model
1438-
index-choice items (**B9h**, **B9j**) are deferred by design — they need a
1439-
stat4-enabled oracle / a cross-cutting collation refactor (see §4).
1451+
6. **`EXPLAIN QUERY PLAN` fidelity (Track B) — essentially closed.** The whole B9
1452+
cluster shipped in 2026-07 (B9a incl. the seekable-`IN` render, B9c–B9g, the B9d
1453+
subset). What remains is deferred by design: the cost-model index-choice items
1454+
(**B9h**, **B9j**), and **B9b** window EQP — whose co-routine body is itself the B9h
1455+
index choice (see §4). The lone open non-blocked residual is the `FOR IN-OPERATOR`
1456+
render node for a `NOT IN`/unindexed subquery over an indexed column.
14401457

14411458
**Deferred / blocked** (documented in §4): **B1b** join reordering and **B4**
14421459
`sqlite_stat4` (diverge from / unverifiable against the stat1-only oracle);

0 commit comments

Comments
 (0)