Description
Expected behavior
If I call withNullability(Nullability.NULL)
on the ForcedType, I would expect the conversion to be invoked every time – even if the value is null.
In the problem reproduction below, I would expect the output to be
(unimportant JOOQ prints...)
name: Alan Turing age: Some(41)
name: Ada Lovelace age: None
When running the reproduction, I also tried putting a breakpoint into OptionIntConverter.from
to see where it gets called from. Here is the call stack that I got:
OptionIntConverter.from(Integer): Option (/Users/martin/development/jooq-option-problem/jooq-generated/src/main/scala/converters/OptionIntConverter.scala:8)
OptionIntConverter.from(Object): Object (/Users/martin/development/jooq-option-problem/jooq-generated/src/main/scala/converters/OptionIntConverter.scala:5)
ContextConverter$1.from(Object,ConverterContext): Object (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/ContextConverter.java:147)
DefaultBinding$DefaultRecordBinding.pgFromString(BindingScope,Field,String): Object (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/DefaultBinding.java:4286)
DefaultBinding$DefaultRecordBinding.pgSetValue(BindingScope,Record,Field,String): void (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/DefaultBinding.java:4411)
DefaultBinding$DefaultRecordBinding.lambda$pgNewRecord$13(BindingScope,List,Record): Record (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/DefaultBinding.java:4403)
DefaultBinding$DefaultRecordBinding$$Lambda/0x00000008002c04e0.apply(Object): Object (Unknown Source:-1)
RecordDelegate.operate(ThrowingFunction): Record (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/RecordDelegate.java:144)
DefaultBinding$DefaultRecordBinding.pgNewRecord(BindingScope,Class,AbstractRow,Object): Record (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/DefaultBinding.java:4399)
DefaultBinding$DefaultRecordBinding.get0(BindingGetResultSetContext): Record (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/DefaultBinding.java:4172)
DefaultBinding$DefaultRecordBinding.get0(BindingGetResultSetContext): Object (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/DefaultBinding.java:4084)
DefaultBinding$InternalBinding.get(BindingGetResultSetContext): void (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/DefaultBinding.java:1190)
CursorImpl$CursorRecordInitialiser.setValue(AbstractRecord,Field,int): void (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/CursorImpl.java:1582)
CursorImpl$CursorRecordInitialiser.apply(AbstractRecord): AbstractRecord (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/CursorImpl.java:1518)
CursorImpl$CursorRecordInitialiser.apply(Object): Object (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/CursorImpl.java:1433)
RecordDelegate.operate(ThrowingFunction): Record (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/RecordDelegate.java:144)
CursorImpl$CursorIterator.fetchNext(): Record (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/CursorImpl.java:1390)
CursorImpl$CursorIterator.hasNext(): boolean (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/CursorImpl.java:1366)
CursorImpl.fetchNext(int): Result (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/CursorImpl.java:174)
AbstractCursor.fetch(int): Result (file:///Users/martin/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jooq/jooq/3.19.15/jooq-3.19.15-sources.jar!/org/jooq/impl/AbstractCursor.java:177)
What I found interesting is the frame DefaultBinding:4286
:
else if (type == Integer.class)
return converter.from((T) Integer.valueOf(string), ctx.converterContext());
If we scroll up through the different cases of the else-ifs, we see that on the very top there is:
if (string == null)
return null;
In other words, if the value is null, it will indeed return null directly, whereas in other cases, the converter is invoked.
Actual behavior
The conversion is only invoked if the value is not null. If the value is null, the conversion is skipped entirely, and the value remains null.
In the problem reproduction below, the actual output is:
(unimportant JOOQ prints...)
name: Alan Turing age: Some(41)
name: Ada Lovelace age: null
Steps to reproduce the problem
- clone from https://github.com/KuceraMartin/jooq-option-problem
- run
sbt generateJooq
- run
sbt app/run
jOOQ Version
3.19.15
Database product and version
PostgreSQL 17
Java Version
No response
JDBC / R2DBC driver name and version (include name if unofficial driver)
No response