This is an automated email from the ASF dual-hosted git repository.
zstan 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 ea59431e01 IGNITE-22578 Sql. Extend test coverage for numeric to/from
decimal cast with overflow (#3985)
ea59431e01 is described below
commit ea59431e01ac1b74757d904dcc54ddf3a9ca3603
Author: Evgeniy Stanilovskiy <[email protected]>
AuthorDate: Mon Jul 1 09:28:37 2024 +0300
IGNITE-22578 Sql. Extend test coverage for numeric to/from decimal cast
with overflow (#3985)
---
.../ignite/jdbc/ItJdbcComplexQuerySelfTest.java | 4 +-
.../internal/sql/engine/ItDataTypesTest.java | 139 +++++++++++++------
.../internal/sql/engine/ItFunctionsTest.java | 3 +-
.../integrationTest/sql/cast/test_cast_bigint.test | 2 +-
.../integrationTest/sql/cast/test_cast_int.test | 4 +-
.../sql/cast/test_cast_smallint.test | 4 +-
.../sql/cast/test_cast_tinyint.test | 4 +-
.../sql/engine/exec/exp/ConverterUtils.java | 49 ++++---
.../sql/engine/exec/exp/IgniteExpressions.java | 51 ++-----
.../sql/engine/prepare/IgniteSqlValidator.java | 78 -----------
.../internal/sql/engine/rex/IgniteRexBuilder.java | 13 ++
.../internal/sql/engine/util/IgniteMath.java | 148 ++++++++++++++++++---
12 files changed, 285 insertions(+), 214 deletions(-)
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcComplexQuerySelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcComplexQuerySelfTest.java
index f6f3614c68..15c767f503 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcComplexQuerySelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcComplexQuerySelfTest.java
@@ -209,7 +209,7 @@ public class ItJdbcComplexQuerySelfTest extends
AbstractJdbcSelfTest {
// Check non-indexed field.
JdbcTestUtils.assertThrowsSqlException(
- "For input string: \"B\"",
+ "Invalid input syntax for type INTEGER: \"B\"",
() -> stmt.executeQuery("select * from PUBLIC.Org where name =
2"));
// Check indexed field.
@@ -218,7 +218,7 @@ public class ItJdbcComplexQuerySelfTest extends
AbstractJdbcSelfTest {
}
JdbcTestUtils.assertThrowsSqlException(
- "For input string: \"Mike Green\"",
+ "Invalid input syntax for type INTEGER: \"Mike Green\"",
() -> stmt.executeQuery("select * from PUBLIC.Person where
name = 2"));
}
}
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 186c84c521..e068db34c4 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
@@ -150,8 +150,17 @@ public class ItDataTypesTest extends
BaseSqlIntegrationTest {
+ Integer.MAX_VALUE + ", " + Long.MAX_VALUE + ')');
assertQuery("SELECT tiny FROM tbl").returns(Byte.MAX_VALUE).check();
+ assertQuery(format("SELECT tiny FROM tbl WHERE tiny < {}", 2 *
Byte.MAX_VALUE)).returns(Byte.MAX_VALUE).check();
+ assertQuery(format("SELECT tiny FROM tbl WHERE tiny < '{}'::INTEGER",
2 * Byte.MAX_VALUE)).returns(Byte.MAX_VALUE).check();
+
assertQuery("SELECT small FROM tbl").returns(Short.MAX_VALUE).check();
+ assertQuery(format("SELECT small FROM tbl WHERE small < {}", 2 *
Short.MAX_VALUE)).returns(Short.MAX_VALUE).check();
+ assertQuery(format("SELECT small FROM tbl WHERE small <
'{}'::INTEGER", 2 * Short.MAX_VALUE)).returns(Short.MAX_VALUE).check();
+
assertQuery("SELECT i FROM tbl").returns(Integer.MAX_VALUE).check();
+ assertQuery(format("SELECT i FROM tbl WHERE i < {}", 2L *
Integer.MAX_VALUE)).returns(Integer.MAX_VALUE).check();
+ assertQuery(format("SELECT i FROM tbl WHERE i < '{}'::BIGINT", 2L *
Integer.MAX_VALUE)).returns(Integer.MAX_VALUE).check();
+
assertQuery("SELECT big FROM tbl").returns(Long.MAX_VALUE).check();
sql("DELETE from tbl");
@@ -160,8 +169,17 @@ public class ItDataTypesTest extends
BaseSqlIntegrationTest {
+ Integer.MIN_VALUE + ", " + Long.MIN_VALUE + ')');
assertQuery("SELECT tiny FROM tbl").returns(Byte.MIN_VALUE).check();
+ assertQuery(format("SELECT tiny FROM tbl WHERE tiny > {}", 2 *
Byte.MIN_VALUE)).returns(Byte.MIN_VALUE).check();
+ assertQuery(format("SELECT tiny FROM tbl WHERE tiny > '{}'::INTEGER",
2 * Byte.MIN_VALUE)).returns(Byte.MIN_VALUE).check();
+
assertQuery("SELECT small FROM tbl").returns(Short.MIN_VALUE).check();
+ assertQuery(format("SELECT small FROM tbl WHERE small > {}", 2 *
Short.MIN_VALUE)).returns(Short.MIN_VALUE).check();
+ assertQuery(format("SELECT small FROM tbl WHERE small >
'{}'::INTEGER", 2 * Short.MIN_VALUE)).returns(Short.MIN_VALUE).check();
+
assertQuery("SELECT i FROM tbl").returns(Integer.MIN_VALUE).check();
+ assertQuery(format("SELECT i FROM tbl WHERE i > {}", 2L *
Integer.MIN_VALUE)).returns(Integer.MIN_VALUE).check();
+ assertQuery(format("SELECT i FROM tbl WHERE i > '{}'::BIGINT", 2L *
Integer.MIN_VALUE)).returns(Integer.MIN_VALUE).check();
+
assertQuery("SELECT big FROM tbl").returns(Long.MIN_VALUE).check();
}
@@ -453,6 +471,7 @@ public class ItDataTypesTest extends BaseSqlIntegrationTest
{
return Stream.of(
// BIGINT
arguments(SqlTypeName.BIGINT, "SELECT 9223372036854775807 +
1", EMPTY_PARAM),
+ arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775807.5 AS BIGINT)", 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),
@@ -462,15 +481,23 @@ public class ItDataTypesTest extends
BaseSqlIntegrationTest {
// 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),
// 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),
@@ -480,25 +507,91 @@ public class ItDataTypesTest extends
BaseSqlIntegrationTest {
// 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(-128 AS
TINYINT)/-1::TINYINT", EMPTY_PARAM),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(CAST(200 + 1 AS
FLOAT) AS TINYINT)", EMPTY_PARAM)
);
}
@ParameterizedTest(name = "{1}")
@MethodSource("decimalOverflowsValidation")
- public void testCalcOpOverflowValidationCheck(SqlTypeName type, String
expr, Boolean withException) {
+ public void testCastDecimalOverflows(SqlTypeName type, String expr,
Boolean withException) {
if (withException) {
- assertThrowsSqlException(Sql.STMT_PARSE_ERR, "out of range", () ->
sql(expr));
+ assertThrowsSqlException(Sql.RUNTIME_ERR, type + " out of range",
() -> sql(expr));
} else {
sql(expr);
}
}
+ private static Stream<Arguments> decimalOverflowsValidation() {
+ return Stream.of(
+ // BIGINT
+ arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775807.1 AS BIGINT)", false),
+ arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775807.5 - 1 AS BIGINT)", false),
+ arguments(SqlTypeName.BIGINT, "SELECT
CAST(-9223372036854775808.1 AS BIGINT)", false),
+ arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775807.5 AS BIGINT)", true),
+ arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775808.1 AS BIGINT)", true),
+ arguments(SqlTypeName.BIGINT, "SELECT CAST(9223372036854775808
AS BIGINT)", true),
+ arguments(SqlTypeName.BIGINT, "SELECT
CAST('9223372036854775808' AS BIGINT)", true),
+ arguments(SqlTypeName.BIGINT, "SELECT
CAST(-9223372036854775809 AS BIGINT)", true),
+ arguments(SqlTypeName.BIGINT, "SELECT CAST('
-9223372036854775809' AS BIGINT)", true),
+
+
+ // INTEGER
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483647.1 AS
INTEGER)", false),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483647.5 AS
INTEGER)", false),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483647.5 - 1
AS INTEGER)", false),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(-2147483648.1 AS
INTEGER)", false),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483647 + 1 AS
INTEGER)", true),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483648.1 AS
INTEGER)", true),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(CAST(2147483648.1
AS DOUBLE) AS INTEGER)", true),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(CAST(2147483648.1
AS FLOAT) AS INTEGER)", true),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483648 AS
INTEGER)", true),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST('2147483648' AS
INTEGER)", true),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(-2147483649 AS
INTEGER)", true),
+ arguments(SqlTypeName.INTEGER, "SELECT CAST(' -2147483649' AS
INTEGER)", true),
+
+
+ // SMALLINT
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(32767.1 AS
SMALLINT)", false),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(32767.5 AS
SMALLINT)", false),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(32767.5 - 1 AS
SMALLINT)", false),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(-32768.1 AS
SMALLINT)", false),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(32767 + 1 AS
SMALLINT)", true),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(32768.1 AS
SMALLINT)", true),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(CAST(32768 AS
DOUBLE) AS SMALLINT)", true),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(CAST(32768 AS
FLOAT) AS SMALLINT)", true),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(32768 AS
SMALLINT)", true),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST('32768' AS
SMALLINT)", true),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(-32769 AS
SMALLINT)", true),
+ arguments(SqlTypeName.SMALLINT, "SELECT CAST(' -32769' AS
SMALLINT)", true),
+
+
+ // TINYINT
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(127.1 AS
TINYINT)", false),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(127.5 AS
TINYINT)", false),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(127.5 - 1 AS
TINYINT)", false),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(-128.1 AS
TINYINT)", false),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(127 + 1 AS
TINYINT)", true),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(128.1 AS
TINYINT)", true),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(CAST(128 AS
DOUBLE) AS TINYINT)", true),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(CAST(128 AS FLOAT)
AS TINYINT)", true),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(128 AS TINYINT)",
true),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST('128' AS
TINYINT)", true),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(-129 AS TINYINT)",
true),
+ arguments(SqlTypeName.TINYINT, "SELECT CAST(' -129' AS
TINYINT)", true)
+ );
+ }
+
@Test
@SuppressWarnings("ThrowableNotThrown")
public void testCharTypesWithTrailingSpacesAreTrimmed() {
@@ -653,46 +746,6 @@ public class ItDataTypesTest extends
BaseSqlIntegrationTest {
);
}
- private static Stream<Arguments> decimalOverflowsValidation() {
- return Stream.of(
- // BIGINT
- arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775807.1 AS BIGINT)", false),
- arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775807.5 AS BIGINT)", true),
- arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775807.5 - 1 AS BIGINT)", false),
- arguments(SqlTypeName.BIGINT, "SELECT
CAST(9223372036854775808.1 AS BIGINT)", true),
- arguments(SqlTypeName.BIGINT, "SELECT CAST(9223372036854775808
AS BIGINT)", true),
- arguments(SqlTypeName.BIGINT, "SELECT
CAST(-9223372036854775809 AS BIGINT)", true),
- arguments(SqlTypeName.BIGINT, "SELECT
CAST(-9223372036854775808.1 AS BIGINT)", false),
-
- // INTEGER
- arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483647.1 AS
INTEGER)", false),
- arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483647.5 AS
INTEGER)", true),
- arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483647.5 - 1
AS INTEGER)", false),
- arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483648.1 AS
INTEGER)", true),
- arguments(SqlTypeName.INTEGER, "SELECT CAST(2147483648 AS
INTEGER)", true),
- arguments(SqlTypeName.INTEGER, "SELECT CAST(-2147483649 AS
INTEGER)", true),
- arguments(SqlTypeName.INTEGER, "SELECT CAST(-2147483648.1 AS
INTEGER)", false),
-
- // SMALLINT
- arguments(SqlTypeName.SMALLINT, "SELECT CAST(32767.1 AS
SMALLINT)", false),
- arguments(SqlTypeName.SMALLINT, "SELECT CAST(32767.5 AS
SMALLINT)", true),
- arguments(SqlTypeName.SMALLINT, "SELECT CAST(32767.5 - 1 AS
SMALLINT)", false),
- arguments(SqlTypeName.SMALLINT, "SELECT CAST(32768.1 AS
SMALLINT)", true),
- arguments(SqlTypeName.SMALLINT, "SELECT CAST(32768 AS
SMALLINT)", true),
- arguments(SqlTypeName.SMALLINT, "SELECT CAST(-32769 AS
SMALLINT)", true),
- arguments(SqlTypeName.SMALLINT, "SELECT CAST(-32768.1 AS
SMALLINT)", false),
-
- // TINYINT
- arguments(SqlTypeName.TINYINT, "SELECT CAST(127.1 AS
TINYINT)", false),
- arguments(SqlTypeName.TINYINT, "SELECT CAST(127.5 AS
TINYINT)", true),
- arguments(SqlTypeName.TINYINT, "SELECT CAST(127.5 - 1 AS
TINYINT)", false),
- arguments(SqlTypeName.TINYINT, "SELECT CAST(128.1 AS
TINYINT)", true),
- arguments(SqlTypeName.TINYINT, "SELECT CAST(128 AS TINYINT)",
true),
- arguments(SqlTypeName.TINYINT, "SELECT CAST(-129 AS TINYINT)",
true),
- arguments(SqlTypeName.TINYINT, "SELECT CAST(-128.1 AS
TINYINT)", false)
- );
- }
-
static String asLiteral(Object value, RelDataType type) {
if (SqlTypeUtil.isCharacter(type)) {
String str = (String) value;
diff --git
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
index c4486d51ce..cd81db432b 100644
---
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
+++
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
@@ -356,7 +356,8 @@ public class ItFunctionsTest extends BaseSqlIntegrationTest
{
assertQuery("SELECT TYPEOF('abcd' || COALESCE('efg',
?))").withParams("2").returns("VARCHAR").check();
// An expression that produces an error
- assertThrowsSqlException(Sql.STMT_PARSE_ERR, "", () -> sql("SELECT
typeof(CAST('NONE' as INTEGER))"));
+ assertThrowsSqlException(Sql.RUNTIME_ERR, "Invalid input syntax for
type INTEGER: \"NONE\"",
+ () -> sql("SELECT typeof(CAST('NONE' as INTEGER))"));
assertThrowsWithCause(() -> sql("SELECT TYPEOF()"),
SqlValidatorException.class, "Invalid number of arguments");
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 0b723c1d17..cb747047e4 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
@@ -74,7 +74,7 @@ SELECT CAST(100.1::DOUBLE AS BIGINT);
----
100
-statement error: Character x is neither a decimal digit number, decimal point,
nor "e" notation exponential mark.
+statement error: Invalid input syntax for type BIGINT: "100x"
SELECT CAST('100x' AS BIGINT);
statement error: Cast function cannot convert value of type BINARY(1) to type
BIGINT
diff --git a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_int.test
b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_int.test
index c58cd4bf9f..8788a1dfa4 100644
--- a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_int.test
+++ b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_int.test
@@ -14,7 +14,7 @@ SELECT CAST(1e1 AS INT);
----
10
-statement error: For input string: "1e2"
+statement error: Invalid input syntax for type INTEGER: "1e2"
SELECT CAST('1e2' AS INT);
# overflow
@@ -70,7 +70,7 @@ SELECT CAST(100.1::DOUBLE AS INT);
----
100
-statement error: Character x is neither a decimal digit number, decimal point,
nor "e" notation exponential mark.
+statement error: Invalid input syntax for type INTEGER: "100x"
SELECT CAST('100x' AS INT);
statement error: Cast function cannot convert value of type BINARY(1) to type
INT
diff --git
a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_smallint.test
b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_smallint.test
index 6f53d399f4..1d98d3aa4f 100644
--- a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_smallint.test
+++ b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_smallint.test
@@ -14,7 +14,7 @@ SELECT CAST(1e1 AS SMALLINT);
----
10
-statement error: For input string: "1e2"
+statement error: Invalid input syntax for type SMALLINT: "1e2"
SELECT CAST('1e2' AS SMALLINT);
# overflow
@@ -70,7 +70,7 @@ SELECT CAST(100.1::DOUBLE AS SMALLINT);
----
100
-statement error: Character x is neither a decimal digit number, decimal point,
nor "e" notation exponential mark.
+statement error: Invalid input syntax for type SMALLINT: "100x"
SELECT CAST('100x' AS SMALLINT);
statement error: Cast function cannot convert value of type BINARY(1) to type
SMALLINT
diff --git
a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_tinyint.test
b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_tinyint.test
index 76e65345ec..048b4f55a4 100644
--- a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_tinyint.test
+++ b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_tinyint.test
@@ -14,7 +14,7 @@ SELECT CAST(1e1 AS TINYINT);
----
10
-statement error: For input string: "1e2"
+statement error: Invalid input syntax for type TINYINT: "1e2"
SELECT CAST('1e2' AS TINYINT);
# overflow
@@ -70,7 +70,7 @@ SELECT CAST(100.1::DOUBLE AS TINYINT);
----
100
-statement error: Character x is neither a decimal digit number, decimal point,
nor "e" notation exponential mark.
+statement error: Invalid input syntax for type TINYINT: "100x"
SELECT CAST('100x' AS TINYINT);
statement error: Cast function cannot convert value of type BINARY(1) to type
TINYINT
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ConverterUtils.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ConverterUtils.java
index 601176a96e..a9a28218b5 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ConverterUtils.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ConverterUtils.java
@@ -30,9 +30,11 @@ import org.apache.calcite.linq4j.tree.Primitive;
import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.runtime.SqlFunctions;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.BuiltInMethod;
import org.apache.calcite.util.Util;
+import org.apache.ignite.internal.sql.engine.util.IgniteMath;
/**
* ConverterUtils.
@@ -222,31 +224,32 @@ public class ConverterUtils {
Primitive toPrimitive = Primitive.of(toType);
Primitive fromPrimitive = Primitive.of(fromType);
- // check overflow for 'integer' subtypes
- if (fromPrimitive == Primitive.LONG && toPrimitive == Primitive.INT) {
- return IgniteExpressions.convertToIntExact(operand);
- }
-
- if ((fromPrimitive == Primitive.LONG || fromPrimitive ==
Primitive.INT) && toPrimitive == Primitive.SHORT) {
- return IgniteExpressions.convertToShortExact(operand);
- }
+ boolean fromNumber = fromType instanceof Class
+ && Number.class.isAssignableFrom((Class<?>) fromType);
+ Primitive fromBox = Primitive.ofBox(fromType);
- if ((fromPrimitive == Primitive.LONG || fromPrimitive == Primitive.INT
|| fromPrimitive == Primitive.SHORT)
- && toPrimitive == Primitive.BYTE) {
- return IgniteExpressions.convertToByteExact(operand);
- }
-
- if (!Primitive.isBox(fromType)) {
- if ((fromType == BigDecimal.class || fromType == String.class) &&
toPrimitive == Primitive.LONG) {
- return IgniteExpressions.convertToLongExact(operand);
+ if (toPrimitive != null) {
+ if ((toPrimitive == Primitive.LONG || toPrimitive == Primitive.INT
|| toPrimitive == Primitive.SHORT
+ || toPrimitive == Primitive.BYTE) && fromType ==
String.class) {
+ return Expressions.call(IgniteMath.class, "convertTo"
+ + SqlFunctions.initcap(toPrimitive.primitiveName) +
"Exact", operand);
}
- if (fromType == BigDecimal.class && toPrimitive == Primitive.BYTE)
{
- return IgniteExpressions.convertToByteExact(operand);
+ if (fromPrimitive != null) {
+ // E.g. from "float" to "double"
+ return IgniteExpressions.convertChecked(operand,
fromPrimitive, toPrimitive);
}
- if (fromType == BigDecimal.class && toPrimitive ==
Primitive.SHORT) {
- return IgniteExpressions.convertToShortExact(operand);
+ if (fromNumber) {
+ // Generate "x.shortValue()".
+ return IgniteExpressions.unboxChecked(operand, fromBox,
toPrimitive);
+ } else {
+ // E.g. from "Object" to "short".
+ // Generate "SqlFunctions.toShort(x)"
+ return Expressions.call(
+ SqlFunctions.class,
+ "to" + SqlFunctions.initcap(toPrimitive.primitiveName),
+ operand);
}
}
@@ -274,12 +277,6 @@ public class ConverterUtils {
|| Primitive.ofBox(fromType) == primitive;
}
- private static boolean representAsInternalType(Type type) {
- return type == java.sql.Date.class
- || type == java.sql.Time.class
- || type == java.sql.Timestamp.class;
- }
-
/**
* In {@link org.apache.calcite.sql.type.SqlTypeAssignmentRule}, some
rules decide whether one type can be
* assignable to another type.
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 09081d6da0..99d81c1e36 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
@@ -18,11 +18,13 @@
package org.apache.ignite.internal.sql.engine.exec.exp;
import java.lang.reflect.Type;
-import java.math.BigDecimal;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.ExpressionType;
import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.linq4j.tree.Primitive;
+import org.apache.calcite.runtime.SqlFunctions;
import org.apache.ignite.internal.sql.engine.util.IgniteMath;
+import org.jetbrains.annotations.Nullable;
/** Calcite liq4j expressions customized for Ignite. */
public class IgniteExpressions {
@@ -97,49 +99,22 @@ public class IgniteExpressions {
return Expressions.makeBinary(ExpressionType.Divide, left, right);
}
- /** Generate expression for method IgniteMath.convertToIntExact(). */
- public static Expression convertToIntExact(Expression exp) {
- Type type = exp.getType();
-
- if (type == Long.TYPE || type == Long.class) {
- return Expressions.call(IgniteMath.class, "convertToIntExact",
exp);
- }
-
- return exp;
- }
-
- /** Generate expression for method IgniteMath.convertToIntExact(). */
- public static Expression convertToLongExact(Expression exp) {
- Type type = exp.getType();
-
- if (type == BigDecimal.class || type == String.class) {
- return Expressions.call(IgniteMath.class, "convertToLongExact",
exp);
+ static Expression convertChecked(Expression exp, Primitive fromPrimitive,
Primitive toPrimitive) {
+ if (fromPrimitive.ordinal() <= toPrimitive.ordinal() ||
!toPrimitive.isFixedNumeric()) {
+ return Expressions.convert_(exp, toPrimitive.primitiveClass);
}
- return exp;
+ return Expressions.call(IgniteMath.class, "convertTo"
+ + SqlFunctions.initcap(toPrimitive.primitiveName) + "Exact",
exp);
}
- /** Generate expression for method IgniteMath.convertToShortExact(). */
- public static Expression convertToShortExact(Expression exp) {
- Type type = exp.getType();
-
- if (type == Long.TYPE || type == Long.class || type == Integer.TYPE ||
type == Integer.class || type == BigDecimal.class) {
- return Expressions.call(IgniteMath.class, "convertToShortExact",
exp);
- }
-
- return exp;
- }
-
- /** Generate expression for method IgniteMath.convertToByteExact(). */
- public static Expression convertToByteExact(Expression exp) {
- Type type = exp.getType();
-
- if (type == Long.TYPE || type == Long.class || type == Integer.TYPE ||
type == Integer.class
- || type == Short.TYPE || type == Short.class || type ==
BigDecimal.class) {
- return Expressions.call(IgniteMath.class, "convertToByteExact",
exp);
+ static Expression unboxChecked(Expression exp, @Nullable Primitive
fromBox, Primitive toPrimitive) {
+ if ((fromBox != null && fromBox.ordinal() <= toPrimitive.ordinal()) ||
!toPrimitive.isFixedNumeric()) {
+ return Expressions.unbox(exp, toPrimitive);
}
- return exp;
+ return Expressions.call(IgniteMath.class, "convertTo"
+ + SqlFunctions.initcap(toPrimitive.primitiveName) + "Exact",
exp);
}
/** Generate expression for method IgniteMath.negateExact() for integer
subtypes. */
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
index c2b66f287d..832af32cf2 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
@@ -19,18 +19,15 @@ package org.apache.ignite.internal.sql.engine.prepare;
import static java.util.Objects.requireNonNull;
import static org.apache.calcite.sql.type.SqlTypeName.INTEGER;
-import static org.apache.calcite.sql.type.SqlTypeName.INT_TYPES;
import static org.apache.calcite.sql.type.SqlTypeUtil.isNull;
import static org.apache.calcite.util.Static.RESOURCE;
import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
-import static org.apache.ignite.lang.ErrorGroups.Sql.STMT_PARSE_ERR;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.math.BigDecimal;
-import java.math.RoundingMode;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
@@ -41,7 +38,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
-import java.util.regex.Pattern;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.prepare.CalciteCatalogReader;
import org.apache.calcite.prepare.Prepare;
@@ -73,9 +69,7 @@ import org.apache.calcite.sql.dialect.CalciteSqlDialect;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.sql.type.SqlTypeName.Limit;
import org.apache.calcite.sql.type.SqlTypeUtil;
-import org.apache.calcite.sql.util.SqlBasicVisitor;
import org.apache.calcite.sql.util.SqlShuttle;
import org.apache.calcite.sql.validate.SelectScope;
import org.apache.calcite.sql.validate.SqlValidator;
@@ -84,7 +78,6 @@ import org.apache.calcite.sql.validate.SqlValidatorNamespace;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.sql.validate.SqlValidatorTable;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
-import org.apache.calcite.util.Util;
import org.apache.ignite.internal.sql.engine.schema.IgniteDataSource;
import org.apache.ignite.internal.sql.engine.schema.IgniteSystemView;
import org.apache.ignite.internal.sql.engine.schema.IgniteTable;
@@ -95,7 +88,6 @@ import org.apache.ignite.internal.sql.engine.type.UuidType;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.IgniteResource;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
-import org.apache.ignite.sql.SqlException;
import org.jetbrains.annotations.Nullable;
/** Validator. */
@@ -109,9 +101,6 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
public static final String NUMERIC_FIELD_OVERFLOW_ERROR = "Numeric field
overflow";
- // Approximate and exact numeric types.
- private static final Pattern NUMERIC =
Pattern.compile("^\\s*\\d+(\\.{1}\\d*)\\s*$");
-
static {
EnumSet<SqlKind> kinds = EnumSet.noneOf(SqlKind.class);
@@ -138,9 +127,6 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
*/
private final IdentityHashMap<SqlDynamicParam, SqlDynamicParam>
dynamicParamNodes = new IdentityHashMap<>();
- /** Literal processing. */
- private final LiteralExtractor litExtractor = new LiteralExtractor();
-
/**
* Creates a validator.
*
@@ -676,71 +662,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
String typeName =
returnType.getSqlTypeName().getSpaceName();
throw newValidationError(expr,
IgniteResource.INSTANCE.invalidStringLength(typeName));
}
-
- literalCanFitType(expr, returnType);
- }
- }
- }
-
- /** Check literal can fit to declared exact numeric type, work only for
single literal. */
- private void literalCanFitType(SqlNode expr, RelDataType toType) {
- if (INT_TYPES.contains(toType.getSqlTypeName())) {
- SqlLiteral literal = litExtractor.getLiteral(expr);
-
- if (literal == null || literal.toValue() == null) {
- return;
}
-
- int precision = toType.getSqlTypeName().allowsPrec() ?
toType.getPrecision() : -1;
- int scale = toType.getSqlTypeName().allowsScale() ?
toType.getScale() : -1;
-
- BigDecimal max = (BigDecimal)
toType.getSqlTypeName().getLimit(true, Limit.OVERFLOW, false, precision, scale);
- BigDecimal min = (BigDecimal)
toType.getSqlTypeName().getLimit(false, Limit.OVERFLOW, false, precision,
scale);
-
- String litValue = requireNonNull(literal.toValue());
-
- BigDecimal litValueToDecimal = null;
-
- try {
- litValueToDecimal = new BigDecimal(litValue).setScale(0,
RoundingMode.HALF_UP);
- } catch (NumberFormatException e) {
- if (!NUMERIC.matcher(litValue).matches()) {
- throw new SqlException(STMT_PARSE_ERR, e);
- }
- }
-
- if (max.compareTo(litValueToDecimal) < 0 ||
min.compareTo(litValueToDecimal) > 0) {
- throw new SqlException(STMT_PARSE_ERR, "Value '" + litValue +
"'"
- + " out of range for type " + toType.getSqlTypeName());
- }
- }
- }
-
- private static class LiteralExtractor extends SqlBasicVisitor<SqlNode> {
- private @Nullable SqlLiteral extracted;
-
- private @Nullable SqlLiteral getLiteral(SqlNode expr) {
- extracted = null;
- try {
- expr.accept(this);
- } catch (Util.FoundOne e) {
- Util.swallow(e, null);
- }
- return extracted;
- }
-
- /** {@inheritDoc} */
- @Override
- public SqlNode visit(SqlLiteral literal) {
- extracted = extracted != null ? null : literal;
- return literal;
- }
-
- /** {@inheritDoc} */
- @Override
- public SqlNode visit(SqlDynamicParam param) {
- extracted = null;
- throw Util.FoundOne.NULL;
}
}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rex/IgniteRexBuilder.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rex/IgniteRexBuilder.java
index c9914c787d..ccb6de6b66 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rex/IgniteRexBuilder.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rex/IgniteRexBuilder.java
@@ -17,6 +17,9 @@
package org.apache.ignite.internal.sql.engine.rex;
+import static org.apache.calcite.sql.type.SqlTypeName.BIGINT;
+
+import java.math.BigInteger;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexBuilder;
@@ -43,6 +46,16 @@ public class IgniteRexBuilder extends RexBuilder {
/** {@inheritDoc} **/
@Override
public RexNode makeLiteral(@Nullable Object value, RelDataType type,
boolean allowCast, boolean trim) {
+ if (value != null && type.getSqlTypeName() == BIGINT && value
instanceof BigInteger) {
+ BigInteger bd = (BigInteger) value;
+
+ try {
+ bd.longValueExact();
+ } catch (ArithmeticException e) {
+ throw new ArithmeticException(BIGINT.getName() + " out of
range");
+ }
+ }
+
// We need to override this method because otherwise
// default implementation will call RexBuilder::guessType(@Nullable
Object value)
// for a custom data type which will then raise the following
assertion:
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 de069eb7c0..88e612389a 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMath.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMath.java
@@ -24,7 +24,9 @@ import static org.apache.calcite.sql.type.SqlTypeName.TINYINT;
import static org.apache.ignite.lang.ErrorGroups.Sql.RUNTIME_ERR;
import java.math.BigDecimal;
+import java.math.BigInteger;
import java.math.RoundingMode;
+import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.ignite.sql.SqlException;
/** Math operations with overflow checking. */
@@ -32,6 +34,13 @@ public class IgniteMath {
private static final BigDecimal UPPER_LONG =
BigDecimal.valueOf(Long.MAX_VALUE);
private static final BigDecimal LOWER_LONG =
BigDecimal.valueOf(Long.MIN_VALUE);
+ 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);
+ private static final Double UPPER_LONG_DOUBLE = (double) Long.MAX_VALUE;
+ private static final Double LOWER_LONG_DOUBLE = (double) Long.MIN_VALUE;
+ private static final Float UPPER_LONG_FLOAT = (float) Long.MAX_VALUE;
+ private static final Float LOWER_LONG_FLOAT = (float) Long.MIN_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;
@@ -262,6 +271,37 @@ public class IgniteMath {
throw new SqlException(RUNTIME_ERR, "Division by zero");
}
+ /** Cast value to {@code long}, throwing an exception if the result
overflows an {@code long}. */
+ public static long convertToLongExact(BigDecimal x) {
+ if (x.compareTo(UPPER_LONG) > 0 || x.compareTo(LOWER_LONG) < 0) {
+ throw new SqlException(RUNTIME_ERR, BIGINT.getName() + " out of
range");
+ }
+ 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) {
+ throw new ArithmeticException(BIGINT.getName() + " out of range");
+ }
+
+ return (long) x;
+ }
+
+ /** Cast value to {@code long}, throwing an exception if the result
overflows an {@code long}. */
+ public static long convertToLongExact(String x) {
+ try {
+ BigDecimal decimal = new BigDecimal(x.strip());
+ if (UPPER_LONG.compareTo(decimal.setScale(0,
RoundingMode.HALF_UP)) < 0
+ || LOWER_LONG.compareTo(decimal.setScale(0,
RoundingMode.HALF_UP)) > 0) {
+ throw new SqlException(RUNTIME_ERR, BIGINT.getName() + " out
of range");
+ }
+ return decimal.longValue();
+ } catch (NumberFormatException ex1) {
+ throw new SqlException(RUNTIME_ERR, "Invalid input syntax for type
" + BIGINT.getName() + ": \"" + x + "\"", ex1);
+ }
+ }
+
/** Cast value to {@code int}, throwing an exception if the result
overflows an {@code int}. */
public static int convertToIntExact(long x) {
if ((int) x != x) {
@@ -271,23 +311,31 @@ public class IgniteMath {
return (int) x;
}
- /** Cast value to {@code long}, throwing an exception if the result
overflows an {@code long}. */
- public static long convertToLongExact(BigDecimal x) {
- if (x.compareTo(UPPER_LONG) > 0 || x.compareTo(LOWER_LONG) < 0) {
- throw new SqlException(RUNTIME_ERR, BIGINT.getName() + " out of
range");
+ /** Cast value to {@code int}, throwing an exception if the result
overflows an {@code int}. */
+ public static int convertToIntExact(double x) {
+ if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE) {
+ throw new ArithmeticException(INTEGER.getName() + " out of range");
}
- return x.longValue();
+
+ return (int) x;
}
- /** Cast value to {@code long}, throwing an exception if the result
overflows an {@code long}. */
- public static long convertToLongExact(String x) {
- BigDecimal decimal = new BigDecimal(x);
- if (UPPER_LONG.compareTo(decimal.setScale(0, RoundingMode.HALF_UP)) < 0
- || LOWER_LONG.compareTo(decimal.setScale(0,
RoundingMode.HALF_UP)) > 0) {
- throw new SqlException(RUNTIME_ERR, BIGINT.getName() + " out of
range");
- }
+ /** Cast value to {@code int}, throwing an exception if the result
overflows an {@code int}. */
+ public static int convertToIntExact(Number x) {
+ checkNumberLongBounds(INTEGER, x);
+ return convertToIntExact(x.longValue());
+ }
- return decimal.longValue();
+ /** Cast value to {@code int}, throwing an exception if the result
overflows an {@code int}. */
+ public static int convertToIntExact(String x) {
+ try {
+ BigInteger bi = new BigInteger(x.strip());
+ return bi.intValueExact();
+ } catch (ArithmeticException ex) {
+ throw new SqlException(RUNTIME_ERR, INTEGER.getName() + " out of
range", ex);
+ } catch (NumberFormatException ex1) {
+ throw new SqlException(RUNTIME_ERR, "Invalid input syntax for type
" + INTEGER.getName() + ": \"" + x + "\"", ex1);
+ }
}
/** Cast value to {@code short}, throwing an exception if the result
overflows an {@code short}. */
@@ -299,10 +347,46 @@ public class IgniteMath {
return (short) x;
}
+ /** Cast value to {@code short}, throwing an exception if the result
overflows an {@code short}. */
+ public static short convertToShortExact(Number x) {
+ checkNumberLongBounds(SMALLINT, x);
+ return convertToShortExact(x.longValue());
+ }
+
+ /** Cast value to {@code short}, throwing an exception if the result
overflows an {@code short}. */
+ public static short convertToShortExact(double x) {
+ if (x > Short.MAX_VALUE || x < Short.MIN_VALUE) {
+ throw new ArithmeticException(SMALLINT.getName() + " out of
range");
+ }
+
+ return (short) x;
+ }
+
+ /** Cast value to {@code short}, throwing an exception if the result
overflows an {@code short}. */
+ public static short convertToShortExact(String x) {
+ try {
+ BigInteger bi = new BigInteger(x.strip());
+ return bi.shortValueExact();
+ } catch (ArithmeticException ex) {
+ throw new SqlException(RUNTIME_ERR, SMALLINT.getName() + " out of
range", ex);
+ } catch (NumberFormatException ex1) {
+ throw new SqlException(RUNTIME_ERR, "Invalid input syntax for type
" + SMALLINT.getName() + ": \"" + x + "\"", ex1);
+ }
+ }
+
+ /** Cast value to {@code byte}, throwing an exception if the result
overflows an {@code byte}. */
+ public static byte convertToByteExact(double x) {
+ if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) {
+ throw new ArithmeticException(TINYINT.getName() + " out of range");
+ }
+
+ return (byte) x;
+ }
+
/** Cast value to {@code byte}, throwing an exception if the result
overflows an {@code byte}. */
- public static short convertToShortExact(BigDecimal x) {
- long num = x.longValue();
- return convertToShortExact(num);
+ public static byte convertToByteExact(Number x) {
+ checkNumberLongBounds(TINYINT, x);
+ return convertToByteExact(x.longValue());
}
/** Cast value to {@code byte}, throwing an exception if the result
overflows an {@code byte}. */
@@ -315,8 +399,34 @@ public class IgniteMath {
}
/** Cast value to {@code byte}, throwing an exception if the result
overflows an {@code byte}. */
- public static byte convertToByteExact(BigDecimal x) {
- long num = x.longValue();
- return convertToByteExact(num);
+ public static byte convertToByteExact(String x) {
+ try {
+ BigInteger bi = new BigInteger(x.strip());
+ return bi.byteValueExact();
+ } catch (ArithmeticException ex) {
+ throw new SqlException(RUNTIME_ERR, TINYINT.getName() + " out of
range", ex);
+ } catch (NumberFormatException ex1) {
+ throw new SqlException(RUNTIME_ERR, "Invalid input syntax for type
" + TINYINT.getName() + ": \"" + x + "\"", ex1);
+ }
+ }
+
+ 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)) {
+ return;
+ }
+ } else if (x instanceof Double) {
+ if ((((Double) x).compareTo(UPPER_LONG_DOUBLE) <= 0 && ((Double)
x).compareTo(LOWER_LONG_DOUBLE) >= 0)) {
+ return;
+ }
+ } else if (x instanceof Float) {
+ if ((((Float) x).compareTo(UPPER_LONG_FLOAT) <= 0 && ((Float)
x).compareTo(LOWER_LONG_FLOAT) >= 0)) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ throw new ArithmeticException(type.getName() + " out of range");
}
}