Skip to content

Commit 0f4f816

Browse files
MaxGekkcloud-fan
authored andcommitted
[SPARK-27222][SQL] Support Instant and LocalDate in Literal.apply
## What changes were proposed in this pull request? In the PR, I propose to extend `Literal.apply` to support constructing literals of `TimestampType` and `DateType` from `java.time.Instant` and `java.time.LocalDate`. The java classes have been already supported as external types for `TimestampType` and `DateType` by the PRs apache#23811 and apache#23913. ## How was this patch tested? Added new tests to `LiteralExpressionSuite`. Closes apache#24161 from MaxGekk/literal-instant-localdate. Authored-by: Maxim Gekk <[email protected]> Signed-off-by: Wenchen Fan <[email protected]>
1 parent 0627850 commit 0f4f816

File tree

2 files changed

+59
-2
lines changed

2 files changed

+59
-2
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import java.lang.{Short => JavaShort}
2727
import java.math.{BigDecimal => JavaBigDecimal}
2828
import java.nio.charset.StandardCharsets
2929
import java.sql.{Date, Timestamp}
30+
import java.time.{Instant, LocalDate}
3031
import java.util
3132
import java.util.Objects
3233
import javax.xml.bind.DatatypeConverter
@@ -41,6 +42,7 @@ import org.apache.spark.sql.AnalysisException
4142
import org.apache.spark.sql.catalyst.{CatalystTypeConverters, InternalRow, ScalaReflection}
4243
import org.apache.spark.sql.catalyst.expressions.codegen._
4344
import org.apache.spark.sql.catalyst.util.{ArrayData, DateTimeUtils, MapData}
45+
import org.apache.spark.sql.catalyst.util.DateTimeUtils.instantToMicros
4446
import org.apache.spark.sql.types._
4547
import org.apache.spark.unsafe.types._
4648
import org.apache.spark.util.Utils
@@ -64,7 +66,9 @@ object Literal {
6466
case d: JavaBigDecimal =>
6567
Literal(Decimal(d), DecimalType(Math.max(d.precision, d.scale), d.scale()))
6668
case d: Decimal => Literal(d, DecimalType(Math.max(d.precision, d.scale), d.scale))
69+
case i: Instant => Literal(instantToMicros(i), TimestampType)
6770
case t: Timestamp => Literal(DateTimeUtils.fromJavaTimestamp(t), TimestampType)
71+
case ld: LocalDate => Literal(ld.toEpochDay.toInt, DateType)
6872
case d: Date => Literal(DateTimeUtils.fromJavaDate(d), DateType)
6973
case a: Array[Byte] => Literal(a, BinaryType)
7074
case a: collection.mutable.WrappedArray[_] => apply(a.array)
@@ -96,7 +100,9 @@ object Literal {
96100
case JavaBoolean.TYPE => BooleanType
97101

98102
// java classes
103+
case _ if clz == classOf[LocalDate] => DateType
99104
case _ if clz == classOf[Date] => DateType
105+
case _ if clz == classOf[Instant] => TimestampType
100106
case _ if clz == classOf[Timestamp] => TimestampType
101107
case _ if clz == classOf[JavaBigDecimal] => DecimalType.SYSTEM_DEFAULT
102108
case _ if clz == classOf[Array[Byte]] => BinaryType

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

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.apache.spark.sql.catalyst.expressions
1919

2020
import java.nio.charset.StandardCharsets
21+
import java.time.{Instant, LocalDate}
2122

2223
import scala.reflect.runtime.universe.{typeTag, TypeTag}
2324

@@ -26,6 +27,7 @@ import org.apache.spark.sql.Row
2627
import org.apache.spark.sql.catalyst.{CatalystTypeConverters, ScalaReflection}
2728
import org.apache.spark.sql.catalyst.encoders.ExamplePointUDT
2829
import org.apache.spark.sql.catalyst.util.DateTimeUtils
30+
import org.apache.spark.sql.internal.SQLConf
2931
import org.apache.spark.sql.types._
3032
import org.apache.spark.unsafe.types.CalendarInterval
3133

@@ -64,8 +66,14 @@ class LiteralExpressionSuite extends SparkFunSuite with ExpressionEvalHelper {
6466
checkEvaluation(Literal.default(BinaryType), "".getBytes(StandardCharsets.UTF_8))
6567
checkEvaluation(Literal.default(DecimalType.USER_DEFAULT), Decimal(0))
6668
checkEvaluation(Literal.default(DecimalType.SYSTEM_DEFAULT), Decimal(0))
67-
checkEvaluation(Literal.default(DateType), DateTimeUtils.toJavaDate(0))
68-
checkEvaluation(Literal.default(TimestampType), DateTimeUtils.toJavaTimestamp(0L))
69+
withSQLConf(SQLConf.DATETIME_JAVA8API_EANBLED.key -> "false") {
70+
checkEvaluation(Literal.default(DateType), DateTimeUtils.toJavaDate(0))
71+
checkEvaluation(Literal.default(TimestampType), DateTimeUtils.toJavaTimestamp(0L))
72+
}
73+
withSQLConf(SQLConf.DATETIME_JAVA8API_EANBLED.key -> "true") {
74+
checkEvaluation(Literal.default(DateType), LocalDate.ofEpochDay(0))
75+
checkEvaluation(Literal.default(TimestampType), Instant.ofEpochSecond(0))
76+
}
6977
checkEvaluation(Literal.default(CalendarIntervalType), new CalendarInterval(0, 0L))
7078
checkEvaluation(Literal.default(ArrayType(StringType)), Array())
7179
checkEvaluation(Literal.default(MapType(IntegerType, StringType)), Map())
@@ -228,4 +236,47 @@ class LiteralExpressionSuite extends SparkFunSuite with ExpressionEvalHelper {
228236
checkEvaluation(Literal('\u0000'), "\u0000")
229237
checkEvaluation(Literal.create('\n'), "\n")
230238
}
239+
240+
test("construct literals from java.time.LocalDate") {
241+
Seq(
242+
LocalDate.of(1, 1, 1),
243+
LocalDate.of(1582, 10, 1),
244+
LocalDate.of(1600, 7, 30),
245+
LocalDate.of(1969, 12, 31),
246+
LocalDate.of(1970, 1, 1),
247+
LocalDate.of(2019, 3, 20),
248+
LocalDate.of(2100, 5, 17)).foreach { localDate =>
249+
checkEvaluation(Literal(localDate), localDate)
250+
}
251+
}
252+
253+
test("construct literals from arrays of java.time.LocalDate") {
254+
withSQLConf(SQLConf.DATETIME_JAVA8API_EANBLED.key -> "true") {
255+
val localDate0 = LocalDate.of(2019, 3, 20)
256+
checkEvaluation(Literal(Array(localDate0)), Array(localDate0))
257+
val localDate1 = LocalDate.of(2100, 4, 22)
258+
checkEvaluation(Literal(Array(localDate0, localDate1)), Array(localDate0, localDate1))
259+
}
260+
}
261+
262+
test("construct literals from java.time.Instant") {
263+
Seq(
264+
Instant.parse("0001-01-01T00:00:00Z"),
265+
Instant.parse("1582-10-01T01:02:03Z"),
266+
Instant.parse("1970-02-28T11:12:13Z"),
267+
Instant.ofEpochMilli(0),
268+
Instant.parse("2019-03-20T10:15:30Z"),
269+
Instant.parse("2100-12-31T22:17:31Z")).foreach { instant =>
270+
checkEvaluation(Literal(instant), instant)
271+
}
272+
}
273+
274+
test("construct literals from arrays of java.time.Instant") {
275+
withSQLConf(SQLConf.DATETIME_JAVA8API_EANBLED.key -> "true") {
276+
val instant0 = Instant.ofEpochMilli(0)
277+
checkEvaluation(Literal(Array(instant0)), Array(instant0))
278+
val instant1 = Instant.parse("2019-03-20T10:15:30Z")
279+
checkEvaluation(Literal(Array(instant0, instant1)), Array(instant0, instant1))
280+
}
281+
}
231282
}

0 commit comments

Comments
 (0)