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"
