Skip to content

Commit ad1f96e

Browse files
committed
docs(spec): dated addendum — MySQL joined the unresolvable-column refusal AND the #3821 recoveries (#9060)
The ADR-0087 entry driver-sql-unresolvable-where-column-refused stated MySQL was outside the refusal's reach. True at registration (#8790); false since #8926 landed via PR #9061. The historical paragraph is kept verbatim as the state at registration and a dated addendum states both halves of the parity: the INVALID_FILTER / 400 envelope, and the #3821 projection / ORDER-BY recoveries MySQL never had. registry.ts regenerated via gen:migration-registry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01225pUjnCKWqxcc1PeqKFUq
1 parent 2420641 commit ad1f96e

3 files changed

Lines changed: 82 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
docs(spec): the `driver-sql-unresolvable-where-column-refused` ledger entry states MySQL's reach as it is after #8926, not as it was at registration (#9060)
6+
7+
Text amendment to an already-registered ADR-0087 entry — the entry id, `surface`
8+
and `replacement` prescription are unchanged, and no accept/reject behaviour
9+
moves. What changes is the `reason`, which is upgrader-facing documentation: it
10+
is the data source for `objectstack migrate meta`, `spec-changes.json` and the
11+
generated upgrade guide.
12+
13+
The entry's "Reach, stated rather than assumed" paragraph said MySQL was outside
14+
the refusal — true when #8790 registered it, false the moment #8926 merged (PR
15+
#9061). A MySQL user reading "on MySQL this condition still travels out as the
16+
raw dialect error" would have concluded the migration did not apply to them,
17+
which is exactly wrong after parity.
18+
19+
The historical paragraph is kept verbatim as the state at registration, and a
20+
dated addendum states both halves of what the one shared predicate did on MySQL:
21+
22+
- **The envelope** — an unresolvable WHERE column refuses with the same
23+
`INVALID_FILTER` / 400 naming the column, instead of the raw
24+
`ER_BAD_FIELD_ERROR` with the statement's bound literals inlined.
25+
- **The recoveries** — MySQL also gained the #3821 projection and ORDER-BY
26+
recoveries it never had, so those positions now return recovered rows where
27+
they used to throw.
28+
29+
Both arrive together because `ER_BAD_FIELD_ERROR` spells every clause position
30+
with one sentence, so all three ride one arm of the predicate — pinned as the
31+
ruled direction by the widened sweep in
32+
`sql-driver-unresolvable-where-column-refusal.test.ts`. Unchanged by that
33+
ruling, and said so in the addendum: a dotted filter key is still classified per
34+
dialect, the axis #8371 owns.

