diff --git a/.claude/board/entries/2026-10-21-d-qsp-0-scalar-aggregates-null.md b/.claude/board/entries/2026-10-21-d-qsp-0-scalar-aggregates-null.md index 937659e70..966e18875 100644 --- a/.claude/board/entries/2026-10-21-d-qsp-0-scalar-aggregates-null.md +++ b/.claude/board/entries/2026-10-21-d-qsp-0-scalar-aggregates-null.md @@ -61,5 +61,6 @@ Without a server the example prints `SKIP` and exits 0. It is a lab instrument; - `SUM` over a grouped key: the coalescing `Agg::GroupSumI32` still answers `0` for an unreached group (documented zero-fallback). The NULL-preserving form is `GroupReduce { GroupAgg::SumI32 }`. - A reached group whose `x` are all NULL (PostgreSQL: the group appears with `sum = NULL`) is trap T4; it belongs to the GROUP BY slice together with NULL keys (G2). -- Not covered: NULL literals and `NOT IN (…, NULL)` (G1), `COALESCE`/`NULLIF`, nullable arithmetic, `int4` sum overflow past `MASKED_SUM_I32_MAX_ROWS`, `numeric` AVG. +- Not covered: `COALESCE`/`NULLIF`, nullable arithmetic, `int4` sum overflow past `MASKED_SUM_I32_MAX_ROWS`, `numeric` AVG. +- NULL literals and `NOT IN (…, NULL)` (G1) are not pinned here, but they need no new carrier. ⊘ Corrected: EXO-50 `/027` and `/028` said G1 needs a constant-UNKNOWN `Filter` leaf and a FALSE carrier; that was asserted without reading the substrate and is false. A column's NULLs are its presence plane (`lib.rs:18`), and `sql_where`'s leaf arm (`lib.rs:580-591`) rewrites a leaf on a nullable column to `T = valid ∧ leaf`, `F = valid ∧ ¬leaf`. A NULL operand is a borrowed alias of a lane with an all-clear presence plane, so both polarities are empty (UNKNOWN); a constant FALSE is an all-zero `Filter::Plane`. On `x = {1, 2, NULL, 1}` the existing API returns `0, 2, 2, 0, 0, 0` for `x NOT IN (1, NULL)`, its negation, `x IN (1, NULL)`, its negation, `x = NULL` and its negation, identical to PostgreSQL 16.15 (EXO-50 `/029`). The G1 rows of `entries/2026-10-21-pg-classview-polyfill-map.md` ("ABSENT") are wrong for the same reason. Open: a committed test and a random PostgreSQL differential with NULL operands. - Two folds cost two passes over the filter, as for AVG. A fused sum+count sink is a cost question, not a correctness one; no timing was taken here.