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

alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 5f01df4fd76 IGNITE-16181 SQL Calcite: Handle arithmetic overflow 
(#11404)
5f01df4fd76 is described below

commit 5f01df4fd769e530fcdf900c2d8abe2fe9923088
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Fri Jun 28 10:09:20 2024 +0300

    IGNITE-16181 SQL Calcite: Handle arithmetic overflow (#11404)
    
    Co-authored-by: Ilhom Ulmasov <[email protected]>
    Co-authored-by: Ivan Dashchinskiy <[email protected]>
---
 .../query/calcite/exec/exp/ConverterUtils.java     |  23 +-
 .../query/calcite/exec/exp/IgniteExpressions.java  | 134 ++++++++
 .../query/calcite/exec/exp/IgniteRexBuilder.java   |  18 +-
 .../query/calcite/exec/exp/RexImpTable.java        |  12 +-
 .../query/calcite/exec/rel/RootNode.java           |   2 +-
 .../query/calcite/prepare/IgniteSqlValidator.java  |  20 ++
 .../query/calcite/sql/IgniteSqlDecimalLiteral.java |  37 +++
 .../processors/query/calcite/util/IgniteMath.java  | 348 +++++++++++++++++++++
 .../query/calcite/integration/DataTypesTest.java   | 135 ++++++++
 .../query/calcite/integration/IntervalTest.java    |  14 +
 .../integration/TableDmlIntegrationTest.java       |  49 +++
 11 files changed, 772 insertions(+), 20 deletions(-)

diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ConverterUtils.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ConverterUtils.java
index 4f18bd6f5e2..0beb1197011 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ConverterUtils.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ConverterUtils.java
@@ -21,7 +21,6 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
-
 import org.apache.calcite.adapter.enumerable.RexImpTable;
 import org.apache.calcite.linq4j.tree.ConstantExpression;
 import org.apache.calcite.linq4j.tree.ConstantUntypedNull;
@@ -221,14 +220,14 @@ public class ConverterUtils {
         final Primitive fromBox = Primitive.ofBox(fromType);
         final Primitive fromPrimitive = Primitive.of(fromType);
         final boolean fromNumber = fromType instanceof Class
-            && Number.class.isAssignableFrom((Class)fromType);
+            && Number.class.isAssignableFrom((Class<?>)fromType);
         if (fromType == String.class) {
             if (toPrimitive != null) {
+                if (toPrimitive.isFixedNumeric())
+                    return IgniteExpressions.parseStringChecked(operand, 
toPrimitive);
+
                 switch (toPrimitive) {
                     case CHAR:
-                    case SHORT:
-                    case INT:
-                    case LONG:
                     case FLOAT:
                     case DOUBLE:
                         // Generate "SqlFunctions.toShort(x)".
@@ -245,6 +244,9 @@ public class ConverterUtils {
                 }
             }
             if (toBox != null) {
+                if (toBox.isFixedNumeric())
+                    operand = IgniteExpressions.parseStringChecked(operand, 
toBox);
+
                 switch (toBox) {
                     case CHAR:
                         // Generate "SqlFunctions.toCharBoxed(x)".
@@ -277,12 +279,11 @@ public class ConverterUtils {
 
             if (fromPrimitive != null) {
                 // E.g. from "float" to "double"
-                return Expressions.convert_(
-                    operand, toPrimitive.primitiveClass);
+                return IgniteExpressions.convertChecked(operand, 
fromPrimitive, toPrimitive);
             }
-            if (fromNumber || fromBox == Primitive.CHAR) {
+            if (fromNumber) {
                 // Generate "x.shortValue()".
-                return Expressions.unbox(operand, toPrimitive);
+                return IgniteExpressions.unboxChecked(operand, fromBox, 
toPrimitive);
             }
             else {
                 // E.g. from "Object" to "short".
@@ -300,7 +301,7 @@ public class ConverterUtils {
                 Expressions.equal(operand, RexImpTable.NULL_EXPR),
                 RexImpTable.NULL_EXPR,
                 Expressions.box(
-                    Expressions.unbox(operand, toBox),
+                    IgniteExpressions.unboxChecked(operand, fromBox, toBox),
                     toBox));
         }
         else if (fromPrimitive != null && toBox != null) {
@@ -322,7 +323,7 @@ public class ConverterUtils {
             // Convert it first and generate "Byte.valueOf((byte)x)"
             // Because there is no method "Byte.valueOf(int)" in Byte
             return Expressions.box(
-                Expressions.convert_(operand, toBox.primitiveClass),
+                IgniteExpressions.convertChecked(operand, fromPrimitive, 
toBox),
                 toBox);
         }
         // Convert datetime types to internal storage type:
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteExpressions.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteExpressions.java
new file mode 100644
index 00000000000..895c179ae38
--- /dev/null
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteExpressions.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.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.processors.query.calcite.util.IgniteMath;
+import org.jetbrains.annotations.Nullable;
+
+/** 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) {
+        switch (binaryType) {
+            case Add:
+                return addExact(left, right);
+            case Subtract:
+                return subtractExact(left, right);
+            case Multiply:
+                return multiplyExact(left, right);
+            case Divide:
+                return divideExact(left, right);
+            default:
+                return Expressions.makeBinary(binaryType, left, right);
+        }
+    }
+
+    /** Make unary expression with arithmetic operations override. */
+    public static Expression makeUnary(ExpressionType unaryType, Expression 
operand) {
+        switch (unaryType) {
+            case Negate:
+            case NegateChecked:
+                return negateExact(unaryType, operand);
+            default:
+                return Expressions.makeUnary(unaryType, operand);
+        }
+    }
+
+    /** Generate expression for method IgniteMath.addExact() for integer 
subtypes. */
+    public static Expression addExact(Expression left, Expression right) {
+        if (larger(left.getType(), right.getType()).isFixedNumeric())
+            return Expressions.call(IgniteMath.class, "addExact", left, right);
+
+        return Expressions.makeBinary(ExpressionType.Add, left, right);
+    }
+
+    /** Generate expression for method IgniteMath.subtractExact() for integer 
subtypes. */
+    public static Expression subtractExact(Expression left, Expression right) {
+        if (larger(left.getType(), right.getType()).isFixedNumeric())
+            return Expressions.call(IgniteMath.class, "subtractExact", left, 
right);
+
+        return Expressions.makeBinary(ExpressionType.Subtract, left, right);
+    }
+
+    /** Generate expression for method IgniteMath.multiplyExact() for integer 
subtypes. */
+    public static Expression multiplyExact(Expression left, Expression right) {
+        if (larger(left.getType(), right.getType()).isFixedNumeric())
+            return Expressions.call(IgniteMath.class, "multiplyExact", left, 
right);
+
+        return Expressions.makeBinary(ExpressionType.Multiply, left, right);
+    }
+
+    /** Generate expression for method IgniteMath.divideExact() for integer 
subtypes. */
+    public static Expression divideExact(Expression left, Expression right) {
+        if (larger(left.getType(), right.getType()).isFixedNumeric())
+            return Expressions.call(IgniteMath.class, "divideExact", left, 
right);
+
+        return Expressions.makeBinary(ExpressionType.Divide, left, right);
+    }
+
+    /** Generate expression for method IgniteMath.negateExact() for integer 
subtypes. */
+    private static Expression negateExact(ExpressionType unaryType, Expression 
operand) {
+        assert unaryType == ExpressionType.Negate || unaryType == 
ExpressionType.NegateChecked;
+
+        Type opType = operand.getType();
+
+        if (opType == Integer.TYPE || opType == Long.TYPE || opType == 
Short.TYPE || opType == Byte.TYPE)
+            return Expressions.call(IgniteMath.class, "negateExact", operand);
+
+        return Expressions.makeUnary(unaryType, operand);
+    }
+
+    /** Generate expression for conversion from numeric primitive to numeric 
primitive with bounds check. */
+    public static Expression convertChecked(Expression exp, Primitive 
fromPrimitive, Primitive toPrimitive) {
+        if (fromPrimitive.ordinal() <= toPrimitive.ordinal() || 
!toPrimitive.isFixedNumeric())
+            return Expressions.convert_(exp, toPrimitive.primitiveClass);
+
+        return Expressions.call(IgniteMath.class, "convertTo" +
+            SqlFunctions.initcap(toPrimitive.primitiveName) + "Exact", exp);
+    }
+
+    /** Generate expression for conversion from string to numeric primitive 
with bounds check. */
+    public static Expression parseStringChecked(Expression exp, Primitive 
toPrimitive) {
+        return Expressions.call(IgniteMath.class, "convertTo" +
+            SqlFunctions.initcap(toPrimitive.primitiveName) + "Exact", 
Expressions.new_(BigDecimal.class, exp));
+    }
+
+    /** Generate expression for conversion from Number to numeric primitive 
with bounds check. */
+    public 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 Expressions.call(IgniteMath.class, "convertTo" +
+            SqlFunctions.initcap(toPrimitive.primitiveName) + "Exact", exp);
+    }
+
+    /** Find larger in type hierarchy. */
+    private static Primitive larger(Type type0, Type type1) {
+        Primitive primitive0 = Primitive.ofBoxOr(type0);
+        Primitive primitive1 = Primitive.ofBoxOr(type1);
+
+        return primitive0.ordinal() > primitive1.ordinal() ? primitive0 : 
primitive1;
+    }
+}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java
index 7d250c3fc7d..e26d249437c 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java
@@ -24,6 +24,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
 import org.checkerframework.checker.nullness.qual.Nullable;
 
@@ -36,8 +37,21 @@ public class IgniteRexBuilder extends RexBuilder {
 
     /** {@inheritDoc} */
     @Override protected RexLiteral makeLiteral(@Nullable Comparable o, 
RelDataType type, SqlTypeName typeName) {
-        if (o != null && typeName == SqlTypeName.DECIMAL && 
TypeUtils.hasScale(type))
-            return super.makeLiteral(((BigDecimal)o).setScale(type.getScale(), 
RoundingMode.HALF_UP), type, typeName);
+        if (o != null && typeName == SqlTypeName.DECIMAL) {
+            BigDecimal bd = (BigDecimal)o;
+
+            if (type.getSqlTypeName() == SqlTypeName.BIGINT) {
+                try {
+                    bd.longValueExact();
+                }
+                catch (ArithmeticException e) {
+                    throw new IgniteSQLException(SqlTypeName.BIGINT.getName() 
+ " overflow", e);
+                }
+            }
+
+            if (TypeUtils.hasScale(type))
+                return super.makeLiteral(bd.setScale(type.getScale(), 
RoundingMode.HALF_UP), type, typeName);
+        }
 
         return super.makeLiteral(o, type, typeName);
     }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexImpTable.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexImpTable.java
index 05f085ce273..e52342a4320 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexImpTable.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexImpTable.java
@@ -1244,7 +1244,7 @@ public class RexImpTable {
                         argValueList);
                 }
             }
-            return Expressions.makeBinary(expressionType,
+            return IgniteExpressions.makeBinary(expressionType,
                 argValueList.get(0), argValueList.get(1));
         }
 
@@ -1312,7 +1312,7 @@ public class RexImpTable {
                 && null != backupMethodName)
                 e = Expressions.call(argVal, backupMethodName);
             else
-                e = Expressions.makeUnary(expressionType, argVal);
+                e = IgniteExpressions.makeUnary(expressionType, argVal);
 
             if (e.type.equals(argVal.type))
                 return e;
@@ -1771,10 +1771,10 @@ public class RexImpTable {
                                 case INTERVAL_MINUTE:
                                 case INTERVAL_MINUTE_SECOND:
                                 case INTERVAL_SECOND:
-                                    trop1 = Expressions.convert_(
+                                    trop1 = IgniteExpressions.convertChecked(
                                         Expressions.divide(trop1,
                                             
Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
-                                        int.class);
+                                        Primitive.of(long.class), 
Primitive.of(int.class));
                             }
                     }
                     break;
@@ -1813,9 +1813,9 @@ public class RexImpTable {
                 case INTERVAL_SECOND:
                     switch (call.getKind()) {
                         case MINUS:
-                            return normalize(typeName, 
Expressions.subtract(trop0, trop1));
+                            return normalize(typeName, 
IgniteExpressions.subtractExact(trop0, trop1));
                         default:
-                            return normalize(typeName, Expressions.add(trop0, 
trop1));
+                            return normalize(typeName, 
IgniteExpressions.addExact(trop0, trop1));
                     }
 
                 default:
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/RootNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/RootNode.java
index 5449e19eb38..891cff97c79 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/RootNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/RootNode.java
@@ -327,6 +327,6 @@ public class RootNode<Row> extends AbstractNode<Row> 
implements SingleNode<Row>,
         if (e instanceof IgniteSQLException)
             throw (IgniteSQLException)e;
         else
-            throw new IgniteSQLException("An error occurred while query 
executing.", IgniteQueryErrorCode.UNKNOWN, e);
+            throw new IgniteSQLException("An error occurred while query 
executing - " + e.getMessage(), IgniteQueryErrorCode.UNKNOWN, e);
     }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
index 76345aa6895..2565a7bdfd1 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
@@ -43,6 +43,7 @@ import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlMerge;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlNumericLiteral;
 import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.SqlSelect;
 import org.apache.calcite.sql.SqlUpdate;
@@ -67,6 +68,7 @@ import org.apache.ignite.internal.processors.query.QueryUtils;
 import 
org.apache.ignite.internal.processors.query.calcite.schema.CacheTableDescriptor;
 import 
org.apache.ignite.internal.processors.query.calcite.schema.IgniteCacheTable;
 import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
+import 
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlDecimalLiteral;
 import 
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
 import org.apache.ignite.internal.processors.query.calcite.util.IgniteResource;
 import org.apache.ignite.internal.util.typedef.F;
@@ -575,4 +577,22 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
         else
             super.inferUnknownTypes(inferredType, scope, node);
     }
+
+    /** {@inheritDoc} */
+    @Override public SqlLiteral resolveLiteral(SqlLiteral literal) {
+        if (literal instanceof SqlNumericLiteral && 
literal.createSqlType(typeFactory).getSqlTypeName() == SqlTypeName.BIGINT) {
+            BigDecimal bd = literal.getValueAs(BigDecimal.class);
+
+            if (bd.scale() == 0) {
+                try {
+                    bd.longValueExact();
+                }
+                catch (ArithmeticException e) {
+                    return new 
IgniteSqlDecimalLiteral((SqlNumericLiteral)literal);
+                }
+            }
+        }
+
+        return super.resolveLiteral(literal);
+    }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlDecimalLiteral.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlDecimalLiteral.java
new file mode 100644
index 00000000000..20a010e38db
--- /dev/null
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlDecimalLiteral.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.internal.processors.query.calcite.sql;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.sql.SqlNumericLiteral;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+/**
+ * Class for numeric literal with exact value out of valid long type range.
+ */
+public class IgniteSqlDecimalLiteral extends SqlNumericLiteral {
+    /** */
+    public IgniteSqlDecimalLiteral(SqlNumericLiteral lit) {
+        super(lit.bigDecimalValue(), lit.getPrec(), lit.getScale(), 
lit.isExact(), lit.getParserPosition());
+    }
+
+    /** {@inheritDoc} */
+    @Override public RelDataType createSqlType(RelDataTypeFactory typeFactory) 
{
+        return typeFactory.createSqlType(SqlTypeName.DECIMAL, 
bigDecimalValue().precision());
+    }
+}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteMath.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteMath.java
new file mode 100644
index 00000000000..f5350d1c18c
--- /dev/null
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteMath.java
@@ -0,0 +1,348 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.util;
+
+import java.math.BigDecimal;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+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;
+import static org.apache.calcite.sql.type.SqlTypeName.TINYINT;
+
+/** Math operations with overflow checking. */
+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);
+
+    /** */
+    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;
+
+        if (((x ^ r) & (y ^ r)) < 0)
+            throw new ArithmeticException(BIGINT.getName() + " overflow");
+
+        return r;
+    }
+
+    /** Returns the sum of its arguments, throwing an exception if the result 
overflows an {@code int}. */
+    public static int addExact(int x, int y) {
+        int r = x + y;
+
+        if (((x ^ r) & (y ^ r)) < 0)
+            throw new ArithmeticException(INTEGER.getName() + " overflow");
+
+        return r;
+    }
+
+    /** Returns the sum of its arguments, throwing an exception if the result 
overflows an {@code short}. */
+    public static short addExact(short x, short y) {
+        int r = x + y;
+
+        if (r != (short)r)
+            throw new ArithmeticException(SMALLINT.getName() + " overflow");
+
+        return (short)r;
+    }
+
+    /** Returns the sum of its arguments, throwing an exception if the result 
overflows an {@code byte}. */
+    public static byte addExact(byte x, byte y) {
+        int r = x + y;
+
+        if (r != (byte)r)
+            throw new ArithmeticException(TINYINT.getName() + " overflow");
+
+        return (byte)r;
+    }
+
+    /** 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;
+
+        if (x != 0 && x == res)
+            throw new ArithmeticException(BIGINT.getName() + " overflow");
+
+        return res;
+    }
+
+    /** Returns the negation of the argument, throwing an exception if the 
result overflows an {@code int}. */
+    public static int negateExact(int x) {
+        int res = -x;
+
+        if (x != 0 && x == res)
+            throw new ArithmeticException(INTEGER.getName() + " overflow");
+
+        return res;
+    }
+
+    /** Returns the negation of the argument, throwing an exception if the 
result overflows an {@code short}. */
+    public static short negateExact(short x) {
+        int res = -x;
+
+        if (res > Short.MAX_VALUE)
+            throw new ArithmeticException(SMALLINT.getName() + " overflow");
+
+        return (short)res;
+    }
+
+    /** Returns the negation of the argument, throwing an exception if the 
result overflows an {@code byte}. */
+    public static byte negateExact(byte x) {
+        int res = -x;
+
+        if (res > Byte.MAX_VALUE)
+            throw new ArithmeticException(TINYINT.getName() + " overflow");
+
+        return (byte)res;
+    }
+
+    /** Returns the difference of the arguments, throwing an exception if the 
result overflows an {@code long}.*/
+    public static long subtractExact(long x, long y) {
+        long r = x - y;
+
+        if (((x ^ y) & (x ^ r)) < 0)
+            throw new ArithmeticException(BIGINT.getName() + " overflow");
+
+        return r;
+    }
+
+    /** Returns the difference of the arguments, throwing an exception if the 
result overflows an {@code int}.*/
+    public static int subtractExact(int x, int y) {
+        int r = x - y;
+
+        if (((x ^ y) & (x ^ r)) < 0)
+            throw new ArithmeticException(INTEGER.getName() + " overflow");
+
+        return r;
+    }
+
+    /** Returns the difference of the arguments, throwing an exception if the 
result overflows an {@code short}.*/
+    public static short subtractExact(short x, short y) {
+        int r = x - y;
+
+        if (r != (short)r)
+            throw new ArithmeticException(SMALLINT.getName() + " overflow");
+
+        return (short)r;
+    }
+
+    /** Returns the difference of the arguments, throwing an exception if the 
result overflows an {@code byte}.*/
+    public static byte subtractExact(byte x, byte y) {
+        int r = x - y;
+
+        if (r != (byte)r)
+            throw new ArithmeticException(TINYINT.getName() + " overflow");
+
+        return (byte)r;
+    }
+
+    /** Returns the product of the arguments, throwing an exception if the 
result overflows an {@code long}. */
+    public static long multiplyExact(long x, long y) {
+        long r = x * y;
+        long ax = Math.abs(x);
+        long ay = Math.abs(y);
+
+        if ((ax | ay) >>> 31 != 0 && ((y != 0 && r / y != x) || (x == 
Long.MIN_VALUE && y == -1)))
+                throw new ArithmeticException(BIGINT.getName() + " overflow");
+
+        return r;
+    }
+
+    /** Returns the product of the arguments, throwing an exception if the 
result overflows an {@code int}. */
+    public static int multiplyExact(int x, int y) {
+        long r = (long)x * (long)y;
+
+        return convertToIntExact(r);
+    }
+
+    /** Returns the product of the arguments, throwing an exception if the 
result overflows an {@code short}. */
+    public static short multiplyExact(short x, short y) {
+        int r = x * y;
+
+        if (r != (short)r)
+            throw new ArithmeticException(SMALLINT.getName() + " overflow");
+
+        return (short)r;
+    }
+
+    /** Returns the product of the arguments, throwing an exception if the 
result overflows an {@code byte}. */
+    public static byte multiplyExact(byte x, byte y) {
+        int r = x * y;
+
+        if (r != (byte)r)
+            throw new ArithmeticException(TINYINT.getName() + " overflow");
+
+        return (byte)r;
+    }
+
+    /** Returns the quotient of the arguments, throwing an exception if the 
result overflows an {@code long}. */
+    public static long divideExact(long x, long y) {
+        if (y == -1)
+            return negateExact(x);
+
+        return x / y;
+    }
+
+    /** Returns the quotient of the arguments, throwing an exception if the 
result overflows an {@code int}. */
+    public static int divideExact(int x, int y) {
+        if (y == -1)
+            return negateExact(x);
+
+        return x / y;
+    }
+
+    /** Returns the quotient of the arguments, throwing an exception if the 
result overflows an {@code short}. */
+    public static short divideExact(short x, short y) {
+        if (y == -1)
+            return negateExact(x);
+
+        return (short)(x / y);
+    }
+
+    /** Returns the quotient of the arguments, throwing an exception if the 
result overflows an {@code byte}. */
+    public static byte divideExact(byte x, byte y) {
+        if (y == -1)
+            return negateExact(x);
+
+        return (byte)(x / y);
+    }
+
+    /** */
+    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() + " overflow");
+    }
+
+    /** Cast value to {@code long}, throwing an exception if the result 
overflows an {@code long}. */
+    public static long convertToLongExact(Number x) {
+        checkNumberLongBounds(BIGINT, x);
+
+        return x.longValue();
+    }
+
+    /** Cast value to {@code long}, throwing an exception if the result 
overflows an {@code long}. */
+    public static long convertToLongExact(double x) {
+        if (x > Long.MAX_VALUE || x < Long.MIN_VALUE)
+            throw new ArithmeticException(BIGINT.getName() + " overflow");
+
+        return (long)x;
+    }
+
+    /** Cast value to {@code int}, throwing an exception if the result 
overflows an {@code int}. */
+    public static int convertToIntExact(long x) {
+        int res = (int)x;
+
+        if (res != x)
+            throw new ArithmeticException(INTEGER.getName() + " overflow");
+
+        return res;
+    }
+
+    /** 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() + " overflow");
+
+        return (int)x;
+    }
+
+    /** 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());
+    }
+
+    /** Cast value to {@code short}, throwing an exception if the result 
overflows an {@code short}. */
+    public static short convertToShortExact(long x) {
+        short res = (short)x;
+
+        if (res != x)
+            throw new ArithmeticException(SMALLINT.getName() + " overflow");
+
+        return res;
+    }
+
+    /** 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() + " overflow");
+
+        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 byte}, throwing an exception if the result 
overflows an {@code byte}. */
+    public static byte convertToByteExact(long x) {
+        byte res = (byte)x;
+
+        if (res != x)
+            throw new ArithmeticException(TINYINT.getName() + " overflow");
+
+        return res;
+    }
+
+    /** 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() + " overflow");
+
+        return (byte)x;
+    }
+
+    /** Cast value to {@code byte}, throwing an exception if the result 
overflows an {@code byte}. */
+    public static byte convertToByteExact(Number x) {
+        checkNumberLongBounds(TINYINT, x);
+
+        return convertToByteExact(x.longValue());
+    }
+}
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java
index a3cf6362f24..b6dd0aa7b82 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java
@@ -466,4 +466,139 @@ public class DataTypesTest extends 
AbstractBasicIntegrationTest {
             .returns((byte)7, (short)7, 7, 7L, BigDecimal.valueOf(7), 7f, 7d)
             .check();
     }
+
+    /** */
+    @Test
+    public void testArithmeticOverflow() {
+        // BIGINT
+        assertThrows("select CAST(9223372036854775807.5 + 1 AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("select 9223372036854775807 + 1", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("select 9223372036854775807 * 2", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("select -9223372036854775808 - 1", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("select -(-9223372036854775807 - 1)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("select -CAST(-9223372036854775808 AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("select -(?)", IgniteSQLException.class, "BIGINT 
overflow", -9223372036854775808L);
+        assertThrows("select -9223372036854775808/-1", 
IgniteSQLException.class, "BIGINT overflow");
+
+        // INTEGER
+        assertThrows("select CAST(CAST(3000000000.0 + 1 AS DOUBLE) AS 
INTEGER)",
+            IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("select CAST(9223372036854775807.5 + 
9223372036854775807.5 AS INTEGER)",
+            IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("select CAST(2147483647.5 + 1 AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("select 2147483647 + 1", IgniteSQLException.class, 
"INTEGER overflow");
+        assertThrows("select 2147483647 * 2", IgniteSQLException.class, 
"INTEGER overflow");
+        assertThrows("select -2147483648 - 1", IgniteSQLException.class, 
"INTEGER overflow");
+        assertThrows("select -(-2147483647 - 1)", IgniteSQLException.class, 
"INTEGER overflow");
+        assertThrows("select -CAST(-2147483648 AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("select -(?)", IgniteSQLException.class, "INTEGER 
overflow", -2147483648);
+        assertThrows("select -2147483648/-1", IgniteSQLException.class, 
"INTEGER overflow");
+
+        // SMALLINT
+        assertThrows("select CAST(CAST(90000.0 + 1 AS FLOAT) AS SMALLINT)",
+            IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("select CAST(9223372036854775807.5 + 
9223372036854775807.5 AS SMALLINT)",
+            IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("select 32000::smallint + 1000::smallint", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("select 17000::smallint * 2::smallint", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("select -32000::smallint - 1000::smallint", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("select -(-32767::smallint - 1::smallint)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("select -CAST(-32768 AS smallint)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("select -CAST(? AS smallint)", IgniteSQLException.class, 
"SMALLINT overflow", -32768);
+        assertThrows("select -32768::smallint/-1::smallint", 
IgniteSQLException.class, "SMALLINT overflow");
+
+        // TINYINT
+        assertThrows("select CAST(CAST(200.0 + 1 AS FLOAT) AS TINYINT)",
+            IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("select CAST(9223372036854775807.5 + 
9223372036854775807.5 AS TINYINT)",
+            IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("select 2::tinyint + 127::tinyint", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("select 2::tinyint * 127::tinyint", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("select -2::tinyint - 127::tinyint", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("select -(-127::tinyint - 1::tinyint)", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("select -CAST(-128 AS tinyint)", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("select -CAST(? AS tinyint)", IgniteSQLException.class, 
"TINYINT overflow", -128);
+        assertThrows("select -128::tinyint/-1::tinyint", 
IgniteSQLException.class, "TINYINT overflow");
+    }
+
+    /** */
+    @Test
+    public void testCastDecimalOverflows() {
+        // BIGINT
+        assertQuery("SELECT CAST(9223372036854775807.1 AS 
BIGINT)").returns(9223372036854775807L).check();
+        assertQuery("SELECT CAST(9223372036854775807.9 AS 
BIGINT)").returns(9223372036854775807L).check();
+        assertQuery("SELECT CAST(9223372036854775808.9 - 1 AS 
BIGINT)").returns(9223372036854775807L).check();
+        assertThrows("SELECT CAST(9223372036854775808 AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("SELECT CAST(9223372036854775808.1 AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("SELECT CAST(-9223372036854775809 AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("SELECT CAST(-9223372036854775809.1 AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertQuery("SELECT CAST(-9223372036854775808.1 AS 
BIGINT)").returns(-9223372036854775808L).check();
+        assertQuery("SELECT CAST(-9223372036854775808.9 AS 
BIGINT)").returns(-9223372036854775808L).check();
+        assertQuery("SELECT CAST(-9223372036854775809.9 + 1 AS 
BIGINT)").returns(-9223372036854775808L).check();
+        assertQuery("SELECT CAST('9223372036854775807.1' AS 
BIGINT)").returns(9223372036854775807L).check();
+        assertQuery("SELECT CAST('9223372036854775807.9' AS 
BIGINT)").returns(9223372036854775807L).check();
+        assertThrows("SELECT CAST('9223372036854775808' AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("SELECT CAST('9223372036854775808.1' AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("SELECT CAST('-9223372036854775809' AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertThrows("SELECT CAST('-9223372036854775809.1' AS BIGINT)", 
IgniteSQLException.class, "BIGINT overflow");
+        assertQuery("SELECT CAST('-9223372036854775808.1' AS 
BIGINT)").returns(-9223372036854775808L).check();
+        assertQuery("SELECT CAST('-9223372036854775808.9' AS 
BIGINT)").returns(-9223372036854775808L).check();
+
+        // INTEGER
+        assertQuery("SELECT CAST(2147483647.1 AS 
INTEGER)").returns(2147483647).check();
+        assertQuery("SELECT CAST(2147483647.9 AS 
INTEGER)").returns(2147483647).check();
+        assertQuery("SELECT CAST(2147483648.9 - 1 AS 
INTEGER)").returns(2147483647).check();
+        assertThrows("SELECT CAST(2147483648 AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT CAST(2147483648.1 AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT CAST(-2147483649 AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT CAST(-2147483649.1 AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertQuery("SELECT CAST(-2147483648.1 AS 
INTEGER)").returns(-2147483648).check();
+        assertQuery("SELECT CAST(-2147483648.9 AS 
INTEGER)").returns(-2147483648).check();
+        assertQuery("SELECT CAST('2147483647.1' AS 
INTEGER)").returns(2147483647).check();
+        assertQuery("SELECT CAST('2147483647.9' AS 
INTEGER)").returns(2147483647).check();
+        assertThrows("SELECT CAST('2147483648' AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT CAST('2147483648.1' AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT CAST('-2147483649' AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT CAST('-2147483649.1' AS INTEGER)", 
IgniteSQLException.class, "INTEGER overflow");
+        assertQuery("SELECT CAST('-2147483648.1' AS 
INTEGER)").returns(-2147483648).check();
+        assertQuery("SELECT CAST('-2147483648.9' AS 
INTEGER)").returns(-2147483648).check();
+
+        // SMALLINT
+        assertQuery("SELECT CAST(32767.1 AS 
SMALLINT)").returns((short)32767).check();
+        assertQuery("SELECT CAST(32767.9 AS 
SMALLINT)").returns((short)32767).check();
+        assertQuery("SELECT CAST(32768.9 - 1 AS 
SMALLINT)").returns((short)32767).check();
+        assertThrows("SELECT CAST(32768 AS SMALLINT)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("SELECT CAST(32768.1 AS SMALLINT)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("SELECT CAST(-32769 AS SMALLINT)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("SELECT CAST(-32769.1 AS SMALLINT)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertQuery("SELECT CAST(-32768.1 AS 
SMALLINT)").returns((short)-32768).check();
+        assertQuery("SELECT CAST(-32768.9 AS 
SMALLINT)").returns((short)-32768).check();
+        assertQuery("SELECT CAST('32767.1' AS 
SMALLINT)").returns((short)32767).check();
+        assertQuery("SELECT CAST('32767.9' AS 
SMALLINT)").returns((short)32767).check();
+        assertThrows("SELECT CAST('32768' AS SMALLINT)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("SELECT CAST('32768.1' AS SMALLINT)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("SELECT CAST('-32769' AS SMALLINT)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertThrows("SELECT CAST('-32769.1' AS SMALLINT)", 
IgniteSQLException.class, "SMALLINT overflow");
+        assertQuery("SELECT CAST('-32768.1' AS 
SMALLINT)").returns((short)-32768).check();
+        assertQuery("SELECT CAST('-32768.9' AS 
SMALLINT)").returns((short)-32768).check();
+
+        // TINYINT
+        assertQuery("SELECT CAST(127.1 AS 
TINYINT)").returns((byte)127).check();
+        assertQuery("SELECT CAST(127.9 AS 
TINYINT)").returns((byte)127).check();
+        assertQuery("SELECT CAST(128.9 - 1 AS 
TINYINT)").returns((byte)127).check();
+        assertThrows("SELECT CAST(128 AS TINYINT)", IgniteSQLException.class, 
"TINYINT overflow");
+        assertThrows("SELECT CAST(128.1 AS TINYINT)", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("SELECT CAST(-129 AS TINYINT)", IgniteSQLException.class, 
"TINYINT overflow");
+        assertThrows("SELECT CAST(-129.1 AS TINYINT)", 
IgniteSQLException.class, "TINYINT overflow");
+        assertQuery("SELECT CAST(-128.1 AS 
TINYINT)").returns((byte)-128).check();
+        assertQuery("SELECT CAST(-128.9 AS 
TINYINT)").returns((byte)-128).check();
+        assertQuery("SELECT CAST('127.1' AS 
TINYINT)").returns((byte)127).check();
+        assertQuery("SELECT CAST('127.9' AS 
TINYINT)").returns((byte)127).check();
+        assertThrows("SELECT CAST('128' AS TINYINT)", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("SELECT CAST('128.1' AS TINYINT)", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("SELECT CAST('-129' AS TINYINT)", 
IgniteSQLException.class, "TINYINT overflow");
+        assertThrows("SELECT CAST('-129.1' AS TINYINT)", 
IgniteSQLException.class, "TINYINT overflow");
+        assertQuery("SELECT CAST('-128.1' AS 
TINYINT)").returns((byte)-128).check();
+        assertQuery("SELECT CAST('-128.9' AS 
TINYINT)").returns((byte)-128).check();
+    }
 }
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IntervalTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IntervalTest.java
index f3e94a95330..1d03d5f3633 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IntervalTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IntervalTest.java
@@ -331,6 +331,20 @@ public class IntervalTest extends 
AbstractBasicIntegrationTest {
         assertEquals(Duration.ofSeconds(31), eval("INTERVAL '1:2' MINUTE TO 
SECOND / 2"));
         assertEquals(Duration.ofSeconds(1862), eval("INTERVAL '1:2:4' HOUR TO 
SECOND / 2"));
         assertEquals(Duration.ofMillis(1862228), eval("INTERVAL '0 1:2:4.456' 
DAY TO SECOND / 2"));
+
+        // Interval range overflow
+        assertThrows("SELECT INTERVAL 5000000 MONTHS * 1000",
+            IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT DATE '2021-01-01' + INTERVAL 999999999999 DAY",
+            IgniteSQLException.class, "BIGINT overflow"); // Overflow for 
interval type (long).
+        assertThrows("SELECT DATE '2021-01-01' + INTERVAL 3000000000 DAYS",
+            IgniteSQLException.class, "INTEGER overflow"); // Overflow for 
date type (integer).
+        assertThrows("SELECT DATE '2021-01-01' + INTERVAL -999999999 YEAR",
+            IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT INTERVAL 1000000000 YEARS + INTERVAL 1 MONTH",
+            IgniteSQLException.class, "INTEGER overflow");
+        assertThrows("SELECT INTERVAL 100000000000 DAYS + INTERVAL 
100000000000 DAYS",
+            IgniteSQLException.class, "BIGINT overflow");
     }
 
     /**
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDmlIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDmlIntegrationTest.java
index 513db94d7c0..0e1e1435c83 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDmlIntegrationTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDmlIntegrationTest.java
@@ -30,6 +30,7 @@ import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.binary.BinaryObjectBuilder;
@@ -585,6 +586,54 @@ public class TableDmlIntegrationTest extends 
AbstractBasicIntegrationTest {
                 "Failed to INSERT some keys because they are already in 
cache");
     }
 
+    /** */
+    @Test
+    public void testInsertValueOverflow() {
+        List<List<Object>> args = F.asList(
+            F.asList(SqlTypeName.BIGINT.getName(), Long.MAX_VALUE, 
Long.MIN_VALUE),
+            F.asList(SqlTypeName.INTEGER.getName(), (long)Integer.MAX_VALUE, 
(long)Integer.MIN_VALUE),
+            F.asList(SqlTypeName.SMALLINT.getName(), (long)Short.MAX_VALUE, 
(long)Short.MIN_VALUE),
+            F.asList(SqlTypeName.TINYINT.getName(), (long)Byte.MAX_VALUE, 
(long)Byte.MIN_VALUE)
+        );
+
+        for (List<Object> arg : args) {
+            try {
+                String type = (String)arg.get(0);
+                long max = (Long)arg.get(1);
+                long min = (Long)arg.get(2);
+
+                sql(String.format("CREATE TABLE TEST_SOURCE (ID INT PRIMARY 
KEY, VAL %s)", type));
+                sql(String.format("CREATE TABLE TEST_DEST (ID INT PRIMARY KEY, 
VAL %s)", type));
+
+                sql("INSERT INTO TEST_SOURCE VALUES (1, 1)");
+                sql(String.format("INSERT INTO TEST_SOURCE VALUES (2, %d)", 
max));
+                sql("INSERT INTO TEST_SOURCE VALUES (3, -1)");
+                sql(String.format("INSERT INTO TEST_SOURCE VALUES (4, %d)", 
min));
+
+                BigDecimal moreThanMax = new 
BigDecimal(max).add(BigDecimal.ONE);
+
+                assertThrows(String.format("INSERT INTO TEST_DEST (ID, VAL) 
VALUES (1, %s)", moreThanMax.toString()),
+                    IgniteSQLException.class, type + " overflow");
+                assertThrows(String.format("INSERT INTO TEST_DEST (ID, VAL) 
VALUES (1, %d + 1)", max),
+                    IgniteSQLException.class, type + " overflow");
+                assertThrows(String.format("INSERT INTO TEST_DEST (ID, VAL) 
VALUES (1, %d - 1)", min),
+                    IgniteSQLException.class, type + " overflow");
+                assertThrows(String.format("INSERT INTO TEST_DEST (ID, VAL) 
VALUES (1, %d + (SELECT 1))", max),
+                    IgniteSQLException.class, type + " overflow");
+                assertThrows(String.format("INSERT INTO TEST_DEST (ID, VAL) 
VALUES (1, %d + (SELECT -1))", min),
+                    IgniteSQLException.class, type + " overflow");
+                assertThrows("INSERT INTO TEST_DEST (ID, VAL) VALUES (1, 
(SELECT SUM(VAL) FROM TEST_SOURCE WHERE VAL > 0))",
+                    IgniteSQLException.class, type + " overflow");
+                assertThrows("INSERT INTO TEST_DEST (ID, VAL) VALUES (1, 
(SELECT SUM(VAL) FROM TEST_SOURCE WHERE VAL < 0))",
+                    IgniteSQLException.class, type + " overflow");
+            }
+            finally {
+                sql("DROP TABLE TEST_SOURCE");
+                sql("DROP TABLE TEST_DEST");
+            }
+        }
+    }
+
     /** */
     private void checkDefaultValue(String sqlType, String sqlVal, Object 
expectedVal) {
         try {

Reply via email to