Skip to content

Commit 13d62dc

Browse files
AbinayaJayaprakasamdongjoon-hyun
authored andcommitted
[SPARK-50072][SQL] Handle ArithmeticException in interval parsing with large values
SPARK-50072][SQL] Handle ArithmeticException in interval parsing with large values ### What changes were proposed in this pull request? This PR will fixe SPARK-50072 where parsing intervals with large day values throws an raw java.lang.ArithmeticException instead of a user-friendly SparkArithmeticException. ### Why are the changes needed? When executing queries like 'SELECT interval 106751991 day 4 hour 0 minute 54.776 second', Spark throws a raw ArithmeticException with 'long overflow' message instead of a SparkArithmeticException with error class INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION. This will improve user experience in terms of understanding the error. ### Does this PR introduce any user-facing change? Yes. Users will now see a proper SparkArithmeticException with error class INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION instead of a raw ArithmeticException. ### How was this patch tested? - I Added unit test 'interval overflow with large day values' in IntervalUtilsSuite - Updated existing 'interval duration' test to expect SparkArithmeticException - Added SQL integration test in interval.sql -i have manually tested with spark-shell ### Was this patch authored or co-authored using generative AI tooling? No. ### What changes were proposed in this pull request? ### Why are the changes needed? ### Does this PR introduce _any_ user-facing change? ### How was this patch tested? ### Was this patch authored or co-authored using generative AI tooling? Closes #53172 from AbinayaJayaprakasam/SPARK-50072-fix. Authored-by: AbinayaJayaprakasam <jaiabiman@gmail.com> Signed-off-by: Dongjoon Hyun <dongjoon@apache.org> (cherry picked from commit 3861ba4) Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
1 parent 97a5c87 commit 13d62dc

File tree

7 files changed

+125
-28
lines changed

7 files changed

+125
-28
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/IntervalUtils.scala

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -594,14 +594,19 @@ object IntervalUtils extends SparkIntervalUtils {
594594
interval: CalendarInterval,
595595
targetUnit: TimeUnit,
596596
daysPerMonth: Int = 31): Long = {
597-
val monthsDuration = Math.multiplyExact(
598-
daysPerMonth * MICROS_PER_DAY,
599-
interval.months)
600-
val daysDuration = Math.multiplyExact(
601-
MICROS_PER_DAY,
602-
interval.days)
603-
val result = Math.addExact(interval.microseconds, Math.addExact(daysDuration, monthsDuration))
604-
targetUnit.convert(result, TimeUnit.MICROSECONDS)
597+
try {
598+
val monthsDuration = Math.multiplyExact(
599+
daysPerMonth * MICROS_PER_DAY,
600+
interval.months)
601+
val daysDuration = Math.multiplyExact(
602+
MICROS_PER_DAY,
603+
interval.days)
604+
val result = Math.addExact(interval.microseconds, Math.addExact(daysDuration, monthsDuration))
605+
targetUnit.convert(result, TimeUnit.MICROSECONDS)
606+
} catch {
607+
case _: ArithmeticException =>
608+
throw QueryExecutionErrors.withoutSuggestionIntervalArithmeticOverflowError(context = null)
609+
}
605610
}
606611

