Skip to content

Commit a7990f3

Browse files
committed
[SPARK-55755][SQL][TESTS] Handle null ArithmeticException message on JDK 25
### What changes were proposed in this pull request? On JDK 25, `Math.multiplyExact`/`Math.addExact` may throw `ArithmeticException` without a message (null instead of `"long overflow"`). This causes `NullPointerException` in tests that check the raw exception message via `.getMessage.contains(...)`. This PR: 1. **`MathUtils.withOverflow`**: Provides a fallback message `"Overflow"` when the raw JDK exception message is null, preventing null propagation into Spark error conditions (both interpreted and codegen paths). 2. **Test assertions**: Makes raw `ArithmeticException` message checks null-safe across: - `DateExpressionsSuite` (line 1825) - `CatalystTypeConvertersSuite` (line 305) - `DateTimeUtilsSuite` (line 835) - `IntervalUtilsSuite` (line 593) - `TimestampFormatterSuite` (lines 451, 453) ### Why are the changes needed? The JDK 25 scheduled CI build fails with: ``` java.lang.NullPointerException: Cannot invoke "String.contains(java.lang.CharSequence)" because "$org_scalatest_assert_macro_left" is null at DateExpressionsSuite.scala:1825 ``` See: https://github.com/apache/spark/actions/runs/22513372344/job/65226962993 ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? Existing tests pass on JDK 17. The `MathUtils.withOverflow` fix ensures non-null messages regardless of JDK version. ### Was this patch authored or co-authored using generative AI tooling? Yes, co-authored with GitHub Copilot. Closes #54553 from yaooqinn/fix-jdk25-long-overflow. Authored-by: Kent Yao <kentyao@microsoft.com> Signed-off-by: Kent Yao <kentyao@microsoft.com>
1 parent 730d375 commit a7990f3

6 files changed

Lines changed: 16 additions & 8 deletions

File tree

sql/api/src/main/scala/org/apache/spark/sql/catalyst/util/MathUtils.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ object MathUtils {
9494
f
9595
} catch {
9696
case e: ArithmeticException =>
97-
throw ExecutionErrors.arithmeticOverflowError(e.getMessage, hint, context)
97+
// On JDK 25+, Math.*Exact may throw ArithmeticException without a message
98+
val message = if (e.getMessage != null) e.getMessage else "Overflow"
99+
throw ExecutionErrors.arithmeticOverflowError(message, hint, context)
98100
}
99101
}
100102

@@ -103,7 +105,8 @@ object MathUtils {
103105
|try {
104106
| $evalCode
105107
|} catch (ArithmeticException e) {
106-
| throw QueryExecutionErrors.arithmeticOverflowError(e.getMessage(), "", $context);
108+
| String msg = e.getMessage() != null ? e.getMessage() : "Overflow";
109+
| throw QueryExecutionErrors.arithmeticOverflowError(msg, "", $context);
107110
|}
108111
|""".stripMargin
109112
}

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/CatalystTypeConvertersSuite.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@ class CatalystTypeConvertersSuite extends SparkFunSuite with SQLHelper {
302302
val errMsg = intercept[ArithmeticException] {
303303
IntervalUtils.durationToMicros(Duration.ofSeconds(Long.MaxValue, Long.MaxValue))
304304
}.getMessage
305-
assert(errMsg.contains("long overflow"))
305+
// On JDK 25+, Math.multiplyExact may throw ArithmeticException without a message
306+
assert(errMsg == null || errMsg.contains("long overflow"))
306307
}
307308

308309
test("SPARK-35726: Truncate java.time.Duration by fields of day-time interval type") {

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1822,7 +1822,8 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
18221822
null)
18231823
}.getCause
18241824
assert(e.isInstanceOf[ArithmeticException])
1825-
assert(e.getMessage.contains("long overflow"))
1825+
// On JDK 25+, Math.multiplyExact may throw ArithmeticException without a message
1826+
assert(e.getMessage == null || e.getMessage.contains("long overflow"))
18261827

18271828
checkEvaluation(
18281829
TimestampAddInterval(

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,8 @@ class DateTimeUtilsSuite extends SparkFunSuite with Matchers with SQLHelper {
832832
val msg = intercept[ArithmeticException] {
833833
DateTimeUtils.localDateTimeToMicros(dt)
834834
}.getMessage
835-
assert(msg == "long overflow")
835+
// On JDK 25+, Math.multiplyExact may throw ArithmeticException without a message
836+
assert(msg == null || msg == "long overflow")
836837
}
837838
}
838839

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,8 @@ class IntervalUtilsSuite extends SparkFunSuite with SQLHelper {
590590
val errMsg = intercept[ArithmeticException] {
591591
durationToMicros(Duration.ofDays(106751991 + 1))
592592
}.getMessage
593-
assert(errMsg.contains("long overflow"))
593+
// On JDK 25+, Math.multiplyExact may throw ArithmeticException without a message
594+
assert(errMsg == null || errMsg.contains("long overflow"))
594595
}
595596

596597
test("SPARK-34615: period to months") {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -448,9 +448,10 @@ class TimestampFormatterSuite extends DatetimeFormatterSuite {
448448
assert(formatter.parse("294247") === date(294247))
449449
assert(formatter.parse("-290307") === date(-290307))
450450
val e1 = intercept[ArithmeticException](formatter.parse("294248"))
451-
assert(e1.getMessage === "long overflow")
451+
// On JDK 25+, Math.multiplyExact may throw ArithmeticException without a message
452+
assert(e1.getMessage == null || e1.getMessage === "long overflow")
452453
val e2 = intercept[ArithmeticException](formatter.parse("-290308"))
453-
assert(e2.getMessage === "long overflow")
454+
assert(e2.getMessage == null || e2.getMessage === "long overflow")
454455
}
455456

456457
test("SPARK-36418: default parsing w/o pattern") {

0 commit comments

Comments
 (0)