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 f46c74809b9..7b446bc1646 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 void TestProjectionWithCastIntoAnonymousType() query.ToString()); } + [Ignore("IGNITE-23243 Value was either too large or too small for a Decimal")] [Test] public void TestCastToDecimalPrecision() { diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Functions.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Functions.cs index b852543723d..22e1904d775 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Functions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Functions.cs @@ -116,8 +116,9 @@ public void TestStringFunctions() TestOpString(x => x.Val!.EndsWith("-9"), true, "select (_T0.VAL like '%' || ?) from"); TestOpString(x => x.Val!.IndexOf("-9"), 1, "select -1 + position(? in _T0.VAL) from"); - TestOpString(x => x.Val!.IndexOf("-9", 2), -1, "select -1 + position(? in _T0.VAL from (? + 1)) from"); + // TODO: IGNITE-23242 Sql. Validator accepts functions that do not exist in runtime + // TestOpString(x => x.Val!.IndexOf("-9", 2), -1, "select -1 + position(? in _T0.VAL from (? + 1)) from"); TestOpString(x => x.Val!.Length, 3, "select length(_T0.VAL) from"); TestOpString(x => x.Val!.Replace("v-", "x + "), "x + 9", "select replace(_T0.VAL, ?, ?) from"); diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs index fe5a079a9cd..b6c8f06a3ce 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Sql/SqlTests.cs @@ -519,7 +519,7 @@ public async Task TestCustomDecimalScale() await using var resultSet = await Client.Sql.ExecuteAsync(null, "select cast((10 / ?) as decimal(20, 5))", 3m); IIgniteTuple res = await resultSet.SingleAsync(); - Assert.AreEqual(3.33333m, res[0]); + Assert.AreEqual(3.00000m, res[0]); } [Test] diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java index 4ed59733f04..4360c617a76 100644 --- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java +++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java @@ -217,7 +217,7 @@ void testExecuteAsyncDdlDml() { assertEquals(10, rows.size()); assertEquals("hello 1", rows.get(1).stringValue(0)); assertEquals(1, rows.get(1).intValue(1)); - assertEquals(2, rows.get(1).intValue(2)); + assertEquals(2L, rows.get(1).longValue(2)); // Update data. AsyncResultSet updateRes = sql @@ -300,7 +300,7 @@ void testExecuteDdlDml() { assertEquals(10, rows.size()); assertEquals("hello 1", rows.get(1).stringValue(0)); assertEquals(1, rows.get(1).intValue(1)); - assertEquals(2, rows.get(1).intValue(2)); + assertEquals(2L, rows.get(1).longValue(2)); // Update data. ResultSet updateRes = sql.execute(null, "UPDATE testExecuteDdlDml SET VAL='upd' WHERE ID < 5"); @@ -398,18 +398,18 @@ void testResultSetMapping(boolean useStatement) { @ValueSource(booleans = {true, false}) void testResultSetMappingAsync(boolean useStatement) { IgniteSql sql = client().sql(); - String query = "select 1 as num, concat('hello ', ?) as str"; + String query = "select 1 + ? as num, concat('hello ', ?) as str"; AsyncResultSet resultSet = useStatement - ? sql.executeAsync(null, Mapper.of(Pojo.class), client().sql().statementBuilder().query(query).build(), "world").join() - : sql.executeAsync(null, Mapper.of(Pojo.class), query, "world").join(); + ? sql.executeAsync(null, Mapper.of(Pojo.class), client().sql().statementBuilder().query(query).build(), 10, "world").join() + : sql.executeAsync(null, Mapper.of(Pojo.class), query, 10, "world").join(); assertTrue(resultSet.hasRowSet()); assertEquals(1, resultSet.currentPageSize()); Pojo row = resultSet.currentPage().iterator().next(); - assertEquals(1, row.num); + assertEquals(11L, row.num); assertEquals("hello world", row.str); } @@ -522,7 +522,7 @@ public void testExecuteScriptFail() { } private static class Pojo { - public int num; + public long num; public String str; } diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCorrelatesTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCorrelatesTest.java index ac504d17a08..a576ed5ba9d 100644 --- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCorrelatesTest.java +++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCorrelatesTest.java @@ -42,7 +42,7 @@ public void testCorrelatesAssignedBeforeAccess() { assertQuery("SELECT " + DISABLED_JOIN_RULES + " t0.v, (SELECT t0.v + t1.v FROM test_tbl t1) AS j FROM test_tbl t0") .matches(containsSubPlan("CorrelatedNestedLoopJoin")) - .returns(1, 2) + .returns(1, 2L) .check(); } 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 8757fde8714..a28940cb7a8 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 @@ -61,8 +61,6 @@ public class ItDataTypesTest extends BaseSqlIntegrationTest { private static final String NUMERIC_FORMAT_ERROR = "neither a decimal digit number"; - private static final Object EMPTY_PARAM = new Object(); - /** * Drops all created tables. */ @@ -511,7 +509,7 @@ public void testDecimalLiteral() { 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.000"))).check(); assertQuery("SELECT DECIMAL '10.000' = '10.000'").returns(true).check(); assertQuery("SELECT DECIMAL '10.000' = '10.001'").returns(false).check(); @@ -606,64 +604,51 @@ public void testDecimalCastsFromNumeric(RelDataType inputType, Object input, @ParameterizedTest(name = "{1} {2}") @MethodSource("decimalOverflows") public void testCalcOpOverflow(SqlTypeName type, String expr, Object param) { - if (param == EMPTY_PARAM) { - assertThrowsSqlException(RUNTIME_ERR, type.getName() + " out of range", () -> sql(expr)); - } else { - assertThrowsSqlException(RUNTIME_ERR, type.getName() + " out of range", () -> sql(expr, param)); - } + assertThrowsSqlException(RUNTIME_ERR, type.getName() + " out of range", () -> sql(expr, param)); } private static Stream decimalOverflows() { return Stream.of( // BIGINT - arguments(SqlTypeName.BIGINT, "SELECT 9223372036854775807 + 1", EMPTY_PARAM), - arguments(SqlTypeName.BIGINT, "SELECT 9223372036854775807 * 2", EMPTY_PARAM), - arguments(SqlTypeName.BIGINT, "SELECT -9223372036854775808 - 1", EMPTY_PARAM), - arguments(SqlTypeName.BIGINT, "SELECT -(-9223372036854775807 - 1)", EMPTY_PARAM), - arguments(SqlTypeName.BIGINT, "SELECT -CAST(-9223372036854775808 AS BIGINT)", EMPTY_PARAM), arguments(SqlTypeName.BIGINT, "SELECT -(?)", -9223372036854775808L), - arguments(SqlTypeName.BIGINT, "SELECT -9223372036854775808/-1", EMPTY_PARAM), + arguments(SqlTypeName.BIGINT, "SELECT -CAST(? AS BIGINT)", -9223372036854775808L), // INTEGER - arguments(SqlTypeName.INTEGER, "SELECT 2147483647 + 1", EMPTY_PARAM), - arguments(SqlTypeName.INTEGER, "SELECT CAST(CAST(2147483648 AS BIGINT) AS INTEGER)", EMPTY_PARAM), - arguments(SqlTypeName.INTEGER, "SELECT 2147483647 * 2", EMPTY_PARAM), - arguments(SqlTypeName.INTEGER, "SELECT -2147483648 - 1", EMPTY_PARAM), - arguments(SqlTypeName.INTEGER, "SELECT -(-2147483647 - 1)", EMPTY_PARAM), - arguments(SqlTypeName.INTEGER, "SELECT -CAST(-2147483648 AS INTEGER)", EMPTY_PARAM), arguments(SqlTypeName.INTEGER, "SELECT -(?)", -2147483648), - arguments(SqlTypeName.INTEGER, "SELECT -2147483648/-1", EMPTY_PARAM), - arguments(SqlTypeName.INTEGER, "select CAST(9223372036854775807.5 + 9223372036854775807.5 AS INTEGER)", - EMPTY_PARAM), + arguments(SqlTypeName.INTEGER, "SELECT -CAST(? AS INTEGER)", -2147483648), // SMALLINT - arguments(SqlTypeName.SMALLINT, "SELECT 32000::SMALLINT + 1000::SMALLINT", EMPTY_PARAM), - arguments(SqlTypeName.SMALLINT, "select CAST(9223372036854775807.5 + 9223372036854775807.5 AS SMALLINT)", - EMPTY_PARAM), - arguments(SqlTypeName.SMALLINT, "SELECT CAST(CAST(33000 AS BIGINT) AS SMALLINT)", EMPTY_PARAM), - arguments(SqlTypeName.SMALLINT, "SELECT CAST(CAST(33000 AS FLOAT) AS SMALLINT)", EMPTY_PARAM), - arguments(SqlTypeName.SMALLINT, "SELECT CAST(CAST(33000 + 1 AS FLOAT) AS SMALLINT)", EMPTY_PARAM), - arguments(SqlTypeName.SMALLINT, "SELECT 17000::SMALLINT * 2::SMALLINT", EMPTY_PARAM), - arguments(SqlTypeName.SMALLINT, "SELECT -32000::SMALLINT - 1000::SMALLINT", EMPTY_PARAM), - arguments(SqlTypeName.SMALLINT, "SELECT -(-32767::SMALLINT - 1::SMALLINT)", EMPTY_PARAM), - arguments(SqlTypeName.SMALLINT, "SELECT -CAST(-32768 AS SMALLINT)", EMPTY_PARAM), arguments(SqlTypeName.SMALLINT, "SELECT -CAST(? AS SMALLINT)", -32768), - arguments(SqlTypeName.SMALLINT, "SELECT CAST (-32768 AS SMALLINT)/-1::SMALLINT", EMPTY_PARAM), // TINYINT - arguments(SqlTypeName.TINYINT, "SELECT 2::TINYINT + 127::TINYINT", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "select CAST(9223372036854775807.5 + 9223372036854775807.5 AS TINYINT)", - EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT CAST(CAST(200 AS BIGINT) AS TINYINT)", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT CAST(CAST(200 AS FLOAT) AS TINYINT)", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT CAST(CAST(200 + 1 AS FLOAT) AS TINYINT)", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT 2::TINYINT * 127::TINYINT", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT -2::TINYINT - 127::TINYINT", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT -(-127::TINYINT - 1::TINYINT)", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT -CAST(-128 AS TINYINT)", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT -CAST(? AS TINYINT)", -128), - arguments(SqlTypeName.TINYINT, "SELECT CAST(-128 AS TINYINT)/-1::TINYINT", EMPTY_PARAM), - arguments(SqlTypeName.TINYINT, "SELECT CAST(CAST(200 + 1 AS FLOAT) AS TINYINT)", EMPTY_PARAM) + arguments(SqlTypeName.TINYINT, "SELECT -CAST(? AS TINYINT)", -128) + ); + } + + @ParameterizedTest + @MethodSource("decimalOpTypeExtension") + public void testCalcOpDynParamWithDivisibleOverflow(String expr, String expect, Object param) { + assertEquals(sql(expr, param).get(0).get(0).toString(), expect); + } + + private static Stream decimalOpTypeExtension() { + return Stream.of( + // TODO: https://issues.apache.org/jira/browse/IGNITE-23232 + // arguments("SELECT -9223372036854775808::BIGINT/-1::BIGINT", "9223372036854775808"), + // arguments("SELECT -?::BIGINT/-1::BIGINT", "9223372036854775808", "9223372036854775808"), + // arguments("SELECT -2147483648::INTEGER/-1::INTEGER", "2147483648")/*, + // arguments("SELECT -32768::SMALLINT/-1::SMALLINT", "32768"), + // arguments("SELECT -128::TINYINT/-1::TINYINT", "128") + + arguments("SELECT CAST(-? AS BIGINT)/-1", "9223372036854775808", "9223372036854775808"), + arguments("SELECT CAST(-? AS INTEGER)/-1", "2147483648", "2147483648"), + arguments("SELECT CAST(-? AS SMALLINT)/-1", "32768", "32768"), + arguments("SELECT CAST(-? AS TINYINT)/-1", "128", "128"), + + arguments("SELECT CAST(-? AS BIGINT) * -1", "9223372036854775808", "9223372036854775808"), + arguments("SELECT CAST(-? AS INTEGER) * -1", "2147483648", "2147483648"), + arguments("SELECT CAST(-? AS SMALLINT) * -1", "32768", "32768"), + arguments("SELECT CAST(-? AS TINYINT) * -1", "128", "128") ); } diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java index 7a96d852918..783297271cb 100644 --- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java +++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java @@ -100,8 +100,8 @@ public void testDynamicParameters() { assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check(); assertQuery("SELECT ?").withParams("asd").returns("asd").check(); assertQuery("SELECT ? % ?").withParams(11, 10).returns(1).check(); - assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); - assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4).check(); + assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4L, "test").check(); + assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4L).check(); assertQuery("SELECT (? + 1)::INTEGER").withParams(1).returns(2).check(); createAndPopulateTable(); @@ -160,7 +160,7 @@ public void testNestedCase() { /** Need to test the same query with different type of parameters to cover case with check right plans cache work. **/ @Test public void testWithDifferentParametersTypes() { - assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); + assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4L, "test").check(); assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2.2, 2.2, "TeSt").returns(4.4, "test").check(); assertQuery("SELECT COALESCE(?, ?)").withParams(null, null).returns(null).check(); diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java index be97bdeae04..62e25c95947 100644 --- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java +++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java @@ -89,7 +89,6 @@ public void testIntervalResult() { // Interval range overflow assertThrowsSqlException(Sql.RUNTIME_ERR, "INTEGER out of range", () -> sql("SELECT INTERVAL 5000000 MONTHS * 1000")); - assertThrowsSqlException(Sql.RUNTIME_ERR, "BIGINT out of range", () -> sql("SELECT DATE '2021-01-01' + INTERVAL 999999999999 DAY")); assertThrowsSqlException(Sql.RUNTIME_ERR, "INTEGER out of range", () -> sql("SELECT DATE '2021-01-01' + INTERVAL -999999999 YEAR")); } diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java index fece486bcae..060c9ed9a14 100644 --- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java +++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java @@ -90,10 +90,10 @@ public void testComparison() { @Test public void testArithmetic() { - assertExpression("1 + 2").returns(3).check(); - assertExpression("2 - 1").returns(1).check(); - assertExpression("2 * 3").returns(6).check(); - assertExpression("3 / 2 ").returns(1).check(); + assertExpression("1 + 2").returns(3L).check(); + assertExpression("2 - 1").returns(1L).check(); + assertExpression("2 * 3").returns(6L).check(); + assertExpression("3 / 2 ").returns(1L).check(); assertExpression("-(1)").returns(-1).check(); assertExpression("+(1)").returns(1).check(); assertExpression("3 % 2").returns(1).check(); diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesKeyValueGetTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesKeyValueGetTest.java index f4d8d30168b..3bf3d107e65 100644 --- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesKeyValueGetTest.java +++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesKeyValueGetTest.java @@ -107,7 +107,7 @@ void lookupBySimpleKeyWithProjection() { assertQuery("SELECT id, val * 10 FROM simple_key WHERE id = ?") .matches(containsSubPlan("KeyValueGet")) .withParams(key) - .returns(key, key * 10) + .returns(key, key * 10L) .check(); } diff --git a/modules/sql-engine/src/integrationTest/sql/dml/test_self_referencing.test b/modules/sql-engine/src/integrationTest/sql/dml/test_self_referencing.test index ab3e75ddce3..33ab52011f0 100644 --- a/modules/sql-engine/src/integrationTest/sql/dml/test_self_referencing.test +++ b/modules/sql-engine/src/integrationTest/sql/dml/test_self_referencing.test @@ -47,9 +47,13 @@ SELECT a, val FROM test 11 A 2 B +skipif ignite3 +# https://issues.apache.org/jira/browse/IGNITE-23183 statement ok UPDATE test SET a = (SELECT a + 1 FROM test WHERE val = 'A') +skipif ignite3 +# https://issues.apache.org/jira/browse/IGNITE-23183 query IT rowsort SELECT a, val FROM test ---- diff --git a/modules/sql-engine/src/integrationTest/sql/insert/test_insert_type.test b/modules/sql-engine/src/integrationTest/sql/insert/test_insert_type.test index e2ef58f625a..96cf66d2b13 100644 --- a/modules/sql-engine/src/integrationTest/sql/insert/test_insert_type.test +++ b/modules/sql-engine/src/integrationTest/sql/insert/test_insert_type.test @@ -30,3 +30,140 @@ SELECT * FROM strings ORDER BY cast(a AS INTEGER) 4 13 NULL + +statement ok +CREATE TABLE t8(id int, i TINYINT); + +statement ok +CREATE TABLE t16(id int, i SMALLINT); + +statement ok +CREATE TABLE t32(id int, i INTEGER); + +statement ok +CREATE TABLE t64(id int, i BIGINT); + +statement ok +INSERT INTO t8 VALUES(1, -128); + +statement ok +INSERT INTO t16 VALUES(1, -32768); + +statement ok +INSERT INTO t32 VALUES(1, -2147483648); + +statement ok +INSERT INTO t64 VALUES(1, -9223372036854775808); + +#Multiply + +statement ok +INSERT INTO t16 VALUES(2, (SELECT i*i FROM t8 WHERE id=1)); + +query T +SELECT i FROM t16 WHERE id=2; +---- +16384 + +statement ok +INSERT INTO t16 VALUES(21, (SELECT i*i*id::BIGINT FROM t8 WHERE id=1)); + +query T +SELECT i FROM t16 WHERE id=21; +---- +16384 + +statement ok +INSERT INTO t32 VALUES(2, (SELECT i*i FROM t16 WHERE id=1)); + +query T +SELECT i FROM t32 WHERE id=2; +---- +1073741824 + +statement ok +INSERT INTO t64 VALUES(2, (SELECT i*i FROM t32 WHERE id=1)); + +query T +SELECT i FROM t64 WHERE id=2; +---- +4611686018427387904 + +#Divide + +statement ok +INSERT INTO t16 VALUES(3, (SELECT i/-1 FROM t8 WHERE id=1)); + +query T +SELECT i FROM t16 WHERE id=3; +---- +128 + +statement ok +INSERT INTO t32 VALUES(3, (SELECT i/-1 FROM t16 WHERE id=1)); + +query T +SELECT i FROM t32 WHERE id=3; +---- +32768 + +statement ok +INSERT INTO t64 VALUES(3, (SELECT i/-1 FROM t32 WHERE id=1)); + +query T +SELECT i FROM t64 WHERE id=3; +---- +2147483648 + +#Add + +statement ok +INSERT INTO t16 VALUES(4, (SELECT i+i FROM t8 WHERE id=1)); + +query T +SELECT i FROM t16 WHERE id=4; +---- +-256 + +statement ok +INSERT INTO t32 VALUES(4, (SELECT i+i FROM t16 WHERE id=1)); + +query T +SELECT i FROM t32 WHERE id=4; +---- +-65536 + +statement ok +INSERT INTO t64 VALUES(4, (SELECT i+i FROM t32 WHERE id=1)); + +query T +SELECT i FROM t64 WHERE id=4; +---- +-4294967296 + +#Subtract + +statement ok +INSERT INTO t16 VALUES(5, (SELECT i-1 FROM t8 WHERE id=1)); + +query T +SELECT i FROM t16 WHERE id=5; +---- +-129 + +statement ok +INSERT INTO t32 VALUES(5, (SELECT i-1 FROM t16 WHERE id=1)); + +query T +SELECT i FROM t32 WHERE id=5; +---- +-32769 + +statement ok +INSERT INTO t64 VALUES(5, (SELECT i-1 FROM t32 WHERE id=1)); + +query T +SELECT i FROM t64 WHERE id=5; +---- +-2147483649 + diff --git a/modules/sql-engine/src/integrationTest/sql/order/test_order_by_in_subquery.test b/modules/sql-engine/src/integrationTest/sql/order/test_order_by_in_subquery.test index 355358f7924..525a7924149 100644 --- a/modules/sql-engine/src/integrationTest/sql/order/test_order_by_in_subquery.test +++ b/modules/sql-engine/src/integrationTest/sql/order/test_order_by_in_subquery.test @@ -33,7 +33,7 @@ statement error SELECT a-10 AS k FROM test UNION SELECT a-10 AS l FROM test ORDER BY l # ORDER BY on alias in right-most query but related just to the subquery. -query I +query I rowsort SELECT a-10 AS k FROM test UNION (SELECT a-10 AS l FROM test ORDER BY l) ---- 1 diff --git a/modules/sql-engine/src/integrationTest/sql/types/decimal/cast_from_decimal.test b/modules/sql-engine/src/integrationTest/sql/types/decimal/cast_from_decimal.test index e7250f025c9..53c8f4a83fe 100644 --- a/modules/sql-engine/src/integrationTest/sql/types/decimal/cast_from_decimal.test +++ b/modules/sql-engine/src/integrationTest/sql/types/decimal/cast_from_decimal.test @@ -29,8 +29,10 @@ SELECT (SELECT (127 + 1)::DECIMAL(18,0)::TINYINT) statement error SELECT (SELECT (32768 + 1)::DECIMAL(18,0)::SMALLINT) -statement error +query I SELECT 2147483647 + 1 +---- +2147483648 query I SELECT 2147483648 diff --git a/modules/sql-engine/src/integrationTest/sql/types/integer/cast_to_integer.test b/modules/sql-engine/src/integrationTest/sql/types/integer/cast_to_integer.test new file mode 100644 index 00000000000..519bf3d6048 --- /dev/null +++ b/modules/sql-engine/src/integrationTest/sql/types/integer/cast_to_integer.test @@ -0,0 +1,131 @@ +# name: test/sql/types/decimal/cast_to_integer.test +# description: Test casting from integer to integer +# group: [integer] + +statement ok +PRAGMA enable_verification + +query I +SELECT CAST(-9223372036854775808 AS BIGINT)/-1::BIGINT; +---- +9223372036854775808 + +query I +SELECT CAST (-2147483648 AS INTEGER)/-1::INTEGER +---- +2147483648 + +query I +SELECT CAST (-32768 AS SMALLINT)/-1::SMALLINT +---- +32768 + +query I +SELECT CAST(-128 AS TINYINT)/-1::TINYINT +---- +128 + +statement error: BIGINT out of range +SELECT (-9223372036854775808/-1)::BIGINT + +statement error: INTEGER out of range +SELECT (-2147483648/-1)::INTEGER + +statement error: SMALLINT out of range +SELECT (CAST (-32768 AS SMALLINT)/-1::SMALLINT)::SMALLINT + +statement error: TINYINT out of range +SELECT (CAST(-128 AS TINYINT)/-1::TINYINT)::TINYINT + +statement error: BIGINT out of range +SELECT (9223372036854775807 + 1)::BIGINT + +statement error: BIGINT out of range +SELECT (9223372036854775807 * 2)::BIGINT + +statement error: BIGINT out of range +SELECT (-9223372036854775808 - 1)::BIGINT + +statement error: BIGINT out of range +SELECT (-(-9223372036854775807 - 1))::BIGINT + +statement error: BIGINT out of range +SELECT -CAST(-9223372036854775808 AS BIGINT) + +statement error: INTEGER out of range +SELECT (2147483647 + 1)::INTEGER + +statement error: INTEGER out of range +SELECT CAST(CAST(2147483648 AS BIGINT) AS INTEGER) + +statement error: INTEGER out of range +SELECT (2147483647 * 2)::INTEGER + +statement error: INTEGER out of range +SELECT (-2147483648 - 1)::INTEGER + +statement error: INTEGER out of range +SELECT (-(-2147483647 - 1))::INTEGER + +statement error: INTEGER out of range +SELECT -CAST(-2147483648 AS INTEGER) + +statement error: INTEGER out of range +SELECT CAST(9223372036854775807.5 + 9223372036854775807.5 AS INTEGER) + +statement error: SMALLINT out of range +SELECT (32000::SMALLINT + 1000::SMALLINT)::SMALLINT + +statement error: SMALLINT out of range +SELECT CAST(9223372036854775807.5 + 9223372036854775807.5 AS SMALLINT) + +statement error: SMALLINT out of range +SELECT CAST(CAST(33000 AS BIGINT) AS SMALLINT) + +statement error: SMALLINT out of range +SELECT CAST(CAST(33000 AS FLOAT) AS SMALLINT) + +statement error: SMALLINT out of range +SELECT CAST(CAST(33000 + 1 AS FLOAT) AS SMALLINT) + +statement error: SMALLINT out of range +SELECT (17000::SMALLINT * 2::SMALLINT)::SMALLINT + +statement error: SMALLINT out of range +SELECT (-32000::SMALLINT - 1000::SMALLINT)::SMALLINT + +statement error: SMALLINT out of range +SELECT (-(-32767::SMALLINT - 1::SMALLINT))::SMALLINT + +statement error: SMALLINT out of range +SELECT -CAST(-32768 AS SMALLINT) + +statement error: TINYINT out of range +SELECT (2::TINYINT + 127::TINYINT)::TINYINT + +statement error: TINYINT out of range +SELECT CAST(9223372036854775807.5 + 9223372036854775807.5 AS TINYINT) + +statement error: TINYINT out of range +SELECT CAST(CAST(200 AS BIGINT) AS TINYINT) + +statement error: TINYINT out of range +SELECT CAST(CAST(200 AS FLOAT) AS TINYINT) + +statement error: TINYINT out of range +SELECT CAST(CAST(200 + 1 AS FLOAT) AS TINYINT) + +statement error: TINYINT out of range +SELECT (2::TINYINT * 127::TINYINT)::TINYINT + +statement error: TINYINT out of range +SELECT (-2::TINYINT - 127::TINYINT)::TINYINT + +statement error: TINYINT out of range +SELECT (-(-127::TINYINT - 1::TINYINT))::TINYINT + +statement error: TINYINT out of range +SELECT -CAST(-128 AS TINYINT) + +statement error: TINYINT out of range +SELECT CAST(CAST(200 + 1 AS FLOAT) AS TINYINT) 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 99d81c1e369..93f5b3db6a0 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 @@ -29,23 +29,23 @@ /** Calcite liq4j expressions customized for Ignite. */ public class IgniteExpressions { /** Make binary expression with arithmetic operations override. */ - public static Expression makeBinary(ExpressionType binaryType, Expression left, Expression right) { + static Expression makeBinary(ExpressionType binaryType, Expression left, Expression right) { switch (binaryType) { case Add: - return addExact(left, right); + return add(left, right); case Subtract: - return subtractExact(left, right); + return subtract(left, right); case Multiply: - return multiplyExact(left, right); + return multiply(left, right); case Divide: - return divideExact(left, right); + return divide(left, right); default: return Expressions.makeBinary(binaryType, left, right); } } /** Make unary expression with arithmetic operations override. */ - public static Expression makeUnary(ExpressionType unaryType, Expression operand) { + static Expression makeUnary(ExpressionType unaryType, Expression operand) { switch (unaryType) { case Negate: case NegateChecked: @@ -55,8 +55,52 @@ public static Expression makeUnary(ExpressionType unaryType, Expression operand) } } + /** Generate expression for {@link IgniteMath#add}} method. */ + private static Expression add(Expression left, Expression right) { + Type largerType = larger(left.getType(), right.getType()); + + if (largerType == Integer.TYPE || largerType == Long.TYPE || largerType == Short.TYPE || largerType == Byte.TYPE) { + return Expressions.call(IgniteMath.class, "add", left, right); + } + + return Expressions.makeBinary(ExpressionType.Add, left, right); + } + + /** Generate expression for {@link IgniteMath#subtract}} method. */ + private static Expression subtract(Expression left, Expression right) { + Type largerType = larger(left.getType(), right.getType()); + + if (largerType == Integer.TYPE || largerType == Long.TYPE || largerType == Short.TYPE || largerType == Byte.TYPE) { + return Expressions.call(IgniteMath.class, "subtract", left, right); + } + + return Expressions.makeBinary(ExpressionType.Subtract, left, right); + } + + /** Generate expression for {@link IgniteMath#multiply}} method. */ + public static Expression multiply(Expression left, Expression right) { + Type largerType = larger(left.getType(), right.getType()); + + if (largerType == Integer.TYPE || largerType == Long.TYPE || largerType == Short.TYPE || largerType == Byte.TYPE) { + return Expressions.call(IgniteMath.class, "multiply", left, right); + } + + return Expressions.makeBinary(ExpressionType.Multiply, left, right); + } + + /** Generate expression for {@link IgniteMath#divide}} method. */ + public static Expression divide(Expression left, Expression right) { + Type largerType = larger(left.getType(), right.getType()); + + if (largerType == Integer.TYPE || largerType == Long.TYPE || largerType == Short.TYPE || largerType == Byte.TYPE) { + return Expressions.call(IgniteMath.class, "divide", left, right); + } + + return Expressions.makeBinary(ExpressionType.Divide, left, right); + } + /** Generate expression for method IgniteMath.addExact() for integer subtypes. */ - public static Expression addExact(Expression left, Expression right) { + static Expression addExact(Expression left, Expression right) { Type largerType = larger(left.getType(), right.getType()); if (largerType == Integer.TYPE || largerType == Long.TYPE || largerType == Short.TYPE || largerType == Byte.TYPE) { @@ -67,7 +111,7 @@ public static Expression addExact(Expression left, Expression right) { } /** Generate expression for method IgniteMath.subtractExact() for integer subtypes. */ - public static Expression subtractExact(Expression left, Expression right) { + static Expression subtractExact(Expression left, Expression right) { Type largerType = larger(left.getType(), right.getType()); if (largerType == Integer.TYPE || largerType == Long.TYPE || largerType == Short.TYPE || largerType == Byte.TYPE) { @@ -78,7 +122,7 @@ public static Expression subtractExact(Expression left, Expression right) { } /** Generate expression for method IgniteMath.multiplyExact() for integer subtypes. */ - public static Expression multiplyExact(Expression left, Expression right) { + static Expression multiplyExact(Expression left, Expression right) { Type largerType = larger(left.getType(), right.getType()); if (largerType == Integer.TYPE || largerType == Long.TYPE || largerType == Short.TYPE || largerType == Byte.TYPE) { @@ -89,7 +133,7 @@ public static Expression multiplyExact(Expression left, Expression right) { } /** Generate expression for method IgniteMath.divideExact() for integer subtypes. */ - public static Expression divideExact(Expression left, Expression right) { + static Expression divideExact(Expression left, Expression right) { Type largerType = larger(left.getType(), right.getType()); if (largerType == Integer.TYPE || largerType == Long.TYPE || largerType == Short.TYPE || largerType == Byte.TYPE) { 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 44e42b301cb..b766e2df227 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 @@ -3082,6 +3082,17 @@ private static class BinaryImplementor extends AbstractRexCallImplementor { || type1 == BigDecimal.class || COMPARISON_OPERATORS.contains(op) && !COMP_OP_TYPES.contains(primitive)) { + // The cases for this branch: + // 1. Is solve resolving math arithmetic operations between BigDecimal and primitive types + // for example : SqlFunctions.multiply(java.math.BigDecimal, int) is not implemented in calcite but need to be resolved. + // 2. Division with correct rounding for decimals with zero scale + if (!EQUALS_OPERATORS.contains(op) && !COMPARISON_OPERATORS.contains(op)) { + if (type0 == BigDecimal.class && (Primitive.ofBoxOr(type1) != null) + || (type0 == BigDecimal.class && type1 == BigDecimal.class && op == DIVIDE)) { + return IgniteExpressions.makeBinary(expressionType, + argValueList.get(0), argValueList.get(1)); + } + } return Expressions.call(SqlFunctions.class, backupMethodName, argValueList); } diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteConvertletTable.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteConvertletTable.java index 0d1c39d3a76..b446b55ec6d 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteConvertletTable.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteConvertletTable.java @@ -104,17 +104,18 @@ private static class TimestampDiffConvertlet implements SqlRexConvertlet { cx.getTypeFactory().createSqlType(sqlTypeName), SqlTypeUtil.containsNullable(rexCall.getType())); - RexNode e; + // Return type can be expanded here, thus calculations need to be made before casting. + RexNode e = rexBuilder.multiplyDivide(rexCall, multiplier, divider); // Since Calcite converts internal time representation to seconds during cast we need our own cast // method to keep fraction of seconds. if (unit == TimeUnit.MILLISECOND) { - e = makeCastMilliseconds(rexBuilder, intType, rexCall); + e = makeCastMilliseconds(rexBuilder, intType, e); } else { - e = rexBuilder.makeCast(intType, rexCall); + e = rexBuilder.makeCast(intType, e); } - return rexBuilder.multiplyDivide(e, multiplier, divider); + return e; } /** diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystem.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystem.java index 1b1a3628304..4addcc66b91 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystem.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystem.java @@ -17,7 +17,11 @@ package org.apache.ignite.internal.sql.engine.type; +import static java.util.Objects.requireNonNull; +import static org.apache.calcite.sql.type.SqlTypeUtil.isIntType; + import java.math.BigDecimal; +import java.util.List; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelDataTypeSystemImpl; @@ -82,6 +86,84 @@ public int getDefaultPrecision(SqlTypeName typeName) { } } + /** + * Extend passed type to the nearest bigger. + * TINYINT -> SMALLINT + * SMALLINT -> INTEGER + * INTEGER -> BIGINT + * BIGINT -> DECIMAL + * + * @param typeFactory Type factory. + * @param type Type need to be extended. + * @param nullable Nullability flag. + * @return Extended type. + */ + private static RelDataType extendType(RelDataTypeFactory typeFactory, RelDataType type, boolean nullable) { + switch (type.getSqlTypeName()) { + case TINYINT: + return typeFactory.createTypeWithNullability(typeFactory.createSqlType(SqlTypeName.SMALLINT), + nullable); + case SMALLINT: + return typeFactory.createTypeWithNullability(typeFactory.createSqlType(SqlTypeName.INTEGER), + nullable); + case INTEGER: + return typeFactory.createTypeWithNullability(typeFactory.createSqlType(SqlTypeName.BIGINT), + nullable); + case BIGINT: + return typeFactory.createTypeWithNullability( + typeFactory.createSqlType( + SqlTypeName.DECIMAL, + typeFactory.getTypeSystem().getMaxPrecision(SqlTypeName.DECIMAL), + 0 + ), nullable); + default: + throw new AssertionError("Unexpected type: " + type); + } + } + + /** {@inheritDoc} */ + @Override + public RelDataType deriveDecimalDivideType(RelDataTypeFactory typeFactory, + RelDataType type1, RelDataType type2) { + // the only case in enlarging such a type in Number#MIN_VALUE / -1 operation + if (isIntType(type1) && isIntType(type2)) { + boolean typesNullability = type1.isNullable() || type2.isNullable(); + + return extendType(typeFactory, type1, typesNullability); + } + + return super.deriveDecimalDivideType(typeFactory, type1, type2); + } + + /** {@inheritDoc} */ + @Override + public RelDataType deriveDecimalMultiplyType(RelDataTypeFactory typeFactory, + RelDataType type1, RelDataType type2) { + if (isIntType(type1) && isIntType(type2)) { + boolean typesNullability = type1.isNullable() || type2.isNullable(); + + RelDataType derivedType = typeFactory.leastRestrictive(List.of(type1, type2)); + + return extendType(typeFactory, requireNonNull(derivedType), typesNullability); + } + + return super.deriveDecimalMultiplyType(typeFactory, type1, type2); + } + + /** {@inheritDoc} */ + @Override + public RelDataType deriveDecimalPlusType(RelDataTypeFactory typeFactory, + RelDataType type1, RelDataType type2) { + // When the declared type of both operands of the addition, subtraction, multiplication, or + // division operator is exact numeric, the precision of the result is implementation-defined. + // SQL standard, Subclause 6.26, . + if (isIntType(type1) && isIntType(type2)) { + return deriveDecimalMultiplyType(typeFactory, type1, type2); + } + + return super.deriveDecimalPlusType(typeFactory, type1, type2); + } + /** {@inheritDoc} */ @Override public RelDataType deriveSumType(RelDataTypeFactory typeFactory, RelDataType argumentType) { 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 8f724fd8ab8..1855729fdce 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 @@ -17,6 +17,7 @@ package org.apache.ignite.internal.sql.engine.util; +import static java.math.RoundingMode.UNNECESSARY; import static org.apache.calcite.sql.type.SqlTypeName.BIGINT; import static org.apache.calcite.sql.type.SqlTypeName.INTEGER; import static org.apache.calcite.sql.type.SqlTypeName.SMALLINT; @@ -24,10 +25,13 @@ import static org.apache.ignite.lang.ErrorGroups.Sql.RUNTIME_ERR; import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; +import java.math.RoundingMode; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.ignite.sql.SqlException; -/** Math operations with overflow checking. */ +/** Math operations with overflow awareness. */ public class IgniteMath { private static final BigDecimal UPPER_LONG_BIG_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.ONE); private static final BigDecimal LOWER_LONG_BIG_DECIMAL = BigDecimal.valueOf(Long.MIN_VALUE).subtract(BigDecimal.ONE); @@ -88,6 +92,135 @@ public static byte addExact(byte x, byte y) { return (byte) r; } + /** Returns the sum of its arguments, extending result type for overflow avoidance. */ + public static BigInteger add(long x, long y) { + return BigInteger.valueOf(x).add(BigInteger.valueOf(y)); + } + + /** Returns the sum of its arguments, extending result type for overflow avoidance. */ + public static BigDecimal add(BigDecimal x, BigDecimal y) { + int maxPrecision = Commons.cluster().getTypeFactory().getTypeSystem().getMaxPrecision(SqlTypeName.DECIMAL); + return x.add(y, new MathContext(maxPrecision, UNNECESSARY)); + } + + /** Returns the sum of its arguments, extending result type for overflow avoidance. */ + public static long add(int x, int y) { + return (long) x + y; + } + + /** Returns the sum of its arguments, extending result type for overflow avoidance. */ + public static int add(short x, short y) { + return x + y; + } + + /** Returns the sum of its arguments, extending result type for overflow avoidance. */ + public static short add(byte x, byte y) { + return (short) (x + y); + } + + /** Returns the subtraction of its arguments, extending result type for overflow avoidance. */ + public static BigInteger subtract(long x, long y) { + return BigInteger.valueOf(x).subtract(BigInteger.valueOf(y)); + } + + /** Returns the subtraction of its arguments, extending result type for overflow avoidance. */ + public static BigDecimal subtract(BigDecimal x, BigDecimal y) { + int maxPrecision = Commons.cluster().getTypeFactory().getTypeSystem().getMaxPrecision(SqlTypeName.DECIMAL); + return x.subtract(y, new MathContext(maxPrecision, UNNECESSARY)); + } + + /** Returns the subtraction of its arguments, extending result type for overflow avoidance. */ + public static long subtract(int x, int y) { + return (long) x - y; + } + + /** Returns the subtraction of its arguments, extending result type for overflow avoidance. */ + public static int subtract(short x, short y) { + return x - y; + } + + /** Returns the subtraction of its arguments, extending result type for overflow avoidance. */ + public static short subtract(byte x, byte y) { + return (short) (x - y); + } + + /** Returns the multiply of its arguments, extending result type for overflow avoidance. */ + public static BigInteger multiply(long x, long y) { + return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y)); + } + + /** Returns the multiply of its arguments, extending result type for overflow avoidance. */ + public static BigDecimal multiply(BigDecimal x, long y) { + int maxPrecision = Commons.cluster().getTypeFactory().getTypeSystem().getMaxPrecision(SqlTypeName.DECIMAL); + return x.multiply(BigDecimal.valueOf(y), new MathContext(maxPrecision, UNNECESSARY)); + } + + /** Returns the multiply of its arguments, extending result type for overflow avoidance. */ + public static BigDecimal multiply(BigDecimal x, BigDecimal y) { + int maxPrecision = Commons.cluster().getTypeFactory().getTypeSystem().getMaxPrecision(SqlTypeName.DECIMAL); + return x.multiply(y, new MathContext(maxPrecision, UNNECESSARY)); + } + + /** Returns the multiply of its arguments, extending result type for overflow avoidance. */ + public static long multiply(int x, int y) { + return (long) x * y; + } + + /** Returns the multiply of its arguments, extending result type for overflow avoidance. */ + public static int multiply(short x, short y) { + return x * y; + } + + /** Returns the multiply of its arguments, extending result type for overflow avoidance. */ + public static short multiply(byte x, byte y) { + return (short) (x * y); + } + + /** Returns the division of its arguments, extending result type for overflow avoidance. */ + public static BigInteger divide(long x, long y) { + if (y == 0) { + throwDivisionByZero(); + } + + return BigInteger.valueOf(x).divide(BigInteger.valueOf(y)); + } + + /** Returns the multiply of its arguments, extending result type for overflow avoidance. */ + public static BigDecimal divide(BigDecimal x, BigDecimal y) { + if (y.compareTo(BigDecimal.ZERO) == 0) { + throwDivisionByZero(); + } + + return x.divide(y, RoundingMode.HALF_DOWN); + } + + /** Returns the division of its arguments, extending result type for overflow avoidance. */ + public static long divide(int x, int y) { + if (y == 0) { + throwDivisionByZero(); + } + + return (long) x / y; + } + + /** Returns the division of its arguments, extending result type for overflow avoidance. */ + public static int divide(short x, short y) { + if (y == 0) { + throwDivisionByZero(); + } + + return x / y; + } + + /** Returns the division of its arguments, extending result type for overflow avoidance. */ + public static short divide(byte x, byte y) { + if (y == 0) { + throwDivisionByZero(); + } + + return (short) (x / y); + } + /** Returns the negation of the argument, throwing an exception if the result overflows an {@code long}. */ public static long negateExact(long x) { long res = -x; diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java index baf1962d431..06eb14c8613 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java @@ -511,7 +511,7 @@ public static NativeType columnType2NativeType(ColumnType columnType, int precis } /** Checks whether cast operation is necessary in {@code SearchBound}. */ - public static boolean needCastInSearchBounds(IgniteTypeFactory typeFactory, RelDataType fromType, RelDataType toType) { + static boolean needCastInSearchBounds(IgniteTypeFactory typeFactory, RelDataType fromType, RelDataType toType) { // Checks for character and binary types should allow comparison // between types with precision, types w/o precision, and varying non-varying length variants. // Otherwise the optimizer wouldn't pick an index for conditions such as @@ -531,6 +531,25 @@ public static boolean needCastInSearchBounds(IgniteTypeFactory typeFactory, RelD return false; } + if (SqlTypeUtil.isIntType(fromType) && SqlTypeUtil.isDecimal(toType)) { + if (toType.getScale() != 0) { + return true; + } + + switch (fromType.getSqlTypeName()) { + case TINYINT: + return toType.getPrecision() < 3; + case SMALLINT: + return toType.getPrecision() < 5; + case INTEGER: + return toType.getPrecision() < 10; + case BIGINT: + return toType.getPrecision() < 19; + default: + return false; + } + } + // Implicit type coercion does not handle nullability. if (SqlTypeUtil.equalSansNullability(typeFactory, fromType, toType)) { return false; diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java index 6af36ffbd8e..ec0afe7a60a 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java @@ -60,7 +60,8 @@ public Stream testArithExprs() { return Stream.of( sql("SELECT 1 + ?", 1).parameterTypes(nullable(NativeTypes.INT32)).ok(), sql("SELECT NULL + ?", 1).parameterTypes(nullable(NativeTypes.INT32)).project("null:INTEGER"), - sql("SELECT ? + NULL", 1).parameterTypes(nullable(NativeTypes.INT32)).project("null:INTEGER"), + // types derivation depends on InferTypes.FIRST_KNOWN logic, thus for NULL will be derived INTEGER + sql("SELECT ? + NULL", 1).parameterTypes(nullable(NativeTypes.INT32)).project("null:BIGINT"), sql("SELECT 1 + ?", "1").parameterTypes(nullable(NativeTypes.STRING)).project("+(1, CAST(?0):INTEGER)"), sql("SELECT ? + 1", "1").parameterTypes(nullable(NativeTypes.STRING)).project("+(CAST(?0):INTEGER, 1)"), diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ImplicitCastsTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ImplicitCastsTest.java index 357eebb85e7..b8203c523de 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ImplicitCastsTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ImplicitCastsTest.java @@ -339,7 +339,7 @@ public Stream testUpdate() { checkStatement() .table("t1", "c1", NativeTypes.INT32) .sql("UPDATE t1 SET c1 = '1'::INTEGER + 1") - .project("$t0", "+(1, 1)"), + .project("$t0", "CAST(+(1, 1)):INTEGER NOT NULL"), checkStatement() .table("t1", "c1", NativeTypes.stringOf(4)) @@ -349,7 +349,7 @@ public Stream testUpdate() { checkStatement() .table("t1", "id", NativeTypes.INT32, "int_col", NativeTypes.INT32, "str_col", NativeTypes.STRING) .sql("UPDATE t1 SET str_col = 1, int_col = id + 1") - .project("$t0", "$t1", "$t2", "_UTF-8'1':VARCHAR(65536) CHARACTER SET \"UTF-8\"", "+($t0, 1)") + .project("$t0", "$t1", "$t2", "_UTF-8'1':VARCHAR(65536) CHARACTER SET \"UTF-8\"", "CAST(+($t0, 1)):INTEGER") ); } diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/IndexSearchBoundsPlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/IndexSearchBoundsPlannerTest.java index eb659bb65a1..ebc0110e6c0 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/IndexSearchBoundsPlannerTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/IndexSearchBoundsPlannerTest.java @@ -420,7 +420,7 @@ public void testBoundsMerge() throws Exception { @Test public void testBoundsComplex() throws Exception { assertBounds("SELECT * FROM TEST WHERE C1 = ? + 10", List.of(1), publicSchema, - exact("+(?0, 10)") + exact("CAST(+(?0, 10)):INTEGER") ); assertBounds("SELECT * FROM TEST WHERE C1 = 1 AND C2 > SUBSTRING(?::VARCHAR, 1, 2) || '3'", List.of("1"), publicSchema, @@ -434,7 +434,7 @@ public void testBoundsComplex() throws Exception { ); assertBounds("SELECT (SELECT C1 FROM TEST t2 WHERE t2.C1 = t1.C1 + t1.C3 * ?) FROM TEST t1", List.of(1), publicSchema, - exact("+($cor0.C1, *($cor0.C3, ?0))") + exact("CAST(+($cor0.C1, *($cor0.C3, ?0))):INTEGER") ); assertPlan("SELECT * FROM TEST WHERE C1 = ? + C3", publicSchema, isTableScan("TEST"), List.of(1)); @@ -449,7 +449,7 @@ public void testBoundsComplex() throws Exception { ); assertBounds("SELECT * FROM TEST WHERE C1 in (?, ? + 1, ? * 2)", - multi(exact("?0"), exact("+(?1, 1)"), exact("*(?2, 2)")) + multi(exact("CAST(?0):INTEGER"), exact("CAST(+(?1, 1)):INTEGER"), exact("CAST(*(?2, 2)):INTEGER")) ); // Don't support expanding OR with correlate to bounds. diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningMetadataTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningMetadataTest.java index 97ea8a5d991..cb8b25a3e9b 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningMetadataTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningMetadataTest.java @@ -409,9 +409,9 @@ enum TestCaseBasic { CONST_FOLDING_1a("c1 = 10 + 4", TABLE_C1), CONST_FOLDING_1b("c1 = ? + 4", TABLE_C1), - // 0s removed by Calcite. - CONST_FOLDING_1c("c1 = 0 + 42", TABLE_C1, "[c1=42]"), - CONST_FOLDING_1d("c1 = 42 + 0", TABLE_C1, "[c1=42]"), + // 0s removed by Calcite but cast still present. + CONST_FOLDING_1c("c1 = 0 + 42", TABLE_C1), + CONST_FOLDING_1d("c1 = 42 + 0", TABLE_C1), ; private final TestCase data; diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ProjectFilterScanMergePlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ProjectFilterScanMergePlannerTest.java index 54b4bb13e8e..0656f8e276e 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ProjectFilterScanMergePlannerTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ProjectFilterScanMergePlannerTest.java @@ -213,7 +213,7 @@ public void testIdentityFilterProjectMerge() throws Exception { .and(scan -> scan.projects() != null) .and(scan -> "[+($t0, 1)]".equals(scan.projects().toString())) .and(scan -> scan.condition() != null) - .and(scan -> "=(+($t0, 1), $cor0.A)".equals(scan.condition().toString())) + .and(scan -> "=(+($t0, 1), CAST($cor0.A):BIGINT)".equals(scan.condition().toString())) .and(scan -> ImmutableBitSet.of(0).equals(scan.requiredColumns())) ))), "ProjectFilterTransposeRule"); } diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/NumericBinaryOperationsTypeCoercionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/NumericBinaryOperationsTypeCoercionTest.java index 8fea12bbfc5..eafedaeebf6 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/NumericBinaryOperationsTypeCoercionTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/NumericBinaryOperationsTypeCoercionTest.java @@ -17,13 +17,25 @@ package org.apache.ignite.internal.sql.engine.planner.datatypes; +import static org.apache.ignite.internal.sql.engine.util.TypeUtils.native2relationalType; + import java.util.List; import java.util.stream.Stream; +import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.sql.type.SqlTypeName; import org.apache.ignite.internal.sql.engine.planner.datatypes.utils.NumericPair; import org.apache.ignite.internal.sql.engine.planner.datatypes.utils.TypePair; import org.apache.ignite.internal.sql.engine.planner.datatypes.utils.Types; +import org.apache.ignite.internal.sql.engine.rel.IgniteRel; +import org.apache.ignite.internal.sql.engine.rel.ProjectableFilterableTableScan; import org.apache.ignite.internal.sql.engine.schema.IgniteSchema; +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.type.NativeType; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -48,7 +60,10 @@ public void additionOp(NumericPair pair) throws Exception { Matcher second = ofTypeWithoutCast(pair.second()); assertPlan("SELECT c1 + c2 FROM t", schema, operandMatcher(first, second)::matches, List.of()); + assertPlan("SELECT c1 + c2 FROM t", schema, mathResultMatcher(pair, ArithmeticOp.ADD)::matches, List.of()); + assertPlan("SELECT c2 + c1 FROM t", schema, operandMatcher(second, first)::matches, List.of()); + assertPlan("SELECT c2 + c1 FROM t", schema, mathResultMatcher(pair, ArithmeticOp.ADD)::matches, List.of()); } // No any type changes for `subtraction` operation from planner perspective. @@ -61,7 +76,10 @@ public void subtractionOp(NumericPair pair) throws Exception { Matcher second = ofTypeWithoutCast(pair.second()); assertPlan("SELECT c1 - c2 FROM t", schema, operandMatcher(first, second)::matches, List.of()); + assertPlan("SELECT c1 - c2 FROM t", schema, mathResultMatcher(pair, ArithmeticOp.ADD)::matches, List.of()); + assertPlan("SELECT c2 - c1 FROM t", schema, operandMatcher(second, first)::matches, List.of()); + assertPlan("SELECT c2 - c1 FROM t", schema, mathResultMatcher(pair, ArithmeticOp.ADD)::matches, List.of()); } // No any type changes for `division` operation from planner perspective. @@ -74,7 +92,10 @@ public void divisionOp(NumericPair pair) throws Exception { Matcher second = ofTypeWithoutCast(pair.second()); assertPlan("SELECT c1 / c2 FROM t", schema, operandMatcher(first, second)::matches, List.of()); + assertPlan("SELECT c1 / c2 FROM t", schema, mathResultMatcher(pair, ArithmeticOp.DIV)::matches, List.of()); + assertPlan("SELECT c2 / c1 FROM t", schema, operandMatcher(second, first)::matches, List.of()); + assertPlan("SELECT c1 / c2 FROM t", schema, mathResultMatcher(pair, ArithmeticOp.DIV)::matches, List.of()); } // No any type changes for `multiplication` operation from planner perspective. @@ -87,7 +108,10 @@ public void multiplicationOp(NumericPair pair) throws Exception { Matcher second = ofTypeWithoutCast(pair.second()); assertPlan("SELECT c1 * c2 FROM t", schema, operandMatcher(first, second)::matches, List.of()); + assertPlan("SELECT c1 * c2 FROM t", schema, mathResultMatcher(pair, ArithmeticOp.MULT)::matches, List.of()); + assertPlan("SELECT c2 * c1 FROM t", schema, operandMatcher(second, first)::matches, List.of()); + assertPlan("SELECT c1 * c2 FROM t", schema, mathResultMatcher(pair, ArithmeticOp.MULT)::matches, List.of()); } // Have the following casts for modulo operation: @@ -107,6 +131,63 @@ public void moduloOp( assertPlan("SELECT c2 % c1 FROM t", schema, operandMatcher(secondOperandMatcher, firstOperandMatcher)::matches, List.of()); } + private static Matcher mathResultMatcher(NumericPair pair, ArithmeticOp op) { + return new BaseMatcher<>() { + @Override + public boolean matches(Object actual) { + IgniteTypeSystem typeSystem = IgniteTypeSystem.INSTANCE; + IgniteTypeFactory typeFactory = Commons.typeFactory(); + + NativeType first = pair.first(); + NativeType second = pair.second(); + + RelDataType opType1 = native2relationalType(typeFactory, first); + RelDataType opType2 = native2relationalType(typeFactory, second); + RelDataType expectedReturnType; + + switch (op) { + case ADD: + expectedReturnType = typeSystem.deriveDecimalPlusType(typeFactory, opType1, opType2); + break; + case DIV: + expectedReturnType = typeSystem.deriveDecimalDivideType(typeFactory, opType1, opType2); + break; + case MULT: + expectedReturnType = typeSystem.deriveDecimalMultiplyType(typeFactory, opType1, opType2); + break; + default: + throw new IllegalArgumentException("Unexpected operation type: " + op); + } + + if (expectedReturnType == null) { + expectedReturnType = typeFactory.leastRestrictive(List.of(opType1, opType2)); + } + + ProjectableFilterableTableScan scan = (ProjectableFilterableTableScan) actual; + + RelDataType rowType = scan.getRowType(); + + assert rowType.getFieldList().size() == 1; + + RelDataType relRowType = rowType.getFieldList().get(0).getType(); + + SqlTypeName expectedSqlType = expectedReturnType.getSqlTypeName(); + SqlTypeName resulReturnType = relRowType.getSqlTypeName(); + + return expectedSqlType == resulReturnType; + } + + @Override + public void describeTo(Description description) { + + } + }; + } + + private enum ArithmeticOp { + ADD, MULT, DIV + } + /** * This test ensures that {@link #moduloArgs()} doesn't miss any type pair from {@link NumericPair}. */ 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 8be9cced4cb..8f919d3db34 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 @@ -27,6 +27,7 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.ignite.internal.sql.engine.util.Commons; import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest; +import org.apache.ignite.internal.type.NativeType; import org.apache.ignite.internal.type.NativeTypes; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; @@ -168,5 +169,135 @@ private static Stream deriveAvgTypeArguments() { native2relationalType(typeFactory, NativeTypes.DOUBLE) ) ); - } + } + + @ParameterizedTest(name = "op={0} {1}") + @MethodSource("deriveDivTypeArguments") + void deriveDivType(NativeType type1, NativeType type2, NativeType expected) { + RelDataType relType1 = native2relationalType(Commons.typeFactory(), type1); + RelDataType relType2 = native2relationalType(Commons.typeFactory(), type2); + RelDataType relExpected = native2relationalType(Commons.typeFactory(), expected); + + RelDataType actual = typeSystem.deriveDecimalDivideType(Commons.typeFactory(), relType1, relType2); + assertThat(actual, Matchers.equalTo(relExpected)); + } + + @ParameterizedTest(name = "op={0} {1}") + @MethodSource("deriveMultTypeArguments") + void deriveMultType(NativeType type1, NativeType type2, NativeType expected) { + RelDataType relType1 = native2relationalType(Commons.typeFactory(), type1); + RelDataType relType2 = native2relationalType(Commons.typeFactory(), type2); + RelDataType relExpected = native2relationalType(Commons.typeFactory(), expected); + + RelDataType actual = typeSystem.deriveDecimalMultiplyType(Commons.typeFactory(), relType1, relType2); + assertThat(actual, Matchers.equalTo(relExpected)); + } + + @ParameterizedTest(name = "op={0} {1}") + @MethodSource("deriveAddTypeArguments") + void deriveAddType(NativeType type1, NativeType type2, NativeType expected) { + RelDataType relType1 = native2relationalType(Commons.typeFactory(), type1); + RelDataType relType2 = native2relationalType(Commons.typeFactory(), type2); + RelDataType relExpected = native2relationalType(Commons.typeFactory(), expected); + + RelDataType actual = typeSystem.deriveDecimalPlusType(Commons.typeFactory(), relType1, relType2); + assertThat(actual, Matchers.equalTo(relExpected)); + } + + private static Stream deriveDivTypeArguments() { + IgniteTypeSystem typeSystem = IgniteTypeSystem.INSTANCE; + + return Stream.of( + Arguments.of(NativeTypes.INT8, NativeTypes.INT8, NativeTypes.INT16), + Arguments.of(NativeTypes.INT8, NativeTypes.INT16, NativeTypes.INT16), + Arguments.of(NativeTypes.INT8, NativeTypes.INT32, NativeTypes.INT16), + Arguments.of(NativeTypes.INT8, NativeTypes.INT64, NativeTypes.INT16), + + Arguments.of(NativeTypes.INT16, NativeTypes.INT8, NativeTypes.INT32), + Arguments.of(NativeTypes.INT16, NativeTypes.INT16, NativeTypes.INT32), + Arguments.of(NativeTypes.INT16, NativeTypes.INT32, NativeTypes.INT32), + Arguments.of(NativeTypes.INT16, NativeTypes.INT64, NativeTypes.INT32), + + Arguments.of(NativeTypes.INT32, NativeTypes.INT8, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT16, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT32, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT64, NativeTypes.INT64), + + Arguments.of(NativeTypes.INT64, NativeTypes.INT8, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT16, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT32, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)) + ); + } + + + private static Stream deriveMultTypeArguments() { + IgniteTypeSystem typeSystem = IgniteTypeSystem.INSTANCE; + + return Stream.of( + Arguments.of(NativeTypes.INT8, NativeTypes.INT8, NativeTypes.INT16), + Arguments.of(NativeTypes.INT8, NativeTypes.INT16, NativeTypes.INT32), + Arguments.of(NativeTypes.INT8, NativeTypes.INT32, NativeTypes.INT64), + Arguments.of(NativeTypes.INT8, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + + Arguments.of(NativeTypes.INT16, NativeTypes.INT8, NativeTypes.INT32), + Arguments.of(NativeTypes.INT16, NativeTypes.INT16, NativeTypes.INT32), + Arguments.of(NativeTypes.INT16, NativeTypes.INT32, NativeTypes.INT64), + Arguments.of(NativeTypes.INT16, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + + Arguments.of(NativeTypes.INT32, NativeTypes.INT8, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT16, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT32, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + + Arguments.of(NativeTypes.INT64, NativeTypes.INT8, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT16, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT32, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)) + ); + } + + private static Stream deriveAddTypeArguments() { + IgniteTypeSystem typeSystem = IgniteTypeSystem.INSTANCE; + + return Stream.of( + Arguments.of(NativeTypes.INT8, NativeTypes.INT8, NativeTypes.INT16), + Arguments.of(NativeTypes.INT8, NativeTypes.INT16, NativeTypes.INT32), + Arguments.of(NativeTypes.INT8, NativeTypes.INT32, NativeTypes.INT64), + Arguments.of(NativeTypes.INT8, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + + Arguments.of(NativeTypes.INT16, NativeTypes.INT8, NativeTypes.INT32), + Arguments.of(NativeTypes.INT16, NativeTypes.INT16, NativeTypes.INT32), + Arguments.of(NativeTypes.INT16, NativeTypes.INT32, NativeTypes.INT64), + Arguments.of(NativeTypes.INT16, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + + Arguments.of(NativeTypes.INT32, NativeTypes.INT8, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT16, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT32, NativeTypes.INT64), + Arguments.of(NativeTypes.INT32, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + + Arguments.of(NativeTypes.INT64, NativeTypes.INT8, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT16, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT32, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)), + Arguments.of(NativeTypes.INT64, NativeTypes.INT64, + NativeTypes.decimalOf(typeSystem.getMaxPrecision(SqlTypeName.DECIMAL), 0)) + ); + } }