‎packages/spec/src/migrations/entries/semantic/18.driver-sql-unresolvable-where-column-refused.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,30 @@ export const entry: SemanticMigration = {
5151
+ 'matches, so on MySQL this condition still travels out as the raw dialect error; '
5252
+ 'widening that predicate would also hand MySQL the #3821 recoveries it has never had, '
5353
+ 'which is an accept-set change in the opposite direction and is filed separately.\n\n'
54+
+ 'Addendum 2026-08-16 (#8926, landed by PR #9061). The paragraph above is kept as the '
55+
+ 'state at registration; this amends it. MySQL joined the one shared predicate, so the '
56+
+ 'reach is now all three dialects this driver speaks, and a MySQL reader must NOT '
57+
+ 'conclude the migration does not apply — it applies exactly as it does on SQLite and '
58+
+ 'Postgres. Because that predicate serves both consumers at once, the accept set moved '
59+
+ "in BOTH directions on MySQL in one line, and both halves were ruled together (option "
60+
+ 'A, maintainer, 2026-08-16; a split predicate — the envelope while withholding the '
61+
+ 'recoveries — was considered and refused). (1) THE ENVELOPE: an unresolvable WHERE '
62+
+ 'column now refuses with the same `INVALID_FILTER` / 400 naming the column, instead of '
63+
+ "travelling out as the raw `ER_BAD_FIELD_ERROR` with the statement's bound literals "
64+
+ 'inlined — the #7929 disclosure shape closed on the last dialect that still had it. '
65+
+ '(2) THE RECOVERIES: MySQL also gained the #3821 projection and ORDER-BY recoveries it '
66+
+ 'had never had, so an unresolvable column in a projection or an ORDER BY now returns '
67+
+ 'recovered rows where it used to throw. The two halves arrive together because '
68+
+ '`ER_BAD_FIELD_ERROR` spells every clause position with one sentence — '
69+
+ "`Unknown column 'x' in 'where clause'` / `'field list'` / `'order clause'` — so all "
70+
+ 'three ride one arm of the predicate; that is pinned as the ruled direction by the '
71+
+ 'widened predicate sweep in `sql-driver-unresolvable-where-column-refusal.test.ts`. '
72+
+ 'The widening can never drop a predicate: every ladder rung is rebuilt from '
73+
+ '`buildBase()`, which unconditionally re-applies `query.where`. Unchanged by the '
74+
+ 'ruling: a DOTTED filter key is still classified per dialect (Postgres raises '
75+
+ 'undefined_table, which neither arm matches), the axis #8371 owns. The entry id, '
76+
+ 'surface and prescription are unchanged — this is a text amendment, not a new '
77+
+ 'migration.\n\n'
5478
+ 'This is a CODE-path API, not stored metadata, so — like '
5579
+ '`engine-dotted-projection-refused` and `engine-find-formula-filter-refused` — there '
5680
+ 'is no `sys_metadata` row for the D2 chain to rewrite and this entry is the '

‎packages/spec/src/migrations/registry.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5118,6 +5118,30 @@ const step18: MigrationStep = {
51185118
+ 'matches, so on MySQL this condition still travels out as the raw dialect error; '
51195119
+ 'widening that predicate would also hand MySQL the #3821 recoveries it has never had, '
51205120
+ 'which is an accept-set change in the opposite direction and is filed separately.\n\n'
5121+
+ 'Addendum 2026-08-16 (#8926, landed by PR #9061). The paragraph above is kept as the '
5122+
+ 'state at registration; this amends it. MySQL joined the one shared predicate, so the '
5123+
+ 'reach is now all three dialects this driver speaks, and a MySQL reader must NOT '
5124+
+ 'conclude the migration does not apply — it applies exactly as it does on SQLite and '
5125+
+ 'Postgres. Because that predicate serves both consumers at once, the accept set moved '
5126+
+ "in BOTH directions on MySQL in one line, and both halves were ruled together (option "
5127+
+ 'A, maintainer, 2026-08-16; a split predicate — the envelope while withholding the '
5128+
+ 'recoveries — was considered and refused). (1) THE ENVELOPE: an unresolvable WHERE '
5129+
+ 'column now refuses with the same `INVALID_FILTER` / 400 naming the column, instead of '
5130+
+ "travelling out as the raw `ER_BAD_FIELD_ERROR` with the statement's bound literals "
5131+
+ 'inlined — the #7929 disclosure shape closed on the last dialect that still had it. '
5132+
+ '(2) THE RECOVERIES: MySQL also gained the #3821 projection and ORDER-BY recoveries it '
5133+
+ 'had never had, so an unresolvable column in a projection or an ORDER BY now returns '
5134+
+ 'recovered rows where it used to throw. The two halves arrive together because '
5135+
+ '`ER_BAD_FIELD_ERROR` spells every clause position with one sentence — '
5136+
+ "`Unknown column 'x' in 'where clause'` / `'field list'` / `'order clause'` — so all "
5137+
+ 'three ride one arm of the predicate; that is pinned as the ruled direction by the '
5138+
+ 'widened predicate sweep in `sql-driver-unresolvable-where-column-refusal.test.ts`. '
5139+
+ 'The widening can never drop a predicate: every ladder rung is rebuilt from '
5140+
+ '`buildBase()`, which unconditionally re-applies `query.where`. Unchanged by the '
5141+
+ 'ruling: a DOTTED filter key is still classified per dialect (Postgres raises '
5142+
+ 'undefined_table, which neither arm matches), the axis #8371 owns. The entry id, '
5143+
+ 'surface and prescription are unchanged — this is a text amendment, not a new '
5144+
+ 'migration.\n\n'
51215145
+ 'This is a CODE-path API, not stored metadata, so — like '
51225146
+ '`engine-dotted-projection-refused` and `engine-find-formula-filter-refused` — there '
51235147
+ 'is no `sys_metadata` row for the D2 chain to rewrite and this entry is the '

0 commit comments

Comments
 (0)