607612
/**

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/IntervalUtilsSuite.scala

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ package org.apache.spark.sql.catalyst.util
2020
import java.time.{Duration, Period}
2121
import java.util.concurrent.TimeUnit
2222

23-
import org.apache.spark.{SparkFunSuite, SparkIllegalArgumentException}
23+
import org.apache.spark.{SparkArithmeticException, SparkFunSuite, SparkIllegalArgumentException}
2424
import org.apache.spark.sql.catalyst.plans.SQLHelper
2525
import org.apache.spark.sql.catalyst.util.DateTimeConstants._
2626
import org.apache.spark.sql.catalyst.util.DateTimeUtils.millisToMicros
@@ -364,10 +364,29 @@ class IntervalUtilsSuite extends SparkFunSuite with SQLHelper {
364364
assert(duration("1 microsecond", TimeUnit.MICROSECONDS, 30) === 1)
365365
assert(duration("1 month -30 days", TimeUnit.DAYS, 31) === 1)
366366

367-
val e = intercept[ArithmeticException] {
368-
duration(s"${Integer.MAX_VALUE} month", TimeUnit.SECONDS, 31)
367+
checkError(
368+
exception = intercept[SparkArithmeticException] {
369+
duration(s"${Integer.MAX_VALUE} month", TimeUnit.SECONDS, 31)
370+
},
371+
condition = "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
372+
parameters = Map.empty
373+
)
374+
}
375+
376+
test("interval overflow with large day values") {
377+
// Test case for SPARK-50072: handling ArithmeticException during interval parsing
378+
// The value 106751991 days causes overflow when converted to microseconds
379+
def duration(s: String, unit: TimeUnit): Long = {
380+
IntervalUtils.getDuration(stringToInterval(UTF8String.fromString(s)), unit)
369381
}
370-
assert(e.getMessage.contains("overflow"))
382+
383+
checkError(
384+
exception = intercept[SparkArithmeticException] {
385+
duration("106751991 days 4 hours 0 minutes 54.776 seconds", TimeUnit.MICROSECONDS)
386+
},
387+
condition = "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
388+
parameters = Map.empty
389+
)
371390
}
372391

373392
test("negative interval") {

sql/core/src/test/resources/sql-tests/analyzer-results/interval.sql.out

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,21 @@ Project [(INTERVAL '2147483647' MONTH / 0.5) AS (INTERVAL '2147483647' MONTH / 0
6161
-- !query
6262
select interval 2147483647 day * 2
6363
-- !query analysis
64-
java.lang.ArithmeticException
65-
long overflow
64+
org.apache.spark.SparkArithmeticException
65+
{
66+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
67+
"sqlState" : "22015"
68+
}
6669

6770

6871
-- !query
6972
select interval 2147483647 day / 0.5
7073
-- !query analysis
71-
java.lang.ArithmeticException
72-
long overflow
74+
org.apache.spark.SparkArithmeticException
75+
{
76+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
77+
"sqlState" : "22015"
78+
}
7379

7480

7581
-- !query
@@ -3212,3 +3218,13 @@ SELECT width_bucket(INTERVAL '-59' MINUTE, INTERVAL -'1 01' DAY TO HOUR, INTERVA
32123218
-- !query analysis
32133219
Project [width_bucket(INTERVAL '-59' MINUTE, INTERVAL '-1 01' DAY TO HOUR, INTERVAL '1 02:03:04.001' DAY TO SECOND, cast(10 as bigint)) AS width_bucket(INTERVAL '-59' MINUTE, INTERVAL '-1 01' DAY TO HOUR, INTERVAL '1 02:03:04.001' DAY TO SECOND, 10)#xL]
32143220
+- OneRowRelation
3221+
3222+
3223+
-- !query
3224+
SELECT interval 106751991 day 4 hour 0 minute 54.776 second
3225+
-- !query analysis
3226+
org.apache.spark.SparkArithmeticException
3227+
{
3228+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
3229+
"sqlState" : "22015"
3230+
}

sql/core/src/test/resources/sql-tests/analyzer-results/nonansi/interval.sql.out

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,21 @@ Project [(INTERVAL '2147483647' MONTH / 0.5) AS (INTERVAL '2147483647' MONTH / 0
6161
-- !query
6262
select interval 2147483647 day * 2
6363
-- !query analysis
64-
java.lang.ArithmeticException
65-
long overflow
64+
org.apache.spark.SparkArithmeticException
65+
{
66+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
67+
"sqlState" : "22015"
68+
}
6669

6770

6871
-- !query
6972
select interval 2147483647 day / 0.5
7073
-- !query analysis
71-
java.lang.ArithmeticException
72-
long overflow
74+
org.apache.spark.SparkArithmeticException
75+
{
76+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
77+
"sqlState" : "22015"
78+
}
7379

7480

7581
-- !query
@@ -3212,3 +3218,13 @@ SELECT width_bucket(INTERVAL '-59' MINUTE, INTERVAL -'1 01' DAY TO HOUR, INTERVA
32123218
-- !query analysis
32133219
Project [width_bucket(INTERVAL '-59' MINUTE, INTERVAL '-1 01' DAY TO HOUR, INTERVAL '1 02:03:04.001' DAY TO SECOND, cast(10 as bigint)) AS width_bucket(INTERVAL '-59' MINUTE, INTERVAL '-1 01' DAY TO HOUR, INTERVAL '1 02:03:04.001' DAY TO SECOND, 10)#xL]
32143220
+- OneRowRelation
3221+
3222+
3223+
-- !query
3224+
SELECT interval 106751991 day 4 hour 0 minute 54.776 second
3225+
-- !query analysis
3226+
org.apache.spark.SparkArithmeticException
3227+
{
3228+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
3229+
"sqlState" : "22015"
3230+
}

sql/core/src/test/resources/sql-tests/inputs/interval.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,3 +385,8 @@ SELECT width_bucket(INTERVAL '0' YEAR, INTERVAL '0' YEAR, INTERVAL '10' YEAR, 10
385385
SELECT width_bucket(INTERVAL '-1' YEAR, INTERVAL -'1-2' YEAR TO MONTH, INTERVAL '1-2' YEAR TO MONTH, 10);
386386
SELECT width_bucket(INTERVAL '0' DAY, INTERVAL '0' DAY, INTERVAL '10' DAY, 10);
387387
SELECT width_bucket(INTERVAL '-59' MINUTE, INTERVAL -'1 01' DAY TO HOUR, INTERVAL '1 2:3:4.001' DAY TO SECOND, 10);
388+
389+
-- interval overflow with large day values (SPARK-50072)
390+
-- This should throw INTERVAL_ARITHMETIC_OVERFLOW error
391+
SELECT interval 106751991 day 4 hour 0 minute 54.776 second;
392+

sql/core/src/test/resources/sql-tests/results/interval.sql.out

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,23 @@ select interval 2147483647 day * 2
7575
-- !query schema
7676
struct<>
7777
-- !query output
78-
java.lang.ArithmeticException
79-
long overflow
78+
org.apache.spark.SparkArithmeticException
79+
{
80+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
81+
"sqlState" : "22015"
82+
}
8083

8184

8285
-- !query
8386
select interval 2147483647 day / 0.5
8487
-- !query schema
8588
struct<>
8689
-- !query output
87-
java.lang.ArithmeticException
88-
long overflow
90+
org.apache.spark.SparkArithmeticException
91+
{
92+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
93+
"sqlState" : "22015"
94+
}
8995

9096

9197
-- !query
@@ -3897,3 +3903,15 @@ SELECT width_bucket(INTERVAL '-59' MINUTE, INTERVAL -'1 01' DAY TO HOUR, INTERVA
38973903
struct<width_bucket(INTERVAL '-59' MINUTE, INTERVAL '-1 01' DAY TO HOUR, INTERVAL '1 02:03:04.001' DAY TO SECOND, 10):bigint>
38983904
-- !query output
38993905
5
3906+
3907+
3908+
-- !query
3909+
SELECT interval 106751991 day 4 hour 0 minute 54.776 second
3910+
-- !query schema
3911+
struct<>
3912+
-- !query output
3913+
org.apache.spark.SparkArithmeticException
3914+
{
3915+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
3916+
"sqlState" : "22015"
3917+
}

sql/core/src/test/resources/sql-tests/results/nonansi/interval.sql.out

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,23 @@ select interval 2147483647 day * 2
7575
-- !query schema
7676
struct<>
7777
-- !query output
78-
java.lang.ArithmeticException
79-
long overflow
78+
org.apache.spark.SparkArithmeticException
79+
{
80+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
81+
"sqlState" : "22015"
82+
}
8083

8184

8285
-- !query
8386
select interval 2147483647 day / 0.5
8487
-- !query schema
8588
struct<>
8689
-- !query output
87-
java.lang.ArithmeticException
88-
long overflow
90+
org.apache.spark.SparkArithmeticException
91+
{
92+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
93+
"sqlState" : "22015"
94+
}
8995

9096

9197
-- !query
@@ -3710,3 +3716,15 @@ SELECT width_bucket(INTERVAL '-59' MINUTE, INTERVAL -'1 01' DAY TO HOUR, INTERVA
37103716
struct<width_bucket(INTERVAL '-59' MINUTE, INTERVAL '-1 01' DAY TO HOUR, INTERVAL '1 02:03:04.001' DAY TO SECOND, 10):bigint>
37113717
-- !query output
37123718
5
3719+
3720+
3721+
-- !query
3722+
SELECT interval 106751991 day 4 hour 0 minute 54.776 second
3723+
-- !query schema
3724+
struct<>
3725+
-- !query output
3726+
org.apache.spark.SparkArithmeticException
3727+
{
3728+
"errorClass" : "INTERVAL_ARITHMETIC_OVERFLOW.WITHOUT_SUGGESTION",
3729+
"sqlState" : "22015"
3730+
}

0 commit comments

Comments
 (0)