Measured on a live PostgreSQL 16.13 while taking the #11152 readings. Filed unassigned as an out-of-scope finding — #11152 is scoped to packages/spec and may not touch a driver.
What was measured
driver-sql maps a boolean field to table.boolean(name), which is a real boolean column on Postgres (confirmed via columnInfo(): {"flag":{"type":"boolean",...}}). SQL_AGGREGATE_FUNCTIONS lowers the arithmetic aggregates to a bare function name with no cast, so the statement reaches the server as avg("flag").
PostgreSQL has no avg/sum/min/max over boolean, so the call fails — and the failure escapes SqlDriver.aggregate()un-enveloped:
sum(flag) => THREW code=42883 status=undefined
msg=select sum("flag") as "n" from "probe_bool_agg_pg" - function sum(boolean) does not exist
avg(flag) => THREW code=42883 status=undefined
msg=select avg("flag") as "n" from "probe_bool_agg_pg" - function avg(boolean) does not exist
min(flag) => THREW code=42883 status=undefined - function min(boolean) does not exist
max(flag) => THREW code=42883 status=undefined - function max(boolean) does not exist
count(flag) => OK 6 (count is defined over any type)
count_distinct(flag) => OK 2
code is the raw SQLSTATE and status is undefined. The same four calls answer fine on SQLite (sum 3, avg 0.5, min false, max true).
Why this is a defect independent of the contract question
Whether the platform should answer a number here (by casting boolean to int in the lowering) or refuse is a contract question that belongs to #11152 / #11249. This card is the other half, and it holds either way: when it fails, the failure must carry a catalogued code and status.
A raw 42883 is not in @objectstack/rest's isExpectedQueryRejection list, so a caller-shaped mistake (asking for a rate measure over a flag column — an ordinary analytics shape) is logged as an unhandled server fault and answered as a 500.
This is a new instance of a well-worn family, all previously fixed the same way: #5907 (bare Error, code/status undefined on the aggregate door), #8790 (count() threw a raw dialect error with no ADR-0112 envelope), #8926 (MySQL wording matched by neither predicate arm, so no envelope), #8931 (dotted WHERE key escaping as a raw dialect error).
Repro
Declare an object with a boolean field on a live PG connection, seed rows, then driver.aggregate(table, { aggregations: [{ function: 'avg', field: 'flag', alias: 'n' }] }). Measured with PostgreSQL 16.13, driver-sql at 5a916c4d4d.
Out of scope here
Related: #11152, #11249, #11065, #11151.
Measured on a live PostgreSQL 16.13 while taking the #11152 readings. Filed unassigned as an out-of-scope finding — #11152 is scoped to
packages/specand may not touch a driver.What was measured
driver-sqlmaps abooleanfield totable.boolean(name), which is a realbooleancolumn on Postgres (confirmed viacolumnInfo():{"flag":{"type":"boolean",...}}).SQL_AGGREGATE_FUNCTIONSlowers the arithmetic aggregates to a bare function name with no cast, so the statement reaches the server asavg("flag").PostgreSQL has no
avg/sum/min/maxoverboolean, so the call fails — and the failure escapesSqlDriver.aggregate()un-enveloped:codeis the raw SQLSTATE andstatusisundefined. The same four calls answer fine on SQLite (sum3,avg0.5,minfalse,maxtrue).Why this is a defect independent of the contract question
Whether the platform should answer a number here (by casting boolean to int in the lowering) or refuse is a contract question that belongs to #11152 / #11249. This card is the other half, and it holds either way: when it fails, the failure must carry a catalogued code and status.
A raw
42883is not in@objectstack/rest'sisExpectedQueryRejectionlist, so a caller-shaped mistake (asking for a rate measure over a flag column — an ordinary analytics shape) is logged as an unhandled server fault and answered as a 500.This is a new instance of a well-worn family, all previously fixed the same way: #5907 (bare
Error, code/status undefined on the aggregate door), #8790 (count()threw a raw dialect error with no ADR-0112 envelope), #8926 (MySQL wording matched by neither predicate arm, so no envelope), #8931 (dotted WHERE key escaping as a raw dialect error).Repro
Declare an object with a
booleanfield on a live PG connection, seed rows, thendriver.aggregate(table, { aggregations: [{ function: 'avg', field: 'flag', alias: 'n' }] }). Measured with PostgreSQL 16.13,driver-sqlat5a916c4d4d.Out of scope here
AGGREGATION_ROWShas no boolean column, so the cross-driver aggregation conformance family cannot see a boolean aggregand on any face #11152 half (a), and [Decision]min/maxover a boolean aggregand: pin the cross-driver JSON answer —0/1(SQL) vsfalse/true(both in-memory faces) #11249 formin/max.tinyint(1)may well answer arithmetically; that is a reading someone should take rather than assume.Related: #11152, #11249, #11065, #11151.