This is an automated email from the ASF dual-hosted git repository.
mihaibudiu 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 1d21c0ec8b [CALCITE-7451] REINTERPRET should not be used in logical
plans
1d21c0ec8b is described below
commit 1d21c0ec8bf565fdfa12af986aa7eba5fb428553
Author: Mihai Budiu <[email protected]>
AuthorDate: Wed Aug 5 19:01:23 2026 -0700
[CALCITE-7451] REINTERPRET should not be used in logical plans
Signed-off-by: Mihai Budiu <[email protected]>
---
.../calcite/adapter/enumerable/RexImpTable.java | 15 --------
.../adapter/enumerable/RexToLixTranslator.java | 18 ++++++++++
.../main/java/org/apache/calcite/plan/Strong.java | 2 +-
.../org/apache/calcite/rel/rules/CoreRules.java | 5 ++-
.../calcite/rel/rules/ReduceDecimalsRule.java | 11 ++++++
.../java/org/apache/calcite/rex/RexBuilder.java | 41 +++++++++++++++-------
.../main/java/org/apache/calcite/rex/RexUtil.java | 20 +++++++++++
.../main/java/org/apache/calcite/sql/SqlKind.java | 5 +++
.../calcite/sql/fun/SqlStdOperatorTable.java | 7 ++++
.../apache/calcite/sql2rel/SqlToRelConverter.java | 27 +++-----------
.../org/apache/calcite/test/RelOptRulesTest.java | 1 +
.../apache/calcite/test/SqlToRelConverterTest.java | 34 ++++++++++++++++++
.../apache/calcite/test/SqlToRelConverterTest.xml | 28 +++++++++++++++
core/src/test/resources/sql/operator.iq | 14 ++++++++
14 files changed, 175 insertions(+), 53 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 12663a2bba..36fd19f67b 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -513,7 +513,6 @@
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.RAND_INTEGER;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.RANK;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.REGR_COUNT;
-import static org.apache.calcite.sql.fun.SqlStdOperatorTable.REINTERPRET;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.REPLACE;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.RIGHTSHIFT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ROUND;
@@ -1174,7 +1173,6 @@ void populate2() {
define(SAFE_CAST, new CastImplementor());
define(TRY_CAST, new CastImplementor());
- define(REINTERPRET, new ReinterpretImplementor());
define(CONVERT, new ConvertImplementor());
define(TRANSLATE, new TranslateImplementor());
@@ -3800,19 +3798,6 @@ private static RelDataType nullifyType(JavaTypeFactory
typeFactory,
}
}
- /** Implementor for the {@code REINTERPRET} internal SQL operator. */
- private static class ReinterpretImplementor extends
AbstractRexCallImplementor {
- ReinterpretImplementor() {
- super("reinterpret", NullPolicy.STRICT, false);
- }
-
- @Override Expression implementSafe(final RexToLixTranslator translator,
- final RexCall call, final List<Expression> argValueList) {
- assert call.getOperands().size() == 1;
- return argValueList.get(0);
- }
- }
-
/** Implementor for sort_array. */
private static class SortArrayImplementor extends AbstractRexCallImplementor
{
SortArrayImplementor() {
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 9bad3dc3f5..cf9cbde5e3 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
@@ -720,6 +720,20 @@ private Expression getConvertExpression(
case INTEGER:
case TINYINT:
case SMALLINT: {
+ if (sourceType.getFamily() == SqlTypeFamily.INTERVAL_DAY_TIME
+ || sourceType.getFamily() == SqlTypeFamily.INTERVAL_YEAR_MONTH) {
+ // An interval is represented by its count of base units (milliseconds
+ // or months); the cast yields the count of the interval's end unit,
+ // truncated towards zero.
+ final BigDecimal multiplier =
+ sourceType.getSqlTypeName().getEndUnit().multiplier;
+ final Expression ticks = EnumUtils.convert(operand, long.class);
+ final Expression scaled = multiplier.equals(BigDecimal.ONE)
+ ? ticks
+ : Expressions.divide(ticks,
+ Expressions.constant(multiplier.longValueExact()));
+ return EnumUtils.convert(scaled, typeFactory.getJavaClass(targetType));
+ }
if (SqlTypeName.NUMERIC_TYPES.contains(sourceType.getSqlTypeName())) {
Type javaClass = typeFactory.getJavaClass(targetType);
Primitive primitive = Primitive.of(javaClass);
@@ -1393,6 +1407,10 @@ private static Expression scaleValue(
// multiplyDivide cannot handle DECIMALs, but for DECIMAL
// target types the result is already scaled.
&& targetType.getSqlTypeName() != SqlTypeName.DECIMAL
+ // Integer targets divide before narrowing, in getConvertExpression;
+ // dividing here, after the narrowing, would overflow for tick counts
+ // wider than the target type.
+ && !SqlTypeName.INT_TYPES.contains(targetType.getSqlTypeName())
&& (sourceFamily == SqlTypeFamily.INTERVAL_YEAR_MONTH
|| sourceFamily == SqlTypeFamily.INTERVAL_DAY_TIME)) {
// Scale to the given field.
diff --git a/core/src/main/java/org/apache/calcite/plan/Strong.java
b/core/src/main/java/org/apache/calcite/plan/Strong.java
index ee349b837c..b6aa9678ed 100644
--- a/core/src/main/java/org/apache/calcite/plan/Strong.java
+++ b/core/src/main/java/org/apache/calcite/plan/Strong.java
@@ -358,7 +358,7 @@ private static Map<SqlKind, Policy> createPolicyMap() {
map.put(SqlKind.DIVIDE, Policy.ANY);
map.put(SqlKind.CAST, Policy.ANY);
- map.put(SqlKind.REINTERPRET, Policy.ANY);
+ map.put(SqlKind.REINTERPRET, Policy.ANY); // deprecated, kept until
removed
map.put(SqlKind.TRIM, Policy.ANY);
map.put(SqlKind.LTRIM, Policy.ANY);
map.put(SqlKind.RTRIM, Policy.ANY);
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
b/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
index 11708c5fac..98cbeaae21 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
@@ -221,7 +221,10 @@ private CoreRules() {}
/** Rule that reduces operations on the DECIMAL type, such as casts or
* arithmetic, into operations involving more primitive types such as BIGINT
- * and DOUBLE. */
+ * and DOUBLE.
+ *
+ * @deprecated See {@link ReduceDecimalsRule}. */
+ @Deprecated // to be removed before 2.0
public static final ReduceDecimalsRule CALC_REDUCE_DECIMALS =
ReduceDecimalsRule.Config.DEFAULT.toRule();
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
index 06bf0f67ee..8918bf55f9 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
@@ -73,8 +73,19 @@
* would like to push down decimal operations to an external database.
*
* @see CoreRules#CALC_REDUCE_DECIMALS
+ *
+ * @deprecated The rule rewrites decimal values as their unscaled BIGINT
+ * representation, connected by REINTERPRET operators. This assumes a physical
+ * representation of DECIMAL values that only an adapter or calling convention
+ * knows, so the rewritten plan is no longer a logical plan. This rule is
opt-in
+ * for engines that represent DECIMAL values as scaled integers. The
+ * REINTERPRET operator is deprecated.
*/
+@Deprecated // to be removed before 2.0
@Value.Enclosing
+// Immutables copies this suppression into the generated class, which
+// references this deprecated class
+@SuppressWarnings("deprecation")
public class ReduceDecimalsRule
extends RelRule<ReduceDecimalsRule.Config>
implements TransformationRule {
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 95030b5b90..667de88227 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -874,9 +874,6 @@ public RexNode makeCast(
}
return literal2;
}
- } else if (SqlTypeUtil.isExactNumeric(type)
- && SqlTypeUtil.isInterval(exp.getType())) {
- return makeCastIntervalToExact(pos, type, exp);
} else if (sqlType == SqlTypeName.BOOLEAN
&& SqlTypeUtil.isExactNumeric(exp.getType())) {
return makeCastExactToBoolean(type, exp);
@@ -1125,16 +1122,6 @@ private RexNode makeCastBooleanToExact(RelDataType
toType, RexNode exp) {
casted, makeNullLiteral(toType)));
}
- private RexNode makeCastIntervalToExact(SqlParserPos pos, RelDataType
toType, RexNode exp) {
- final TimeUnit endUnit = exp.getType().getSqlTypeName().getEndUnit();
- final TimeUnit baseUnit = baseUnit(exp.getType().getSqlTypeName());
- final BigDecimal multiplier = baseUnit.multiplier;
- final BigDecimal divider = endUnit.multiplier;
- RexNode value =
- multiplyDivide(pos, decodeIntervalOrDecimal(pos, exp), multiplier,
divider);
- return ensureType(pos, toType, value, false);
- }
-
public RexNode multiplyDivide(RexNode e, BigDecimal multiplier,
BigDecimal divider) {
return multiplyDivide(SqlParserPos.ZERO, e, multiplier, divider);
@@ -1177,7 +1164,10 @@ public RexNode multiplyDivide(SqlParserPos pos, RexNode
e, BigDecimal multiplier
* arithmetic, but is often required for rounding and
* explicit casts.
* @return the integer reinterpreted as an opaque decimal type
+ *
+ * @deprecated The REINTERPRET operator is deprecated
*/
+ @Deprecated // to be removed before 2.0
public RexNode encodeIntervalOrDecimal(
RexNode value,
RelDataType type,
@@ -1185,6 +1175,11 @@ public RexNode encodeIntervalOrDecimal(
return encodeIntervalOrDecimal(SqlParserPos.ZERO, value, type,
checkOverflow);
}
+ /** Encodes an interval or decimal, with an explicit parser position.
+ *
+ * @deprecated The REINTERPRET operator is deprecated
+ */
+ @Deprecated // to be removed before 2.0
public RexNode encodeIntervalOrDecimal(
SqlParserPos pos,
RexNode value,
@@ -1201,11 +1196,19 @@ public RexNode encodeIntervalOrDecimal(
*
* @param node the interval or decimal value as an opaque type
* @return an integer representation of the decimal value
+ *
+ * @deprecated The REINTERPRET operator is deprecated
*/
+ @Deprecated // to be removed before 2.0
public RexNode decodeIntervalOrDecimal(RexNode node) {
return decodeIntervalOrDecimal(SqlParserPos.ZERO, node);
}
+ /** Decodes an interval or decimal, with an explicit parser position.
+ *
+ * @deprecated The REINTERPRET operator is deprecated
+ */
+ @Deprecated // to be removed before 2.0
public RexNode decodeIntervalOrDecimal(SqlParserPos pos, RexNode node) {
assert SqlTypeUtil.isDecimal(node.getType())
|| SqlTypeUtil.isInterval(node.getType());
@@ -1289,7 +1292,13 @@ public RexNode makeAbstractCast(
* @param exp expression to be casted
* @param checkOverflow whether an overflow check is required
* @return a RexCall with two operands and a special return type
+ *
+ * @deprecated The REINTERPRET operator is deprecated; its semantics depend
+ * on the physical representation of values, which only an adapter or
+ * calling convention knows. Use {@link #makeCast(RelDataType, RexNode)}
+ * instead
*/
+ @Deprecated // to be removed before 2.0
public RexNode makeReinterpretCast(
RelDataType type,
RexNode exp,
@@ -1305,7 +1314,13 @@ public RexNode makeReinterpretCast(
* @param exp expression to be cast
* @param checkOverflow whether an overflow check is required
* @return a RexCall with two operands and a special return type
+ *
+ * @deprecated The REINTERPRET operator is deprecated; its semantics depend
+ * on the physical representation of values, which only an adapter or
+ * calling convention knows. Use
+ * {@link #makeCast(SqlParserPos, RelDataType, RexNode)} instead
*/
+ @Deprecated // to be removed before 2.0
public RexNode makeReinterpretCast(
SqlParserPos pos,
RelDataType type,
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index 2faa2633b0..99045f0a3c 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -1067,7 +1067,12 @@ public static boolean containsFieldAccess(RexNode node) {
* @param expr expression possibly in need of expansion
* @param recurse whether to check nested calls
* @return whether the expression requires expansion
+ *
+ * @deprecated Used only by
+ * {@link org.apache.calcite.rel.rules.ReduceDecimalsRule}, which is
+ * deprecated
*/
+ @Deprecated // to be removed before 2.0
public static boolean requiresDecimalExpansion(
RexNode expr,
boolean recurse) {
@@ -1118,7 +1123,12 @@ public static boolean requiresDecimalExpansion(
/**
* Determines whether any operand of a set requires decimal expansion.
+ *
+ * @deprecated Used only by
+ * {@link org.apache.calcite.rel.rules.ReduceDecimalsRule}, which is
+ * deprecated
*/
+ @Deprecated // to be removed before 2.0
public static boolean requiresDecimalExpansion(
List<RexNode> operands,
boolean recurse) {
@@ -1136,7 +1146,12 @@ public static boolean requiresDecimalExpansion(
/**
* Returns whether a {@link RexProgram} contains expressions which require
* decimal expansion.
+ *
+ * @deprecated Used only by
+ * {@link org.apache.calcite.rel.rules.ReduceDecimalsRule}, which is
+ * deprecated
*/
+ @Deprecated // to be removed before 2.0
public static boolean requiresDecimalExpansion(
RexProgram program,
boolean recurse) {
@@ -1149,6 +1164,11 @@ public static boolean requiresDecimalExpansion(
return false;
}
+ /** Returns whether a REINTERPRET call performs an overflow check.
+ *
+ * @deprecated The REINTERPRET operator is deprecated
+ */
+ @Deprecated // to be removed before 2.0
public static boolean canReinterpretOverflow(RexCall call) {
assert call.isA(SqlKind.REINTERPRET) : "call is not a reinterpret";
return call.operands.size() > 1;
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index a70c71df76..92bbfe4c59 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -1005,6 +1005,11 @@ public enum SqlKind {
/**
* The internal REINTERPRET operator (meaning a reinterpret cast).
* An internal operator that does not appear in SQL syntax.
+ *
+ * <p>Do not use. The
+ * {@link org.apache.calcite.sql.fun.SqlStdOperatorTable#REINTERPRET}
+ * operator is deprecated and will be removed, together with this value;
+ * use {@link #CAST} instead.
*/
REINTERPRET,
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 20a5e690ff..be9f23abe6 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -1786,7 +1786,14 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
* it accepts one operand and stores the target type as the return type. It
* performs an overflow check if it has <i>any</i> second operand, whether
* true or not.
+ *
+ * @deprecated The semantics of REINTERPRET depend on the physical
+ * representation of values, which only an adapter or calling convention
+ * knows; a logical plan must not contain this operator.
+ * The enumerable convention does not implement it.
+ * Use {@link #CAST} instead.
*/
+ @Deprecated // to be removed before 2.0
public static final SqlSpecialOperator REINTERPRET =
new SqlSpecialOperator("Reinterpret", SqlKind.REINTERPRET) {
@Override public SqlOperandCountRange getOperandCountRange() {
diff --git
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 3ca7ae44f8..5c524eee17 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -6572,23 +6572,12 @@ private class HistogramShuttle extends RexShuttle {
if (histogramOp != null) {
final RelDataType histogramType = computeHistogramType(type);
- // For DECIMAL, since it's already represented as a bigint we
- // want to do a reinterpretCast instead of a cast to avoid
- // losing any precision.
- boolean reinterpretCast =
- type.getSqlTypeName() == SqlTypeName.DECIMAL;
-
// Replace original expression with CAST of not one
// of the supported types
if (histogramType != type) {
exprs = new ArrayList<>(exprs);
- exprs.set(
- 0,
- reinterpretCast
- ? rexBuilder.makeReinterpretCast(
- call.getParserPosition(), histogramType, exprs.get(0),
- rexBuilder.makeLiteral(false))
- : rexBuilder.makeCast(call.getParserPosition(), histogramType,
exprs.get(0)));
+ exprs.set(0,
+ rexBuilder.makeCast(call.getParserPosition(), histogramType,
exprs.get(0)));
}
RexNode over =
@@ -6615,16 +6604,8 @@ private class HistogramShuttle extends RexShuttle {
// If needed, post Cast result back to original
// type.
if (histogramType != type) {
- if (reinterpretCast) {
- histogramCall =
- rexBuilder.makeReinterpretCast(call.getParserPosition(),
- type,
- histogramCall,
- rexBuilder.makeLiteral(false));
- } else {
- histogramCall =
- rexBuilder.makeCast(call.getParserPosition(), type,
histogramCall);
- }
+ histogramCall =
+ rexBuilder.makeCast(call.getParserPosition(), type,
histogramCall);
}
return histogramCall;
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 0cfb76b9d5..ca5cc42385 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -10812,6 +10812,7 @@ public interface Config extends RelRule.Config {
* Test case for
* <a
href="https://issues.apache.org/jira/browse/CALCITE-3319">[CALCITE-3319]
* AssertionError for ReduceDecimalsRule</a>. */
+ @SuppressWarnings("deprecation") // tests the deprecated ReduceDecimalsRule
@Test void testReduceDecimal() {
final String sql = "select ename from emp where sal > cast (100.0 as
decimal(4, 1))";
sql(sql)
diff --git
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 90ed8a6333..05093c2d3c 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -6355,6 +6355,40 @@ void checkUserDefinedOrderByOver(NullCollation
nullCollation) {
+ " supported"));
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7451">[CALCITE-7451]
+ * REINTERPRET should not be used in logical plans</a>.
+ *
+ * <p>Casting an interval to an exact numeric type, which the TIMESTAMPDIFF
+ * family of functions relies on, remains a CAST call in the logical plan;
+ * it used to be rewritten in terms of the deprecated REINTERPRET
+ * operator. */
+ @Test void testCastIntervalToNumericNoReinterpret() {
+ final String sql = "select cast(x as integer) as i,\n"
+ + " cast(x as decimal(6, 1)) as d,\n"
+ + " timestampdiff(minute, ts, ts) as m\n"
+ + "from (values (interval '90' minute,\n"
+ + " timestamp '2020-01-01 00:00:00')) as t(x, ts)";
+ final String plan = RelOptUtil.toString(sql(sql).toRel());
+ assertThat(plan, not(containsString("Reinterpret")));
+ sql(sql).ok();
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7451">[CALCITE-7451]
+ * REINTERPRET should not be used in logical plans</a>.
+ *
+ * <p>The deprecated REINTERPRET operator must not appear in a logical plan;
+ * FLOOR and CEIL of an interval literal used to produce one. */
+ @Test void testFloorCeilOfIntervalLiteral() {
+ final String sql = "select floor(interval '3:4:5' hour to second) as f,\n"
+ + " ceil(interval '3:4:5' hour to second) as c\n"
+ + "from emp";
+ final String plan = RelOptUtil.toString(sql(sql).toRel());
+ assertThat(plan, not(containsString("Reinterpret")));
+ sql(sql).ok();
+ }
+
/** Test case of
* <a
href="https://issues.apache.org/jira/browse/CALCITE-5406">[CALCITE-5406]
* Support the SELECT DISTINCT ON statement for PostgreSQL dialect</a>. */
diff --git
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index e916c08131..bafc21fc62 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -802,6 +802,21 @@ GROUP BY GROUPING SETS (
LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}]])
LogicalProject(EMPNO=[$0], EXPR$1=[CASE(SEARCH($1, Sarg['Eric':VARCHAR(20),
'Fred':VARCHAR(20)]:VARCHAR(20)), 'Manager', 'Other ')])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testCastIntervalToNumericNoReinterpret">
+ <Resource name="sql">
+ <![CDATA[select cast(x as integer) as i,
+ cast(x as decimal(6, 1)) as d,
+ timestampdiff(minute, ts, ts) as m
+from (values (interval '90' minute,
+ timestamp '2020-01-01 00:00:00')) as t(x, ts)]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(I=[CAST($0):INTEGER NOT NULL], D=[CAST($0):DECIMAL(6, 1) NOT
NULL], M=[CAST(-($1, $1)):INTEGER NOT NULL])
+ LogicalValues(tuples=[[{ 5400000, 2020-01-01 00:00:00 }]])
]]>
</Resource>
</TestCase>
@@ -2636,6 +2651,19 @@ from (values (interval '3:4:5' hour to second)) as
t(x)]]>
<![CDATA[
LogicalProject(F=[*(/INT(CASE(>=($0, 0), $0, -($0, 3599999)), 3600000),
3600000)], C=[*(/INT(CASE(>=($0, 0), +($0, 3599999), $0), 3600000), 3600000)])
LogicalValues(tuples=[[{ 11045000 }]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testFloorCeilOfIntervalLiteral">
+ <Resource name="sql">
+ <![CDATA[select floor(interval '3:4:5' hour to second) as f,
+ ceil(interval '3:4:5' hour to second) as c
+from emp]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(F=[*(/INT(CASE(>=(11045000:INTERVAL HOUR TO SECOND, 0),
11045000:INTERVAL HOUR TO SECOND, -(11045000:INTERVAL HOUR TO SECOND,
3599999:INTERVAL HOUR TO SECOND)), 3600000), 3600000)],
C=[*(/INT(CASE(>=(11045000:INTERVAL HOUR TO SECOND, 0), +(11045000:INTERVAL
HOUR TO SECOND, 3599999:INTERVAL HOUR TO SECOND), 11045000:INTERVAL HOUR TO
SECOND), 3600000), 3600000)])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
diff --git a/core/src/test/resources/sql/operator.iq
b/core/src/test/resources/sql/operator.iq
index 45bee30f0c..5b0a082595 100644
--- a/core/src/test/resources/sql/operator.iq
+++ b/core/src/test/resources/sql/operator.iq
@@ -883,4 +883,18 @@ select floor(interval '2' hour + interval '90' minute) =
interval '3' hour as fa
!ok
+# [CALCITE-7451] REINTERPRET should not be used in logical plans
+# CAST of a non-literal interval to an exact numeric type. A DECIMAL target
+# preserves the fractional part; an integer target truncates towards zero.
+select cast(x as decimal(2,1)) as d, cast(x as integer) as i
+from (values (interval '1.29' second(1,2))) as t(x);
++-----+---+
+| D | I |
++-----+---+
+| 1.2 | 1 |
++-----+---+
+(1 row)
+
+!ok
+
# End operator.iq