Skip to content

Commit 8d1deee

Browse files
committed
fail casting from integral to timestamp
1 parent 1459d5b commit 8d1deee

File tree

2 files changed

+15
-18
lines changed
  • sql/catalyst/src
    • main/scala/org/apache/spark/sql/catalyst/expressions
    • test/scala/org/apache/spark/sql/catalyst/expressions

2 files changed

+15
-18
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/Cast.scala

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ object Cast {
5959
case (StringType, TimestampType) => true
6060
case (BooleanType, TimestampType) => true
6161
case (DateType, TimestampType) => true
62-
case (_: NumericType, TimestampType) => true
62+
case (_: FractionalType, TimestampType) => true
6363

6464
case (StringType, DateType) => true
6565
case (TimestampType, DateType) => true
@@ -138,10 +138,8 @@ object Cast {
138138
case (_: CalendarIntervalType, StringType) => true
139139
case (NullType, _) => true
140140

141-
// Spark supports casting between long and timestamp, please see `longToTimestamp` and
142-
// `timestampToLong` for details.
141+
// spark forbid casting from integral to timestamp, more details in [SPARK-31790]
143142
case (TimestampType, LongType) => true
144-
case (LongType, TimestampType) => true
145143

146144
case (ArrayType(fromType, fn), ArrayType(toType, tn)) =>
147145
resolvableNullability(fn, tn) && canUpCast(fromType, toType)
@@ -266,7 +264,12 @@ abstract class CastBase extends UnaryExpression with TimeZoneAwareExpression wit
266264
TypeCheckResult.TypeCheckSuccess
267265
} else {
268266
TypeCheckResult.TypeCheckFailure(
269-
s"cannot cast ${child.dataType.catalogString} to ${dataType.catalogString}")
267+
if (child.dataType.isInstanceOf[IntegralType] && dataType.isInstanceOf[TimestampType]) {
268+
s"cannot cast ${child.dataType.catalogString} to ${dataType.catalogString}," +
269+
s"please use function TIMESTAMP_SECONDS/TIMESTAMP_MILLIS/TIMESTAMP_MICROS instand"
270+
} else {
271+
s"cannot cast ${child.dataType.catalogString} to ${dataType.catalogString}"
272+
})
270273
}
271274
}
272275

@@ -425,14 +428,6 @@ abstract class CastBase extends UnaryExpression with TimeZoneAwareExpression wit
425428
buildCast[UTF8String](_, utfs => DateTimeUtils.stringToTimestamp(utfs, zoneId).orNull)
426429
case BooleanType =>
427430
buildCast[Boolean](_, b => if (b) 1L else 0)
428-
case LongType =>
429-
buildCast[Long](_, l => longToTimestamp(l))
430-
case IntegerType =>
431-
buildCast[Int](_, i => longToTimestamp(i.toLong))
432-
case ShortType =>
433-
buildCast[Short](_, s => longToTimestamp(s.toLong))
434-
case ByteType =>
435-
buildCast[Byte](_, b => longToTimestamp(b.toLong))
436431
case DateType =>
437432
buildCast[Int](_, d => epochDaysToMicros(d, zoneId))
438433
// TimestampWritable.decimalToTimestamp
@@ -453,8 +448,6 @@ abstract class CastBase extends UnaryExpression with TimeZoneAwareExpression wit
453448
if (d.isNaN || d.isInfinite) null else (d * MICROS_PER_SECOND).toLong
454449
}
455450

456-
// converting seconds to us
457-
private[this] def longToTimestamp(t: Long): Long = SECONDS.toMicros(t)
458451
// converting us to seconds
459452
private[this] def timestampToLong(ts: Long): Long = {
460453
Math.floorDiv(ts, MICROS_PER_SECOND)
@@ -1229,8 +1222,6 @@ abstract class CastBase extends UnaryExpression with TimeZoneAwareExpression wit
12291222
"""
12301223
case BooleanType =>
12311224
(c, evPrim, evNull) => code"$evPrim = $c ? 1L : 0L;"
1232-
case _: IntegralType =>
1233-
(c, evPrim, evNull) => code"$evPrim = ${longToTimeStampCode(c)};"
12341225
case DateType =>
12351226
val zoneIdClass = classOf[ZoneId]
12361227
val zid = JavaCode.global(
@@ -1277,7 +1268,6 @@ abstract class CastBase extends UnaryExpression with TimeZoneAwareExpression wit
12771268
val block = inline"new java.math.BigDecimal($MICROS_PER_SECOND)"
12781269
code"($d.toBigDecimal().bigDecimal().multiply($block)).longValue()"
12791270
}
1280-
private[this] def longToTimeStampCode(l: ExprValue): Block = code"$l * (long)$MICROS_PER_SECOND"
12811271
private[this] def timestampToLongCode(ts: ExprValue): Block =
12821272
code"java.lang.Math.floorDiv($ts, $MICROS_PER_SECOND)"
12831273
private[this] def timestampToDoubleCode(ts: ExprValue): Block =

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,13 @@ class CastSuite extends CastSuiteBase {
13111311
checkEvaluation(cast(negativeTs, LongType), expectedSecs)
13121312
}
13131313
}
1314+
1315+
test("SPARK-31710:fail casting from integral to timestamp by default") {
1316+
assert(!cast(2.toByte, TimestampType).resolved)
1317+
assert(!cast(10.toShort, TimestampType).resolved)
1318+
assert(!cast(3, TimestampType).resolved)
1319+
assert(!cast(10L, TimestampType).resolved)
1320+
}
13141321
}
13151322

13161323
/**

0 commit comments

Comments
 (0)