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

xuyangzhong commented on CALCITE-4777:
--------------------------------------

I think i find the bug.So let me try to explain the following two question:

Q1: Why  {{CAST (0.42e0 AS BOOLEAN)}} instead of {{CAST (0.42 AS BOOLEAN)}} 
avoids the exception?

A1: The calcite treats that 0.42 as decimal(3,2), and 0.42e0 as double, so 
casting from a decimal to boolean throws exception, which caused by that the 
class decimal has no primitive class and tries to call the "booleanValue" 
function in "java.math.BigDecimal". But 0.42e0 is considered as a double, and 
calcite converts this cast to the expression "(boolean) 0.42", which seems to 
be right.

Q2: Why casting from double to boolean is always false, no matter the number is 
the zero or not?

A2: Now we know that {{(0.42e0 AS BOOLEAN)}}  will be convert to "(boolean) 
0.42", and calcite try to compute the literal value. So it comes to the 
function named "_public static ConstantExpression constant(Object value, Type 
type)_" in _org.apache.calcite.liq4j.tree.Expressions_, where the error happens.
{code:java}
// code placeholder
public static ConstantExpression constant(Object value, Type type) {
  if (value != null && type instanceof Class) {
    // Fix up value so that it matches type.
    Class clazz = (Class) type;
    Primitive primitive = Primitive.ofBoxOr(clazz);
    if (primitive != null) {
      clazz = primitive.boxClass;
    }
    if ((clazz == Float.class || clazz == Double.class)
        && value instanceof BigDecimal) {
      // Don't try to convert the value of float and double literals.
      // We'd experience rounding, e.g. 3.2 becomes 3.1999998.
    } else if (!clazz.isInstance(value)) {
      String stringValue = String.valueOf(value); //// 
here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      if (type == BigDecimal.class) {
        value = new BigDecimal(stringValue);
      }
      if (type == BigInteger.class) {
        value = new BigInteger(stringValue);
      }
      if (primitive != null) {
        value = primitive.parse(stringValue);
      }
    }
  }
  return new ConstantExpression(type, value);
}
{code}
In this  function, value is converted to string, and boolean's primitive class 
Boolean will parse a string "0.42" to boolean, so "false" is given out.

> Casting from decimal to boolean throws an exception
> ---------------------------------------------------
>
>                 Key: CALCITE-4777
>                 URL: https://issues.apache.org/jira/browse/CALCITE-4777
>             Project: Calcite
>          Issue Type: Bug
>            Reporter: xuyangzhong
>            Priority: Major
>         Attachments: calcite.png
>
>
> My sql is the following:
> {code:java}
> // code placeholder
> select * from test where cast (0.10915913549909961 as boolean){code}
>  
> I want to simplify the cast. An exception is thrown:
>  
> {code:java}
> // code placeholder
> Exception in thread "main" java.lang.RuntimeException: while resolving method 
> 'booleanValue' in class class java.math.BigDecimal at 
> org.apache.calcite.linq4j.tree.Expressions.call(Expressions.java:424) at 
> org.apache.calcite.linq4j.tree.Expressions.call(Expressions.java:435) at 
> org.apache.calcite.linq4j.tree.Expressions.unbox(Expressions.java:1453) at 
> org.apache.calcite.adapter.enumerable.EnumUtils.convert(EnumUtils.java:398) 
> at 
> org.apache.calcite.adapter.enumerable.EnumUtils.convert(EnumUtils.java:326) 
> at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.translateCast(RexToLixTranslator.java:543)
>  at 
> org.apache.calcite.adapter.enumerable.RexImpTable$CastImplementor.implementSafe(RexImpTable.java:2450)
>  at 
> org.apache.calcite.adapter.enumerable.RexImpTable$AbstractRexCallImplementor.genValueStatement(RexImpTable.java:2894)
>  at 
> org.apache.calcite.adapter.enumerable.RexImpTable$AbstractRexCallImplementor.implement(RexImpTable.java:2859)
>  at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.visitCall(RexToLixTranslator.java:1089)
>  at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.visitCall(RexToLixTranslator.java:90)
>  at org.apache.calcite.rex.RexCall.accept(RexCall.java:174) at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.visitLocalRef(RexToLixTranslator.java:975)
>  at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.visitLocalRef(RexToLixTranslator.java:90)
>  at org.apache.calcite.rex.RexLocalRef.accept(RexLocalRef.java:75) at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.translate(RexToLixTranslator.java:237)
>  at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.translate(RexToLixTranslator.java:231)
>  at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.translateList(RexToLixTranslator.java:823)
>  at 
> org.apache.calcite.adapter.enumerable.RexToLixTranslator.translateProjects(RexToLixTranslator.java:198)
>  at org.apache.calcite.rex.RexExecutorImpl.compile(RexExecutorImpl.java:90) 
> at org.apache.calcite.rex.RexExecutorImpl.compile(RexExecutorImpl.java:66) at 
> org.apache.calcite.rex.RexExecutorImpl.reduce(RexExecutorImpl.java:128) at 
> org.apache.calcite.rex.RexSimplify.simplifyCast(RexSimplify.java:2101) at 
> org.apache.calcite.rex.RexSimplify.simplify(RexSimplify.java:326) at 
> org.apache.calcite.rex.RexSimplify.simplifyUnknownAs(RexSimplify.java:287) at 
> org.apache.flink.table.examples.java.tests.CalciteTest.main(CalciteTest.java:47)
> Caused by: java.lang.NoSuchMethodException: 
> java.math.BigDecimal.booleanValue() at 
> java.lang.Class.getMethod(Class.java:1786) at 
> org.apache.calcite.linq4j.tree.Expressions.call(Expressions.java:421) ... 25 
> more
> {code}
> In order to avoid that I used the wrong rule or it caused by my bad coding, i 
> write the test case following:
>  
> {code:java}
> // code placeholder
> JavaTypeFactory typeFactory = new 
> JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
> RexBuilder rexBuilder = new RexBuilder(typeFactory);
> final RexSimplify simplify = new RexSimplify(rexBuilder, 
> RelOptPredicateList.EMPTY, RexUtil.EXECUTOR);
> RelDataType type = new BasicSqlType(typeFactory.getTypeSystem(), 
> SqlTypeName.BOOLEAN);
> RelDataType bb = new 
> BasicSqlType(typeFactory.getTypeSystem(),SqlTypeName.DECIMAL,18,17);
> SqlOperator op = new SqlCastFunction();
> RexLiteral lt = 
> rexBuilder.makeExactLiteral(BigDecimal.valueOf(0.10915913549909961),bb);
> List<RexNode> list = new ArrayList<>();
> list.add(lt);
> RexNode rexNode = rexBuilder.makeCall(type,op,list);
> simplify.simplifyUnknownAs(rexNode, RexUnknownAs.UNKNOWN);
> {code}
> and it throws the same exception.
>  
> Actually, the cast simplify operation will enter the function _translateCast_ 
> in _RexToLixTranslator_.It misses the "case BOOLEAN" and uses the convert in 
> EnumUtils. However, because the Decimal's Primitive is null and fromNumber is 
> true, the Expression's function named "call" calls the "booleanValue" 
> function in "java.math.BigDecimal", which does not actually exist. So the 
> exception is thrown. 
> I find in SqFunctions, we have a function "toBoolean(Number number)" (which 
> seems never to be used?). This function may very fit dealing with this 
> question, right?
>  



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to