[ 
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)

Reply via email to