From 3c43871cbf3803263427a94a255765f5c52ee387 Mon Sep 17 00:00:00 2001 From: Max Zhuravkov Date: Thu, 29 Aug 2024 16:55:41 +0300 Subject: [PATCH] IGNITE-22160: Sql. Usability of error message in case of numeric overflow (#4296) --- .../sql/cast/test_cast_decimal.test | 32 +++++++++++++++++-- .../engine/exec/exp/IgniteSqlFunctions.java | 23 +++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_decimal.test b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_decimal.test index b0761afafee..919a9cbfce6 100644 --- a/modules/sql-engine/src/integrationTest/sql/cast/test_cast_decimal.test +++ b/modules/sql-engine/src/integrationTest/sql/cast/test_cast_decimal.test @@ -17,13 +17,39 @@ SELECT CAST('1e2' AS DECIMAL); 100 # overflow -statement error + +statement error: Numeric field overflow. A field with precision 5, scale 0 must round to an absolute value less than 10^5. SELECT CAST(1e20 AS DECIMAL(5)); -# overflow -statement error +statement error: Numeric field overflow. A field with precision 5, scale 0 must round to an absolute value less than 10^5. SELECT CAST(-1e20 AS DECIMAL(5)); +statement error: Numeric field overflow. A field with precision 3, scale 5 must round to an absolute value less than 10^-2. +SELECT 0.3::DECIMAL(3, 5); + +statement error: Numeric field overflow. A field with precision 3, scale 3 must round to an absolute value less than 1. +SELECT -1::DECIMAL(3, 3); + +# the first cast produces must report an error +statement error: Numeric field overflow. A field with precision 3, scale 0 must round to an absolute value less than 10^3. +SELECT 9999999::DECIMAL(3)::DECIMAL(4, 2); + +for val in ['10000', 10000::SMALLINT, 10000::INT, 10000::BIGINT, 10000::REAL, 10000::FLOAT, 10000::DOUBLE, 10000::DECIMAL, 10000::DECIMAL(10)] + +statement error: Numeric field overflow. A field with precision 4, scale 2 must round to an absolute value less than 10^2. +SELECT ${val}::DECIMAL(4, 2); + +statement error: Numeric field overflow. A field with precision 2, scale 4 must round to an absolute value less than 10^-2. +SELECT ${val}::DECIMAL(2, 4); + +statement error: Numeric field overflow. A field with precision 2, scale 2 must round to an absolute value less than 1. +SELECT ${val}::DECIMAL(2, 2); + +statement error: Numeric field overflow. A field with precision 3, scale 0 must round to an absolute value less than 10^3. +SELECT ${val}::DECIMAL(3); + +endfor + for val in [100::TINYINT, 100::SMALLINT, 100::INT, 100::BIGINT, 100::DECIMAL, 100::DECIMAL(3)] query T diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java index 1b32ca19e03..ec41d301150 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.sql.engine.exec.exp; import static org.apache.calcite.runtime.SqlFunctions.charLength; +import static org.apache.ignite.internal.lang.IgniteStringFormatter.format; import static org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.NUMERIC_FIELD_OVERFLOW_ERROR; import static org.apache.ignite.lang.ErrorGroups.Sql.RUNTIME_ERR; @@ -368,13 +369,13 @@ private static BigDecimal processValueWithIntegralPart(Number value, int precisi BigDecimal dec = convertToBigDecimal(value); if (scale > precision) { - throw new SqlException(RUNTIME_ERR, NUMERIC_FIELD_OVERFLOW_ERROR); + throw numericOverflowError(precision, scale); } else { int currentSignificantDigits = dec.precision() - dec.scale(); int expectedSignificantDigits = precision - scale; if (currentSignificantDigits > expectedSignificantDigits) { - throw new SqlException(RUNTIME_ERR, NUMERIC_FIELD_OVERFLOW_ERROR); + throw numericOverflowError(precision, scale); } } @@ -394,7 +395,7 @@ private static BigDecimal processFractionData(Number value, int precision, int s int numPrecision = Math.min(num0.precision(), scale); if (numPrecision > precision) { - throw new SqlException(RUNTIME_ERR, NUMERIC_FIELD_OVERFLOW_ERROR); + throw numericOverflowError(precision, scale); } return num.setScale(scale, roundingMode); @@ -417,6 +418,22 @@ private static BigDecimal convertToBigDecimal(Number value) { return dec; } + private static SqlException numericOverflowError(int precision, int scale) { + String maxVal; + + if (precision == scale) { + maxVal = "1"; + } else { + maxVal = format("10^{}", precision - scale); + } + + String detail = format("A field with precision {}, scale {} must round to an absolute value less than {}.", + precision, scale, maxVal + ); + + throw new SqlException(RUNTIME_ERR, NUMERIC_FIELD_OVERFLOW_ERROR + ". " + detail); + } + /** CAST(VARCHAR AS VARBINARY). */ public static ByteString toByteString(String s) { return s == null ? null : new ByteString(s.getBytes(Commons.typeFactory().getDefaultCharset()));