wasabii opened a new pull request, #5150:
URL: https://github.com/apache/calcite/pull/5150

   https://issues.apache.org/jira/browse/CALCITE-7690
   
   `DELETE` against a table with exactly one `NOT NULL` column of a primitive 
type fails at runtime:
   
   ```sql
   create table t (i int not null);
   insert into t values (1);
   delete from t where i = 1;
   ```
   
   ```
   java.lang.RuntimeException: Error while compiling generated Java code:
   ...
   Caused by: org.codehaus.commons.compiler.CompileException: Cannot cast 
"java.lang.Object" to "int"
   ```
   
   ### Cause
   
   `EnumerableTableModify.deleteFromCollection` declares the sink row as 
`Object` and then casts it to the
   table's Java row type:
   
   ```java
   final ParameterExpression sinkRow = Expressions.parameter(Object.class, 
"sinkRow");
   final Expression typedSinkRow =
       Expressions.convert_(sinkRow, tablePhysType.getJavaRowType());
   ```
   
   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:
   
   ```
   (int) o                              -> Cannot cast "java.lang.Object" to 
"int"
   (java.lang.Integer) o                -> compiles
   ((java.lang.Integer) o).intValue()   -> compiles
   ```
   
   ### Fix
   
   Box the target type. `Primitive.box` leaves `Object[]` unchanged, so the 
multi-column case generates exactly
   what it generated before, and the sink values are read with a storage type 
of `Object`, so boxing loses
   nothing.
   
   ### Tests
   
   `ServerTest.testDeleteSingleColumn` and 
`ServerTest.testDeleteSingleNullableColumn`. Every test added by
   [CALCITE-7510] uses a two-column table (`create table t (i int not null, j 
int not null)`), so the
   single-column shape was never exercised. The nullable case was never broken 
— its Java row type is already
   a box — and is covered to document that boundary.
   
   Verified with `./gradlew :server:test`: the two new tests pass with the fix, 
and `testDelete`,
   `testDeleteDuplicateRows` and `testUpdate` are unaffected. `checkstyleMain`, 
`checkstyleTest` and
   `autostyleCheck` pass for `core` and `server`.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to