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
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
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:
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:
it also gives MySQL the projection and ORDER-BY recoveries — an accept-set widening on a GA read path, turning a throw into returned rows for callers who today receive an error.
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.
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
SqlDriverdecides "is this an unresolvable column?" with one predicate (isUnresolvableColumnError, extracted from the inline test the #3821 ladder carried):MySQL spells the same condition
Unknown column 'x' in 'where clause'/'field list'(ER_BAD_FIELD_ERROR). It containscolumnbut notdoes not exist, so neither arm matches. Two consequences, both live on MySQL only:find()silently returns [] whilecount()throws a raw dialect error with no ADR-0112 envelope #8790, SQLite and Postgres answer an unresolvable WHERE column withINVALID_FILTER/ 400 naming the column, on bothfind()andcount(). MySQL still throws the raw dialect error — the dialect's owncode, nostatus(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.catcharm, 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.tshandlesER_BAD_FIELD_ERRORexplicitly, andpackages/restmaps MySQL's "Unknown column" to400 INVALID_FIELD(rest.test.ts). Only the driver's ladder predicate does not.Measured
Not measured against a live MySQL — no
OS_TEST_MYSQL_URLin 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 insql-driver-unresolvable-where-column-refusal.test.ts's message-shape sweep, which assertsisUnresolvableColumnErrorreturnsfalsefor 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:
INVALID_FILTERenvelope — an accept-set narrowing, the direction driver-sql: one unresolvable WHERE column, two answers —find()silently returns [] whilecount()throws a raw dialect error with no ADR-0112 envelope #8790 was ruled on;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.