[ 
https://issues.apache.org/jira/browse/CALCITE-6265?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17836187#comment-17836187
 ] 

Ruben Q L edited comment on CALCITE-6265 at 4/11/24 1:45 PM:
-------------------------------------------------------------

FYI, this fix caused a few regressions on my project:
A) one test that used to return 1 row now returns 0 rows
B) a couple of tests that used to work fine now fail with a ClassCastException 
on the dynamic code

After some investigation, I'd say A seems a real regression on Calcite, whereas 
B could be arguably on my side; but I share this info because probably both 
could happen to other projects if we release the next version with this patch 
as it is.

A) The problem with the test that no longer returns the expected 1 row is as 
follows:
- A table with a decimal field, there is one column with value {{BigDecimal 
DECIMAL1 = new BigDecimal("299792.457999999985");}}
- A simple query: SELECT t.id FROM myTable t WHERE t.myDecimalField = ?
- I set the parameter value to DECIMAL1 
- Execute the query, should get 1 row, it gets nothing.

The problem is that, before the patch, the dynamic code would simply "read" the 
parameter value from the root context and use it, [but now with the new code 
there is this cast logic into Number and back to 
BigDecimal|https://github.com/apache/calcite/blob/e7c1f0c9baecef62289facf9aaa03bdff950b938/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java#L1396]
 (unnecessary in this case, since everything is BigDecimal), which leads to the 
following dynamic code:
{code}
(Number) root.get("?1") == null ? (java.math.BigDecimal) null : 
java.math.BigDecimal.valueOf(((Number) root.get("?1")).longValue())
{code}
The problem is that doing {{BigDecimal.valueOf(myBigDecimal.longValue())}} does 
not necessarily bring back the original value of myBigDecimal, since 
BigDecimal#longValue can lose information, and I guess this is what is 
happening here.

B) The issue with the ClassCastException (BigDecimal cannot be cast to Integer) 
is also related to the new casting in dynamic code, but it is a bit more 
subtle, and it seems related to some internal Hoisting that we have in place on 
my project (transforming literals into parameters to increase the chances of 
internal cache hits when converting a logical plan into physical plan). The 
thing is, in the literal=>parameter conversion, we use 
[RexLiteral#getValue3|https://github.com/apache/calcite/blob/e7c1f0c9baecef62289facf9aaa03bdff950b938/core/src/main/java/org/apache/calcite/rex/RexLiteral.java#L989]
 (which as stated on its javadoc returns "the value of this literal, in the 
form that the rex-to-lix translator wants it") to extract the value (and use it 
as parameter value). So far it worked like charm. But with this change, the 
ClassCastException arises because actually getValue3 of a RexLiteral with type 
= BasicSqlTypeINTEGER, returns the value as a BigDecimal (as it is stored in 
RexLiteral#value field), because its typeName is SqlTypeName#DECIMAL; btw this 
RexLiteral is created by the standard behavior that we get if we create a 
RexLiteral from an Integer via Calcite's RelBuilder#literal(IntegerValue).

 






was (Author: rubenql):
FYI, this fix caused a few regressions on my project:
A) one test that used to return 1 row now returns 0 rows
B) a couple of tests that used to work fine now fail with a ClassCastException 
on the dynamic code

After some investigation, I'd say A seems a real regression on Calcite, whereas 
B could be arguably on my side; but I share this info because probably both 
could happen to other projects if we release the next version with this patch 
as it is.

A) The problem with the test that no longer returns the expected 1 row is as 
follows:
- A table with a decimal field, there is one column with value {{BigDecimal 
DECIMAL1 = new BigDecimal("299792.457999999985");}}
- A simple query: SELECT t.id FROM myTable t WHERE t.myDecimalValue = ?
- I set the parameter value to DECIMAL1 
- Execute the query, should get 1 row, it gets nothing.

The problem is that, before the patch, the dynamic code would simply "read" the 
parameter value from the root context and use it, [but now with the new code 
there is this cast logic into Number and back to 
BigDecimal|https://github.com/apache/calcite/blob/e7c1f0c9baecef62289facf9aaa03bdff950b938/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java#L1396]
 (unnecessary in this case, since everything is BigDecimal), which leads to the 
following dynamic code:
{code}
(Number) root.get("?1") == null ? (java.math.BigDecimal) null : 
java.math.BigDecimal.valueOf(((Number) root.get("?1")).longValue())
{code}
The problem is that doing {{BigDecimal.valueOf(myBigDecimal.longValue())}} does 
not necessarily bring back the original value of myBigDecimal, since 
BigDecimal#longValue can lose information, and I guess this is what is 
happening here.

B) The issue with the ClassCastException (BigDecimal cannot be cast to Integer) 
is also related to the new casting in dynamic code, but it is a bit more 
subtle, and it seems related to some internal Hoisting that we have in place on 
my project (transforming literals into parameters to increase the chances of 
internal cache hits when converting a logical plan into physical plan). The 
thing is, in the literal=>parameter conversion, we use 
[RexLiteral#getValue3|https://github.com/apache/calcite/blob/e7c1f0c9baecef62289facf9aaa03bdff950b938/core/src/main/java/org/apache/calcite/rex/RexLiteral.java#L989]
 (which as stated on its javadoc returns "the value of this literal, in the 
form that the rex-to-lix translator wants it") to extract the value (and use it 
as parameter value). So far it worked like charm. But with this change, the 
ClassCastException arises because actually getValue3 of a RexLiteral with type 
= BasicSqlTypeINTEGER, returns the value as a BigDecimal (as it is stored in 
RexLiteral#value field), because its typeName is SqlTypeName#DECIMAL; btw this 
RexLiteral is created by the standard behavior that we get if we create a 
RexLiteral from an Integer via Calcite's RelBuilder#literal(IntegerValue).

 





> Type coercion is failing for numeric values in prepared statements
> ------------------------------------------------------------------
>
>                 Key: CALCITE-6265
>                 URL: https://issues.apache.org/jira/browse/CALCITE-6265
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>            Reporter: Tim Nieradzik
>            Assignee: Tim Nieradzik
>            Priority: Major
>              Labels: pull-request-available
>             Fix For: 1.37.0
>
>
> Given a column of type {{{}INT{}}}. When providing a {{short}} value as a 
> placeholder in a prepared statement, a {{ClassCastException}} is thrown.
> h2. Test case
> {{final String sql =}}
> {{    "select \"empid\" from \"hr\".\"emps\" where \"empid\" in (?, ?)";}}{{  
>   CalciteAssert.hr()}}
> {{    .query(sql)}}
> {{    .consumesPreparedStatement(p -> {}}
> {{        p.setShort(1, (short) 100);}}
> {{        p.setShort(2, (short) 110);}}
> {{    })}}
> {{    .returnsUnordered("empid=100", "empid=110");}}
> h2. Stack trace
> {{java.lang.ClassCastException: class java.lang.Short cannot be cast to class 
> java.lang.Integer (java.lang.Short and java.lang.Integer are in module 
> java.base of loader 'bootstrap')}}
> {{    at Baz$1$1.moveNext(Unknown Source)}}
> {{    at 
> org.apache.calcite.linq4j.Linq4j$EnumeratorIterator.<init>(Linq4j.java:679)}}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to