You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ma...@apache.org on 2021/04/16 05:22:57 UTC

[spark] branch master updated: [SPARK-35099][SQL] Convert ANSI interval literals to SQL string in ANSI style

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 3f4c32b  [SPARK-35099][SQL] Convert ANSI interval literals to SQL string in ANSI style
3f4c32b is described below

commit 3f4c32b3ca2d0f20806f81422e086d035b2eb5ce
Author: Max Gekk <ma...@gmail.com>
AuthorDate: Fri Apr 16 08:22:25 2021 +0300

    [SPARK-35099][SQL] Convert ANSI interval literals to SQL string in ANSI style
    
    ### What changes were proposed in this pull request?
    Handle `YearMonthIntervalType` and `DayTimeIntervalType` in the `sql()` and `toString()` method of `Literal`, and format the ANSI interval in the ANSI style.
    
    ### Why are the changes needed?
    To improve readability and UX with Spark SQL. For example, a test output before the changes:
    ```
    -- !query
    select timestamp'2011-11-11 11:11:11' - interval '2' day
    -- !query schema
    struct<TIMESTAMP '2011-11-11 11:11:11' - 172800000000:timestamp>
    -- !query output
    2011-11-09 11:11:11
    ```
    
    ### Does this PR introduce _any_ user-facing change?
    Should not since the new intervals haven't been released yet.
    
    ### How was this patch tested?
    By running new tests:
    ```
    $ ./build/sbt "test:testOnly *LiteralExpressionSuite"
    ```
    
    Closes #32196 from MaxGekk/literal-ansi-interval-sql.
    
    Authored-by: Max Gekk <ma...@gmail.com>
    Signed-off-by: Max Gekk <ma...@gmail.com>
---
 .../spark/sql/catalyst/expressions/literals.scala  |  7 +++++-
 .../expressions/LiteralExpressionSuite.scala       | 27 ++++++++++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/literals.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/literals.scala
index 2ea73e8..47eebc5 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/literals.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/literals.scala
@@ -43,7 +43,8 @@ import org.apache.spark.sql.catalyst.{CatalystTypeConverters, InternalRow, Scala
 import org.apache.spark.sql.catalyst.expressions.codegen._
 import org.apache.spark.sql.catalyst.util._
 import org.apache.spark.sql.catalyst.util.DateTimeUtils.instantToMicros
-import org.apache.spark.sql.catalyst.util.IntervalUtils.{durationToMicros, periodToMonths}
+import org.apache.spark.sql.catalyst.util.IntervalStringStyles.ANSI_STYLE
+import org.apache.spark.sql.catalyst.util.IntervalUtils.{durationToMicros, periodToMonths, toDayTimeIntervalString, toYearMonthIntervalString}
 import org.apache.spark.sql.errors.{QueryCompilationErrors, QueryExecutionErrors}
 import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.types._
@@ -317,6 +318,8 @@ case class Literal (value: Any, dataType: DataType) extends LeafExpression {
           DateFormatter(timeZoneId).format(value.asInstanceOf[Int])
         case TimestampType =>
           TimestampFormatter.getFractionFormatter(timeZoneId).format(value.asInstanceOf[Long])
+        case DayTimeIntervalType => toDayTimeIntervalString(value.asInstanceOf[Long], ANSI_STYLE)
+        case YearMonthIntervalType => toYearMonthIntervalString(value.asInstanceOf[Int], ANSI_STYLE)
         case _ =>
           other.toString
       }
@@ -437,6 +440,8 @@ case class Literal (value: Any, dataType: DataType) extends LeafExpression {
     case (i: CalendarInterval, CalendarIntervalType) =>
       s"INTERVAL '${i.toString}'"
     case (v: Array[Byte], BinaryType) => s"X'${DatatypeConverter.printHexBinary(v)}'"
+    case (i: Long, DayTimeIntervalType) => toDayTimeIntervalString(i, ANSI_STYLE)
+    case (i: Int, YearMonthIntervalType) => toYearMonthIntervalString(i, ANSI_STYLE)
     case _ => value.toString
   }
 }
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/LiteralExpressionSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/LiteralExpressionSuite.scala
index f8766f3..a5f70fd 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/LiteralExpressionSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/LiteralExpressionSuite.scala
@@ -19,6 +19,7 @@ package org.apache.spark.sql.catalyst.expressions
 
 import java.nio.charset.StandardCharsets
 import java.time.{Duration, Instant, LocalDate, LocalDateTime, Period, ZoneOffset}
+import java.time.temporal.ChronoUnit
 import java.util.TimeZone
 
 import scala.reflect.runtime.universe.TypeTag
@@ -385,4 +386,30 @@ class LiteralExpressionSuite extends SparkFunSuite with ExpressionEvalHelper {
     val period1 = Period.ofMonths(-1024)
     checkEvaluation(Literal(Array(period0, period1)), Array(period0, period1))
   }
+
+  test("SPARK-35099: convert a literal of day-time interval to SQL string") {
+    Seq(
+      Duration.ofDays(-1) -> "-1 00:00:00",
+      Duration.of(10, ChronoUnit.MICROS) -> "0 00:00:00.00001",
+      Duration.of(MICROS_PER_DAY - 1, ChronoUnit.MICROS) -> "0 23:59:59.999999"
+    ).foreach { case (duration, intervalPayload) =>
+      val literal = Literal.apply(duration)
+      val expected = s"INTERVAL '$intervalPayload' DAY TO SECOND"
+      assert(literal.sql === expected)
+      assert(literal.toString === expected)
+    }
+  }
+
+  test("SPARK-35099: convert a literal of year-month interval to SQL string") {
+    Seq(
+      Period.ofYears(-1) -> "-1-0",
+      Period.of(9999, 11, 0) -> "9999-11",
+      Period.ofMonths(-11) -> "-0-11"
+    ).foreach { case (period, intervalPayload) =>
+      val literal = Literal.apply(period)
+      val expected = s"INTERVAL '$intervalPayload' YEAR TO MONTH"
+      assert(literal.sql === expected)
+      assert(literal.toString === expected)
+    }
+  }
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org