This is an automated email from the ASF dual-hosted git repository.
MaxGekk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new 4e5de2c0518d [SPARK-57577][SQL] Surface timestamp constructor overflow
as Spark error condition
4e5de2c0518d is described below
commit 4e5de2c0518d68fc73410338768a29ec075738c8
Author: Mitchell <[email protected]>
AuthorDate: Thu Jul 2 12:25:46 2026 +0200
[SPARK-57577][SQL] Surface timestamp constructor overflow as Spark error
condition
### What changes were proposed in this pull request?
This PR routes timestamp constructor overflow through `DATETIME_OVERFLOW`
instead of raw `ArithmeticException` for overflowing `timestamp_seconds` and
`timestamp_millis` inputs.
It adds a shared `QueryExecutionErrors` helper for timestamp constructor
overflow, uses it from interpreted and code-generated expression paths, and
keeps the existing non-overflow decimal rounding behavior unchanged.
`timestamp_micros` already accepts microseconds directly and has no
unit-scaling overflow path, so this PR preserves its existing max/min input
behavior.
### Why are the changes needed?
The existing overflow paths can surface raw Java arithmetic errors such as
`long overflow` or `Overflow`, which do not carry a Spark error condition or
SQLSTATE. These timestamp constructor overflow cases should report a Spark
error condition, consistent with other datetime overflow handling.
### Does this PR introduce _any_ user-facing change?
Yes. Overflowing `timestamp_seconds` and `timestamp_millis` inputs now fail
with `DATETIME_OVERFLOW` / SQLSTATE `22008` instead of a raw
`ArithmeticException`.
### How was this patch tested?
```
./build/sbt "catalyst/testOnly
org.apache.spark.sql.catalyst.expressions.DateExpressionsSuite -- -z
TIMESTAMP_SECONDS -z TIMESTAMP_MILLIS -z TIMESTAMP_MICROS"
./build/sbt "sql/testOnly
org.apache.spark.sql.errors.QueryExecutionErrorsSuite -- -z constructors"
./build/sbt "sql/testOnly org.apache.spark.sql.SQLQueryTestSuite -- -z
timestamp.sql -z timestamp-ansi.sql -z datetime-legacy.sql"
```
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: OpenAI Codex
Closes #56906 from
michaelmitchell-bit/SPARK-57577-timestamp-function-overflow.
Authored-by: Mitchell <[email protected]>
Signed-off-by: Max Gekk <[email protected]>
---
.../catalyst/expressions/datetimeExpressions.scala | 102 +++++++++++++++++++--
.../spark/sql/errors/QueryExecutionErrors.scala | 13 +++
.../expressions/DateExpressionsSuite.scala | 23 +++--
.../sql-tests/results/datetime-legacy.sql.out | 40 ++++++--
.../sql-tests/results/nonansi/timestamp.sql.out | 40 ++++++--
.../resources/sql-tests/results/timestamp.sql.out | 40 ++++++--
.../results/timestampNTZ/timestamp-ansi.sql.out | 40 ++++++--
.../results/timestampNTZ/timestamp.sql.out | 40 ++++++--
.../sql/errors/QueryExecutionErrorsSuite.scala | 22 +++++
9 files changed, 306 insertions(+), 54 deletions(-)
diff --git
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala
index 0e557d1be9d8..e7c6a1091861 100644
---
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala
+++
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala
@@ -629,19 +629,36 @@ abstract class IntegralToTimestampBase extends
UnaryExpression
protected def upScaleFactor: Long
+ protected def inputUnit: String
+
override def inputTypes: Seq[AbstractDataType] = Seq(IntegralType)
override def dataType: DataType = TimestampType
override def nullSafeEval(input: Any): Any = {
- Math.multiplyExact(input.asInstanceOf[Number].longValue(), upScaleFactor)
+ try {
+ Math.multiplyExact(input.asInstanceOf[Number].longValue(), upScaleFactor)
+ } catch {
+ case _: ArithmeticException =>
+ throw QueryExecutionErrors.timestampConstructorOverflowError(
+ input, child.dataType, inputUnit)
+ }
}
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode):
ExprCode = {
if (upScaleFactor == 1) {
defineCodeGen(ctx, ev, c => c)
} else {
- defineCodeGen(ctx, ev, c => s"java.lang.Math.multiplyExact($c,
${upScaleFactor}L)")
+ val dataType = ctx.addReferenceObj("dataType", child.dataType,
classOf[DataType].getName)
+ val errors = QueryExecutionErrors.getClass.getName.stripSuffix("$")
+ nullSafeCodeGen(ctx, ev, c =>
+ s"""
+ |try {
+ | ${ev.value} = java.lang.Math.multiplyExact($c,
${upScaleFactor}L);
+ |} catch (java.lang.ArithmeticException e) {
+ | throw $errors.timestampConstructorOverflowError($c, $dataType,
"$inputUnit");
+ |}
+ |""".stripMargin)
}
}
}
@@ -675,10 +692,11 @@ case class SecondsToTimestamp(child: Expression) extends
UnaryExpression
@transient
private lazy val evalFunc: Any => Any = child.dataType match {
case _: IntegralType => input =>
- Math.multiplyExact(input.asInstanceOf[Number].longValue(),
MICROS_PER_SECOND)
+ withOverflowError(input) {
+ Math.multiplyExact(input.asInstanceOf[Number].longValue(),
MICROS_PER_SECOND)
+ }
case _: DecimalType => input =>
- val operand = new java.math.BigDecimal(MICROS_PER_SECOND)
-
input.asInstanceOf[Decimal].toJavaBigDecimal.multiply(operand).longValueExact()
+ decimalToMicros(input.asInstanceOf[Decimal])
case _: FloatType => input =>
val f = input.asInstanceOf[Float]
if (f.isNaN || f.isInfinite) null else (f.toDouble *
MICROS_PER_SECOND).toLong
@@ -687,14 +705,64 @@ case class SecondsToTimestamp(child: Expression) extends
UnaryExpression
if (d.isNaN || d.isInfinite) null else (d * MICROS_PER_SECOND).toLong
}
+ private def withOverflowError(input: Any)(f: => Long): Long = {
+ try {
+ f
+ } catch {
+ case _: ArithmeticException =>
+ throw QueryExecutionErrors.timestampConstructorOverflowError(
+ input, child.dataType, "seconds")
+ }
+ }
+
+ private def decimalToMicros(input: Decimal): Long = {
+ val operand = new java.math.BigDecimal(MICROS_PER_SECOND)
+ val micros = input.toJavaBigDecimal.multiply(operand)
+ try {
+ micros.longValueExact()
+ } catch {
+ case e: ArithmeticException if isOutsideLongRange(micros) =>
+ throw QueryExecutionErrors.timestampConstructorOverflowError(
+ input, child.dataType, "seconds")
+ case e: ArithmeticException =>
+ throw e
+ }
+ }
+
+ private def isOutsideLongRange(value: java.math.BigDecimal): Boolean = {
+ value.compareTo(java.math.BigDecimal.valueOf(Long.MinValue)) < 0 ||
+ value.compareTo(java.math.BigDecimal.valueOf(Long.MaxValue)) > 0
+ }
+
override def nullSafeEval(input: Any): Any = evalFunc(input)
override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode =
child.dataType match {
case _: IntegralType =>
- defineCodeGen(ctx, ev, c => s"java.lang.Math.multiplyExact($c,
${MICROS_PER_SECOND}L)")
+ timestampSecondsCodeGen(ctx, ev) { c =>
+ s"${ev.value} = java.lang.Math.multiplyExact($c,
${MICROS_PER_SECOND}L);"
+ }
case _: DecimalType =>
val operand = s"new java.math.BigDecimal($MICROS_PER_SECOND)"
- defineCodeGen(ctx, ev, c =>
s"$c.toJavaBigDecimal().multiply($operand).longValueExact()")
+ val dataType = ctx.addReferenceObj("dataType", child.dataType,
classOf[DataType].getName)
+ val minLong = ctx.addReferenceObj("minLong",
+ java.math.BigDecimal.valueOf(Long.MinValue),
classOf[java.math.BigDecimal].getName)
+ val maxLong = ctx.addReferenceObj("maxLong",
+ java.math.BigDecimal.valueOf(Long.MaxValue),
classOf[java.math.BigDecimal].getName)
+ val errors = QueryExecutionErrors.getClass.getName.stripSuffix("$")
+ nullSafeCodeGen(ctx, ev, c => {
+ val micros = ctx.freshName("micros")
+ s"""
+ |java.math.BigDecimal $micros =
$c.toJavaBigDecimal().multiply($operand);
+ |try {
+ | ${ev.value} = $micros.longValueExact();
+ |} catch (java.lang.ArithmeticException e) {
+ | if ($micros.compareTo($minLong) < 0 ||
$micros.compareTo($maxLong) > 0) {
+ | throw $errors.timestampConstructorOverflowError($c, $dataType,
"seconds");
+ | }
+ | throw e;
+ |}
+ |""".stripMargin
+ })
case other =>
val castToDouble = if (other.isInstanceOf[FloatType]) "(double)" else ""
nullSafeCodeGen(ctx, ev, c => {
@@ -709,6 +777,22 @@ case class SecondsToTimestamp(child: Expression) extends
UnaryExpression
})
}
+ private def timestampSecondsCodeGen(
+ ctx: CodegenContext,
+ ev: ExprCode)(
+ convert: String => String): ExprCode = {
+ val dataType = ctx.addReferenceObj("dataType", child.dataType,
classOf[DataType].getName)
+ val errors = QueryExecutionErrors.getClass.getName.stripSuffix("$")
+ nullSafeCodeGen(ctx, ev, c =>
+ s"""
+ |try {
+ | ${convert(c)}
+ |} catch (java.lang.ArithmeticException e) {
+ | throw $errors.timestampConstructorOverflowError($c, $dataType,
"seconds");
+ |}
+ |""".stripMargin)
+ }
+
override def prettyName: String = "timestamp_seconds"
override protected def withNewChildInternal(newChild: Expression):
SecondsToTimestamp =
@@ -731,6 +815,8 @@ case class MillisToTimestamp(child: Expression)
override def upScaleFactor: Long = MICROS_PER_MILLIS
+ override def inputUnit: String = "milliseconds"
+
override def prettyName: String = "timestamp_millis"
override protected def withNewChildInternal(newChild: Expression):
MillisToTimestamp =
@@ -753,6 +839,8 @@ case class MicrosToTimestamp(child: Expression)
override def upScaleFactor: Long = 1L
+ override def inputUnit: String = "microseconds"
+
override def prettyName: String = "timestamp_micros"
override protected def withNewChildInternal(newChild: Expression):
MicrosToTimestamp =
diff --git
a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala
b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala
index ad426c347914..71ae98253853 100644
---
a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala
+++
b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala
@@ -2650,6 +2650,19 @@ private[sql] object QueryExecutionErrors extends
QueryErrorsBase with ExecutionE
summary = "")
}
+ def timestampConstructorOverflowError(
+ value: Any,
+ valueType: DataType,
+ unit: String): SparkArithmeticException = {
+ new SparkArithmeticException(
+ errorClass = "DATETIME_OVERFLOW",
+ messageParameters = Map(
+ "operation" ->
+ s"create a TIMESTAMP from ${toSQLValue(value, valueType)} $unit
since the epoch"),
+ context = Array.empty,
+ summary = "")
+ }
+
def timeAddIntervalOverflowError(
time: Long,
timePrecision: Int,
diff --git
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala
index 0d7a01fb2953..a9ee2a8ba476 100644
---
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala
+++
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala
@@ -1860,8 +1860,11 @@ class DateExpressionsSuite extends SparkFunSuite with
ExpressionEvalHelper {
// test integral input
testIntegralInput(testIntegralFunc)
// test overflow
- checkExceptionInExpression[ArithmeticException](
- SecondsToTimestamp(Literal(Long.MaxValue, LongType)), EmptyRow, "long
overflow")
+ checkErrorInExpression[SparkArithmeticException](
+ SecondsToTimestamp(Literal(Long.MaxValue, LongType)),
+ condition = "DATETIME_OVERFLOW",
+ parameters = Map("operation" ->
+ "create a TIMESTAMP from 9223372036854775807L seconds since the
epoch"))
def testFractionalInput(input: String): Unit = {
Seq(input.toFloat, input.toDouble, Decimal(input)).foreach { value =>
@@ -1877,9 +1880,12 @@ class DateExpressionsSuite extends SparkFunSuite with
ExpressionEvalHelper {
testFractionalInput("-1.234567")
// test overflow for decimal input
- checkExceptionInExpression[ArithmeticException](
- SecondsToTimestamp(Literal(Decimal("9".repeat(38)))), "Overflow"
- )
+ checkErrorInExpression[SparkArithmeticException](
+ SecondsToTimestamp(Literal(Decimal("9".repeat(38)))),
+ condition = "DATETIME_OVERFLOW",
+ parameters = Map("operation" -> (
+ "create a TIMESTAMP from 99999999999999999999999999999999999999BD " +
+ "seconds since the epoch")))
// test truncation error for decimal input
checkExceptionInExpression[ArithmeticException](
SecondsToTimestamp(Literal(Decimal("0.1234567"))), "Rounding necessary"
@@ -1914,8 +1920,11 @@ class DateExpressionsSuite extends SparkFunSuite with
ExpressionEvalHelper {
// test integral input
testIntegralInput(testIntegralFunc)
// test overflow
- checkExceptionInExpression[ArithmeticException](
- MillisToTimestamp(Literal(Long.MaxValue, LongType)), EmptyRow, "long
overflow")
+ checkErrorInExpression[SparkArithmeticException](
+ MillisToTimestamp(Literal(Long.MaxValue, LongType)),
+ condition = "DATETIME_OVERFLOW",
+ parameters = Map("operation" ->
+ "create a TIMESTAMP from 9223372036854775807L milliseconds since the
epoch"))
}
test("TIMESTAMP_MICROS") {
diff --git
a/sql/core/src/test/resources/sql-tests/results/datetime-legacy.sql.out
b/sql/core/src/test/resources/sql-tests/results/datetime-legacy.sql.out
index 7705d2b4d3d7..fc9813c59350 100644
--- a/sql/core/src/test/resources/sql-tests/results/datetime-legacy.sql.out
+++ b/sql/core/src/test/resources/sql-tests/results/datetime-legacy.sql.out
@@ -1808,8 +1808,14 @@ select TIMESTAMP_SECONDS(1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 1230219000123123L seconds since the
epoch"
+ }
+}
-- !query
@@ -1817,8 +1823,14 @@ select TIMESTAMP_SECONDS(-1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -1230219000123123L seconds since
the epoch"
+ }
+}
-- !query
@@ -1826,8 +1838,14 @@ select TIMESTAMP_MILLIS(92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
@@ -1835,8 +1853,14 @@ select TIMESTAMP_MILLIS(-92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
diff --git
a/sql/core/src/test/resources/sql-tests/results/nonansi/timestamp.sql.out
b/sql/core/src/test/resources/sql-tests/results/nonansi/timestamp.sql.out
index 191090f736fd..4d7786512e55 100644
--- a/sql/core/src/test/resources/sql-tests/results/nonansi/timestamp.sql.out
+++ b/sql/core/src/test/resources/sql-tests/results/nonansi/timestamp.sql.out
@@ -589,8 +589,14 @@ select TIMESTAMP_SECONDS(1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 1230219000123123L seconds since the
epoch"
+ }
+}
-- !query
@@ -598,8 +604,14 @@ select TIMESTAMP_SECONDS(-1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -1230219000123123L seconds since
the epoch"
+ }
+}
-- !query
@@ -607,8 +619,14 @@ select TIMESTAMP_MILLIS(92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
@@ -616,8 +634,14 @@ select TIMESTAMP_MILLIS(-92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
diff --git a/sql/core/src/test/resources/sql-tests/results/timestamp.sql.out
b/sql/core/src/test/resources/sql-tests/results/timestamp.sql.out
index 9a4d9bc4391d..9f99af68b390 100644
--- a/sql/core/src/test/resources/sql-tests/results/timestamp.sql.out
+++ b/sql/core/src/test/resources/sql-tests/results/timestamp.sql.out
@@ -617,8 +617,14 @@ select TIMESTAMP_SECONDS(1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 1230219000123123L seconds since the
epoch"
+ }
+}
-- !query
@@ -626,8 +632,14 @@ select TIMESTAMP_SECONDS(-1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -1230219000123123L seconds since
the epoch"
+ }
+}
-- !query
@@ -635,8 +647,14 @@ select TIMESTAMP_MILLIS(92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
@@ -644,8 +662,14 @@ select TIMESTAMP_MILLIS(-92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
diff --git
a/sql/core/src/test/resources/sql-tests/results/timestampNTZ/timestamp-ansi.sql.out
b/sql/core/src/test/resources/sql-tests/results/timestampNTZ/timestamp-ansi.sql.out
index 76552bb5b61f..f04353680b23 100644
---
a/sql/core/src/test/resources/sql-tests/results/timestampNTZ/timestamp-ansi.sql.out
+++
b/sql/core/src/test/resources/sql-tests/results/timestampNTZ/timestamp-ansi.sql.out
@@ -617,8 +617,14 @@ select TIMESTAMP_SECONDS(1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 1230219000123123L seconds since the
epoch"
+ }
+}
-- !query
@@ -626,8 +632,14 @@ select TIMESTAMP_SECONDS(-1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -1230219000123123L seconds since
the epoch"
+ }
+}
-- !query
@@ -635,8 +647,14 @@ select TIMESTAMP_MILLIS(92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
@@ -644,8 +662,14 @@ select TIMESTAMP_MILLIS(-92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
diff --git
a/sql/core/src/test/resources/sql-tests/results/timestampNTZ/timestamp.sql.out
b/sql/core/src/test/resources/sql-tests/results/timestampNTZ/timestamp.sql.out
index 11325905521e..904cffe03b44 100644
---
a/sql/core/src/test/resources/sql-tests/results/timestampNTZ/timestamp.sql.out
+++
b/sql/core/src/test/resources/sql-tests/results/timestampNTZ/timestamp.sql.out
@@ -589,8 +589,14 @@ select TIMESTAMP_SECONDS(1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 1230219000123123L seconds since the
epoch"
+ }
+}
-- !query
@@ -598,8 +604,14 @@ select TIMESTAMP_SECONDS(-1230219000123123)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -1230219000123123L seconds since
the epoch"
+ }
+}
-- !query
@@ -607,8 +619,14 @@ select TIMESTAMP_MILLIS(92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from 92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
@@ -616,8 +634,14 @@ select TIMESTAMP_MILLIS(-92233720368547758)
-- !query schema
struct<>
-- !query output
-java.lang.ArithmeticException
-long overflow
+org.apache.spark.SparkArithmeticException
+{
+ "errorClass" : "DATETIME_OVERFLOW",
+ "sqlState" : "22008",
+ "messageParameters" : {
+ "operation" : "create a TIMESTAMP from -92233720368547758L milliseconds
since the epoch"
+ }
+}
-- !query
diff --git
a/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryExecutionErrorsSuite.scala
b/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryExecutionErrorsSuite.scala
index c5b6a2335035..7eccce3fe049 100644
---
a/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryExecutionErrorsSuite.scala
+++
b/sql/core/src/test/scala/org/apache/spark/sql/errors/QueryExecutionErrorsSuite.scala
@@ -420,6 +420,28 @@ class QueryExecutionErrorsSuite
sqlState = "22008")
}
+ test("DATETIME_OVERFLOW: timestamp constructors overflow") {
+ Seq(
+ (
+ s"timestamp_seconds(CAST('${Long.MaxValue}' AS BIGINT))",
+ s"create a TIMESTAMP from ${Long.MaxValue}L seconds since the epoch"),
+ (
+ s"timestamp_millis(CAST('${Long.MaxValue}' AS BIGINT))",
+ s"create a TIMESTAMP from ${Long.MaxValue}L milliseconds since the
epoch"),
+ (
+ s"timestamp_seconds(CAST('${"9".repeat(38)}' AS DECIMAL(38, 0)))",
+ s"create a TIMESTAMP from ${"9".repeat(38)}BD seconds since the epoch")
+ ).foreach { case (expression, operation) =>
+ checkError(
+ exception = intercept[SparkArithmeticException] {
+ sql(s"select $expression").collect()
+ },
+ condition = "DATETIME_OVERFLOW",
+ parameters = Map("operation" -> operation),
+ sqlState = "22008")
+ }
+ }
+
test("CANNOT_PARSE_DECIMAL: unparseable decimal") {
val e1 = intercept[SparkException] {
withTempPath { path =>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]