[
https://issues.apache.org/jira/browse/CALCITE-7690?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Jerome Isaac Haltom updated CALCITE-7690:
-----------------------------------------
Description:
DELETE against a table with a single NOT NULL column of a primitive type fails
at runtime. Present on master; 1.42.0 is unaffected.
{code:sql}
create table t (i int not null);
insert into t values (1);
delete from t where i = 1;
{code}
{noformat}
Caused by: org.codehaus.commons.compiler.CompileException: Cannot cast
"java.lang.Object" to "int"
{noformat}
{{EnumerableTableModify.deleteFromCollection}} casts the sink row, declared as
{{Object}}, to the table's Java row type. For a single-column table that type
is a primitive, so the generated code is {{(int) sinkRow}}. javac accepts such
a cast; Janino does not.
Fix: box the target type with {{Primitive.box}}. Multi-column DELETE is
unaffected, which is why the tests added by CALCITE-7510 -- all two-column --
did not catch it.
was:
DELETE against a table with exactly one NOT NULL column of a primitive type
fails at runtime. Reproduced on master (fc95bb0e6) and on 1.43.0-SNAPSHOT;
1.42.0 is unaffected.
{code:sql}
create table t (i int not null);
insert into t values (1);
delete from t where i = 1;
{code}
{noformat}
java.lang.RuntimeException: Error while compiling generated Java code:
...
Caused by: org.codehaus.commons.compiler.CompileException: Cannot cast
"java.lang.Object" to "int"
{noformat}
h3. Cause
{{EnumerableTableModify.deleteFromCollection}} declares the sink row as
{{Object}} and then casts it to the table's Java row type:
{code:java}
final ParameterExpression sinkRow = Expressions.parameter(Object.class,
"sinkRow");
final Expression typedSinkRow =
Expressions.convert_(sinkRow, tablePhysType.getJavaRowType());
{code}
For a single-column table that row type is a primitive.
{{EnumerableTableScan.deduceFormat}} returns ARRAY, because the table's element
type is {{Object[]}}, and the optimising {{PhysTypeImpl.of}} then rewrites
ARRAY to SCALAR for a one-field row type, so {{getJavaRowType()}} is {{int}}.
The generated source is therefore {{(int) sinkRow}}.
{{(int) someObject}} is legal Java -- JLS 5.5 permits a narrowing reference
conversion followed by an unboxing conversion, and javac compiles it -- but
Janino does not implement it, and Janino is what compiles the generated code.
Measured against the Janino on the classpath:
{noformat}
(int) o -> Cannot cast "java.lang.Object" to "int"
(java.lang.Integer) o -> compiles
((java.lang.Integer) o).intValue() -> compiles
{noformat}
h3. Suggested fix
Box the target type:
{code:java}
final Expression typedSinkRow =
Expressions.convert_(sinkRow,
Primitive.box(tablePhysType.getJavaRowType()));
{code}
{{Primitive.box}} leaves {{Object[]}} unchanged, so the multi-column case
generates exactly what it generates today. The sink values are read with a
storage type of {{Object}}, so boxing loses nothing.
h3. Notes
Introduced by CALCITE-7510. Every test that change added uses a two-column
table ({{create table t (i int not null, j int not null)}}), so the
single-column shape was never exercised. A nullable single column is
unaffected, because its Java row type is already {{Integer}}.
I have a patch with tests in {{ServerTest}} and will open a PR.
> DELETE on a single-column table fails with "Cannot cast java.lang.Object to
> int"
> --------------------------------------------------------------------------------
>
> Key: CALCITE-7690
> URL: https://issues.apache.org/jira/browse/CALCITE-7690
> Project: Calcite
> Issue Type: Bug
> Affects Versions: 1.43.0
> Reporter: Jerome Isaac Haltom
> Priority: Major
> Labels: pull-request-available
>
> DELETE against a table with a single NOT NULL column of a primitive type
> fails at runtime. Present on master; 1.42.0 is unaffected.
> {code:sql}
> create table t (i int not null);
> insert into t values (1);
> delete from t where i = 1;
> {code}
> {noformat}
> Caused by: org.codehaus.commons.compiler.CompileException: Cannot cast
> "java.lang.Object" to "int"
> {noformat}
> {{EnumerableTableModify.deleteFromCollection}} casts the sink row, declared
> as {{Object}}, to the table's Java row type. For a single-column table that
> type is a primitive, so the generated code is {{(int) sinkRow}}. javac
> accepts such a cast; Janino does not.
> Fix: box the target type with {{Primitive.box}}. Multi-column DELETE is
> unaffected, which is why the tests added by CALCITE-7510 -- all two-column --
> did not catch it.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)