Found while implementing #11635 (boolean aggregand answers), measured through the driver boundary on live MySQL 8.0.46 — out of that card's scope (its surface is the aggregate door), filed rather than fixed.
Measurement
Through SqlDriver.find() on live MySQL 8.0.46, over a table whose flag field is declared type: 'boolean' (stored as tinyint(1)), current main (2a6122bd9d):
[mysql] find().flag => 1 (typeof number)
[pg] find().flag => true (typeof boolean)
[sqlite] (converted by the booleanFields read coercion)
Instrument: a plain driver.create(...) + driver.find(...) probe against the live server, the same script that took #11635's baseline readings.
Mechanism
formatOutput's booleanFields read coercion (packages/drivers/driver-sql/src/sql-driver.ts, the Boolean(data[field]) loop) sits inside if (this.isSqlite). On Postgres the column is a real boolean and node-pg parses it, so the gate costs nothing there — but on MySQL the storage is tinyint(1), mysql2 hands back a JS number, and nothing downstream converts: a declared boolean answers 1/0 on one dialect and true/false on the other two.
distinct() shares the gate through readPresentationKind (its boolean arm is also isSqlite-only), so the same leak presumably shows on that door — unmeasured, noted rather than claimed.
Why it matters
Scope note
The fix direction (un-gate the coercion for MySQL vs. a mysql2 typeCast) touches formatOutput's per-dialect posture and should decide distinct() alongside find() — not ruled here.
Generated by Claude Code
Found while implementing #11635 (boolean aggregand answers), measured through the driver boundary on live MySQL 8.0.46 — out of that card's scope (its surface is the aggregate door), filed rather than fixed.
Measurement
Through
SqlDriver.find()on live MySQL 8.0.46, over a table whoseflagfield is declaredtype: 'boolean'(stored astinyint(1)), currentmain(2a6122bd9d):Instrument: a plain
driver.create(...)+driver.find(...)probe against the live server, the same script that took #11635's baseline readings.Mechanism
formatOutput'sbooleanFieldsread coercion (packages/drivers/driver-sql/src/sql-driver.ts, theBoolean(data[field])loop) sits insideif (this.isSqlite). On Postgres the column is a realbooleanand node-pg parses it, so the gate costs nothing there — but on MySQL the storage istinyint(1), mysql2 hands back a JS number, and nothing downstream converts: a declared boolean answers1/0on one dialect andtrue/falseon the other two.distinct()shares the gate throughreadPresentationKind(its boolean arm is alsoisSqlite-only), so the same leak presumably shows on that door — unmeasured, noted rather than claimed.Why it matters
false/true+ arithmetic answers are unproducible on the PG face #11635,min(flag)/max(flag)answerfalse/trueon MySQL (per the [Decision]min/maxover a boolean aggregand: pin the cross-driver JSON answer —0/1(SQL) vsfalse/true(both in-memory faces) #11249 ruling: drivers convert at the driver boundary) whilefind()on the same column answers1/0— the aggregate door and the row-read door now disagree on the same dialect.row.flag === true) silently miss on MySQL only.Scope note
The fix direction (un-gate the coercion for MySQL vs. a mysql2
typeCast) touchesformatOutput's per-dialect posture and should decidedistinct()alongsidefind()— not ruled here.Generated by Claude Code