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

jooger pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 6ff9a297e4 IGNITE-23171: Sql. Result of division doesn't match derived 
type (#4478)
6ff9a297e4 is described below

commit 6ff9a297e4bd7dc941151ffbd73baa54e9bac61b
Author: Max Zhuravkov <[email protected]>
AuthorDate: Fri Oct 11 12:21:06 2024 +0300

    IGNITE-23171: Sql. Result of division doesn't match derived type (#4478)
---
 .../Apache.Ignite.Tests/Linq/LinqTests.Cast.cs     |   5 +-
 .../internal/sql/engine/ItDataTypesTest.java       |   2 +-
 .../engine/datatypes/ItDivisionDecimalTest.java    | 238 +++++++++++++++++++++
 .../sql/engine/exec/exp/IgniteExpressions.java     |   6 +
 .../sql/engine/exec/exp/IgniteSqlFunctions.java    |  16 +-
 .../internal/sql/engine/exec/exp/RexImpTable.java  |  16 +-
 .../sql/engine/exec/exp/agg/Accumulators.java      |   4 +-
 .../sql/engine/sql/fun/IgniteSqlOperatorTable.java |   3 +-
 .../internal/sql/engine/util/IgniteMath.java       |  17 ++
 .../internal/sql/engine/util/IgniteMethod.java     |   6 +-
 .../NumericBinaryOperationsExecutionTest.java      |   4 -
 .../engine/exec/exp/IgniteSqlFunctionsTest.java    |   5 +-
 .../sql/engine/type/IgniteTypeSystemTest.java      | 103 ++++++++-
 13 files changed, 394 insertions(+), 31 deletions(-)

diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
index f46c74809b..f0f392a6bc 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
@@ -64,6 +64,7 @@ public partial class LinqTests
             query.ToString());
     }
 
