Skip to content

driver-sql: MySQL's unresolvable-column wording is matched by neither arm of the recovery/refusal predicate, so MySQL gets no ADR-0112 envelope and no #3821 recovery #8926

Description

@hotlong

Found while implementing #8790 (unresolvable WHERE column refuses on both halves). Filed separately because closing it is an accept-set change in the opposite direction from that card's ruling, so it should not ride along on it.

The gap

SqlDriver decides "is this an unresolvable column?" with one predicate (isUnresolvableColumnError, extracted from the inline test the #3821 ladder carried):

message.includes('no such column') // SQLite
|| (message.includes('column') && message.includes('does not exist')) // Postgres

MySQL spells the same condition Unknown column 'x' in 'where clause' / 'field list' (ER_BAD_FIELD_ERROR). It contains column but not does not exist, so neither arm matches. Two consequences, both live on MySQL only:

  1. No ADR-0112 envelope. After driver-sql: one unresolvable WHERE column, two answers — find() silently returns [] while count() throws a raw dialect error with no ADR-0112 envelope #8790, SQLite and Postgres answer an unresolvable WHERE column with INVALID_FILTER / 400 naming the column, on both find() and count(). MySQL still throws the raw dialect error — the dialect's own code, no status (an unclassified 5xx at the REST boundary rather than a caller mistake), and the statement's bound literals inlined in the message, which is the predicate-text disclosure shape finding: after the #7598 Q1=B ruling, a read scope with a driver-refused field reference answers 400 from the driver — which cuts across #5367's attribution argument on that one path #7929 redacted elsewhere.
  2. No fix(sharing): 共享规则新建页 — 自定义 widget 未国际化,且「接收方」永远无可选项 #3821 recovery. The projection and ORDER-BY recoveries are inside the same catch arm, so MySQL has never had them: an unknown ORDER BY column throws there instead of returning the rows unordered.

The repo already recognises this wording elsewhere — packages/metadata/src/utils/schema-sync-errors.ts handles ER_BAD_FIELD_ERROR explicitly, and packages/rest maps MySQL's "Unknown column" to 400 INVALID_FIELD (rest.test.ts). Only the driver's ladder predicate does not.

Measured

Not measured against a live MySQL — no OS_TEST_MYSQL_URL in the container this was found in. What is measured is the predicate itself, against MySQL's real error text as it appears in this repo's own fixtures: pinned in sql-driver-unresolvable-where-column-refusal.test.ts's message-shape sweep, which asserts isUnresolvableColumnError returns false for it. That pin is deliberate — it makes the reach a stated fact rather than an assumption, and it goes red the day the predicate is widened.

Why it is a decision, not an obvious fix

Adding MySQL's spelling to the predicate is one line, and it does two things at once, because the recovery ladder and the refusal terminal share the arm:

The second half is the one nobody has ruled on. Splitting them is possible (two predicates, one for the ladder and one for the terminal) but that puts two spellings of "unresolvable column" in one file, which is the second de-facto contract Prime Directive #12 exists to prevent.

Worth ruling alongside whatever posture #8371 lands on the same axis.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions