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 {