+    [Ignore("IGNITE-23243 Value was either too large or too small for a 
Decimal")]
     [Test]
     public void TestCastToDecimalPrecision()
     {
@@ -75,8 +76,8 @@ public partial class LinqTests
 
         var res = query.ToList();
 
-        // TODO IGNITE-23171 Sql. Result of division doesn't match derived type
-        // Assert.AreEqual(900m / 33m, res[0]);
+        // The result can not be presented by decimal type.
+        // The expected value should be replaced when 
https://issues.apache.org/jira/browse/IGNITE-23243 is fixed.
         Assert.AreEqual(27.27272727272727m, res[0]);
 
         StringAssert.Contains("select cast((cast(_T0.VAL as decimal(60, 30)) / 
?) as decimal(60, 30))", query.ToString());
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
index ec6c6b8b1b..d96838db87 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
@@ -446,7 +446,7 @@ public class ItDataTypesTest extends BaseSqlIntegrationTest 
{
         assertQuery("SELECT DECIMAL '10.000' + DECIMAL '0.1'").returns(new 
BigDecimal(("10.100"))).check();
         assertQuery("SELECT DECIMAL '10.000' - DECIMAL '0.01'").returns(new 
BigDecimal(("9.990"))).check();
         assertQuery("SELECT DECIMAL '10.000' * DECIMAL '0.01'").returns(new 
BigDecimal(("0.10000"))).check();
-        assertQuery("SELECT DECIMAL '10.000' / DECIMAL '0.01'").returns(new 
BigDecimal(("1000.0"))).check();
+        assertQuery("SELECT DECIMAL '10.000' / DECIMAL '0.01'").returns(new 
BigDecimal(("1000.0000000"))).check();
 
         assertQuery("SELECT CASE WHEN true THEN DECIMAL '1.00' ELSE DECIMAL 
'0' END")
                 .returns(new BigDecimal("1.00")).check();
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItDivisionDecimalTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItDivisionDecimalTest.java
new file mode 100644
index 0000000000..fea774bf8b
--- /dev/null
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItDivisionDecimalTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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.ignite.internal.sql.engine.datatypes;
+
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Random;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.ignite.internal.sql.BaseSqlIntegrationTest;
+import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
+import org.apache.ignite.internal.sql.engine.type.IgniteTypeSystem;
+import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.MetadataMatcher;
+import org.apache.ignite.internal.testframework.IgniteTestUtils;
+import org.apache.ignite.sql.ColumnType;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+/** Test cases for the division operator involving decimal operand(s). */
+public class ItDivisionDecimalTest extends BaseSqlIntegrationTest {
+
+    @BeforeAll
+    public static void createTable() {
+        sql("CREATE TABLE t (id INT PRIMARY KEY, "
+                + " ti TINYINT, si SMALLINT, i INT, bi BIGINT, "
+                + " r REAL, d DOUBLE, "
+                + " d3_1 DECIMAL(3, 1), d5_2 DECIMAL(5, 2)"
+                + ")");
+    }
+
+    /**
+     * Drops all created tables.
+     */
+    @AfterAll
+    public void dropTables() {
+        var igniteTables = CLUSTER.aliveNode().tables();
+
+        for (var table : igniteTables.tables()) {
+            sql("DROP TABLE " + table.name());
+        }
+    }
+
+
+    @AfterEach
+    void clearTable() {
+        sql("DELETE FROM t");
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+            "5, 1, 5, 1",
+            "5, 2, 5, 2",
+            "5, 3, 5, 3",
+            "5, 4, 5, 4",
+
+            "10, 5, 20, 5",
+            "10, 6, 20, 6",
+            "10, 7, 20, 7",
+            "10, 8, 20, 8",
+            "10, 9, 20, 9",
+    })
+    public void decimalDivide(int p1, int s1, int p2, int s2) {
+        IgniteTypeFactory tf = Commons.typeFactory();
+
+        RelDataType t1 = tf.createSqlType(SqlTypeName.DECIMAL, p1, s1);
+        RelDataType t2 = tf.createSqlType(SqlTypeName.DECIMAL, p2, s2);
+        RelDataType rt = 
IgniteTypeSystem.INSTANCE.deriveDecimalDivideType(Commons.typeFactory(), t1, 
t2);
+
+        long seed = System.nanoTime();
+        log.info("Seed: {}", seed);
+
+        Random rnd = new Random();
+        rnd.setSeed(seed);
+
+        BigDecimal a1 = IgniteTestUtils.randomBigDecimal(rnd, 
t1.getPrecision(), t1.getScale());
+        BigDecimal a2 = IgniteTestUtils.randomBigDecimal(rnd, 
t2.getPrecision(), t2.getScale());
+        BigDecimal rs = a1.divide(a2, rt.getScale(), RoundingMode.HALF_UP);
+
+        String type1 = format("DECIMAL({}, {})", p1, s1);
+        String type2 = format("DECIMAL({}, {})", p2, s2);
+
+        // Literals no casts
+        assertQuery("SELECT " + a1 + "/" + a2)
+                .returns(rs)
+                .columnMetadata(new MetadataMatcher()
+                        .type(ColumnType.DECIMAL)
+                        .precision(rt.getPrecision())
+                        .scale(rt.getScale()))
+                .check();
+
+        // Literals w/ casts
+        assertQuery("SELECT " + a1  + "::" + type1 + "/" + a2 + "::" + type2)
+                .returns(rs)
+                .columnMetadata(new MetadataMatcher()
+                        .type(ColumnType.DECIMAL)
+                        .precision(rt.getPrecision())
+                        .scale(rt.getScale()))
+                .check();
+
+        // Dynamic params
+        assertQuery("SELECT ?::" + type1 + "/?::" + type2)
+                .withParams(a1, a2)
+                .returns(rs)
+                .columnMetadata(new MetadataMatcher()
+                        .type(ColumnType.DECIMAL)
+                        .precision(rt.getPrecision())
+                        .scale(rt.getScale()))
+                .check();
+    }
+
+
+    @Test
+    public void decimalTinyint() {
+        sql("INSERT INTO t (id, ti, d3_1) VALUES (1, 10, 3.1)");
+
+        assertQuery("SELECT ti/d3_1 FROM t")
+                .returns(new BigDecimal("3.225806"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(10).scale(6))
+                .check();
+
+        assertQuery("SELECT d3_1/ti FROM t")
+                .returns(new BigDecimal("0.310000"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(8).scale(6))
+                .check();
+    }
+
+    @Test
+    public void decimalSmallint() {
+        sql("INSERT INTO t (id, si, d3_1) VALUES (1, 10, 3.1)");
+
+        assertQuery("SELECT si/d3_1 FROM t")
+                .returns(new BigDecimal("3.225806"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(12).scale(6))
+                .check();
+
+        assertQuery("SELECT d3_1/si FROM t")
+                .returns(new BigDecimal("0.3100000"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(9).scale(7))
+                .check();
+    }
+
+    @Test
+    public void decimalInt() {
+        sql("INSERT INTO t (id, i, d3_1) VALUES (1, 10, 3.1)");
+
+        assertQuery("SELECT i/d3_1 FROM t")
+                .returns(new BigDecimal("3.225806"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(17).scale(6))
+                .check();
+
+        assertQuery("SELECT d3_1/i FROM t")
+                .returns(new BigDecimal("0.310000000000"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(14).scale(12))
+                .check();
+    }
+
+    @Test
+    public void decimalBigInt() {
+        sql("INSERT INTO t (id, bi, d3_1) VALUES (1, 10, 3.1)");
+
+        assertQuery("SELECT bi/d3_1 FROM t")
+                .returns(new BigDecimal("3.225806"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(26).scale(6))
+                .check();
+
+        assertQuery("SELECT d3_1/bi FROM t")
+                .returns(new BigDecimal("0.310000000000000000000"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(23).scale(21))
+                .check();
+    }
+
+    @Test
+    public void decimalReal() {
+        sql("INSERT INTO t (id, r, d3_1) VALUES (1, 10, 2.0)");
+
+        assertQuery("SELECT r/d3_1 FROM t")
+                .returns(5.0d)
+                .columnMetadata(new MetadataMatcher().type(ColumnType.DOUBLE))
+                .check();
+
+        assertQuery("SELECT d3_1/r FROM t")
+                .returns(0.2d)
+                .columnMetadata(new MetadataMatcher().type(ColumnType.DOUBLE))
+                .check();
+    }
+
+    @Test
+    public void decimalDouble() {
+        sql("INSERT INTO t (id, d, d3_1) VALUES (1, 10, 2.0)");
+
+        assertQuery("SELECT d/d3_1 FROM t")
+                .returns(5.0d)
+                .columnMetadata(new MetadataMatcher().type(ColumnType.DOUBLE))
+                .check();
+
+        assertQuery("SELECT d3_1/d FROM t")
+                .returns(0.2d)
+                .columnMetadata(new MetadataMatcher().type(ColumnType.DOUBLE))
+                .check();
+    }
+
+    @Test
+    public void decimalDecimal() {
+        sql("INSERT INTO t (id, d5_2, d3_1) VALUES (1, 10.00, 3.1)");
+
+        assertQuery("SELECT d5_2/d3_1 FROM t")
+                .returns(new BigDecimal("3.225806"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(10).scale(6))
+                .check();
+
+        assertQuery("SELECT d3_1/d5_2 FROM t")
+                .returns(new BigDecimal("0.3100000"))
+                .columnMetadata(new 
MetadataMatcher().type(ColumnType.DECIMAL).precision(11).scale(7))
+                .check();
+    }
+}
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteExpressions.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteExpressions.java
index 99d81c1e36..1291c0fc1b 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteExpressions.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteExpressions.java
@@ -44,6 +44,12 @@ public class IgniteExpressions {
         }
     }
 
+    /** Make decimal division expression. */
+    public static Expression makeDecimalDivision(Expression left, Expression 
right, int precision, int scale) {
+        return Expressions.call(IgniteMath.class, "decimalDivide", left, right,
+                Expressions.constant(precision, int.class), 
Expressions.constant(scale, int.class));
+    }
+
     /** Make unary expression with arithmetic operations override. */
     public static Expression makeUnary(ExpressionType unaryType, Expression 
operand) {
         switch (unaryType) {
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
index 17e18e4d9a..6af137c138 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
@@ -35,9 +35,9 @@ import org.apache.calcite.linq4j.function.NonDeterministic;
 import org.apache.calcite.runtime.SqlFunctions;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.ignite.internal.lang.IgniteStringBuilder;
-import org.apache.ignite.internal.sql.engine.sql.fun.IgniteSqlOperatorTable;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeSystem;
 import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.IgniteMath;
 import org.apache.ignite.internal.sql.engine.util.TypeUtils;
 import org.apache.ignite.sql.SqlException;
 import org.jetbrains.annotations.Nullable;
@@ -46,8 +46,6 @@ import org.jetbrains.annotations.Nullable;
  * Ignite SQL functions.
  */
 public class IgniteSqlFunctions {
-    private static final RoundingMode roundingMode = RoundingMode.HALF_UP;
-
     /**
      * Default constructor.
      */
@@ -356,14 +354,6 @@ public class IgniteSqlFunctions {
         }
     }
 
-    /**
-     * Division function for REDUCE phase of AVG aggregate. Precision and 
scale is only used by type inference
-     * (see {@link IgniteSqlOperatorTable#DECIMAL_DIVIDE}, their values are 
ignored at runtime.
-     */
-    public static BigDecimal decimalDivide(BigDecimal sum, BigDecimal cnt, int 
p, int s) {
-        return sum.divide(cnt, s, roundingMode);
-    }
-
     private static BigDecimal processValueWithIntegralPart(Number value, int 
precision, int scale) {
         BigDecimal dec = convertToBigDecimal(value);
 
@@ -378,7 +368,7 @@ public class IgniteSqlFunctions {
             }
         }
 
-        return dec.setScale(scale, roundingMode);
+        return dec.setScale(scale, IgniteMath.ROUNDING_MODE);
     }
 
     private static BigDecimal processFractionData(Number value, int precision, 
int scale) {
@@ -397,7 +387,7 @@ public class IgniteSqlFunctions {
             throw numericOverflowError(precision, scale);
         }
 
-        return num.setScale(scale, roundingMode);
+        return num.setScale(scale, IgniteMath.ROUNDING_MODE);
     }
 
     private static BigDecimal convertToBigDecimal(Number value) {
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
index 44e42b301c..d4525063e7 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
@@ -476,9 +476,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
  * 5. Amend calls of:
  *    EnumUtils.fromInternal -> ConverterUtils.fromInternal
  *    Expressions.makeUnary  -> IgniteExpressions.makeUnary
- *    Expressions.makeBinary -> IgniteExpressions.makeBinary
+ *    Expressions.makeBinary -> IgniteExpressions.makeBinary (or 
IgniteExpressions.decimalDivision in cause of decimal division).
  *    Expressions.multiply   -> IgniteExpressions.multiplyExact
  *    Expressions.divide     -> IgniteExpressions.divideExact
+ *    Expressions.divide     -> IgniteExpressions.makeDecimalDivision (if one 
or both operands are decimals)
  *    Expressions.negate     -> IgniteExpressions.makeUnary
  *    Expressions.subtract   -> IgniteExpressions.subtractExact
  *    Expressions.add        -> IgniteExpressions.addExact
@@ -3077,6 +3078,19 @@ public class RexImpTable {
         argValueList = FlatLists.append(argValueList, fieldComparator);
       }
 
+      if (type0 == BigDecimal.class && type1 == BigDecimal.class && op == 
IgniteSqlOperatorTable.DIVIDE) {
+        int precision = call.getType().getPrecision();
+        int scale = call.getType().getScale();
+
+        assert precision != RelDataType.PRECISION_NOT_SPECIFIED || scale != 
RelDataType.SCALE_NOT_SPECIFIED
+                : "No precision/scale for decimal division. Return type: "
+                + call.getType()
+                + " operands: "
+                + 
call.getOperands().stream().map(RexNode::getType).collect(Collectors.toList());
+
+        return IgniteExpressions.makeDecimalDivision(argValueList.get(0), 
argValueList.get(1), precision, scale);
+      }
+
       final Primitive primitive = Primitive.ofBoxOr(type0);
       if (primitive == null
           || type1 == BigDecimal.class
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
index a0a8b641eb..54bec16a39 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
@@ -37,9 +37,9 @@ import org.apache.calcite.avatica.util.ByteString;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.ignite.internal.sql.engine.exec.exp.IgniteSqlFunctions;
 import org.apache.ignite.internal.sql.engine.type.IgniteCustomType;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
+import org.apache.ignite.internal.sql.engine.util.IgniteMath;
 import org.apache.ignite.internal.util.ArrayUtils;
 import org.apache.ignite.lang.ErrorGroups.Sql;
 import org.apache.ignite.sql.SqlException;
@@ -341,7 +341,7 @@ public class Accumulators {
         /** {@inheritDoc} */
         @Override
         public Object end() {
-            return cnt.compareTo(BigDecimal.ZERO) == 0 ? null : 
IgniteSqlFunctions.decimalDivide(sum, cnt, precision, scale);
+            return cnt.compareTo(BigDecimal.ZERO) == 0 ? null : 
IgniteMath.decimalDivide(sum, cnt, precision, scale);
         }
 
         /** {@inheritDoc} */
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
index db7c07c767..7e14873ae6 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
@@ -183,8 +183,7 @@ public class IgniteSqlOperatorTable extends 
ReflectiveSqlOperatorTable {
             SqlFunctionCategory.NUMERIC);
 
     /**
-     * Division operator used by REDUCE phase of AVG aggregate.
-     * Uses provided values of {@code scale} and {@code precision} to return 
inferred type.
+     * Division operator for decimal type. Uses provided values of {@code 
scale} and {@code precision} to return inferred type.
      */
     public static final SqlFunction DECIMAL_DIVIDE = 
SqlBasicFunction.create("DECIMAL_DIVIDE",
             new SqlReturnTypeInference() {
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMath.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMath.java
index 8f724fd8ab..b1edf426ab 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMath.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMath.java
@@ -24,8 +24,11 @@ import static 
org.apache.calcite.sql.type.SqlTypeName.TINYINT;
 import static org.apache.ignite.lang.ErrorGroups.Sql.RUNTIME_ERR;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.ignite.internal.sql.engine.sql.fun.IgniteSqlOperatorTable;
 import org.apache.ignite.sql.SqlException;
+import org.jetbrains.annotations.Nullable;
 
 /** Math operations with overflow checking. */
 public class IgniteMath {
@@ -44,6 +47,9 @@ public class IgniteMath {
     private static final double UPPER_FLOAT_DOUBLE = Float.MAX_VALUE;
     private static final double LOWER_FLOAT_DOUBLE = -Float.MAX_VALUE;
 
+    /** Decimal rounding mode. */
+    public static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;
+
     /** Returns the sum of its arguments, throwing an exception if the result 
overflows an {@code long}. */
     public static long addExact(long x, long y) {
         long r = x + y;
@@ -270,6 +276,17 @@ public class IgniteMath {
         return (byte) (x / y);
     }
 
+    /**
+     * Decimal division. Precision is only used by type inferenc, its value is 
ignored at runtime.
+     * See {@link IgniteSqlOperatorTable#DECIMAL_DIVIDE}.
+     */
+    public static @Nullable BigDecimal decimalDivide(@Nullable BigDecimal x, 
@Nullable BigDecimal y, int p, int s) {
+        if (x == null || y == null) {
+            return null;
+        }
+        return x.divide(y, s, ROUNDING_MODE);
+    }
+
     private static void throwDivisionByZero() {
         throw new SqlException(RUNTIME_ERR, "Division by zero");
     }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
index 8605a54231..ab20fcc849 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
@@ -117,10 +117,10 @@ public enum IgniteMethod {
     TRUNCATE(IgniteSqlFunctions.class, "struncate", true),
 
     /**
-     * Division operator used by REDUCE phase of AVG aggregate.
-     * See {@link IgniteSqlFunctions#decimalDivide(BigDecimal, BigDecimal, 
int, int)}.
+     * Decimal division as well as division operator used by REDUCE phase of 
AVG aggregate.
+     * See {@link IgniteMath#decimalDivide(BigDecimal, BigDecimal, int, int)}.
      */
-    DECIMAL_DIVIDE(IgniteSqlFunctions.class, "decimalDivide", 
BigDecimal.class, BigDecimal.class, int.class, int.class),
+    DECIMAL_DIVIDE(IgniteMath.class, "decimalDivide", BigDecimal.class, 
BigDecimal.class, int.class, int.class),
 
     /**
      * Conversion of timestamp to string (precision aware).
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/coercion/NumericBinaryOperationsExecutionTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/coercion/NumericBinaryOperationsExecutionTest.java
index 13ac23f4e5..4ae7031cc0 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/coercion/NumericBinaryOperationsExecutionTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/coercion/NumericBinaryOperationsExecutionTest.java
@@ -19,10 +19,8 @@ package org.apache.ignite.internal.sql.engine.exec.coercion;
 
 import java.util.Set;
 import 
org.apache.ignite.internal.sql.engine.planner.datatypes.utils.NumericPair;
-import org.apache.ignite.internal.type.DecimalNativeType;
 import org.apache.ignite.internal.type.NativeType;
 import org.apache.ignite.internal.type.NativeTypes;
-import org.junit.jupiter.api.Assumptions;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.EnumSource;
 
@@ -61,8 +59,6 @@ public class NumericBinaryOperationsExecutionTest extends 
BaseTypeCheckExecution
     @EnumSource
     public void divOp(NumericPair typePair) throws Exception {
         String sql = "SELECT c1 / c2 FROM t";
-        Assumptions.assumeFalse(typePair.first() instanceof DecimalNativeType 
|| typePair.second() instanceof DecimalNativeType,
-                "need to be fixed after: 
https://issues.apache.org/jira/browse/IGNITE-23171";);
 
         try (ClusterWrapper testCluster = testCluster(typePair, 
dataProviderStrict(typePair))) {
             testCluster.process(sql, checkReturnResult());
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctionsTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctionsTest.java
index c4b90693cd..da55cf97c6 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctionsTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctionsTest.java
@@ -35,6 +35,7 @@ import java.util.TimeZone;
 import java.util.function.Supplier;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeSystem;
+import org.apache.ignite.internal.sql.engine.util.IgniteMath;
 import org.apache.ignite.lang.ErrorGroups.Sql;
 import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.Test;
@@ -535,10 +536,10 @@ public class IgniteSqlFunctionsTest {
         BigDecimal denum = new BigDecimal(b);
 
         if (expected != null) {
-            BigDecimal actual = IgniteSqlFunctions.decimalDivide(num, denum, 
4, 2);
+            BigDecimal actual = IgniteMath.decimalDivide(num, denum, 4, 2);
             assertEquals(new BigDecimal(expected), actual);
         } else {
-            assertThrows(ArithmeticException.class, () -> 
IgniteSqlFunctions.decimalDivide(num, denum, 4, 2));
+            assertThrows(ArithmeticException.class, () -> 
IgniteMath.decimalDivide(num, denum, 4, 2));
         }
     }
 
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystemTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystemTest.java
index 8be9cced4c..47c469619f 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystemTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystemTest.java
@@ -168,5 +168,106 @@ public class IgniteTypeSystemTest extends 
BaseIgniteAbstractTest {
                         native2relationalType(typeFactory, NativeTypes.DOUBLE)
                 )
         );
-    } 
+    }
+
+
+    @ParameterizedTest
+    @MethodSource("deriveDivideDecimalArgs")
+    void deriveDivide(RelDataType a1, RelDataType a2, RelDataType rt) {
+        RelDataType actual = 
typeSystem.deriveDecimalDivideType(Commons.typeFactory(), a1, a2);
+
+        assertThat(actual, Matchers.equalTo(rt));
+    }
+
+    private static Stream<Arguments> deriveDivideDecimalArgs() {
+        IgniteTypeFactory typeFactory = Commons.typeFactory();
+
+        return Stream.of(
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(2, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(2, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(8, 6))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(11, 6))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 1)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(11, 7))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 1)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 1)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(12, 7))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 2)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 2)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(13, 8))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 3)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(5, 3)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(14, 9))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(10, 5)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(10, 5)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(26, 16))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(10, 9)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(10, 9)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(30, 20))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32000, 9)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32000, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 776))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32000, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32000, 9)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 758))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32000, 9)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32000, 9)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 767))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 9)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 9))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 150)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 0))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 32767)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 0)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 32767))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 32767)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 32765)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 2))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 32767)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 32667)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 100))
+                ),
+                Arguments.of(
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 32767)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 32767)),
+                        native2relationalType(typeFactory, 
NativeTypes.decimalOf(32767, 0))
+                )
+        );
+    }
 }

Reply via email to