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

xiong 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 6c7685cb56 [CALCITE-4838] Add RoundingMode in RelDataTypeSystem to 
specify the rounding behavior
6c7685cb56 is described below

commit 6c7685cb568494cfe927420a157eae414d80f5e3
Author: Xiong Duan <[email protected]>
AuthorDate: Fri Aug 16 20:07:53 2024 +0800

    [CALCITE-4838] Add RoundingMode in RelDataTypeSystem to specify the 
rounding behavior
---
 .../adapter/enumerable/RexToLixTranslator.java     |  34 +-
 .../calcite/rel/type/DelegatingTypeSystem.java     |   6 +
 .../apache/calcite/rel/type/RelDataTypeSystem.java |   5 +
 .../calcite/rel/type/RelDataTypeSystemImpl.java    |   6 +
 .../org/apache/calcite/util/BuiltInMethod.java     |  21 +-
 .../org/apache/calcite/test/CoreQuidemTest.java    |   9 +
 .../calcite/test/CustomRelDataTypeSystem.java      |  40 ++
 core/src/test/resources/sql/misc.iq                | 426 +++++++++++++++++++++
 .../apache/calcite/linq4j/tree/Expressions.java    |  12 +-
 .../org/apache/calcite/linq4j/tree/Primitive.java  | 105 ++++-
 .../calcite/linq4j/tree/UnaryExpression.java       |   4 +-
 11 files changed, 626 insertions(+), 42 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 39014391da..aedee05227 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
@@ -456,45 +456,51 @@ public class RexToLixTranslator implements 
RexVisitor<RexToLixTranslator.Result>
           && scale != RelDataType.SCALE_NOT_SPECIFIED) {
         if (sourceType.getFamily() == SqlTypeFamily.CHARACTER) {
           return Expressions.call(
-              BuiltInMethod.CHAR_DECIMAL_CAST.method,
+              BuiltInMethod.CHAR_DECIMAL_CAST_ROUNDING_MODE.method,
               operand,
               Expressions.constant(precision),
-              Expressions.constant(scale));
+              Expressions.constant(scale),
+              
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
         } else if (sourceType.getFamily() == SqlTypeFamily.INTERVAL_DAY_TIME) {
           return Expressions.call(
-              BuiltInMethod.SHORT_INTERVAL_DECIMAL_CAST.method,
+              BuiltInMethod.SHORT_INTERVAL_DECIMAL_CAST_ROUNDING_MODE.method,
               operand,
               Expressions.constant(precision),
               Expressions.constant(scale),
-              
Expressions.constant(sourceType.getSqlTypeName().getEndUnit().multiplier));
+              
Expressions.constant(sourceType.getSqlTypeName().getEndUnit().multiplier),
+              
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
         } else if (sourceType.getFamily() == 
SqlTypeFamily.INTERVAL_YEAR_MONTH) {
           return Expressions.call(
-              BuiltInMethod.LONG_INTERVAL_DECIMAL_CAST.method,
+              BuiltInMethod.LONG_INTERVAL_DECIMAL_CAST_ROUNDING_MODE.method,
               operand,
               Expressions.constant(precision),
               Expressions.constant(scale),
-              
Expressions.constant(sourceType.getSqlTypeName().getEndUnit().multiplier));
+              
Expressions.constant(sourceType.getSqlTypeName().getEndUnit().multiplier),
+              
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
         } else if (sourceType.getSqlTypeName() == SqlTypeName.DECIMAL) {
           // Cast from DECIMAL to DECIMAL, may adjust scale and precision.
           return Expressions.call(
-              BuiltInMethod.DECIMAL_DECIMAL_CAST.method,
+              BuiltInMethod.DECIMAL_DECIMAL_CAST_ROUNDING_MODE.method,
               operand,
               Expressions.constant(precision),
-              Expressions.constant(scale));
+              Expressions.constant(scale),
+              
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
         } else if 
(SqlTypeName.INT_TYPES.contains(sourceType.getSqlTypeName())) {
           // Cast from INTEGER to DECIMAL, check for overflow
           return Expressions.call(
-              BuiltInMethod.INTEGER_DECIMAL_CAST.method,
+              BuiltInMethod.INTEGER_DECIMAL_CAST_ROUNDING_MODE.method,
               operand,
               Expressions.constant(precision),
-              Expressions.constant(scale));
+              Expressions.constant(scale),
+              
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
         }  else if 
(SqlTypeName.APPROX_TYPES.contains(sourceType.getSqlTypeName())) {
           // Cast from FLOAT/DOUBLE to DECIMAL
           return Expressions.call(
-              BuiltInMethod.FP_DECIMAL_CAST.method,
+              BuiltInMethod.FP_DECIMAL_CAST_ROUNDING_MODE.method,
               operand,
               Expressions.constant(precision),
-              Expressions.constant(scale));
+              Expressions.constant(scale),
+              
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
         }
       }
       return defaultExpression.get();
