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

korlov 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 a4d39fb194 IGNITE-22519: Sql. Numerics. Conversion produces incorrect 
results (#4044)
a4d39fb194 is described below

commit a4d39fb1944204644b5f1aaf52ff67e2d1f5585a
Author: Max Zhuravkov <[email protected]>
AuthorDate: Wed Jul 10 15:05:49 2024 +0300

    IGNITE-22519: Sql. Numerics. Conversion produces incorrect results (#4044)
---
 .../internal/sql/engine/ItDataTypesTest.java       | 156 +++++++++------------
 .../aggregate/group/test_group_by_distinct.test    |  10 +-
 .../integrationTest/sql/cast/test_cast_bigint.test |   4 +-
 .../sql/cast/test_cast_decimal.test                |   2 -
 .../integrationTest/sql/cast/test_cast_float.test  |   8 +-
 .../integrationTest/sql/cast/test_cast_real.test   |   8 +-
 .../integrationTest/sql/set/test_intersect.test    |   2 +-
 .../sql/types/decimal/test_decimal.test            |  22 +++
 .../sql/engine/exec/exp/ExpressionFactoryImpl.java |  91 +++++++++++-
 .../sql/engine/exec/exp/RexToLixTranslator.java    |   5 +-
 .../internal/sql/engine/util/IgniteMath.java       |  56 ++++++++
 .../internal/sql/engine/util/Primitives.java       |  27 ++++
 .../ignite/internal/sql/engine/util/RexUtils.java  |   6 +-
 .../engine/exec/exp/ExpressionFactoryImplTest.java | 111 ++++++++++++++-
 14 files changed, 381 insertions(+), 127 deletions(-)

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 e068db34c4..9e75263fe1 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
@@ -45,7 +45,6 @@ import 
org.apache.ignite.internal.sql.engine.util.QueryChecker;
 import org.apache.ignite.lang.ErrorGroups.Sql;
 import org.apache.ignite.lang.IgniteException;
 import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assumptions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
@@ -366,7 +365,7 @@ public class ItDataTypesTest extends BaseSqlIntegrationTest 
{
                 .returns(new BigDecimal("1.00")).check();
 
         assertQuery("SELECT CASE WHEN false THEN DECIMAL '1.00' ELSE DECIMAL 
'0.0' END")
-                .returns(new BigDecimal("0.0")).check();
+                .returns(new BigDecimal("0.00")).check();
 
         assertQuery(
                 "SELECT DECIMAL '0.09'  BETWEEN DECIMAL '0.06' AND DECIMAL 
'0.07'")
@@ -386,15 +385,12 @@ public class ItDataTypesTest extends 
BaseSqlIntegrationTest {
         assertQuery("SELECT id FROM tbl WHERE val = DECIMAL 
'10.20'").returns(1).check();
     }
 
-
     /** decimal casts - cast literal to decimal. */
     @ParameterizedTest(name = "{2}:{1} AS {3} = {4}")
     @MethodSource("decimalCastFromLiterals")
-    public void testDecimalCastsNumericLiterals(CaseStatus status, RelDataType 
inputType, Object input,
+    public void testDecimalCastsNumericLiterals(RelDataType inputType, Object 
input,
             RelDataType targetType, Result<BigDecimal> result) {
 
-        Assumptions.assumeTrue(status == CaseStatus.RUN);
-
         String literal = asLiteral(input, inputType);
         String query = format("SELECT CAST({} AS {})", literal, targetType);
 
@@ -410,31 +406,28 @@ public class ItDataTypesTest extends 
BaseSqlIntegrationTest {
         // TODO Align test datasets 
https://issues.apache.org/jira/browse/IGNITE-20130
         return Stream.of(
                 // String
-                arguments(CaseStatus.RUN, varcharType, "100", decimalType(3), 
bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, varcharType, "100.12", 
decimalType(5, 1), bigDecimalVal("100.1")),
-                arguments(CaseStatus.RUN, varcharType, "lame", decimalType(5, 
1), error(NUMERIC_FORMAT_ERROR)),
-                arguments(CaseStatus.RUN, varcharType, "12345", decimalType(5, 
1), error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.RUN, varcharType, "1234", decimalType(5, 
1), bigDecimalVal("1234.0")),
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, varcharType, "100.12", 
decimalType(1, 0), error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(varcharType, "100", decimalType(3), 
bigDecimalVal("100")),
+                arguments(varcharType, "100.12", decimalType(5, 1), 
bigDecimalVal("100.1")),
+                arguments(varcharType, "lame", decimalType(5, 1), 
error(NUMERIC_FORMAT_ERROR)),
+                arguments(varcharType, "12345", decimalType(5, 1), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(varcharType, "1234", decimalType(5, 1), 
bigDecimalVal("1234.0")),
+                arguments(varcharType, "100.12", decimalType(1, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
 
                 // Numeric
-                arguments(CaseStatus.RUN, numeric, "100", decimalType(3), 
bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, numeric, "100", decimalType(3, 0), 
bigDecimalVal("100")),
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, numeric, "100.12", decimalType(5, 
1), bigDecimalVal("100.1")),
-                arguments(CaseStatus.SKIP, numeric, "100.12", decimalType(5, 
0), bigDecimalVal("100")),
-                arguments(CaseStatus.SKIP, numeric, "100", decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.SKIP, numeric, "100.12", decimalType(5, 
2), bigDecimalVal("100.12"))
+                arguments(numeric, "100", decimalType(3), 
bigDecimalVal("100")),
+                arguments(numeric, "100", decimalType(3, 0), 
bigDecimalVal("100")),
+                arguments(numeric, "100.12", decimalType(5, 1), 
bigDecimalVal("100.1")),
+                arguments(numeric, "100.12", decimalType(5, 0), 
bigDecimalVal("100")),
+                arguments(numeric, "100", decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(numeric, "100.12", decimalType(5, 2), 
bigDecimalVal("100.12"))
         );
     }
 
     /** decimal casts - cast dynamic param to decimal. */
     @ParameterizedTest(name = "{2}:?{1} AS {3} = {4}")
     @MethodSource("decimalCasts")
-    public void testDecimalCastsDynamicParams(CaseStatus ignore, RelDataType 
inputType, Object input,
+    public void testDecimalCastsDynamicParams(RelDataType inputType, Object 
input,
             RelDataType targetType, Result<BigDecimal> result) {
-        // We ignore status because every case should work for dynamic 
parameter.
 
         String query = format("SELECT CAST(? AS {})", targetType);
 
@@ -445,11 +438,9 @@ public class ItDataTypesTest extends 
BaseSqlIntegrationTest {
     /** decimals casts - cast numeric literal to specific type then cast the 
result to decimal. */
     @ParameterizedTest(name = "{1}: {2}::{1} AS {3} = {4}")
     @MethodSource("decimalCasts")
-    public void testDecimalCastsFromNumeric(CaseStatus status, RelDataType 
inputType, Object input,
+    public void testDecimalCastsFromNumeric(RelDataType inputType, Object 
input,
             RelDataType targetType, Result<BigDecimal> result) {
 
-        Assumptions.assumeTrue(status == CaseStatus.RUN);
-
         String literal = asLiteral(input, inputType);
         String query = format("SELECT CAST({}::{} AS {})", literal, inputType, 
targetType);
 
@@ -755,20 +746,6 @@ public class ItDataTypesTest extends 
BaseSqlIntegrationTest {
         }
     }
 
-    /**
-     * Indicates whether a test case should run or should be skipped.
-     * We need this because the set of test cases is the same for both dynamic 
params
-     * and numeric values.
-     *
-     * <p>TODO Should be removed after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-     */
-    enum CaseStatus {
-        /** Case should run. */
-        RUN,
-        /** Case should be skipped. */
-        SKIP
-    }
-
     private static Stream<Arguments> decimalCasts() {
         RelDataType varcharType = varcharType();
         RelDataType tinyIntType = sqlType(SqlTypeName.TINYINT);
@@ -780,75 +757,68 @@ public class ItDataTypesTest extends 
BaseSqlIntegrationTest {
 
         return Stream.of(
                 // String
-                arguments(CaseStatus.RUN, varcharType, "100", decimalType(3), 
bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, varcharType, "100", decimalType(3), 
bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, varcharType, "100", decimalType(3, 
0), bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, varcharType, "100", decimalType(4, 
1), bigDecimalVal("100.0")),
-                arguments(CaseStatus.RUN, varcharType, "100", decimalType(2, 
0), error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(varcharType, "100", decimalType(3), 
bigDecimalVal("100")),
+                arguments(varcharType, "100", decimalType(3), 
bigDecimalVal("100")),
+                arguments(varcharType, "100", decimalType(3, 0), 
bigDecimalVal("100")),
+                arguments(varcharType, "100", decimalType(4, 1), 
bigDecimalVal("100.0")),
+                arguments(varcharType, "100", decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
 
                 // Tinyint
-                arguments(CaseStatus.RUN, tinyIntType, (byte) 100, 
decimalType(3), bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, tinyIntType, (byte) 100, 
decimalType(3, 0), bigDecimalVal("100")),
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, tinyIntType, (byte) 100, 
decimalType(4, 1), bigDecimalVal("100.0")),
-                arguments(CaseStatus.RUN, tinyIntType, (byte) 100, 
decimalType(2, 0), error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(tinyIntType, (byte) 100, decimalType(3), 
bigDecimalVal("100")),
+                arguments(tinyIntType, (byte) 100, decimalType(3, 0), 
bigDecimalVal("100")),
+                arguments(tinyIntType, (byte) 100, decimalType(4, 1), 
bigDecimalVal("100.0")),
+                arguments(tinyIntType, (byte) 100, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
 
                 // Smallint
-                arguments(CaseStatus.RUN, smallIntType, (short) 100, 
decimalType(3), bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, smallIntType, (short) 100, 
decimalType(3, 0), bigDecimalVal("100")),
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, smallIntType, (short) 100, 
decimalType(4, 1), bigDecimalVal("100.0")),
-                arguments(CaseStatus.RUN, smallIntType, (short) 100, 
decimalType(2, 0), error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(smallIntType, (short) 100, decimalType(3), 
bigDecimalVal("100")),
+                arguments(smallIntType, (short) 100, decimalType(3, 0), 
bigDecimalVal("100")),
+                arguments(smallIntType, (short) 100, decimalType(4, 1), 
bigDecimalVal("100.0")),
+                arguments(smallIntType, (short) 100, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
 
                 // Integer
-                arguments(CaseStatus.RUN, integerType, 100, decimalType(3), 
bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, integerType, 100, decimalType(3, 0), 
bigDecimalVal("100")),
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, integerType, 100, decimalType(4, 
1), bigDecimalVal("100.0")),
-                arguments(CaseStatus.RUN, integerType, 100, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(integerType, 100, decimalType(3), 
bigDecimalVal("100")),
+                arguments(integerType, 100, decimalType(3, 0), 
bigDecimalVal("100")),
+                arguments(integerType, 100, decimalType(4, 1), 
bigDecimalVal("100.0")),
+                arguments(integerType, 100, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
 
                 // Bigint
-                arguments(CaseStatus.RUN, bigintType, 100L, decimalType(3), 
bigDecimalVal("100")),
-                arguments(CaseStatus.RUN, bigintType, 100L, decimalType(3, 0), 
bigDecimalVal("100")),
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, bigintType, 100L, decimalType(4, 
1), bigDecimalVal("100.0")),
-                arguments(CaseStatus.RUN, bigintType, 100L, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(bigintType, 100L, decimalType(3), 
bigDecimalVal("100")),
+                arguments(bigintType, 100L, decimalType(3, 0), 
bigDecimalVal("100")),
+                arguments(bigintType, 100L, decimalType(4, 1), 
bigDecimalVal("100.0")),
+                arguments(bigintType, 100L, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
 
                 // Real
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, realType, 100.0f, decimalType(3), 
bigDecimalVal("100")),
-                arguments(CaseStatus.SKIP, realType, 100.0f, decimalType(3, 
0), bigDecimalVal("100")),
-                arguments(CaseStatus.SKIP, realType, 100.0f, decimalType(4, 
1), bigDecimalVal("100.0")),
-                arguments(CaseStatus.RUN, realType, 100.0f, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.SKIP, realType, 0.1f, decimalType(1, 1), 
bigDecimalVal("0.1")),
-                arguments(CaseStatus.SKIP, realType, 0.1f, decimalType(2, 2), 
bigDecimalVal("0.10")),
-                arguments(CaseStatus.RUN, realType, 10.12f, decimalType(2, 1), 
error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.RUN, realType, 0.12f, decimalType(1, 2), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(realType, 100.0f, decimalType(3), 
bigDecimalVal("100")),
+                arguments(realType, 100.0f, decimalType(3, 0), 
bigDecimalVal("100")),
+                arguments(realType, 100.0f, decimalType(4, 1), 
bigDecimalVal("100.0")),
+                arguments(realType, 100.0f, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(realType, 0.1f, decimalType(1, 1), 
bigDecimalVal("0.1")),
+                arguments(realType, 0.1f, decimalType(2, 2), 
bigDecimalVal("0.10")),
+                arguments(realType, 10.12f, decimalType(2, 1), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(realType, 0.12f, decimalType(1, 2), 
error(NUMERIC_OVERFLOW_ERROR)),
 
                 // Double
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, doubleType, 100.0d, decimalType(3), 
bigDecimalVal("100")),
-                arguments(CaseStatus.SKIP, doubleType, 100.0d, decimalType(3, 
0), bigDecimalVal("100")),
-                arguments(CaseStatus.SKIP, doubleType, 100.0d, decimalType(4, 
1), bigDecimalVal("100.0")),
-                arguments(CaseStatus.RUN, doubleType, 100.0d, decimalType(2, 
0), error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.SKIP, doubleType, 0.1d, decimalType(1, 
1), bigDecimalVal("0.1")),
-                arguments(CaseStatus.SKIP, doubleType, 0.1d, decimalType(2, 
2), bigDecimalVal("0.10")),
-                arguments(CaseStatus.RUN, doubleType, 10.12d, decimalType(2, 
1), error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.RUN, doubleType, 0.12d, decimalType(1, 
2), error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(doubleType, 100.0d, decimalType(3), 
bigDecimalVal("100")),
+                arguments(doubleType, 100.0d, decimalType(3, 0), 
bigDecimalVal("100")),
+                arguments(doubleType, 100.0d, decimalType(4, 1), 
bigDecimalVal("100.0")),
+                arguments(doubleType, 100.0d, decimalType(2, 0), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(doubleType, 0.1d, decimalType(1, 1), 
bigDecimalVal("0.1")),
+                arguments(doubleType, 0.1d, decimalType(2, 2), 
bigDecimalVal("0.10")),
+                arguments(doubleType, 10.12d, decimalType(2, 1), 
error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(doubleType, 0.12d, decimalType(1, 2), 
error(NUMERIC_OVERFLOW_ERROR)),
 
                 // Decimal
-                arguments(CaseStatus.RUN, decimalType(1, 1), new 
BigDecimal("0.1"), decimalType(1, 1), bigDecimalVal("0.1")),
-                arguments(CaseStatus.RUN, decimalType(3), new 
BigDecimal("100"), decimalType(3), bigDecimalVal("100")),
+                arguments(decimalType(1, 1), new BigDecimal("0.1"), 
decimalType(1, 1), bigDecimalVal("0.1")),
+                arguments(decimalType(3), new BigDecimal("100"), 
decimalType(3), bigDecimalVal("100")),
                 // passed with runtime call and failed with parsing 
substitution
-                arguments(CaseStatus.SKIP, decimalType(5, 2), new 
BigDecimal("100.16"), decimalType(4, 1), bigDecimalVal("100.2")),
-                arguments(CaseStatus.RUN, decimalType(3), new 
BigDecimal("100"), decimalType(3, 0), bigDecimalVal("100")),
-                // TODO Uncomment these test cases after 
https://issues.apache.org/jira/browse/IGNITE-19822 is fixed.
-                arguments(CaseStatus.SKIP, decimalType(3), new 
BigDecimal("100"), decimalType(4, 1), bigDecimalVal("100.0")),
-                arguments(CaseStatus.RUN, decimalType(3), new 
BigDecimal("100"), decimalType(2, 0), error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.SKIP, decimalType(1, 1), new 
BigDecimal("0.1"), decimalType(2, 2), bigDecimalVal("0.10")),
-                arguments(CaseStatus.RUN, decimalType(4, 2), new 
BigDecimal("10.12"), decimalType(2, 1), error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.RUN, decimalType(2, 2), new 
BigDecimal("0.12"), decimalType(1, 2), error(NUMERIC_OVERFLOW_ERROR)),
-                arguments(CaseStatus.SKIP, decimalType(1, 1), new 
BigDecimal("0.1"), decimalType(1, 1), bigDecimalVal("0.1"))
+                arguments(decimalType(5, 2), new BigDecimal("100.16"), 
decimalType(4, 1), bigDecimalVal("100.2")),
+                arguments(decimalType(3), new BigDecimal("100"), 
decimalType(3, 0), bigDecimalVal("100")),
+                arguments(decimalType(3), new BigDecimal("100"), 
decimalType(4, 1), bigDecimalVal("100.0")),
+                arguments(decimalType(3), new BigDecimal("100"), 
decimalType(2, 0), error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(decimalType(1, 1), new BigDecimal("0.1"), 
decimalType(2, 2), bigDecimalVal("0.10")),
+                arguments(decimalType(4, 2), new BigDecimal("10.12"), 
decimalType(2, 1), error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(decimalType(2, 2), new BigDecimal("0.12"), 
decimalType(1, 2), error(NUMERIC_OVERFLOW_ERROR)),
+                arguments(decimalType(1, 1), new BigDecimal("0.1"), 
decimalType(1, 1), bigDecimalVal("0.1"))
         );
     }
 
diff --git 
a/modules/sql-engine/src/integrationTest/sql/aggregate/group/test_group_by_distinct.test
 
b/modules/sql-engine/src/integrationTest/sql/aggregate/group/test_group_by_distinct.test
index 4d6a784996..caf5146ee3 100644
--- 
a/modules/sql-engine/src/integrationTest/sql/aggregate/group/test_group_by_distinct.test
+++ 
b/modules/sql-engine/src/integrationTest/sql/aggregate/group/test_group_by_distinct.test
@@ -29,20 +29,20 @@ SELECT val FROM integers GROUP BY ALL GROUPING SETS ((val), 
(val))
 4
 
 query R rowsort
-SELECT val FROM ( VALUES (1), (1.0::REAL), (1.0::REAL), (2::DOUBLE), 
(1.1::REAL), (2::DECIMAL(2)) ) t(val) GROUP BY ALL GROUPING SETS ((val), (val))
+SELECT val FROM ( VALUES (1), (1.0::REAL), (1.0::REAL), (2::DOUBLE), 
(1.5::REAL), (2::DECIMAL(2)) ) t(val) GROUP BY ALL GROUPING SETS ((val), (val))
 ----
 1.0
 1.0
-1.1
-1.1
+1.5
+1.5
 2.0
 2.0
 
 query R rowsort
-SELECT val FROM ( VALUES (1), (1.0::REAL), (2::DOUBLE), (1.1::REAL), 
(2::DECIMAL(2)) ) t(val) GROUP BY DISTINCT GROUPING SETS ((val), (val))
+SELECT val FROM ( VALUES (1), (1.0::REAL), (2::DOUBLE), (1.5::REAL), 
(2::DECIMAL(2)) ) t(val) GROUP BY DISTINCT GROUPING SETS ((val), (val))
 ----
 1.0
-1.1
+1.5
 2.0
 
 # NOTE: Results include trailing whitespace
diff --git 
a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_bigint.test 
b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_bigint.test
index cb747047e4..b476fefd72 100644
--- a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_bigint.test
+++ b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_bigint.test
@@ -6,7 +6,7 @@
 statement ok
 SELECT CAST('100' AS BIGINT);
 
-# https://issues.apache.org/jira/browse/IGNITE-22519
+# https://issues.apache.org/jira/browse/IGNITE-20889
 skipif ignite3
 statement error
 SELECT CAST('100.1' AS BIGINT);
@@ -16,7 +16,7 @@ SELECT CAST(1e1 AS BIGINT);
 ----
 10
 
-# https://issues.apache.org/jira/browse/IGNITE-22519
+# https://issues.apache.org/jira/browse/IGNITE-20889
 skipif ignite3
 statement error: For input string: "1e2"
 SELECT CAST('1e2' AS BIGINT);
diff --git 
a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_decimal.test 
b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_decimal.test
index af42b78d7c..f21021db7b 100644
--- a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_decimal.test
+++ b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_decimal.test
@@ -69,8 +69,6 @@ SELECT CAST(100.1::DOUBLE AS DECIMAL);
 ----
 100.1
 
-# https://issues.apache.org/jira/browse/IGNITE-22519
-skipif ignite3
 query T
 SELECT CAST(100.1234::DOUBLE AS DECIMAL(7, 2));
 ----
diff --git 
a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_float.test 
b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_float.test
index 5f3bfbb7a9..c88e5c758f 100644
--- a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_float.test
+++ b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_float.test
@@ -11,16 +11,12 @@ SELECT CAST('100' AS FLOAT);
 ----
 100.0
 
-# https://issues.apache.org/jira/browse/IGNITE-22519
 # overflow
-skipif ignite3
-statement error
+statement error: REAL out of range
 SELECT CAST(1e39 AS FLOAT);
 
-# https://issues.apache.org/jira/browse/IGNITE-22519
 # overflow
-skipif ignite3
-statement error
+statement error: REAL out of range
 SELECT CAST(-1e39 AS FLOAT);
 
 query T
diff --git 
a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_real.test 
b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_real.test
index f402af5f19..1f6f5e391b 100644
--- a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_real.test
+++ b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_real.test
@@ -11,16 +11,12 @@ SELECT CAST('100' AS REAL);
 ----
 100.0
 
-# https://issues.apache.org/jira/browse/IGNITE-22519
 # overflow
-skipif ignite3
-statement error
+statement error: REAL out of range
 SELECT CAST(1e39 AS REAL);
 
-# https://issues.apache.org/jira/browse/IGNITE-22519
 # overflow
-skipif ignite3
-statement error
+statement error: REAL out of range
 SELECT CAST(-1e39 AS REAL);
 
 query T
diff --git a/modules/sql-engine/src/integrationTest/sql/set/test_intersect.test 
b/modules/sql-engine/src/integrationTest/sql/set/test_intersect.test
index 375c1fc6cd..fda27d7f37 100644
--- a/modules/sql-engine/src/integrationTest/sql/set/test_intersect.test
+++ b/modules/sql-engine/src/integrationTest/sql/set/test_intersect.test
@@ -74,7 +74,7 @@ statement ok
 CREATE TABLE t_float(f1 float);
 
 statement ok
-INSERT INTO t_float(f1) VALUES (-1.2345678901234e+200), (-34.84),  (123), 
(456), (456.1), (-456);
+INSERT INTO t_float(f1) VALUES (-1.2345678901234e+10), (-34.84),  (123), 
(456), (456.1), (-456);
 
 query I
 (SELECT f1 FROM t_float INTERSECT SELECT q1 FROM t_int)
diff --git 
a/modules/sql-engine/src/integrationTest/sql/types/decimal/test_decimal.test 
b/modules/sql-engine/src/integrationTest/sql/types/decimal/test_decimal.test
index 0180161bb4..9d603b5eeb 100644
--- a/modules/sql-engine/src/integrationTest/sql/types/decimal/test_decimal.test
+++ b/modules/sql-engine/src/integrationTest/sql/types/decimal/test_decimal.test
@@ -153,3 +153,25 @@ SELECT '0.1'::DECIMAL(1, 2, 3);
 
 statement error
 SELECT '1'::INTEGER(1000);
+
+query T
+SELECT CASE WHEN true THEN DECIMAL '1.00' ELSE DECIMAL '0' END
+----
+1.00
+
+# Same as above but won't constant fold
+query T
+SELECT CASE WHEN true THEN a ELSE b END FROM (VALUES (DECIMAL '1.00', DECIMAL 
'0.0')) t(a, b)
+----
+1.00
+
+query T
+SELECT CASE WHEN false THEN DECIMAL '1.00' ELSE DECIMAL '0.0' END
+----
+0.00
+
+# Same as above but won't constant fold
+query T
+SELECT CASE WHEN false THEN a ELSE b END FROM (VALUES (DECIMAL '1.00', DECIMAL 
'0.0')) t(a, b)
+----
+0.00
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
index ed337b02a9..bb6a35ab12 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
@@ -22,6 +22,7 @@ import static 
org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
 import com.github.benmanes.caffeine.cache.Caffeine;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -47,6 +48,7 @@ import org.apache.calcite.linq4j.tree.Expression;
 import org.apache.calcite.linq4j.tree.Expressions;
 import org.apache.calcite.linq4j.tree.MethodDeclaration;
 import org.apache.calcite.linq4j.tree.ParameterExpression;
+import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelFieldCollation;
@@ -65,6 +67,7 @@ import org.apache.calcite.rex.RexProgramBuilder;
 import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.sql.validate.SqlConformance;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
@@ -83,6 +86,7 @@ import 
org.apache.ignite.internal.sql.engine.prepare.bounds.SearchBounds;
 import org.apache.ignite.internal.sql.engine.rex.IgniteRexBuilder;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
 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.IgniteMethod;
 import org.apache.ignite.internal.sql.engine.util.Primitives;
 import org.apache.ignite.internal.sql.engine.util.TypeUtils;
@@ -346,14 +350,25 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
     }
 
     private @Nullable Object literalValue(RexLiteral literal, Class<?> type) {
-        Object val = literal.getValueAs(type);
+        RelDataType dataType = literal.getType();
 
-        // Literal was parsed as UTC timestamp, now we need to adjust it to 
the client's time zone.
-        if (val != null && literal.getTypeName() == 
SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
-            return IgniteSqlFunctions.subtractTimeZoneOffset((long) val, 
(TimeZone) ctx.get(Variable.TIME_ZONE.camelName));
-        }
+        if (SqlTypeUtil.isNumeric(dataType)) {
+            BigDecimal value = (BigDecimal) literal.getValue();
+            if (value == null) {
+                return null;
+            }
+
+            return convertNumericLiteral(dataType, value, type);
+        } else {
+            Object val = literal.getValueAs(type);
+
+            // Literal was parsed as UTC timestamp, now we need to adjust it 
to the client's time zone.
+            if (val != null && literal.getTypeName() == 
SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
+                return IgniteSqlFunctions.subtractTimeZoneOffset((long) val, 
(TimeZone) ctx.get(Variable.TIME_ZONE.camelName));
+            }
 
-        return val;
+            return val;
+        }
     }
 
     /** {@inheritDoc} */
@@ -1207,4 +1222,68 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
             return variable;
         }
     }
+
+    private static Object convertNumericLiteral(RelDataType dataType, 
BigDecimal value, Class<?> type) {
+        Primitive primitive = Primitive.ofBoxOr(type);
+        assert primitive != null || type == BigDecimal.class : "Neither 
primitive nor BigDecimal: " + type;
+
+        switch (dataType.getSqlTypeName()) {
+            case TINYINT:
+                byte b = IgniteMath.convertToByteExact(value);
+                if (primitive != null) {
+                    return Primitives.convertPrimitiveExact(primitive, b);
+                } else {
+                    return new BigDecimal(b);
+                }
+            case SMALLINT:
+                short s = IgniteMath.convertToShortExact(value);
+                if (primitive != null) {
+                    return Primitives.convertPrimitiveExact(primitive, s);
+                } else {
+                    return new BigDecimal(s);
+                }
+            case INTEGER:
+                int i = IgniteMath.convertToIntExact(value);
+                if (primitive != null) {
+                    return Primitives.convertPrimitiveExact(primitive, i);
+                } else {
+                    return new BigDecimal(i);
+                }
+            case BIGINT:
+                long l = IgniteMath.convertToLongExact(value);
+                if (primitive != null) {
+                    return Primitives.convertPrimitiveExact(primitive, l);
+                } else {
+                    return new BigDecimal(l);
+                }
+            case REAL:
+            case FLOAT:
+                float r = IgniteMath.convertToFloatExact(value);
+                if (primitive != null) {
+                    return Primitives.convertPrimitiveExact(primitive, r);
+                } else {
+                    // Preserve the exact form of a float value.
+                    return new BigDecimal(Float.toString(r));
+                }
+            case DOUBLE:
+                double d = IgniteMath.convertToDoubleExact(value);
+                if (primitive != null) {
+                    return Primitives.convertPrimitiveExact(primitive, d);
+                } else {
+                    // Preserve the exact form of a double value.
+                    return new BigDecimal(Double.toString(d));
+                }
+            case DECIMAL:
+                BigDecimal bd = IgniteSqlFunctions.toBigDecimal(value, 
dataType.getPrecision(), dataType.getScale());
+                assert bd != null;
+
+                if (primitive != null) {
+                    return Primitives.convertPrimitiveExact(primitive, bd);
+                } else {
+                    return bd;
+                }
+            default:
+                throw new IllegalStateException("Unexpected numeric type: " + 
dataType);
+        }
+    }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
index 9dab3002b5..9af6157beb 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
@@ -93,6 +93,7 @@ import org.apache.calcite.util.ControlFlowException;
 import org.apache.calcite.util.Pair;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
 import org.apache.ignite.internal.sql.engine.util.IgniteMethod;
+import org.apache.ignite.internal.sql.engine.util.Primitives;
 import org.checkerframework.checker.nullness.qual.Nullable;
 import org.locationtech.jts.geom.Geometry;
 
@@ -105,7 +106,7 @@ import org.locationtech.jts.geom.Geometry;
  * 3. RexToLixTranslator#translateCast() BYTESTRING_TO_STRING, 
STRING_TO_BYTESTRING special case converters.
  * 4. RexToLixTranslator#translateCast() case INTERVAL_SECOND -> case 
CHARACTER special case converters.
  * 5. RexToLixTranslator#translateCast() case TIMESTAMP -> case CHAR  special 
case converters.
- * 6. RexToLixTranslator#translateLiteral() case DECIMAL special case 
converters.
+ * 6. RexToLixTranslator#translateLiteral() case DECIMAL special case 
converters. Primitive float/double conversion.
  * 7. RexToLixTranslator#translateCast() ANY branch
  * 8. EnumUtils.convert -> ConverterUtils.convert
  */
@@ -912,7 +913,7 @@ public class RexToLixTranslator implements 
RexVisitor<RexToLixTranslator.Result>
                 final Primitive primitive = Primitive.ofBoxOr(javaClass);
                 final Comparable value = literal.getValueAs(Comparable.class);
                 if (primitive != null && value instanceof Number) {
-                    value2 = primitive.number((Number) value);
+                    value2 = Primitives.convertPrimitiveExact(primitive, 
(Number) value);
                 } else {
                     value2 = value;
                 }
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 88e612389a..21e9784679 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
@@ -41,6 +41,14 @@ public class IgniteMath {
     private static final Float UPPER_LONG_FLOAT = (float) Long.MAX_VALUE;
     private static final Float LOWER_LONG_FLOAT = (float) Long.MIN_VALUE;
 
+    private static final BigDecimal UPPER_DOUBLE_BIG_DECIMAL = new 
BigDecimal(String.valueOf(Double.MAX_VALUE));
+    private static final BigDecimal LOWER_DOUBLE_BIG_DECIMAL = 
UPPER_DOUBLE_BIG_DECIMAL.negate();
+    private static final BigDecimal UPPER_FLOAT_BIG_DECIMAL = new 
BigDecimal(String.valueOf(Float.MAX_VALUE));
+    private static final BigDecimal LOWER_FLOAT_BIG_DECIMAL = 
UPPER_FLOAT_BIG_DECIMAL.negate();
+
+    private static final double UPPER_FLOAT_DOUBLE = Float.MAX_VALUE;
+    private static final double LOWER_FLOAT_DOUBLE = -Float.MAX_VALUE;
+
     /** 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;
@@ -279,6 +287,13 @@ public class IgniteMath {
         return x.longValue();
     }
 
+    /** Cast value to {@code long}, throwing an exception if the result 
overflows an {@code long}. */
+    public static long convertToLongExact(Number x) {
+        checkNumberLongBounds(BIGINT, x);
+
+        return x.longValue();
+    }
+
     /** Cast value to {@code long}, throwing an exception if the result 
overflows an {@code long}. */
     public static long convertToLongExact(double x) {
         if (x > Long.MAX_VALUE || x < Long.MIN_VALUE) {
@@ -410,6 +425,47 @@ public class IgniteMath {
         }
     }
 
+    /** Cast value to {@code float}, throwing an exception if the result 
overflows. */
+    public static float convertToFloatExact(Number x) {
+        if (x instanceof BigDecimal) {
+            BigDecimal value = (BigDecimal) x;
+
+            if (value.compareTo(UPPER_FLOAT_BIG_DECIMAL) > 0) {
+                throw new SqlException(RUNTIME_ERR, "REAL out of range");
+            }
+            if (value.compareTo(LOWER_FLOAT_BIG_DECIMAL) < 0) {
+                throw new SqlException(RUNTIME_ERR, "REAL out of range");
+            }
+
+            return value.floatValue();
+        } else {
+            double v = x.doubleValue();
+            if (v > UPPER_FLOAT_DOUBLE || v < LOWER_FLOAT_DOUBLE) {
+                throw new SqlException(RUNTIME_ERR, "REAL out of range");
+            }
+
+            return x.floatValue();
+        }
+    }
+
+    /** Cast value to {@code double}, throwing an exception if the result 
overflows. */
+    public static double convertToDoubleExact(Number x) {
+        if (x instanceof BigDecimal) {
+            BigDecimal value = (BigDecimal) x;
+            if (value.compareTo(UPPER_DOUBLE_BIG_DECIMAL) > 0) {
+                throw new SqlException(RUNTIME_ERR, "DOUBLE out of range");
+            }
+
+            if (value.compareTo(LOWER_DOUBLE_BIG_DECIMAL) < 0) {
+                throw new SqlException(RUNTIME_ERR, "DOUBLE out of range");
+            }
+
+            return value.doubleValue();
+        } else {
+            return x.doubleValue();
+        }
+    }
+
     private static void checkNumberLongBounds(SqlTypeName type, Number x) {
         if (x instanceof BigDecimal) {
             if ((((BigDecimal) x).compareTo(UPPER_LONG_BIG_DECIMAL) < 0 && 
((BigDecimal) x).compareTo(LOWER_LONG_BIG_DECIMAL) > 0)) {
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Primitives.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Primitives.java
index a3da601dc7..b717a743b3 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Primitives.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Primitives.java
@@ -21,6 +21,7 @@ import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
+import org.apache.calcite.linq4j.tree.Primitive;
 
 /**
  * Primitives.
@@ -106,4 +107,30 @@ public final class Primitives {
 
         return (unwrapped == null) ? type : unwrapped;
     }
+
+    /**
+     * Converts the given numeric value into the given primitive type with 
overflow checks.
+     *
+     * @param primitive Primitive type.
+     * @param value Numeric value.
+     * @return Number converted to the given primitive.
+     */
+    public static Number convertPrimitiveExact(Primitive primitive, Number 
value) {
+        switch (primitive) {
+            case BYTE:
+                return IgniteMath.convertToByteExact(value);
+            case SHORT:
+                return IgniteMath.convertToShortExact(value);
+            case INT:
+                return IgniteMath.convertToIntExact(value);
+            case LONG:
+                return IgniteMath.convertToLongExact(value);
+            case FLOAT:
+                return IgniteMath.convertToFloatExact(value);
+            case DOUBLE:
+                return IgniteMath.convertToDoubleExact(value);
+            default:
+                throw new IllegalArgumentException("Unexpected primitive type: 
" + primitive);
+        }
+    }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
index 9070de185f..13f06e18f9 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
@@ -114,14 +114,14 @@ public class RexUtils {
     /** Hash index permitted search operations. */
     private static final EnumSet<SqlKind> HASH_SEARCH_OPS = EnumSet.of(EQUALS, 
IS_NOT_DISTINCT_FROM);
 
-    private static final BigDecimal MIN_DOUBLE_VALUE = 
BigDecimal.valueOf(Double.MIN_VALUE);
-
     private static final BigDecimal MAX_DOUBLE_VALUE = 
BigDecimal.valueOf(Double.MAX_VALUE);
 
-    private static final BigDecimal MIN_FLOAT_VALUE = 
BigDecimal.valueOf(Float.MIN_VALUE);
+    private static final BigDecimal MIN_DOUBLE_VALUE = 
MAX_DOUBLE_VALUE.negate();
 
     private static final BigDecimal MAX_FLOAT_VALUE = 
BigDecimal.valueOf(Float.MAX_VALUE);
 
+    private static final BigDecimal MIN_FLOAT_VALUE = MAX_FLOAT_VALUE.negate();
+
     /**
      * Builder.
      * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
index 739cf3d394..6bf85ca094 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.sql.engine.exec.exp;
 
 import static java.util.Collections.singletonList;
+import static 
org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -39,9 +40,12 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Random;
 import java.util.UUID;
+import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelFieldCollation.Direction;
@@ -61,6 +65,7 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.BasicSqlType;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeName.Limit;
 import org.apache.calcite.sql.validate.SqlConformanceEnum;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.ImmutableIntList;
@@ -79,6 +84,7 @@ import org.apache.ignite.internal.sql.engine.util.RexUtils;
 import org.apache.ignite.internal.sql.engine.util.SqlTestUtils;
 import org.apache.ignite.internal.sql.engine.util.TypeUtils;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.lang.ErrorGroups.Sql;
 import org.apache.ignite.network.NetworkAddress;
 import org.apache.ignite.sql.ColumnType;
 import org.jetbrains.annotations.Nullable;
@@ -629,7 +635,7 @@ public class ExpressionFactoryImplTest extends 
BaseIgniteAbstractTest {
         RexBuilder rexBuilder = Commons.rexBuilder();
         IgniteTypeFactory tf = Commons.typeFactory();
 
-        RelDataType intType = tf.createSqlType(SqlTypeName.INTEGER);
+        RelDataType intType = 
tf.createTypeWithNullability(tf.createSqlType(SqlTypeName.INTEGER), true);
         RelDataType rowType = new Builder(tf)
                 .add("c1", intType)
                 .build();
@@ -761,6 +767,109 @@ public class ExpressionFactoryImplTest extends 
BaseIgniteAbstractTest {
         assertEquals("42", actual);
     }
 
+    @ParameterizedTest
+    @MethodSource("numericLiterals")
+    public void testConvertNumericExact(RexLiteral lit, RelDataType dataType, 
@Nullable Object expected, boolean err) {
+        RelDataType rowType = new Builder(typeFactory).add("c1", 
dataType).build();
+
+        if (!err) {
+            Object[] rowValues = expFactory.rowSource(List.of(lit)).get();
+            assertArrayEquals(new Object[]{expected}, rowValues, "rowSource");
+
+            Object[] litValues = expFactory.values(List.of(lit), 
rowType).iterator().next();
+            assertArrayEquals(new Object[]{expected}, litValues, "values");
+        } else {
+            String errorMessage = "out of range";
+
+            Supplier<Object[]> rowExpr = expFactory.rowSource(List.of(lit));
+            assertThrowsSqlException(Sql.RUNTIME_ERR, errorMessage, 
rowExpr::get);
+
+            assertThrowsSqlException(Sql.RUNTIME_ERR, errorMessage, () -> {
+                expFactory.values(List.of(lit), rowType).iterator().next();
+            });
+        }
+    }
+
+    private static Stream<Arguments> numericLiterals() {
+        RexBuilder rexBuilder = Commons.rexBuilder();
+        // Make literal function
+        BiFunction<Object, RelDataType, RexLiteral> makeLit = (v, t) -> 
(RexLiteral) rexBuilder.makeLiteral(v, t, false, false);
+
+        RelDataType tinyInt = 
Commons.typeFactory().createSqlType(SqlTypeName.TINYINT);
+        Object tinyIntMax = SqlTypeName.TINYINT.getLimit(true, Limit.OVERFLOW, 
true, -1, -1);
+        Object tinyIntMin = SqlTypeName.TINYINT.getLimit(false, 
Limit.OVERFLOW, true, -1, -1);
+
+        RelDataType shortType = 
Commons.typeFactory().createSqlType(SqlTypeName.SMALLINT);
+        Object shortMax = SqlTypeName.SMALLINT.getLimit(true, Limit.OVERFLOW, 
true, -1, -1);
+        Object shortMin = SqlTypeName.SMALLINT.getLimit(false, Limit.OVERFLOW, 
true, -1, -1);
+
+        RelDataType intType = 
Commons.typeFactory().createSqlType(SqlTypeName.INTEGER);
+        Object maxInt = SqlTypeName.INTEGER.getLimit(true, Limit.OVERFLOW, 
true, -1, -1);
+        Object minInt = SqlTypeName.INTEGER.getLimit(false, Limit.OVERFLOW, 
true, -1, -1);
+
+        RelDataType bigIntType = 
Commons.typeFactory().createSqlType(SqlTypeName.BIGINT);
+        Object bigIntMax = SqlTypeName.BIGINT.getLimit(true, Limit.OVERFLOW, 
true, -1, -1);
+        Object bigIntMin = SqlTypeName.BIGINT.getLimit(false, Limit.OVERFLOW, 
true, -1, -1);
+
+        RelDataType realType = 
Commons.typeFactory().createSqlType(SqlTypeName.REAL);
+        BigDecimal realMax = new 
BigDecimal(String.valueOf(Float.MAX_VALUE)).add(BigDecimal.ONE);
+        Object realMin = realMax.negate();
+
+        RelDataType floatType = 
Commons.typeFactory().createSqlType(SqlTypeName.FLOAT);
+        BigDecimal floatMax = new 
BigDecimal(String.valueOf(Float.MAX_VALUE)).add(BigDecimal.ONE);
+        Object floatMin = realMax.negate();
+
+        RelDataType doubleType = 
Commons.typeFactory().createSqlType(SqlTypeName.DOUBLE);
+        BigDecimal doubleMax = new 
BigDecimal(String.valueOf(Double.MAX_VALUE)).add(BigDecimal.ONE);
+        Object doubleMin = doubleMax.negate();
+
+        // makeLiteral for decimal validates precision/scale, so it is not 
possible to create a decimal literal
+        // that lies outside of the range
+        RelDataType decimal5 = 
Commons.typeFactory().createSqlType(SqlTypeName.DECIMAL, 5);
+        RelDataType decimal52 = 
Commons.typeFactory().createSqlType(SqlTypeName.DECIMAL, 5, 2);
+
+        return Stream.of(
+                // TINYINT
+                Arguments.of(makeLit.apply(BigDecimal.ONE, tinyInt), tinyInt, 
(byte) 1, false),
+                Arguments.of(makeLit.apply(tinyIntMax, tinyInt), tinyInt, 
null, true),
+                Arguments.of(makeLit.apply(tinyIntMin, tinyInt), tinyInt, 
null, true),
+
+                // SMALLINT
+                Arguments.of(makeLit.apply(BigDecimal.ONE, shortType), 
shortType, (short) 1, false),
+                Arguments.of(makeLit.apply(shortMax, shortType), shortType, 
null, true),
+                Arguments.of(makeLit.apply(shortMin, shortType), shortType, 
null, true),
+
+                // INT
+                Arguments.of(makeLit.apply(BigDecimal.ONE, intType), intType, 
1, false),
+                Arguments.of(makeLit.apply(maxInt, intType), intType, null, 
true),
+                Arguments.of(makeLit.apply(minInt, intType), intType, null, 
true),
+
+                // BIGINT
+                Arguments.of(makeLit.apply(BigDecimal.ONE, bigIntType), 
bigIntType, 1L, false),
+                Arguments.of(makeLit.apply(bigIntMax, bigIntType), bigIntType, 
null, true),
+                Arguments.of(makeLit.apply(bigIntMin, bigIntType), bigIntType, 
null, true),
+
+                // REAL
+                Arguments.of(makeLit.apply(BigDecimal.ONE, realType), 
realType, 1.0f, false),
+                Arguments.of(makeLit.apply(realMax, realType), realType, null, 
true),
+                Arguments.of(makeLit.apply(realMin, realType), realType, null, 
true),
+
+                // FLOAT
+                Arguments.of(makeLit.apply(BigDecimal.ONE, floatType), 
floatType, 1.0f, false),
+                Arguments.of(makeLit.apply(floatMax, floatType), floatType, 
null, true),
+                Arguments.of(makeLit.apply(floatMin, floatType), floatType, 
null, true),
+
+                // DOUBLE
+                Arguments.of(makeLit.apply(BigDecimal.ONE, doubleType), 
doubleType, 1.0d, false),
+                Arguments.of(makeLit.apply(doubleMax, doubleType), doubleType, 
null, true),
+                Arguments.of(makeLit.apply(doubleMin, doubleType), doubleType, 
null, true),
+
+                // DECIMAL
+                Arguments.of(makeLit.apply(new BigDecimal("1"), decimal5), 
decimal5, new BigDecimal("1"), false),
+                Arguments.of(makeLit.apply(new BigDecimal("1.0"), decimal52), 
decimal52, new BigDecimal("1.00"), false)
+        );
+    }
+
     private static List<Arguments> rowSourceTestArgs() {
         EnumSet<ColumnType> ignoredTypes = EnumSet.of(
                 // Not supported.

Reply via email to