ID |
|
|---|---|
Status |
Backlog |
Bucket |
bug |
Priority |
6 |
Theme |
codegen-correctness |
Created |
2026-08-20 |
Updated |
2026-08-20 |
A String-typed field over a jOOQ-enum column reads the Java constant name, not the database literal
A GraphQL field typed String over a column whose Postgres type is an enum returns the Java
constant name, not the value the database stores. The two coincide only while every literal of
that enum happens to be a valid Java identifier, so the defect is invisible in the current fixture
tree and appears the moment a consumer’s enum carries a literal like 'PG-13'.
Where it comes from
The emitted per-column fetcher reads the value off the record through the typed jOOQ field:
public static Object subjectKind(Object source) {
return ((Record) source).get(Tables.JTI_APP_ACCOUNT.SUBJECT_KIND);
}
On an enum column SUBJECT_KIND is a TableField<..., SubjectKind>, so get returns the Java
enum constant rather than a String. graphql-java’s String coercion then serialises it with
String.valueOf(...), and a jOOQ-generated enum does not override toString(): it carries the
database literal on getLiteral() only. So MpaaRating.PG_13 serialises as "PG_13" where the
column holds 'PG-13'.
The typed read is not itself wrong; the missing step is asking for the literal. record.get(field,
String.class) converts through the column’s binding and yields 'PG-13', which is what a
String-typed SDL field promises.
Scope
Only the projection direction is affected, and only where the SDL type is String. Two adjacent
paths are already correct and should stay untouched:
-
A field typed as a GraphQL enum goes through the enum-mapping path, which
EnumMappingResolver.validateEnumFiltergates and which resolves the namespace deliberately.FilmContent.rating: MpaaRatingis the tree’s instance. -
The discriminated
__discriminator__routing alias projects untyped (Object.class) and is read back withString.class, so it already carries the database literal. That asymmetry is deliberate and documented onDiscriminatedTableFragments.
Whether a String field over an enum column should be rejected rather than converted is the
design question this item has to answer: converting is the least surprising behavior and the SDL
type is a plain promise, but rejecting would push authors toward declaring the GraphQL enum and
getting the typed path. The write direction already converts (a String mutation input binds
through DSL.val(value, col.getDataType())), which argues for symmetry.
Provenance
Surfaced while converting two discriminated fixture families to Postgres enum discriminators. The
spec being implemented predicted that a String field over the converted column would read back as
the literal and asked for a finding rather than a fixture retype if it turned out otherwise; it
does turn out otherwise. The conversion itself is unaffected because every converted literal
(FILM, SHORT, PODCAST, APP, PERSON) is a valid Java identifier, so the two spellings
coincide and Subject.subjectKind keeps answering correctly.
Reproduction sketch
Give an interface over film a String field on rating (mpaa_rating), seed a PG-13 row, and
query it: the field answers "PG_13". The existing Subject.subjectKind: String! field over
jti_subject.subject_kind is the same shape with literals that hide the divergence.