Repository navigation
[Decision] min/max over a boolean aggregand: pin the cross-driver JSON answer — 0/1 (SQL) vs false/true (both in-memory faces) #11249
Description
Activity
Maintainer ruling recorded (atomic four-part write)
Ruled 2026-08-23, live PM chat, verbatim: 「10950 不考虑存量,其他接受你的建议」 — this card falls under the accepted recommendations. Ruled: the contract answer is
false/true— min/max are order statistics and return a member of the input domain; a boolean column answers booleans in JSON. SQL drivers convert0/1at the driver boundary.needs-user-decision→pm:queuein the same stroke. Implementation: pin the expected values inAGGREGATION_ROWSconformance cases (SQL side converts); contract-answer pin ⇒ treat asClause-②: yes, contract-review tier.
Generated by Claude Code
The reading this card asked for — plus two corrections to its stated premises
This card's own "本分析看不见什么" note says the PG reading would be taken on #11152 half (a) and posted back here. I was dispatched on #11152; here it is. ⛔ I have added no
min/maxcases and am not proposing a ruling — this is measurement only, so the maintainer decides on facts rather than on the premises below.Measured 2026-08-23 on base
5a916c4d4d: live PostgreSQL 16.13 (a cluster I provisioned and tore down), better-sqlite3 through the realSqlDriver,InMemoryDriver.aggregate(AST),applyInMemoryAggregation, and driver-mongodb's emitted pipeline through the in-process evaluator. Fixture: the sixAGGREGATION_ROWSrows plus a booleanflagcolumn.min/maxover a boolean — what each face actually answersface min(flag)max(flag)JSON type driver-sql— SQLitefalsetrueboolean driver-sql— PostgresTHROWS 42883 function min(boolean) does not existTHROWS 42883— driver-memoryfalsetrueboolean objectql in-memory fallback falsetrueboolean driver-mongodbnullnullnull Correction 1 — the SQL family does not answer
0/1at the driver contractThis card states:
SQLite / the SQL family:
MIN(col)/MAX(col)over a boolean answer0/1.That is true of the raw SQL and false of what
driver-sqlhands back. SQLite does return1/0, and thenpresentAggregateReadValuesruns themin/maxresult columns throughpresentReadValue('boolean', v)—Boolean(value)— so the driver'saggregate()answersfalse/true. Measured, not read:min(flag) => [{"n":false}] types=["boolean"].So on the axis this decision actually pins — the JSON value the platform contract promises — three of the four working faces already agree on
false/true, and option B costs no conversion ondriver-sql/SQLite at all. The card's framing of "the two in-memory faces agree with each other and differ from SQL" does not survive the measurement.One dependency worth stating: that presentation is keyed off the field being declared
type: 'boolean'in the object metadata. A harness that declares the conformance column as something else would see the raw0/1— so whichever way this is decided, the case table's harnesses must declare the column boolean.Correction 2 — Postgres is a third behaviour, not a third value
Option A and option B both assume every face answers something. Postgres answers nothing: it has no
min,max,sumoravgoverbooleanand errors with SQLSTATE42883.countandcount_distinctare fine (they are defined over any type).That matters here because whichever literal is chosen,
driver-sqlon Postgres cannot produce it without the lowering casting boolean to int — which is a driver change in the engine lane, not a fixture edit. A decision of "B,false/true" is therefore not free on the PG face; it implies a cast plus the existing boolean read-presentation.Two related facts filed separately while measuring: the PG failure escapes with no ADR-0112 envelope (
statusundefined,code= raw SQLSTATE) — #11455; and the aggregation conformance suite hard-codesbetter-sqlite3, which is why none of this was visible — #11456.For completeness — the
sum/avghalf this card split awaySame measurement round, since it bears on whether half (a) is as settled as the split assumed:
sum(flag)is 3 andavg(flag)is 0.5 on driver-sql/SQLite, driver-memory and objectql; 0 and null on driver-mongodb (#11151, on hold); and both throw on Postgres. Reported in full on #11152, which I returnedblocked.
Generated by Claude Code
Generated by Claude Code
- added a commit that references this issue
on Aug 24, 2026 Closing per this card's own pre-recorded disposition (spec seat,
session_01Rxnd8cyFnoU8V5y21PaTsy, R3).Provenance, all three pieces: the instruction is this card's body clause 「裁后:结论写进 #11152 的实施约束,min/max 案例随裁决入列;本卡关闭」, activated by the maintainer ruling recorded in comment 5386670755 (2026-08-23, live PM chat, verbatim 「10950 不考虑存量,其他接受你的建议」 — ruled B:
false/true, SQL drivers convert at the driver boundary).The 20:23Z measurement corrections (comment 5388270392) do NOT reopen the ruled answer — they correct the option-cost premises (driver-sql/SQLite already answers
false/trueviapresentAggregateReadValues; PG produces nothing without a lowering cast; harnesses must declare the columntype: 'boolean') and they are carried forward as implementation constraints, recorded on #11152 this stroke. The driver half the ruling now requires is filed as #11635 (engine lane, unassigned,pm:queue); the min/max cases enter #11152's scope per the ruling.State:
pm:queueremoved in the same stroke as closing (nopm:*residue on a closed card). Closed as completed — the decision this card carried is made and recorded.
Generated by Claude Code
- added a commit that references this issue
on Aug 24, 2026 - added a commit that references this issue
on Aug 27, 2026 - added 3 commits that reference this issue
on Sep 1, 2026 - added 3 commits that reference this issue
on Oct 7, 2026
Split from #11152 (retriage, 2026-08-23): half (a) —
sum/avgboolean conformance cases, settled by #11065's precedent — stays on #11152 and is dispatchable. This card carries half (b), the genuinely unpinned contract question, which is 协议变化类别 ⇒ manual floor.The question
When an aggregation query takes
minormaxover a boolean column, what JSON value does the platform contract promise?MIN(col)/MAX(col)over a boolean answer0/1.in-memory-aggregation.ts) anddriver-memoryboth reduce with</>and answerfalse/true.Unlike #11065 (driver-memory was the lone outlier; two faces pinned the answer), here the two in-memory faces agree with each other and differ from SQL — precedent does not settle it. Whatever is chosen becomes the expected value in
AGGREGATION_ROWSconformance cases and forces the losing side to convert.[facets-block]
0/1或false/true都能用,但两驱动答案不同就是同一查询两个结果,由调用方看不见的驱动能力位决定;真实需求是一致,不是哪个字面量。true/false;聚合结果突然变0/1是 SQL 存储表示的泄漏。选false/true让契约与字段类型系统自洽;选0/1则是让存储实现定义契约。=== true判断——0/1会静默永假。收敛到false/true(SQL 侧结果在驱动层归一化)是结构上更难写错的一侧。AGGREGATION_ROWShas no boolean column, so the cross-driver aggregation conformance family cannot see a boolean aggregand on any face #11152 的列一加,期望值必须写死。推荐 B(
false/true,置信度高):四轴同向——契约按字段类型说话,SQL 驱动在聚合读出侧把0/1归一化为布尔;与布尔字段普通读取路径的既有归一化同族。本分析看不见什么:driver-sql 各方言布尔存储表示的现状差异(TINYINT/BOOLEAN/INTEGER),归一化实现成本以实测为准;PG 对avg(boolean)可能直接报错(#11152 half (a) 的实测项,读数会回贴该卡)。裁决格式:回「A」(=
0/1,SQL 面答案为准)或「B」(=false/true,布尔类型面答案为准)。裁后:结论写进 #11152 的实施约束,min/max 案例随裁决入列;本卡关闭。Related: #11152 (carrier of half (a)), #11065 (the
avgcell that exposed the family), #11151 (driver-mongodb sibling, on hold).