ID |
|
|---|---|
Status |
Backlog |
Bucket |
cleanup |
Priority |
5 |
Theme |
model-cleanup |
Created |
2026-08-18 |
Updated |
2026-08-18 |
Catalog-vs-catalog name comparisons in Java fold case as a hedge; make them exact
Problem
The generator compares catalog names case-insensitively in places where both operands come from the
same jOOQ catalog reading and are therefore already canonical. Case-insensitive matching is a
semantic only where an author’s written name meets the catalog (an author may write film for
FILM); between two values the catalog itself produced, the fold is a hedge inherited from the
legacy generator, which folded at every comparison because it never established which strings were
canonical. A hedge is not harmless: on a database using quoted identifiers, two genuinely distinct
columns can differ only by case, and a folded comparison silently equates them.
The census, all comparing ColumnRef.sqlName() (or a catalog table name) against another value from
the same catalog:
-
FieldBuilder(key-column agreement in the reverse-join arm) -
TypeBuilder(column-list equality) -
BuildContext(three key-column filters and one start-vs-target table-name check) -
NodeIdLeafResolver(three key-column alignment loops; the count is exact at:357,:522and:561, and the habitat is moving, see the note below) -
JoinedTableReprojection(source-side slot lookup) -
MutationFieldandProducerBinding(source-vs-target column-pair checks)
One census entry changes habitat rather than disappearing (2026-08-20). The @nodeId decode
resolution became store relations on trunk, and the alignment those three loops perform moves into
them, where the fold happens in SQL. The relation-move item states the same crossing from the other
side, that every reader matching against the resolved key column’s spelling folds case at the
crossing. So a census keyed to Java call sites will silently lose this entry once the resolver
becomes a reader of rows; re-point it at the decode relations then rather than deleting the row.
Detail: roadmap/audits/2026-08-20-nodeid-relation-impact-sweep.md, Finding 7.
Not in scope, named so silence does not read as a claim: OrderByResolver’s `"DESC" token is an
authored value with its own vocabulary; TenantBindingIndex’s two-tier match compares an authored
configuration name against the catalog, which is the resolution stratum’s job (R697) and retires
there; `ConnectionHelperClassGenerator emits a runtime lookup into generated code, an author-facing
runtime semantic that changes independently if at all.
Relationship to the filed case-drift defects
R358 (table names, shipped) and R359 (its column sibling, Backlog) document the drift this hedging
already caused: exact and folded comparisons of the same identity string coexisting, with the live
.equals site the odd one out. R359 leaves the unification direction open (a shared predicate, or a
canonical-identity pass). This item states the direction: where both operands are catalog-canonical,
the shared predicate is exact equality, and the fold is removed rather than standardized. Whether
R359 folds into this item or lands first as the guard is a Spec question; the two must not land
opposite conventions.
Shape of the fix
Per-site reachability call first, in R359’s own manner: some operand pairs may share provenance and be non-divergent, in which case the change is provably behavior-preserving; where a site is reachable with divergent-case operands, the change is a bug fix on quoted-identifier catalogs and wants a test. Then one sweep converting the census to exact comparison, plus whatever guard R359’s Spec settles on so the next hedge fails review mechanically.