From b5e7ec3c3b8875f508c736ab2062d637f23147e9 Mon Sep 17 00:00:00 2001 From: Mihai Budiu Date: Fri, 27 Dec 2024 17:13:48 -0800 Subject: [PATCH] [CALCITE-6751] Reduction of CAST from string to interval is incorrect Signed-off-by: Mihai Budiu --- .../enumerable/RexToLixTranslator.java | 11 +++-- .../apache/calcite/runtime/SqlFunctions.java | 4 ++ core/src/test/resources/sql/misc.iq | 42 +++++++++++++++++++ .../apache/calcite/test/CalciteAssert.java | 7 ++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java index f46cdad24c7..b3e8c244467 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java @@ -304,8 +304,8 @@ Expression translateCast( ConstantExpression format) { Expression convert = getConvertExpression(sourceType, targetType, operand, format); Expression convert2 = checkExpressionPadTruncate(convert, sourceType, targetType); - Expression convert3 = expressionHandlingSafe(convert2, safe, targetType); - return scaleValue(sourceType, targetType, convert3); + Expression convert3 = scaleValue(sourceType, targetType, convert2); + return expressionHandlingSafe(convert3, safe, targetType); } private Expression getConvertExpression( @@ -1213,7 +1213,7 @@ private static Expression scaleValue( final SqlTypeFamily sourceFamily = sourceType.getSqlTypeName().getFamily(); if (targetFamily == SqlTypeFamily.NUMERIC // multiplyDivide cannot handle DECIMALs, but for DECIMAL - // destination types the result is already scaled. + // target types the result is already scaled. && targetType.getSqlTypeName() != SqlTypeName.DECIMAL && (sourceFamily == SqlTypeFamily.INTERVAL_YEAR_MONTH || sourceFamily == SqlTypeFamily.INTERVAL_DAY_TIME)) { @@ -1223,6 +1223,11 @@ private static Expression scaleValue( sourceType.getSqlTypeName().getEndUnit().multiplier; return RexImpTable.multiplyDivide(operand, multiplier, divider); } + if (SqlTypeName.INTERVAL_TYPES.contains(targetType.getSqlTypeName())) { + final BigDecimal multiplier = targetType.getSqlTypeName().getEndUnit().multiplier; + final BigDecimal divider = BigDecimal.ONE; + return RexImpTable.multiplyDivide(operand, multiplier, divider); + } return operand; } diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java index 156508a5dd4..e98aa2fcb0e 100644 --- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java @@ -4310,6 +4310,10 @@ public static int toInt(java.sql.Time v) { return v == null ? castNonNull(null) : toInt(v); } + // Method tagged as non-deterministic because it can throw. + // The DeterministicCodeOptimizer may otherwise try to lift it out of try-catch blocks. + // See https://issues.apache.org/jira/browse/CALCITE-6753 + @NonDeterministic public static int toInt(String s) { return parseInt(s.trim()); } diff --git a/core/src/test/resources/sql/misc.iq b/core/src/test/resources/sql/misc.iq index f8856e5ad96..83d045f1bd1 100644 --- a/core/src/test/resources/sql/misc.iq +++ b/core/src/test/resources/sql/misc.iq @@ -18,6 +18,48 @@ !use post !set outputformat mysql +# [CALCITE-6751] Reduction of CAST from string to interval is incorrect +SELECT TIME '10:00:00' + CAST('1' AS INTERVAL SECOND); ++----------+ +| EXPR$0 | ++----------+ +| 10:00:01 | ++----------+ +(1 row) + +!ok + +# Due to CALCITE-6752 the following test crashes: +# SELECT TIME '10:00:00' + CAST('1.1' AS INTERVAL SECOND); +# +------------+ +# | EXPR$0 | +# +------------+ +# | 11:00:00.1 | +# +------------+ +# (1 row) +# +# !ok + +SELECT TIME '10:00:00' + CAST('1' AS INTERVAL HOUR); ++----------+ +| EXPR$0 | ++----------+ +| 11:00:00 | ++----------+ +(1 row) + +!ok + +SELECT TIME '10:00:00' + CAST('1' AS INTERVAL MINUTE); ++----------+ +| EXPR$0 | ++----------+ +| 10:01:00 | ++----------+ +(1 row) + +!ok + SELECT UUID '123e4567-e89b-12d3-a456-426655440000'; +--------------------------------------+ | EXPR$0 | diff --git a/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java b/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java index 8c484d4506a..7931318df45 100644 --- a/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java +++ b/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java @@ -569,6 +569,13 @@ static void assertQuery( try { if (updateChecker == null) { resultSet = statement.executeQuery(sql); + if (resultChecker == null && exceptionChecker != null) { + // Pull data from result set, otherwise exceptions that happen during evaluation + // won't be triggered + while (resultSet.next()) { + // no need to do anything with the data + } + } } else { updateCount = statement.executeUpdate(sql); }