This is an automated email from the ASF dual-hosted git repository.

mbudiu pushed a commit to branch issue2067
in repository https://gitbox.apache.org/repos/asf/calcite.git

commit 9828ad222a68475364e1cfaad150150d43d714b0
Author: Mihai Budiu <[email protected]>
AuthorDate: Wed Jan 31 04:01:20 2024 -0800

    [CALCITE-2067] RexBuilder can't handle NaN,Infinity double constants
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../apache/calcite/rel/rel2sql/SqlImplementor.java |   2 +-
 .../java/org/apache/calcite/rex/RexBuilder.java    |  40 ++++
 .../java/org/apache/calcite/rex/RexLiteral.java    |  90 +++++--
 .../java/org/apache/calcite/tools/RelBuilder.java  |   2 +-
 .../main/java/org/apache/calcite/util/Util.java    |  15 ++
 .../org/apache/calcite/rex/RexExecutorTest.java    |   7 +-
 .../org/apache/calcite/test/RelOptRulesTest.java   |  25 ++
 .../org/apache/calcite/test/RelOptRulesTest.xml    |  36 ++-
 core/src/test/resources/sql/misc.iq                | 266 +++++++++++++++++++++
 .../org/apache/calcite/piglet/PigRelExVisitor.java |   8 +-
 .../java/org/apache/calcite/test/PigRelExTest.java |   5 +-
 .../java/org/apache/calcite/test/PigRelOpTest.java |   2 +-
 12 files changed, 464 insertions(+), 34 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java 
