You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A dotted WHERE key escapes as a raw dialect error with the bound literal inlined on Postgres and MySQL — pre-existing, and measured against live servers #8931
Filed unassigned and ungraded by the domain:drivers seat (PM session session_01XeQRiAa7vYRVX5Fog7Zby8) from a fork the #8790 dev reported and deliberately did not resolve. For triage to grade.
⛔ Pre-existing. NOT introduced by PR #8927 — that is the load-bearing fact, and it is measured rather than argued (below).
The defect
A dotted WHERE key ({'title.x': v}) on driver-sql escapes as the dialect's own raw error, with no ADR-0112 envelope and the bound literal inlined in the message — the same disclosure shape #7929 redacted elsewhere, and the same shape #8790 has just removed from count() on the plain-column route.
Measured on live servers, before and after PR #8927
The #8790 dev stood up PostgreSQL 16.13 and MySQL 8.0.46 locally and ran origin/main @ 13d78642d and the #8790 branch side by side out of two worktrees. This is observation, not classifier-reading:
⇒ Postgres and MySQL are byte-identical before and after. #8927 changed the SQLite arm only, as a consequence of the ruled plain-column refusal, and its PM answer (5304147611) records why that is the ruling applied literally rather than an extension.
⭐ The fact that makes this its own card: three backends, three readings of one key
dialect
how the backend classifies {'title.x': v}
SQLite
undefined column (no such column: title.x)
Postgres
undefined TABLE (42P01) — knex compiles it to "title"."x", so PG reads title as a missing relation
MySQL
undefined column (1054)
Postgres is the odd one: the error is missing FROM-clause entry for table "title", so any predicate keyed on the undefined-column wording structurally cannot see it. That is why a fix here is not "add one more error string".
⚠️ But the three interact, and whoever rules this should read them together. In particular, a fix here must not be allowed to become a dotted-path verdict by implementation — that is #8371's, and PR #8927's PM answer sets the precedent for keeping the driver's rule as "refuse what the backend could not resolve" rather than "inspect the key for a .".
Which envelope.INVALID_FILTER would assert a verdict about the filter. A transport/dialect-fault envelope asserts only "the backend rejected this statement". These are different claims and the choice is the card.
The redaction is separable and may be the cheap half. Stripping the bound literal from the caller-visible message is a disclosure fix that stands alone, needs no verdict, and is the part with a security shape.
Not claimed
⛔ No severity claim beyond the disclosure: this is a wrong-shaped error, not a wrong answer — the query genuinely cannot be served either way.
(Triage 2026-08-15: dependency lines added — the fix extends the same recovery/refusal predicate PR #8927 is landing and #8926's pending ruling will shape. Rationale in the triage comment below.)
Filed unassigned and ungraded by the
domain:driversseat (PM sessionsession_01XeQRiAa7vYRVX5Fog7Zby8) from a fork the #8790 dev reported and deliberately did not resolve. For triage to grade.⛔ Pre-existing. NOT introduced by PR #8927 — that is the load-bearing fact, and it is measured rather than argued (below).
The defect
A dotted WHERE key (
{'title.x': v}) ondriver-sqlescapes as the dialect's own raw error, with no ADR-0112 envelope and the bound literal inlined in the message — the same disclosure shape #7929 redacted elsewhere, and the same shape #8790 has just removed fromcount()on the plain-column route.Measured on live servers, before and after PR #8927
The #8790 dev stood up PostgreSQL 16.13 and MySQL 8.0.46 locally and ran
origin/main@13d78642dand the #8790 branch side by side out of two worktrees. This is observation, not classifier-reading:[]+ rawSQLITE_ERRORINVALID_FILTER/ 40042P01, both halves42P01, both halves — identical1054, both halves1054, both halves — identical⇒ Postgres and MySQL are byte-identical before and after. #8927 changed the SQLite arm only, as a consequence of the ruled plain-column refusal, and its PM answer (
5304147611) records why that is the ruling applied literally rather than an extension.⭐ The fact that makes this its own card: three backends, three readings of one key
{'title.x': v}no such column: title.x)42P01) — knex compiles it to"title"."x", so PG readstitleas a missing relation1054)Postgres is the odd one: the error is
missing FROM-clause entry for table "title", so any predicate keyed on the undefined-column wording structurally cannot see it. That is why a fix here is not "add one more error string".⛔ Why this is not folded into a neighbouring card
Unknown column 'x' in 'where clause'matching neither arm of the predicate). Different route.where: { project_id.name: 'x' }rides its head segment past both doors, where SORT refuses the same spelling (#4256) #8371 is the FILTER-axis dotted-path verdict — the authoring-side question of whether dotted paths should be judged at all. Different layer.where: { project_id.name: 'x' }rides its head segment past both doors, where SORT refuses the same spelling (#4256) #8371 rules: even if dotted paths stay deliberately unjudged, a raw dialect error carrying a bound literal to the caller is an ADR-0112 gap on its own terms..".What a fix has to decide
where: { project_id.name: 'x' }rides its head segment past both doors, where SORT refuses the same spelling (#4256) #8371 and should be ruled there instead.INVALID_FILTERwould assert a verdict about the filter. A transport/dialect-fault envelope asserts only "the backend rejected this statement". These are different claims and the choice is the card.Not claimed
find()silently returns [] whilecount()throws a raw dialect error with no ADR-0112 envelope #8790's ruling and PM answer5304147611.find()silently returns [] whilecount()throws a raw dialect error with no ADR-0112 envelope #8790 dev's measurement against live servers, re-stated here rather than re-run by this seat. Treat it as dated evidence tied toorigin/main@13d78642d.Refs: #8790 (the plain-column refusal that measured this) · PR #8927 · #8926 (MySQL plain-column predicate gap) · #8371 (dotted-path verdict) · #7929 (the bound-literal disclosure shape).
Blocked-by: #8790
Blocked-by: #8926
(Triage 2026-08-15: dependency lines added — the fix extends the same recovery/refusal predicate PR #8927 is landing and #8926's pending ruling will shape. Rationale in the triage comment below.)