From a50a433bf0f8aa1d7577cca09921fe03ee352004 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 20:32:38 +0000 Subject: [PATCH] =?UTF-8?q?board:=20correct=20the=20D-QSP-0=20entry's=20G1?= =?UTF-8?q?=20claim=20=E2=80=94=20NULL=20literals=20need=20no=20new=20carr?= =?UTF-8?q?ier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The entry and EXO-50 /027-/028 said NULL literals and NOT IN (..., NULL) need a constant-UNKNOWN Filter leaf and a FALSE carrier. That was asserted without reading the substrate. A NULL operand is a lane alias with an all-clear presence plane under sql_where, and the existing API matches PostgreSQL 16.15 on the six measured predicates (EXO-50 /029). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013z2e1YXkm6o6HNrDn5BPWA --- .../board/entries/2026-10-21-d-qsp-0-scalar-aggregates-null.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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.