Commit d46deba
fix(driver-sql): keep multi-valued boolean/toggle columns out of the read-coercion registry (#17637)
* fix(driver-sql): keep multi-valued boolean/toggle columns out of the read-coercion registry
`formatOutput`'s `booleanFields` pass does `data[field] = Boolean(data[field])`
after the `jsonFields` pass has already parsed the cell into a real array.
Every non-empty array is truthy, so a `multiple: true` boolean/toggle column
presented a single `true` whatever the array held — a stored `[false]` read
back as `true`, the opposite of what is stored, with no error anywhere.
`&& !field.multiple` is the house spelling of both registration blocks, already
written three times in each (`mediaCols`, `numericCols`, `numericValueCols`);
`booleanCols.push(name)` was the single omission, in BOTH fills
(`registerExternalObject` and `registerManagedObjectMetadata`) — repairing one
would have left the other live.
The carve-out is at the registry rather than at a reader because no reader of
`booleanFields` needs a multi-valued column: the #11635 Postgres aggregate cast
would emit `cast(?? as int)` over a json column, `readPresentationKind` hands
the same one-boolean presenter to `aggregate()`/`distinct()` where it inverts
identically, `formatOutput`'s row pass is the defect, and `isNonTextColumn`
already carves multi-valued out at the reader so its answer is unchanged.
Claude-Session: https://claude.ai/code/session_01RuoNSXUbBoWHkNS4AknTrM
Co-authored-by: Claude <noreply@anthropic.com>
* chore(changeset): patch @objectstack/driver-sql for the multi-valued boolean read inversion
Claude-Session: https://claude.ai/code/session_01RuoNSXUbBoWHkNS4AknTrM
Co-authored-by: Claude <noreply@anthropic.com>
* fix(driver-sql): pin the live-Postgres distinct() divergence instead of asserting an answer it cannot give
The suite's reader-2 row asserted that `distinct()` returns the stored array
for a `multiple: true` toggle column. On live PostgreSQL that statement cannot
execute at all: `multiple: true` is a `json` column, `json` defines no equality
operator, and `SELECT DISTINCT` needs one — so the backend refuses with
"could not identify an equality operator for type json". The row was written
and validated against SQLite, the only cell provisioned locally, and first ran
for real on the Temporal Conformance job.
The divergence is class-wide and predates this branch. Measured on live
PostgreSQL 16.13, two legs, `sql-driver.ts` blob verified on disk each time:
leg A blob f7fe22f (guard present) toggles/flags/nums/tags_ -> SQLSTATE 42883
leg B blob a2b37dc (= merge base) toggles/flags/nums/tags_ -> SQLSTATE 42883
both legs scalar_flag -> [false]
`nums` and `tags_` were never in `booleanFields`, so no part of this change can
reach them, and they fail identically; a scalar boolean — a real `boolean`
column rather than `json` — answers normally in the same run.
So the row is replaced by a named divergence pinned on CLASS IDENTITY: the
boolean cell must fail exactly as the untouched NUMBER control does. Pinned,
not skipped, and not weakened — a future edit that broke the boolean cell for a
reason of its own would stop matching the control and turn this red.
The envelope is asserted on SQLSTATE rather than on ADR-0112 `code`/`status`
because this door leaks the backend's own object (`status` undefined) — the gap
#11455 closed for `aggregate()` and left open here. Filed as #17639; asserting
a 500 would have pinned a fiction.
Whole driver-sql suite re-run against live PostgreSQL under CI's own zone
configuration (server Asia/Shanghai, process TZ=America/New_York):
179 passed | 3 skipped, zero failures.
Claude-Session: https://claude.ai/code/session_01RuoNSXUbBoWHkNS4AknTrM
Co-authored-by: Claude <noreply@anthropic.com>
* test(driver-sql): execute reader 1 on live Postgres instead of pinning it at its registry input
The four-reader enumeration pinned the #11635 Postgres aggregate cast at the
one term that gates it, because no live PostgreSQL existed in the round's
container. One is now provisioned, so the arm is measured by reading the
statements the server actually received:
guard present select max("flags") as "m" -> function max(json) does not exist
guard reverted select max(cast("flags" as int)) as "m" -> cannot cast type json to integer
So the registry entry really was buying that reader a `cast(?? as int)` over a
`json` column, and the guard stops it being emitted. Both shapes are refused by
the backend, so what moves is which refusal, never a correct answer becoming an
error.
The assertion added is the SCALAR half — `max(scalar_flag)` must still compute
`1` — because that is the half a regression could silently take away: #11635
exists so a declared boolean can be aggregated on Postgres at all. Pinning the
multi-valued half would assert one dialect error string against another and
would go red the day the #17590 family is ruled.
Claude-Session: https://claude.ai/code/session_01RuoNSXUbBoWHkNS4AknTrM
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d07fc17 commit d46deba
3 files changed
Lines changed: 593 additions & 14 deletions
File tree
- .changeset
- packages/drivers/driver-sql/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
0 commit comments