@@ -505,9 +511,9 @@ public class RexToLixTranslator implements 
RexVisitor<RexToLixTranslator.Result>
     case SMALLINT: {
       if (SqlTypeName.NUMERIC_TYPES.contains(sourceType.getSqlTypeName())) {
         return Expressions.call(
-            BuiltInMethod.INTEGER_CAST.method,
+            BuiltInMethod.INTEGER_CAST_ROUNDING_MODE.method,
             
Expressions.constant(Primitive.of(typeFactory.getJavaClass(targetType))),
-            operand);
+            operand, 
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
       }
       return defaultExpression.get();
     }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/type/DelegatingTypeSystem.java 
b/core/src/main/java/org/apache/calcite/rel/type/DelegatingTypeSystem.java
index 96b8628a9b..edc8d16d50 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/DelegatingTypeSystem.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/DelegatingTypeSystem.java
@@ -20,6 +20,8 @@ import org.apache.calcite.sql.type.SqlTypeName;
 
 import org.checkerframework.checker.nullness.qual.Nullable;
 
+import java.math.RoundingMode;
+
 /** Implementation of {@link org.apache.calcite.rel.type.RelDataTypeSystem}
  * that sends all methods to an underlying object. */
 public class DelegatingTypeSystem implements RelDataTypeSystem {
@@ -50,6 +52,10 @@ public class DelegatingTypeSystem implements 
RelDataTypeSystem {
     return typeSystem.getMaxNumericPrecision();
   }
 
+  @Override public RoundingMode roundingMode() {
+    return typeSystem.roundingMode();
+  }
+
   @Override public @Nullable String getLiteral(SqlTypeName typeName, boolean 
isPrefix) {
     return typeSystem.getLiteral(typeName, isPrefix);
   }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java 
b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java
index ba2b0e6f60..ebe900650e 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java
@@ -22,6 +22,8 @@ import org.apache.calcite.util.Glossary;
 
 import org.checkerframework.checker.nullness.qual.Nullable;
 
+import java.math.RoundingMode;
+
 /**
  * Type system.
  *
@@ -60,6 +62,9 @@ public interface RelDataTypeSystem {
   /** Returns the maximum precision of a NUMERIC or DECIMAL type. */
   int getMaxNumericPrecision();
 
+  /** Returns the rounding behavior for numerical operations capable of 
discarding precision. */
+  RoundingMode roundingMode();
+
   /** Returns the LITERAL string for the type, either PREFIX/SUFFIX. */
   @Nullable String getLiteral(SqlTypeName typeName, boolean isPrefix);
 
diff --git 
a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java 
b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
index d4a9cac0f8..1b057909e3 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
@@ -22,6 +22,8 @@ import org.apache.calcite.sql.type.SqlTypeName;
 
 import org.checkerframework.checker.nullness.qual.Nullable;
 
+import java.math.RoundingMode;
+
 /** Default implementation of
  * {@link org.apache.calcite.rel.type.RelDataTypeSystem},
  * providing parameters from the SQL standard.
@@ -160,6 +162,10 @@ public abstract class RelDataTypeSystemImpl implements 
RelDataTypeSystem {
     return 19;
   }
 
+  @Override public RoundingMode roundingMode() {
+    return RoundingMode.DOWN;
+  }
+
   @Override public @Nullable String getLiteral(SqlTypeName typeName, boolean 
isPrefix) {
     switch (typeName) {
     case VARBINARY:
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java 
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 7d74ccd37a..bd94cad2f2 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -125,6 +125,7 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.nio.charset.Charset;
 import java.sql.ResultSet;
 import java.sql.Time;
@@ -159,10 +160,16 @@ public enum BuiltInMethod {
   AS_QUERYABLE(Enumerable.class, "asQueryable"),
   ABSTRACT_ENUMERABLE_CTOR(AbstractEnumerable.class),
   CHAR_DECIMAL_CAST(Primitive.class, "charToDecimalCast", String.class, 
int.class, int.class),
+  CHAR_DECIMAL_CAST_ROUNDING_MODE(Primitive.class, "charToDecimalCast",
+      String.class, int.class, int.class, RoundingMode.class),
   SHORT_INTERVAL_DECIMAL_CAST(Primitive.class, "shortIntervalToDecimalCast",
       Long.class, int.class, int.class, BigDecimal.class),
+  SHORT_INTERVAL_DECIMAL_CAST_ROUNDING_MODE(Primitive.class, 
"shortIntervalToDecimalCast",
+      Long.class, int.class, int.class, BigDecimal.class, RoundingMode.class),
   LONG_INTERVAL_DECIMAL_CAST(Primitive.class, "longIntervalToDecimalCast",
       Integer.class, int.class, int.class, BigDecimal.class),
+  LONG_INTERVAL_DECIMAL_CAST_ROUNDING_MODE(Primitive.class, 
"longIntervalToDecimalCast",
+      Integer.class, int.class, int.class, BigDecimal.class, 
RoundingMode.class),
   INTO(ExtendedEnumerable.class, "into", Collection.class),
   REMOVE_ALL(ExtendedEnumerable.class, "removeAll", Collection.class),
   SCHEMA_GET_SUB_SCHEMA(Schema.class, "getSubSchema", String.class),
@@ -310,11 +317,17 @@ public enum BuiltInMethod {
   AS_LIST(Primitive.class, "asList", Object.class),
   DECIMAL_DECIMAL_CAST(Primitive.class, "decimalDecimalCast",
       BigDecimal.class, int.class, int.class),
-  INTEGER_DECIMAL_CAST(Primitive.class, "integerDecimalCast",
-      Number.class, int.class, int.class),
-  FP_DECIMAL_CAST(Primitive.class, "fpDecimalCast",
-      Number.class, int.class, int.class),
+  DECIMAL_DECIMAL_CAST_ROUNDING_MODE(Primitive.class, "decimalDecimalCast",
+      BigDecimal.class, int.class, int.class, RoundingMode.class),
+  INTEGER_DECIMAL_CAST(Primitive.class, "integerDecimalCast", Number.class, 
int.class, int.class),
+  INTEGER_DECIMAL_CAST_ROUNDING_MODE(Primitive.class, "integerDecimalCast",
+      Number.class, int.class, int.class, RoundingMode.class),
+  FP_DECIMAL_CAST(Primitive.class, "fpDecimalCast", Number.class, int.class, 
int.class),
+  FP_DECIMAL_CAST_ROUNDING_MODE(Primitive.class, "fpDecimalCast",
+      Number.class, int.class, int.class, RoundingMode.class),
   INTEGER_CAST(Primitive.class, "integerCast", Primitive.class, Object.class),
+  INTEGER_CAST_ROUNDING_MODE(Primitive.class, "integerCast",
+      Primitive.class, Object.class, RoundingMode.class),
   MEMORY_GET0(MemoryFactory.Memory.class, "get"),
   MEMORY_GET1(MemoryFactory.Memory.class, "get", int.class),
   ENUMERATOR_CURRENT(Enumerator.class, "current"),
diff --git a/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java 
b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
index 8fbbf3d6ab..5b325e1a54 100644
--- a/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
+++ b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
@@ -68,6 +68,15 @@ class CoreQuidemTest extends QuidemTest {
               .with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
               .with(CalciteAssert.Config.SCOTT)
               .connect();
+        case "scott-rounding-half-up":
+          return CalciteAssert.that()
+              .with(CalciteConnectionProperty.PARSER_FACTORY,
+                  ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
+              .with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
+              .with(CalciteConnectionProperty.TYPE_SYSTEM,
+                  CustomRelDataTypeSystem.class.getName() + 
"#ROUNDING_MODE_HALF_UP")
+              .with(CalciteAssert.Config.SCOTT)
+              .connect();
         case "scott-lenient":
           // Same as "scott", but uses LENIENT conformance.
           // TODO: add a way to change conformance without defining a new
diff --git 
a/core/src/test/java/org/apache/calcite/test/CustomRelDataTypeSystem.java 
b/core/src/test/java/org/apache/calcite/test/CustomRelDataTypeSystem.java
new file mode 100644
index 0000000000..a230d18ae4
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/CustomRelDataTypeSystem.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.rel.type.RelDataTypeSystem;
+import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
+
+import java.math.RoundingMode;
+
+/**
+ * Custom type system only for Quidem test.
+ *
+ * <p> Specify the rounding behaviour. In the default implementation,
+ * the rounding mode is {@link RoundingMode#DOWN}, but here is  {@link 
RoundingMode#HALF_UP}
+ *
+ * <p>The default implementation is {@link #DEFAULT}.
+ */
+
+public class CustomRelDataTypeSystem extends RelDataTypeSystemImpl {
+
+  public static final RelDataTypeSystem ROUNDING_MODE_HALF_UP = new 
CustomRelDataTypeSystem();
+
+  @Override public RoundingMode roundingMode() {
+    return RoundingMode.HALF_UP;
+  }
+}
diff --git a/core/src/test/resources/sql/misc.iq 
b/core/src/test/resources/sql/misc.iq
index 224d6a1d13..fdc8802bab 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -2658,4 +2658,430 @@ FROM "hr"."emps";
 
 !ok
 
+# [CALCITE-4838] Add RoundingMode in RelDataTypeSystem to specify the rounding 
behavior
+!use scott-rounding-half-up
+
+# Test cast approximate numeric to int when rounding mode is HALF-UP
+select cast(5.5 as int),
+       cast(3.5 as int),
+       cast(1.6 as int),
+       cast(1.1 as int),
+       cast(1.0 as int),
+       cast(-1.0 as int),
+       cast(-1.1 as int),
+       cast(-1.6 as int),
+       cast(-2.5 as int),
+       cast(15.5 as int)
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      6 |      4 |      2 |      1 |      1 |     -1 |     -1 |     -2 |     
-3 |     16 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast approximate numeric to tinyint when rounding mode is HALF-UP
+select cast(5.5 as tinyint),
+       cast(3.5 as tinyint),
+       cast(1.6 as tinyint),
+       cast(1.1 as tinyint),
+       cast(1.0 as tinyint),
+       cast(-1.0 as tinyint),
+       cast(-1.1 as tinyint),
+       cast(-1.6 as tinyint),
+       cast(-2.5 as tinyint),
+       cast(15.5 as tinyint)
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      6 |      4 |      2 |      1 |      1 |     -1 |     -1 |     -2 |     
-3 |     16 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast approximate numeric to smallint when rounding mode is HALF-UP
+select cast(5.5 as smallint),
+       cast(3.5 as smallint),
+       cast(1.6 as smallint),
+       cast(1.1 as smallint),
+       cast(1.0 as smallint),
+       cast(-1.0 as smallint),
+       cast(-1.1 as smallint),
+       cast(-1.6 as smallint),
+       cast(-2.5 as smallint),
+       cast(15.5 as smallint)
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      6 |      4 |      2 |      1 |      1 |     -1 |     -1 |     -2 |     
-3 |     16 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast approximate numeric to bigint when rounding mode is HALF-UP
+select cast(5.5 as bigint),
+       cast(3.5 as bigint),
+       cast(1.6 as bigint),
+       cast(1.1 as bigint),
+       cast(1.0 as bigint),
+       cast(-1.0 as bigint),
+       cast(-1.1 as bigint),
+       cast(-1.6 as bigint),
+       cast(-2.5 as bigint),
+       cast(15.5 as bigint)
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      6 |      4 |      2 |      1 |      1 |     -1 |     -1 |     -2 |     
-3 |     16 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast decimal to decimal when rounding mode is HALF-UP
+select cast(cast(5.55 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(3.55 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(1.66 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(1.11 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(1.00 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(-1.00 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(-1.11 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(-1.66 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(-2.55 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(15.55 as decimal(4,2)) as decimal(3,1))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|    5.6 |    3.6 |    1.7 |    1.1 |    1.0 |   -1.0 |   -1.1 |   -1.7 |   
-2.6 |   15.6 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast varchar to decimal when rounding mode is HALF-UP
+select cast('5.5' as decimal(2, 0)),
+       cast('3.5' as decimal(2, 0)),
+       cast('1.6' as decimal(2, 0)),
+       cast('1.1' as decimal(2, 0)),
+       cast('1.0' as decimal(2, 0)),
+       cast('-1.0' as decimal(2, 0)),
+       cast('-1.1' as decimal(2, 0)),
+       cast('-1.6' as decimal(2, 0)),
+       cast('-2.5' as decimal(2, 0)),
+       cast('15.5' as decimal(2, 0))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      6 |      4 |      2 |      1 |      1 |     -1 |     -1 |     -2 |     
-3 |     16 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast float to decimal when rounding mode is HALF-UP
+select cast(cast(5.5 as float) as decimal(2,0)),
+       cast(cast(3.5 as float) as decimal(2,0)),
+       cast(cast(1.6 as float) as decimal(2,0)),
+       cast(cast(1.1 as float) as decimal(2,0)),
+       cast(cast(1.0 as float) as decimal(2,0)),
+       cast(cast(-1.0 as float) as decimal(2,0)),
+       cast(cast(-1.1 as float) as decimal(2,0)),
+       cast(cast(-1.6 as float) as decimal(2,0)),
+       cast(cast(-2.5 as float) as decimal(2,0)),
+       cast(cast(15.5 as float) as decimal(2,0))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      6 |      4 |      2 |      1 |      1 |     -1 |     -1 |     -2 |     
-3 |     16 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast float to decimal but rounding behavior happens in scala when 
rounding mode is HALF-UP
+select cast(cast(5.55 as float) as decimal(2,1)),
+       cast(cast(3.55 as float) as decimal(2,1)),
+       cast(cast(1.66 as float) as decimal(2,1)),
+       cast(cast(1.11 as float) as decimal(2,1)),
+       cast(cast(1.00 as float) as decimal(2,1)),
+       cast(cast(-1.00 as float) as decimal(2,1)),
+       cast(cast(-1.11 as float) as decimal(2,1)),
+       cast(cast(-1.66 as float) as decimal(2,1)),
+       cast(cast(-2.55 as float) as decimal(2,1)),
+       cast(cast(15.55 as float) as decimal(3,1))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|    5.6 |    3.6 |    1.7 |    1.1 |    1.0 |   -1.0 |   -1.1 |   -1.7 |   
-2.6 |   15.6 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast double to decimal when rounding mode is HALF-UP
+select cast(cast(5.5 as double) as decimal(2,0)),
+       cast(cast(3.5 as double) as decimal(2,0)),
+       cast(cast(1.6 as double) as decimal(2,0)),
+       cast(cast(1.1 as double) as decimal(2,0)),
+       cast(cast(1.0 as double) as decimal(2,0)),
+       cast(cast(-1.0 as double) as decimal(2,0)),
+       cast(cast(-1.1 as double) as decimal(2,0)),
+       cast(cast(-1.6 as double) as decimal(2,0)),
+       cast(cast(-2.5 as double) as decimal(2,0)),
+       cast(cast(15.5 as double) as decimal(2,0))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      6 |      4 |      2 |      1 |      1 |     -1 |     -1 |     -2 |     
-3 |     16 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast double to decimal but rounding behavior happens in scala  when 
rounding mode is HALF-UP
+select cast(cast(5.55 as double) as decimal(2,1)),
+       cast(cast(3.55 as double) as decimal(2,1)),
+       cast(cast(1.66 as double) as decimal(2,1)),
+       cast(cast(1.11 as double) as decimal(2,1)),
+       cast(cast(1.00 as double) as decimal(2,1)),
+       cast(cast(-1.00 as double) as decimal(2,1)),
+       cast(cast(-1.11 as double) as decimal(2,1)),
+       cast(cast(-1.66 as double) as decimal(2,1)),
+       cast(cast(-2.55 as double) as decimal(2,1)),
+       cast(cast(15.55 as double) as decimal(3,1))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|    5.6 |    3.6 |    1.7 |    1.1 |    1.0 |   -1.0 |   -1.1 |   -1.7 |   
-2.6 |   15.6 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+
+!use scott
+
+# Test cast approximate numeric to int when rounding mode is DOWN
+select cast(5.5 as int),
+       cast(3.5 as int),
+       cast(1.6 as int),
+       cast(1.1 as int),
+       cast(1.0 as int),
+       cast(-1.0 as int),
+       cast(-1.1 as int),
+       cast(-1.6 as int),
+       cast(-2.5 as int),
+       cast(15.5 as int)
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      5 |      3 |      1 |      1 |      1 |     -1 |     -1 |     -1 |     
-2 |     15 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast approximate numeric to tinyint when rounding mode is DOWN
+select cast(5.5 as tinyint),
+       cast(3.5 as tinyint),
+       cast(1.6 as tinyint),
+       cast(1.1 as tinyint),
+       cast(1.0 as tinyint),
+       cast(-1.0 as tinyint),
+       cast(-1.1 as tinyint),
+       cast(-1.6 as tinyint),
+       cast(-2.5 as tinyint),
+       cast(15.5 as tinyint)
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      5 |      3 |      1 |      1 |      1 |     -1 |     -1 |     -1 |     
-2 |     15 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast approximate numeric to smallint when rounding mode is DOWN
+select cast(5.5 as smallint),
+       cast(3.5 as smallint),
+       cast(1.6 as smallint),
+       cast(1.1 as smallint),
+       cast(1.0 as smallint),
+       cast(-1.0 as smallint),
+       cast(-1.1 as smallint),
+       cast(-1.6 as smallint),
+       cast(-2.5 as smallint),
+       cast(15.5 as smallint)
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      5 |      3 |      1 |      1 |      1 |     -1 |     -1 |     -1 |     
-2 |     15 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast approximate numeric to bigint when rounding mode is DOWN
+select cast(5.5 as bigint),
+       cast(3.5 as bigint),
+       cast(1.6 as bigint),
+       cast(1.1 as bigint),
+       cast(1.0 as bigint),
+       cast(-1.0 as bigint),
+       cast(-1.1 as bigint),
+       cast(-1.6 as bigint),
+       cast(-2.5 as bigint),
+       cast(15.5 as bigint)
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      5 |      3 |      1 |      1 |      1 |     -1 |     -1 |     -1 |     
-2 |     15 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast decimal to decimal when rounding mode is DOWN
+select cast(cast(5.55 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(3.55 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(1.66 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(1.11 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(1.00 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(-1.00 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(-1.11 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(-1.66 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(-2.55 as decimal(3,2)) as decimal(2,1)),
+       cast(cast(15.55 as decimal(4,2)) as decimal(3,1))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|    5.5 |    3.5 |    1.6 |    1.1 |    1.0 |   -1.0 |   -1.1 |   -1.6 |   
-2.5 |   15.5 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast varchar to decimal when rounding mode is DOWN
+select cast('5.5' as decimal(2, 0)),
+       cast('3.5' as decimal(2, 0)),
+       cast('1.6' as decimal(2, 0)),
+       cast('1.1' as decimal(2, 0)),
+       cast('1.0' as decimal(2, 0)),
+       cast('-1.0' as decimal(2, 0)),
+       cast('-1.1' as decimal(2, 0)),
+       cast('-1.6' as decimal(2, 0)),
+       cast('-2.5' as decimal(2, 0)),
+       cast('15.5' as decimal(2, 0))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      5 |      3 |      1 |      1 |      1 |     -1 |     -1 |     -1 |     
-2 |     15 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast float to decimal when rounding mode is DOWN
+select cast(cast(5.5 as float) as decimal(2,0)),
+       cast(cast(3.5 as float) as decimal(2,0)),
+       cast(cast(1.6 as float) as decimal(2,0)),
+       cast(cast(1.1 as float) as decimal(2,0)),
+       cast(cast(1.0 as float) as decimal(2,0)),
+       cast(cast(-1.0 as float) as decimal(2,0)),
+       cast(cast(-1.1 as float) as decimal(2,0)),
+       cast(cast(-1.6 as float) as decimal(2,0)),
+       cast(cast(-2.5 as float) as decimal(2,0)),
+       cast(cast(15.5 as float) as decimal(2,0))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      5 |      3 |      1 |      1 |      1 |     -1 |     -1 |     -1 |     
-2 |     15 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast float to decimal but rounding behavior happens in scala when 
rounding mode is DOWN
+select cast(cast(5.55 as float) as decimal(2,1)),
+       cast(cast(3.55 as float) as decimal(2,1)),
+       cast(cast(1.66 as float) as decimal(2,1)),
+       cast(cast(1.11 as float) as decimal(2,1)),
+       cast(cast(1.00 as float) as decimal(2,1)),
+       cast(cast(-1.00 as float) as decimal(2,1)),
+       cast(cast(-1.11 as float) as decimal(2,1)),
+       cast(cast(-1.66 as float) as decimal(2,1)),
+       cast(cast(-2.55 as float) as decimal(2,1)),
+       cast(cast(15.55 as float) as decimal(3,1))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|    5.5 |    3.5 |    1.6 |    1.1 |    1.0 |   -1.0 |   -1.1 |   -1.6 |   
-2.5 |   15.5 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast double to decimal when rounding mode is DOWN
+select cast(cast(5.5 as double) as decimal(2,0)),
+       cast(cast(3.5 as double) as decimal(2,0)),
+       cast(cast(1.6 as double) as decimal(2,0)),
+       cast(cast(1.1 as double) as decimal(2,0)),
+       cast(cast(1.0 as double) as decimal(2,0)),
+       cast(cast(-1.0 as double) as decimal(2,0)),
+       cast(cast(-1.1 as double) as decimal(2,0)),
+       cast(cast(-1.6 as double) as decimal(2,0)),
+       cast(cast(-2.5 as double) as decimal(2,0)),
+       cast(cast(15.5 as double) as decimal(2,0))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|      5 |      3 |      1 |      1 |      1 |     -1 |     -1 |     -1 |     
-2 |     15 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
+# Test cast double to decimal but rounding behavior happens in scala  when 
rounding mode is DOWN
+select cast(cast(5.55 as double) as decimal(2,1)),
+       cast(cast(3.55 as double) as decimal(2,1)),
+       cast(cast(1.66 as double) as decimal(2,1)),
+       cast(cast(1.11 as double) as decimal(2,1)),
+       cast(cast(1.00 as double) as decimal(2,1)),
+       cast(cast(-1.00 as double) as decimal(2,1)),
+       cast(cast(-1.11 as double) as decimal(2,1)),
+       cast(cast(-1.66 as double) as decimal(2,1)),
+       cast(cast(-2.55 as double) as decimal(2,1)),
+       cast(cast(15.55 as double) as decimal(3,1))
+from (values 0) as t(x);
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+| EXPR$0 | EXPR$1 | EXPR$2 | EXPR$3 | EXPR$4 | EXPR$5 | EXPR$6 | EXPR$7 | 
EXPR$8 | EXPR$9 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+|    5.5 |    3.5 |    1.6 |    1.1 |    1.0 |   -1.0 |   -1.1 |   -1.6 |   
-2.5 |   15.5 |
++--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
+(1 row)
+
+!ok
+
 # End misc.iq
diff --git 
a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java 
b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java
index ebde1a4d3e..e41a07cc01 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java
@@ -35,6 +35,7 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -550,6 +551,15 @@ public abstract class Expressions {
    * properties set to the specified values.
    */
   public static ConstantExpression constant(@Nullable Object value, Type type) 
{
+    return constant(value, type, RoundingMode.DOWN);
+  }
+
+  /**
+   * Creates a ConstantExpression that has the Value 、Type 、RoundingMode
+   * properties set to the specified values.
+   */
+  public static ConstantExpression constant(@Nullable Object value, Type type,
+      RoundingMode roundingMode) {
     if (value != null && type instanceof Class) {
       // Fix up value so that it matches type.
       Class<?> clazz = (Class<?>) type;
@@ -572,7 +582,7 @@ public abstract class Expressions {
         if (primitive != null) {
           if (value instanceof Number) {
             Number valueNumber = (Number) value;
-            value = primitive.numberValue(valueNumber);
+            value = primitive.numberValue(valueNumber, roundingMode);
             if (value == null) {
               value = primitive.parse(stringValue);
             }
diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Primitive.java 
b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Primitive.java
index 71e7d5aae1..34f40e83f6 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Primitive.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Primitive.java
@@ -386,21 +386,24 @@ public enum Primitive {
 
   /** Called from BuiltInMethod.INTEGER_CAST */
   public static @Nullable Object integerCast(Primitive primitive, final Object 
value) {
-    return requireNonNull(primitive, "primitive").numberValue((Number) value);
+    return requireNonNull(primitive, "primitive").numberValue((Number) value, 
RoundingMode.DOWN);
   }
 
-  static BigDecimal checkOverflow(BigDecimal value, int precision, int scale) {
-    // The rounding mode is not specified in any Calcite docs,
-    // but elsewhere Calcite is rounding down.  For example, Calcite is 
frequently
-    // calling BigDecimal.longValue(), which is rounding down, by ignoring all
-    // digits after the decimal point.
-    BigDecimal result = value.setScale(scale, RoundingMode.DOWN);
+  /** Called from BuiltInMethod.INTEGER_CAST_ROUNDING_MODE */
+  public static @Nullable Object integerCast(Primitive primitive, final Object 
value,
+      RoundingMode roundingMode) {
+    return requireNonNull(primitive, "primitive").numberValue((Number) value, 
roundingMode);
+  }
+
+  static BigDecimal checkOverflow(BigDecimal value, int precision, int scale,
+      RoundingMode roundingMode) {
+    BigDecimal result = value.setScale(scale, roundingMode);
     result = result.stripTrailingZeros();
     if (result.scale() < scale) {
       // stripTrailingZeros also removes zeros if there is no
       // decimal point, converting 1000 to 1e+3, using a negative scale.
       // Here we undo this change.
-      result = result.setScale(scale, RoundingMode.DOWN);
+      result = result.setScale(scale, roundingMode);
     }
     int actualPrecision = result.precision();
     if (actualPrecision > precision) {
@@ -413,11 +416,17 @@ public enum Primitive {
   /** Called from BuiltInMethod.CHAR_DECIMAL_CAST */
   public static @Nullable Object charToDecimalCast(
       @Nullable String value, int precision, int scale) {
+    return charToDecimalCast(value, precision, scale, RoundingMode.DOWN);
+  }
+
+  /** Called from BuiltInMethod.CHAR_DECIMAL_CAST_ROUNDING_MODE */
+  public static @Nullable Object charToDecimalCast(
+      @Nullable String value, int precision, int scale, RoundingMode 
roundingMode) {
     if (value == null) {
       return null;
     }
     BigDecimal result = new BigDecimal(value.trim());
-    return checkOverflow(result, precision, scale);
+    return checkOverflow(result, precision, scale, roundingMode);
   }
 
   /**
@@ -425,13 +434,24 @@ public enum Primitive {
    * Called from BuiltInMethod.SHORT_INTERVAL_DECIMAL_CAST.
    * @param unitScale Scale describing source interval type */
   public static @Nullable Object shortIntervalToDecimalCast(
-      @Nullable Long value, int precision, int scale, BigDecimal unitScale) {
+      @Nullable Long value, int precision, int scale,
+      BigDecimal unitScale) {
+    return shortIntervalToDecimalCast(value, precision, scale, unitScale, 
RoundingMode.DOWN);
+  }
+
+  /**
+   * Convert a short time interval to a decimal value.
+   * Called from BuiltInMethod.SHORT_INTERVAL_DECIMAL_CAST_ROUNDING_MODE.
+   * @param unitScale Scale describing source interval type */
+  public static @Nullable Object shortIntervalToDecimalCast(
+      @Nullable Long value, int precision, int scale,
+      BigDecimal unitScale, RoundingMode roundingMode) {
     if (value == null) {
       return null;
     }
     // Divide with the scale expected of the result
-    BigDecimal result = new BigDecimal(value).divide(unitScale, scale, 
RoundingMode.DOWN);
-    return checkOverflow(result, precision, scale);
+    BigDecimal result = new BigDecimal(value).divide(unitScale, scale, 
roundingMode);
+    return checkOverflow(result, precision, scale, roundingMode);
   }
 
   /**
@@ -439,42 +459,75 @@ public enum Primitive {
    * Called from BuiltInMethod.LONG_INTERVAL_DECIMAL_CAST.
    * @param unitScale Scale describing source interval type */
   public static @Nullable Object longIntervalToDecimalCast(
-      @Nullable Integer value, int precision, int scale, BigDecimal unitScale) 
{
+      @Nullable Integer value, int precision, int scale,
+      BigDecimal unitScale) {
+    return longIntervalToDecimalCast(value, precision, scale, unitScale, 
RoundingMode.DOWN);
+  }
+
+  /**
+   * Convert a long time interval to a decimal value.
+   * Called from BuiltInMethod.LONG_INTERVAL_DECIMAL_CAST_ROUNDING_MODE.
+   * @param unitScale Scale describing source interval type */
+  public static @Nullable Object longIntervalToDecimalCast(
+      @Nullable Integer value, int precision, int scale,
+      BigDecimal unitScale, RoundingMode roundingMode) {
     if (value == null) {
       return null;
     }
     // Divide with the scale expected of the result
-    BigDecimal result = new BigDecimal(value).divide(unitScale, scale, 
RoundingMode.DOWN);
-    return checkOverflow(result, precision, scale);
+    BigDecimal result = new BigDecimal(value).divide(unitScale, scale, 
roundingMode);
+    return checkOverflow(result, precision, scale, roundingMode);
   }
 
   /** Called from BuiltInMethod.DECIMAL_DECIMAL_CAST */
   public static @Nullable Object decimalDecimalCast(
       @Nullable BigDecimal value, int precision, int scale) {
+    return decimalDecimalCast(value, precision, scale, RoundingMode.DOWN);
+  }
+
+  /** Called from BuiltInMethod.DECIMAL_DECIMAL_CAST_ROUNDING_MODE */
+  public static @Nullable Object decimalDecimalCast(
+      @Nullable BigDecimal value, int precision, int scale, RoundingMode 
roundingMode) {
     if (value == null) {
       return null;
     }
-    return checkOverflow(value, precision, scale);
+    return checkOverflow(value, precision, scale, roundingMode);
   }
 
   /** Called from BuiltInMethod.INTEGER_DECIMAL_CAST */
   public static @Nullable Object integerDecimalCast(
       @Nullable Number value, int precision, int scale) {
+    return integerDecimalCast(value, precision, scale, RoundingMode.DOWN);
+  }
+
+  /** Called from BuiltInMethod.INTEGER_DECIMAL_CAST_ROUNDING_MODE */
+  public static @Nullable Object integerDecimalCast(
+      @Nullable Number value, int precision, int scale, RoundingMode 
roundingMode) {
     if (value == null) {
       return null;
     }
     final BigDecimal decimal = new BigDecimal(value.longValue());
-    return checkOverflow(decimal, precision, scale);
+    return checkOverflow(decimal, precision, scale, roundingMode);
   }
 
   /** Called from BuiltInMethod.FP_DECIMAL_CAST */
   public static @Nullable Object fpDecimalCast(
       @Nullable Number value, int precision, int scale) {
+    return fpDecimalCast(value, precision, scale, RoundingMode.DOWN);
+  }
+
+  /** Called from BuiltInMethod.FP_DECIMAL_CAST_ROUNDING_MODE */
+  public static @Nullable Object fpDecimalCast(
+      @Nullable Number value, int precision, int scale, RoundingMode 
roundingMode) {
     if (value == null) {
       return null;
     }
     final BigDecimal decimal = BigDecimal.valueOf(value.doubleValue());
-    return checkOverflow(decimal, precision, scale);
+    return checkOverflow(decimal, precision, scale, roundingMode);
+  }
+
+  public @Nullable Object numberValueRoundDown(Number value) {
+    return numberValue(value, RoundingMode.DOWN);
   }
 
   /**
@@ -483,12 +536,16 @@ public enum Primitive {
    * an exception is thrown.
    *
    * @param value  Value to convert.
+   * @param roundingMode Rounding behavior.
    * @return       The converted value, or null if the type of the result is 
not a number.
    */
-  public @Nullable Object numberValue(Number value) {
+  public @Nullable Object numberValue(Number value, RoundingMode roundingMode) 
{
     switch (this) {
     case BYTE:
       checkRoundedRange(value, Byte.MIN_VALUE, Byte.MAX_VALUE);
+      if (value instanceof BigDecimal) {
+        return ((BigDecimal) value).setScale(0, roundingMode).byteValue();
+      }
       return value.byteValue();
     case CHAR:
       // No overflow checks for char values.
@@ -496,9 +553,15 @@ public enum Primitive {
       return (char) value.intValue();
     case SHORT:
       checkRoundedRange(value, Short.MIN_VALUE, Short.MAX_VALUE);
+      if (value instanceof BigDecimal) {
+        return ((BigDecimal) value).setScale(0, roundingMode).shortValue();
+      }
       return value.shortValue();
     case INT:
       checkRoundedRange(value, Integer.MIN_VALUE, Integer.MAX_VALUE);
+      if (value instanceof BigDecimal) {
+        return ((BigDecimal) value).setScale(0, roundingMode).intValue();
+      }
       return value.intValue();
     case LONG:
       if (value instanceof Byte
@@ -513,14 +576,14 @@ public enum Primitive {
         // so we cannot use checkRoundedRange.
         BigDecimal decimal = BigDecimal.valueOf(value.doubleValue())
             // Round to an integer
-            .setScale(0, RoundingMode.DOWN);
+            .setScale(0, roundingMode);
         // longValueExact will throw ArithmeticException if out of range
         return decimal.longValueExact();
       }
       if (value instanceof BigDecimal) {
         BigDecimal decimal = ((BigDecimal) value)
             // Round to an integer
-            .setScale(0, RoundingMode.DOWN);
+            .setScale(0, roundingMode);
         // longValueExact will throw ArithmeticException if out of range
         return decimal.longValueExact();
       }
diff --git 
a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnaryExpression.java 
b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnaryExpression.java
index 09d3bc5212..4c08ab60b1 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnaryExpression.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnaryExpression.java
@@ -59,12 +59,12 @@ public class UnaryExpression extends Expression {
       if (!writer.requireParentheses(this, lprec, rprec)) {
         // Generate Java code that looks like e.g.,
         // ((Number)org.apache.calcite.linq4j.tree.Primitive.of(int.class)
-        //     .numberValue(literal_value)).intValue();
+        //     .numberValueRoundDown(literal_value)).intValue();
         writer.append("((Number)")
             .append("org.apache.calcite.linq4j.tree.Primitive.of(")
             .append(type)
             .append(".class)")
-            .append(".numberValue(");
+            .append(".numberValueRoundDown(");
         expression.accept(writer, nodeType.rprec, rprec);
         writer.append(")).").append(type).append("Value()");
       }


Reply via email to