b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index 8b26d90bf7..0884ca94cb 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -1498,7 +1498,7 @@ public abstract class SqlImplementor {
     case EXACT_NUMERIC: {
       if (SqlTypeName.APPROX_TYPES.contains(typeName)) {
         return SqlLiteral.createApproxNumeric(
-            castNonNull(literal.getValueAs(BigDecimal.class)).toPlainString(), 
POS);
+            castNonNull(literal.getValueAs(Double.class)).toString(), POS);
       } else {
         return SqlLiteral.createExactNumeric(
             castNonNull(literal.getValueAs(BigDecimal.class)).toPlainString(), 
POS);
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java 
b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
index 5cc4b21170..e461b501d8 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -1427,6 +1427,14 @@ public class RexBuilder {
     return makeApproxLiteral(bd, 
typeFactory.createSqlType(SqlTypeName.DOUBLE));
   }
 
+  /**
+   * Creates a double-precision literal from a double value.
+   */
+  public RexLiteral makeApproxLiteral(Double d) {
+    return makeApproxLiteral(d, typeFactory.createSqlType(SqlTypeName.DOUBLE));
+  }
+
+
   /**
    * Creates an approximate numeric literal (double or float).
    *
@@ -1440,6 +1448,20 @@ public class RexBuilder {
     return makeLiteral(bd, type, SqlTypeName.DOUBLE);
   }
 
+  /**
+   * Creates an approximate numeric literal (double or float)
+   * from a Double value.
+   *
+   * @param val  literal value
+   * @param type approximate numeric type
+   * @return new literal
+   */
+  public RexLiteral makeApproxLiteral(Double val, RelDataType type) {
+    assert SqlTypeFamily.APPROXIMATE_NUMERIC.getTypeNames().contains(
+        type.getSqlTypeName());
+    return makeLiteral(val, type, SqlTypeName.DOUBLE);
+  }
+
   /**
    * Creates a search argument literal.
    */
@@ -2013,6 +2035,9 @@ public class RexBuilder {
     case FLOAT:
     case REAL:
     case DOUBLE:
+      if (value instanceof Double) {
+        return makeApproxLiteral((Double) value, type);
+      }
       return makeApproxLiteral((BigDecimal) value, type);
     case BOOLEAN:
       return (Boolean) value ? booleanTrue : booleanFalse;
@@ -2159,11 +2184,26 @@ public class RexBuilder {
               type.getSqlTypeName());
       return new BigDecimal(((Number) o).longValue());
     case REAL:
+      if (o instanceof BigDecimal) {
+        return o;
+      }
+      // Float values are stored as Doubles
+      if (o instanceof Float) {
+        return ((Float) o).doubleValue();
+      }
+      if (o instanceof Double) {
+        return o;
+      }
+      return new BigDecimal(((Number) o).doubleValue(), MathContext.DECIMAL32)
+          .stripTrailingZeros();
     case FLOAT:
     case DOUBLE:
       if (o instanceof BigDecimal) {
         return o;
       }
+      if (o instanceof Double) {
+        return o;
+      }
       return new BigDecimal(((Number) o).doubleValue(), MathContext.DECIMAL64)
           .stripTrailingZeros();
     case CHAR:
diff --git a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java 
b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
index fd34c99674..bcf00b41f4 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
@@ -112,9 +112,11 @@ import static java.util.Objects.requireNonNull;
  * <td>{@link BigDecimal}</td>
  * </tr>
  * <tr>
- * <td>{@link SqlTypeName#DOUBLE}</td>
+ * <td>{@link SqlTypeName#DOUBLE},
+ *     {@link SqlTypeName#REAL},
+ *     {@link SqlTypeName#FLOAT}</td>
  * <td>Approximate number, for example <code>6.023E-23</code>.</td>
- * <td>{@link BigDecimal}</td>
+ * <td>{@link BigDecimal} or {@link Double}.</td>
  * </tr>
  * <tr>
  * <td>{@link SqlTypeName#DATE}</td>
@@ -193,7 +195,7 @@ public class RexLiteral extends RexNode {
   /**
    * The value of this literal. Must be consistent with its type, as per
    * {@link #valueMatchesType}. For example, you can't store an
-   * {@link Integer} value here just because you feel like it -- all numbers 
are
+   * {@link Integer} value here just because you feel like it -- all exact 
numbers are
    * represented by a {@link BigDecimal}. But since this field is private, it
    * doesn't really matter how the values are stored.
    */
@@ -204,12 +206,9 @@ public class RexLiteral extends RexNode {
    */
   private final RelDataType type;
 
-  // TODO jvs 26-May-2006:  Use SqlTypeFamily instead; it exists
-  // for exactly this purpose (to avoid the confusion which results
-  // from overloading SqlTypeName).
   /**
    * An indication of the broad type of this literal -- even if its type isn't
-   * a SQL type. Sometimes this will be different than the SQL type; for
+   * a SQL type. Sometimes this will be different from the SQL type; for
    * example, all exact numbers, including integers have typeName
    * {@link SqlTypeName#DECIMAL}. See {@link #valueMatchesType} for the
    * definitive story.
@@ -294,10 +293,10 @@ public class RexLiteral extends RexNode {
   }
 
   /**
-   * Returns true if {@link RexDigestIncludeType#OPTIONAL} digest would 
include data type.
+   * Returns whether {@link RexDigestIncludeType} digest would include data 
type.
    *
    * @see RexCall#computeDigest(boolean)
-   * @return true if {@link RexDigestIncludeType#OPTIONAL} digest would 
include data type
+   * @return whether {@link RexDigestIncludeType} digest would include data 
type
    */
   @RequiresNonNull("type")
   RexDigestIncludeType digestIncludesType(
@@ -328,11 +327,12 @@ public class RexLiteral extends RexNode {
       }
       // fall through
     case DECIMAL:
+    case BIGINT:
+      return value instanceof BigDecimal;
     case DOUBLE:
     case FLOAT:
     case REAL:
-    case BIGINT:
-      return value instanceof BigDecimal;
+      return value instanceof BigDecimal || value instanceof Double;
     case DATE:
       return value instanceof DateString;
     case TIME:
@@ -527,8 +527,8 @@ public class RexLiteral extends RexNode {
       }
       return litmus.succeed();
     } else if (o instanceof Map) {
-      @SuppressWarnings("unchecked") final Map<Object, Object> map = (Map) o;
-      for (Map.Entry entry : map.entrySet()) {
+      @SuppressWarnings("unchecked") final Map<Object, Object> map = 
(Map<Object, Object>) o;
+      for (Map.Entry<Object, Object> entry : map.entrySet()) {
         if (!validConstant(entry.getKey(), litmus)) {
           return litmus.fail("not a constant: {}", entry.getKey());
         }
@@ -660,8 +660,14 @@ public class RexLiteral extends RexNode {
       break;
     case DOUBLE:
     case FLOAT:
-      assert value instanceof BigDecimal;
-      sb.append(Util.toScientificNotation((BigDecimal) value));
+      if (value instanceof BigDecimal) {
+        sb.append(Util.toScientificNotation((BigDecimal) value));
+      } else {
+        assert value instanceof Double;
+        Double d = (Double) value;
+        String repr = Util.toScientificNotation(d);
+        sb.append(repr);
+      }
       break;
     case BIGINT:
       assert value instanceof BigDecimal;
@@ -1075,22 +1081,56 @@ public class RexLiteral extends RexNode {
     case BIGINT:
     case INTEGER:
     case SMALLINT:
-    case TINYINT:
-    case DOUBLE:
-    case REAL:
-    case FLOAT:
+    case TINYINT: {
+      BigDecimal bd = (BigDecimal) value;
       if (clazz == Long.class) {
-        return clazz.cast(((BigDecimal) value).longValue());
+        return clazz.cast(bd.longValue());
       } else if (clazz == Integer.class) {
-        return clazz.cast(((BigDecimal) value).intValue());
+        return clazz.cast(bd.intValue());
       } else if (clazz == Short.class) {
-        return clazz.cast(((BigDecimal) value).shortValue());
+        return clazz.cast(bd.shortValue());
       } else if (clazz == Byte.class) {
-        return clazz.cast(((BigDecimal) value).byteValue());
+        return clazz.cast(bd.byteValue());
       } else if (clazz == Double.class) {
-        return clazz.cast(((BigDecimal) value).doubleValue());
+        return clazz.cast(bd.doubleValue());
       } else if (clazz == Float.class) {
-        return clazz.cast(((BigDecimal) value).floatValue());
+        return clazz.cast(bd.floatValue());
+      }
+      break;
+    }
+    case DOUBLE:
+    case REAL:
+    case FLOAT:
+      if (value instanceof Double) {
+        Double d = (Double) value;
+        if (clazz == Long.class) {
+          return clazz.cast(d.longValue());
+        } else if (clazz == Integer.class) {
+          return clazz.cast(d.intValue());
+        } else if (clazz == Short.class) {
+          return clazz.cast(d.shortValue());
+        } else if (clazz == Byte.class) {
+          return clazz.cast(d.byteValue());
+        } else if (clazz == Double.class) {
+          return clazz.cast(d);
+        } else if (clazz == Float.class) {
+          return clazz.cast(d.floatValue());
+        }
+      } else {
+        BigDecimal bd = (BigDecimal) value;
+        if (clazz == Long.class) {
+          return clazz.cast(bd.longValue());
+        } else if (clazz == Integer.class) {
+          return clazz.cast(bd.intValue());
+        } else if (clazz == Short.class) {
+          return clazz.cast(bd.shortValue());
+        } else if (clazz == Byte.class) {
+          return clazz.cast(bd.byteValue());
+        } else if (clazz == Double.class) {
+          return clazz.cast(bd.doubleValue());
+        } else if (clazz == Float.class) {
+          return clazz.cast(bd.floatValue());
+        }
       }
       break;
     case DATE:
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java 
b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 86472f9d22..e6927f22a1 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -480,7 +480,7 @@ public class RelBuilder {
       return rexBuilder.makeExactLiteral((BigDecimal) value);
     } else if (value instanceof Float || value instanceof Double) {
       return rexBuilder.makeApproxLiteral(
-          BigDecimal.valueOf(((Number) value).doubleValue()));
+          ((Number) value).doubleValue());
     } else if (value instanceof Number) {
       return rexBuilder.makeExactLiteral(
           BigDecimal.valueOf(((Number) value).longValue()));
diff --git a/core/src/main/java/org/apache/calcite/util/Util.java 
b/core/src/main/java/org/apache/calcite/util/Util.java
index a3fcafaa24..eda2b36e76 100644
--- a/core/src/main/java/org/apache/calcite/util/Util.java
+++ b/core/src/main/java/org/apache/calcite/util/Util.java
@@ -534,6 +534,21 @@ public class Util {
     pw.println();
   }
 
+  /**
+   * Formats a double value to a String ensuring that the output
+   * is in scientific notation if the value is not "special".
+   * (Special values include infinities and NaN.)
+   */
+  public static String toScientificNotation(Double d) {
+    String repr = Double.toString(d);
+    if (!repr.toLowerCase(Locale.ENGLISH).contains("e")
+        && !d.isInfinite()
+        && !d.isNaN()) {
+      repr += "E0";
+    }
+    return repr;
+  }
+
   /**
    * Formats a {@link BigDecimal} value to a string in scientific notation For
    * example<br>
diff --git a/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java 
b/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java
index 8387269782..b233ac5139 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java
@@ -389,11 +389,14 @@ class RexExecutorTest {
       final RexCall first =
           (RexCall) rexBuilder.makeCall(SqlStdOperatorTable.LN,
           rexBuilder.makeLiteral(3, integer, true));
+      // Division by zero causes an exception during evaluation
       final RexCall second =
-          (RexCall) rexBuilder.makeCall(SqlStdOperatorTable.LN,
-          rexBuilder.makeLiteral(-2, integer, true));
+          (RexCall) rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE_INTEGER,
+              rexBuilder.makeLiteral(-2, integer, true),
+              rexBuilder.makeLiteral(0, integer, true));
       executor.reduce(rexBuilder, ImmutableList.of(first, second),
           reducedValues);
+      System.out.println(reducedValues);
       assertThat(reducedValues, hasSize(2));
       assertThat(reducedValues.get(0), instanceOf(RexCall.class));
       assertThat(reducedValues.get(1), instanceOf(RexCall.class));
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java 
b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 6c672b72ab..c578b92ebd 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -3341,6 +3341,31 @@ class RelOptRulesTest extends RelOptTestBase {
         .check();
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-2067";>
+   * RexBuilder can't handle NaN,Infinity double constants</a>. */
+  @Test public void testDoubleReduction() {
+    // Without the fix for CALCITE-2067 the result returned below is
+    // 1008618.49.  Ironically, that result is more accurate; however
+    // it is not the result returned by the pow() function, which is
+    // 1008618.4899999999
+    final String sql = "SELECT power(1004.3, 2)";
+    sql(sql)
+        .withRule(CoreRules.PROJECT_REDUCE_EXPRESSIONS)
+        .check();
+  }
+
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-2067";>
+   * RexBuilder can't handle NaN,Infinity double constants</a>. */
+  @Test public void testDoubleReduction2() {
+    // Without the fix for CALCITE-2067 the following expression is not
+    // reduced to NaN, since NaN cannot be represented
+    // as a BigDecimal value.
+    final String sql2 = "SELECT ln(-2)";
+    sql(sql2)
+        .withRule(CoreRules.PROJECT_REDUCE_EXPRESSIONS)
+        .check();
+  }
+
   /** Tests that {@link UnionMergeRule} does nothing if its arguments have
    * are different set operators, {@link Union} and {@link Intersect}. */
   @Test void testMergeSetOpMixed() {
diff --git 
a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml 
b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 0ebdb9b469..2fa1dd5cb6 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -3026,6 +3026,40 @@ LogicalAggregate(group=[{0}], EXPR$1=[SUM($3)], 
EXPR$2=[MIN($4)], EXPR$3=[COUNT(
 LogicalFilter(condition=[false])
   LogicalAggregate(group=[{}], agg#0=[COUNT()])
     LogicalTableScan(table=[[scott, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testDoubleReduction">
+    <Resource name="sql">
+      <![CDATA[SELECT power(1004.3, 2)]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalProject(EXPR$0=[POWER(1004.3:DECIMAL(5, 1), 2)])
+  LogicalValues(tuples=[[{ 0 }]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+LogicalProject(EXPR$0=[1008618.4899999999E0:DOUBLE])
+  LogicalValues(tuples=[[{ 0 }]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testDoubleReduction2">
+    <Resource name="sql">
+      <![CDATA[SELECT ln(-2)]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalProject(EXPR$0=[LN(-2)])
+  LogicalValues(tuples=[[{ 0 }]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+LogicalProject(EXPR$0=[NaN:DOUBLE])
+  LogicalValues(tuples=[[{ 0 }]])
 ]]>
     </Resource>
   </TestCase>
@@ -12263,7 +12297,7 @@ LogicalProject(NEWCOL=[CASE(false, 2.1:FLOAT, 1:FLOAT)])
     </Resource>
     <Resource name="planAfter">
       <![CDATA[
-LogicalProject(NEWCOL=[1E0:FLOAT])
+LogicalProject(NEWCOL=[1.0E0:FLOAT])
   LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
diff --git a/core/src/test/resources/sql/misc.iq 
b/core/src/test/resources/sql/misc.iq
index 40bcc82ef9..0642999e7c 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -1746,6 +1746,272 @@ select (case when (true) then 1 end) from (values(1));
 EXPR$0 INTEGER(10)
 !type
 
+# Cast a character literal to a timestamp; note: the plan does not contain CAST
+values cast('1969-07-21 12:34:56' as timestamp);
++---------------------+
+| EXPR$0              |
++---------------------+
+| 1969-07-21 12:34:56 |
++---------------------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ 1969-07-21 12:34:56 }]])
+!plan
+
+# Cast a character literal without time to a timestamp; note: the plan does 
not contain CAST
+values cast('1969-07-21' as timestamp);
++---------------------+
+| EXPR$0              |
++---------------------+
+| 1969-07-21 00:00:00 |
++---------------------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ 1969-07-21 00:00:00 }]])
+!plan
+
+# Cast a character literal to a date; note: the plan does not contain CAST
+values cast('1969-07-21' as date);
++------------+
+| EXPR$0     |
++------------+
+| 1969-07-21 |
++------------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ 1969-07-21 }]])
+!plan
+
+# Slightly different format
+# (Incidentally, this format is not allowed in date literals, per the standard)
+values cast('1989-7-4' as date);
++------------+
+| EXPR$0     |
++------------+
+| 1989-07-04 |
++------------+
+(1 row)
+
+!ok
+
+# Cast a character literal to an integer; note: the plan does not contain CAST
+values cast('196907' as integer);
++--------+
+| EXPR$0 |
++--------+
+| 196907 |
++--------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ 196907 }]])
+!plan
+
+# Cast an integer literal to a bigint; note: the plan does not contain CAST
+values cast(123 as bigint);
++--------+
+| EXPR$0 |
++--------+
+|    123 |
++--------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ 123 }]])
+!plan
+
+# Cast an integer literal to a decimal; note: the plan does not contain CAST
+values cast('123.45' as decimal(5, 2));
++--------+
+| EXPR$0 |
++--------+
+| 123.45 |
++--------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ 123.45 }]])
+!plan
+
+# Cast a character literal to a decimal; note: the plan does not contain CAST
+values cast('123.45' as decimal(5, 2));
++--------+
+| EXPR$0 |
++--------+
+| 123.45 |
++--------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ 123.45 }]])
+!plan
+
+# Cast a character literal to a double; note: the plan does not contain CAST
+values cast('-123.45' as double);
++---------+
+| EXPR$0  |
++---------+
+| -123.45 |
++---------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ -123.45E0 }]])
+!plan
+
+values cast('false' as boolean);
++--------+
+| EXPR$0 |
++--------+
+| false  |
++--------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ false }]])
+!plan
+
+values cast('TRUE' as boolean);
++--------+
+| EXPR$0 |
++--------+
+| true   |
++--------+
+(1 row)
+
+!ok
+EnumerableValues(tuples=[[{ true }]])
+!plan
+
+values cast('TR' || 'UE' as boolean);
++--------+
+| EXPR$0 |
++--------+
+| true   |
++--------+
+(1 row)
+
+!ok
+EnumerableCalc(expr#0=[{inputs}], expr#1=['TR'], expr#2=['UE'], 
expr#3=[||($t1, $t2)], expr#4=[CAST($t3):BOOLEAN NOT NULL], EXPR$0=[$t4])
+  EnumerableValues(tuples=[[{ 0 }]])
+!plan
+
+!if (fixed.calcite2539) {
+
+# In the following, that we get an error at run time,
+# and that the plan shows that the expression has not been reduced.
+values cast('null' as boolean);
+Invalid character for cast
+!error
+EnumerableCalc(expr#0=[{inputs}], expr#1=['null'], expr#2=[CAST($t1):BOOLEAN 
NOT NULL], EXPR$0=[$t2])
+  EnumerableValues(tuples=[[{ 0 }]])
+!plan
+
+# The following throw give an error (good!)
+# but throw java.lang.ExceptionInInitializerError (not great).
+values cast('' as date);
+Caused by: java.lang.NumberFormatException: For input string: ""
+!error
+
+values cast('' as timestamp);
+Caused by: java.lang.NumberFormatException: For input string: ""
+!error
+
+values cast('' as integer);
+Caused by: java.lang.NumberFormatException: For input string: ""
+!error
+
+values cast('' as boolean);
+Caused by: java.lang.RuntimeException: Invalid character for cast
+!error
+
+values cast('' as double);
+Caused by: java.lang.NumberFormatException: empty String
+!error
+
+# Postgres fails:
+#  ERROR:  invalid input syntax for integer: "1.56"
+values cast('15.6' as integer);
+Caused by: java.lang.NumberFormatException: For input string: "15.6"
+!error
+
+# Postgres fails:
+#  ERROR:  invalid input syntax for integer: " - 5 "
+values cast(' - 5 ' as double);
+Caused by: java.lang.NumberFormatException: For input string: "- 5"
+!error
+
+# Out of TINYINT range (max 127)
+values cast('200' as tinyint);
+Caused by: java.lang.NumberFormatException: Value out of range. Value:"200" 
Radix:10
+!error
+
+# Out of SMALLINT range (max 32767)
+values cast('50000' as smallint);
+Caused by: java.lang.NumberFormatException: Value out of range. Value:"50000" 
Radix:10
+!error
+
+# Out of INTEGER range (max 2.1e9)
+values cast('4567891234' as integer);
+Caused by: java.lang.NumberFormatException: For input string: "4567891234"
+!error
+
+# Out of BIGINT range (max 9.2e18)
+values cast('12345678901234567890' as bigint);
+Caused by: java.lang.NumberFormatException: For input string: 
"12345678901234567890"
+!error
+!}
+
+# Out of REAL range
+# (Should give an error, not infinity.)
+values cast('12.34e56' as real);
++----------+
+| EXPR$0   |
++----------+
+| Infinity |
++----------+
+(1 row)
+
+!ok
+
+# Out of FLOAT range
+# (Should give an error, not infinity.)
+values cast('12.34e5678' as float);
++----------+
+| EXPR$0   |
++----------+
+| Infinity |
++----------+
+(1 row)
+
+!ok
+
+# Out of DOUBLE range
+# (Should give an error, not infinity.)
+values cast('12.34e5678' as double);
++----------+
+| EXPR$0   |
++----------+
+| Infinity |
++----------+
+(1 row)
+
+!ok
+
+# Postgres succeeds
+values cast(' -5 ' as double);
++--------+
+| EXPR$0 |
++--------+
+|   -5.0 |
++--------+
+(1 row)
+
+!ok
+
 # RAND_INTEGER with seed
 select i, rand_integer(1, 5) as r
 from (values 1, 2, 3, 4, 5) as t(i);
