This is an automated email from the ASF dual-hosted git repository.
chunwei pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 4ee98e0c06 [CALCITE-4861] Optimization of chained CAST calls can lead
to unexpected behavior
4ee98e0c06 is described below
commit 4ee98e0c0625e5adc7b2eed3e60702ee744c435d
Author: Benchao Li <[email protected]>
AuthorDate: Sun Apr 17 15:24:18 2022 +0800
[CALCITE-4861] Optimization of chained CAST calls can lead to unexpected
behavior
---
.../java/org/apache/calcite/rex/RexBuilder.java | 23 +++++++
.../calcite/test/TypeCoercionConverterTest.xml | 2 +-
.../org/apache/calcite/test/SqlOperatorTest.java | 76 ++++++++++++----------
3 files changed, 67 insertions(+), 34 deletions(-)
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 6a6e599dbc..b4d8e66f1d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -642,6 +642,9 @@ public class RexBuilder {
boolean canRemoveCastFromLiteral(RelDataType toType, @Nullable Comparable
value,
SqlTypeName fromTypeName) {
+ if (value == null) {
+ return true;
+ }
final SqlTypeName sqlType = toType.getSqlTypeName();
if (!RexLiteral.valueMatchesType(value, sqlType, false)) {
return false;
@@ -678,6 +681,26 @@ public class RexBuilder {
return SqlTypeUtil.isValidDecimalValue(decimalValue, toType);
}
+ if (SqlTypeName.INT_TYPES.contains(sqlType)) {
+ final BigDecimal decimalValue = (BigDecimal) value;
+ final int s = decimalValue.scale();
+ if (s != 0) {
+ return false;
+ }
+ long l = decimalValue.longValue();
+ switch (sqlType) {
+ case TINYINT:
+ return l >= Byte.MIN_VALUE && l <= Byte.MAX_VALUE;
+ case SMALLINT:
+ return l >= Short.MIN_VALUE && l <= Short.MAX_VALUE;
+ case INTEGER:
+ return l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE;
+ case BIGINT:
+ default:
+ return true;
+ }
+ }
+
return true;
}
diff --git
a/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
b/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
index 9aed98d285..7e3fc66395 100644
---
a/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
+++
b/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
@@ -171,7 +171,7 @@ LogicalUnion(all=[false])
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[UPDATE],
updateColumnList=[[t1_varchar20, t1_date, t1_int]],
sourceExpressionList=[[CAST(123):VARCHAR(20) NOT NULL, CAST(2020-01-03
10:14:34):DATE NOT NULL, 12.3]], flattened=[false])
+LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[UPDATE],
updateColumnList=[[t1_varchar20, t1_date, t1_int]],
sourceExpressionList=[[CAST(123):VARCHAR(20) NOT NULL, CAST(2020-01-03
10:14:34):DATE NOT NULL, CAST(12.3:DECIMAL(3, 1)):INTEGER NOT NULL]],
flattened=[false])
LogicalProject(t1_varchar20=[$0], t1_smallint=[$1], t1_int=[$2],
t1_bigint=[$3], t1_float=[$4], t1_double=[$5], t1_decimal=[$6],
t1_timestamp=[$7], t1_date=[$8], t1_binary=[$9], t1_boolean=[$10],
EXPR$0=[123], EXPR$1=[2020-01-03 10:14:34], EXPR$2=[12.3:DECIMAL(3, 1)])
LogicalTableScan(table=[[CATALOG, SALES, T1]])
]]>
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 538b6349d6..a2337fd985 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -805,36 +805,36 @@ public class SqlOperatorTest {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
- f.checkCastToScalarOkay("1.25", "INTEGER", "1");
- f.checkCastToScalarOkay("1.25E0", "INTEGER", "1");
+ f.checkFails("cast(1.25 as int)", "INTEGER", true);
+ f.checkFails("cast(1.25E0 as int)", "INTEGER", true);
if (!f.brokenTestsEnabled()) {
return;
}
- f.checkCastToScalarOkay("1.5", "INTEGER", "2");
- f.checkCastToScalarOkay("5E-1", "INTEGER", "1");
- f.checkCastToScalarOkay("1.75", "INTEGER", "2");
- f.checkCastToScalarOkay("1.75E0", "INTEGER", "2");
-
- f.checkCastToScalarOkay("-1.25", "INTEGER", "-1");
- f.checkCastToScalarOkay("-1.25E0", "INTEGER", "-1");
- f.checkCastToScalarOkay("-1.5", "INTEGER", "-2");
- f.checkCastToScalarOkay("-5E-1", "INTEGER", "-1");
- f.checkCastToScalarOkay("-1.75", "INTEGER", "-2");
- f.checkCastToScalarOkay("-1.75E0", "INTEGER", "-2");
-
- f.checkCastToScalarOkay("1.23454", "DECIMAL(8, 4)", "1.2345");
- f.checkCastToScalarOkay("1.23454E0", "DECIMAL(8, 4)", "1.2345");
- f.checkCastToScalarOkay("1.23455", "DECIMAL(8, 4)", "1.2346");
- f.checkCastToScalarOkay("5E-5", "DECIMAL(8, 4)", "0.0001");
- f.checkCastToScalarOkay("1.99995", "DECIMAL(8, 4)", "2.0000");
- f.checkCastToScalarOkay("1.99995E0", "DECIMAL(8, 4)", "2.0000");
-
- f.checkCastToScalarOkay("-1.23454", "DECIMAL(8, 4)", "-1.2345");
- f.checkCastToScalarOkay("-1.23454E0", "DECIMAL(8, 4)", "-1.2345");
- f.checkCastToScalarOkay("-1.23455", "DECIMAL(8, 4)", "-1.2346");
- f.checkCastToScalarOkay("-5E-5", "DECIMAL(8, 4)", "-0.0001");
- f.checkCastToScalarOkay("-1.99995", "DECIMAL(8, 4)", "-2.0000");
- f.checkCastToScalarOkay("-1.99995E0", "DECIMAL(8, 4)", "-2.0000");
+ f.checkFails("cast(1.5 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(5E-1 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(1.75 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(1.75E0 as int)", OUT_OF_RANGE_MESSAGE, true);
+
+ f.checkFails("cast(-1.25 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-1.25E0 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-1.5 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-5E-1 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-1.75 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-1.75E0 as int)", OUT_OF_RANGE_MESSAGE, true);
+
+ f.checkFails("cast(1.23454 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(1.23454E0 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(1.23455 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(5E-5 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(1.99995 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(1.99995E0 as int)", OUT_OF_RANGE_MESSAGE, true);
+
+ f.checkFails("cast(-1.23454 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-1.23454E0 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-1.23455 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-5E-5 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-1.99995 as int)", OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast(-1.99995E0 as int)", OUT_OF_RANGE_MESSAGE, true);
// 9.99 round to 10.0, should give out of range error
f.checkFails("cast(9.99 as decimal(2,1))", OUT_OF_RANGE_MESSAGE,
@@ -845,15 +845,15 @@ public class SqlOperatorTest {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
- f.checkScalarExact("cast( cast(1.25 as double) as integer)", 1);
- f.checkScalarExact("cast( cast(-1.25 as double) as integer)", -1);
+ f.checkFails("cast( cast(1.25 as double) as integer)",
OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast( cast(-1.25 as double) as integer)",
OUT_OF_RANGE_MESSAGE, true);
if (!f.brokenTestsEnabled()) {
return;
}
- f.checkScalarExact("cast( cast(1.75 as double) as integer)", 2);
- f.checkScalarExact("cast( cast(-1.75 as double) as integer)", -2);
- f.checkScalarExact("cast( cast(1.5 as double) as integer)", 2);
- f.checkScalarExact("cast( cast(-1.5 as double) as integer)", -2);
+ f.checkFails("cast( cast(1.75 as double) as integer)",
OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast( cast(-1.75 as double) as integer)",
OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast( cast(1.5 as double) as integer)",
OUT_OF_RANGE_MESSAGE, true);
+ f.checkFails("cast( cast(-1.5 as double) as integer)",
OUT_OF_RANGE_MESSAGE, true);
}
@Test void testCastApproxNumericLimits() {
@@ -1237,6 +1237,16 @@ public class SqlOperatorTest {
INVALID_CHAR_MESSAGE, true);
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-4861">[CALCITE-4861]
+ * Optimisation of chained cast calls can lead to unexpected behaviour.</a>.
+ */
+ @Test void testChainedCast() {
+ final SqlOperatorFixture f = fixture();
+ f.checkFails("CAST(CAST(CAST(123456 AS TINYINT) AS INT) AS BIGINT)",
+ "Value out of range. Value:\"123456\"", true);
+ }
+
@Test void testCase() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CASE, VmName.EXPAND);