diff --git 
a/piglet/src/main/java/org/apache/calcite/piglet/PigRelExVisitor.java 
b/piglet/src/main/java/org/apache/calcite/piglet/PigRelExVisitor.java
index c00289f728..49a748e5b2 100644
--- a/piglet/src/main/java/org/apache/calcite/piglet/PigRelExVisitor.java
+++ b/piglet/src/main/java/org/apache/calcite/piglet/PigRelExVisitor.java
@@ -256,8 +256,12 @@ class PigRelExVisitor extends LogicalExpressionVisitor {
     final RexNode operand = stack.pop();
     if (operand instanceof RexLiteral) {
       final Comparable value = ((RexLiteral) operand).getValue();
-      assert value instanceof BigDecimal;
-      stack.push(builder.literal(((BigDecimal) value).negate()));
+      if (value instanceof BigDecimal) {
+        stack.push(builder.literal(((BigDecimal) value).negate()));
+      } else {
+        assert value instanceof Double;
+        stack.push(builder.literal(- (Double) value));
+      }
     } else {
       stack.push(builder.call(SqlStdOperatorTable.UNARY_MINUS, operand));
     }
diff --git a/piglet/src/test/java/org/apache/calcite/test/PigRelExTest.java 
b/piglet/src/test/java/org/apache/calcite/test/PigRelExTest.java
index b36f684fa5..d7ed80756c 100644
--- a/piglet/src/test/java/org/apache/calcite/test/PigRelExTest.java
+++ b/piglet/src/test/java/org/apache/calcite/test/PigRelExTest.java
@@ -79,7 +79,10 @@ class PigRelExTest extends PigRelTestBase {
   }
 
   @Test void testConstantFloat() {
-    checkTranslation(".1E6 == -2.3", inTree("=(1E5:DOUBLE, -2.3:DECIMAL(2, 
1))"));
+    // Add a variable b in the expression to prevent it from being simplified 
to "false".
+    checkTranslation(".1E6 == -2.3 + d",
+        // Validator converts -2.3 from DECIMAL to DOUBLE
+        inTree("=(100000.0E0, +(-2.3E0, $3))"));
   }
 
   @Test void testConstantString() {
diff --git a/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java 
b/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
index 9b5709184a..95de883788 100644
--- a/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
+++ b/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
@@ -227,7 +227,7 @@ class PigRelOpTest extends PigRelTestBase {
         + "A = LOAD 'scott.DEPT' as (DEPTNO:int, DNAME:chararray, 
LOC:CHARARRAY);\n"
         + "B = SAMPLE A 0.5;\n";
     final String plan = ""
-        + "LogicalFilter(condition=[<(RAND(), 5E-1)])\n"
+        + "LogicalFilter(condition=[<(RAND(), 0.5E0)])\n"
         + "  LogicalTableScan(table=[[scott, DEPT]])\n";
     final String sql = ""
         + "SELECT *\n"

Reply via email to