From 184f65e7c7359d4ff68ce5b558b186bca6e8efb1 Mon Sep 17 00:00:00 2001 From: Kousuke Saruta Date: Wed, 16 Jun 2021 11:08:02 +0300 Subject: [PATCH] [SPARK-35771][SQL] Format year-month intervals using type fields ### What changes were proposed in this pull request? This PR proposes to format year-month interval to strings using the start and end fields of `YearMonthIntervalType`. ### Why are the changes needed? Currently, they are ignored, and any `YearMonthIntervalType` is formatted as `INTERVAL YEAR TO MONTH`. ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? New test. Closes #32924 from sarutak/year-month-interval-format. Authored-by: Kousuke Saruta Signed-off-by: Max Gekk --- .../sql/catalyst/util/IntervalUtils.scala | 21 +++++++++++---- .../catalyst/util/IntervalUtilsSuite.scala | 26 +++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/IntervalUtils.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/IntervalUtils.scala index aca3d15c54..e9be3de44f 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/IntervalUtils.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/IntervalUtils.scala @@ -29,7 +29,7 @@ import org.apache.spark.sql.catalyst.util.DateTimeUtils.millisToMicros import org.apache.spark.sql.catalyst.util.IntervalStringStyles.{ANSI_STYLE, HIVE_STYLE, IntervalStyle} import org.apache.spark.sql.errors.QueryExecutionErrors import org.apache.spark.sql.internal.SQLConf -import org.apache.spark.sql.types.{DayTimeIntervalType, Decimal} +import org.apache.spark.sql.types.{DayTimeIntervalType, Decimal, YearMonthIntervalType} import org.apache.spark.unsafe.types.{CalendarInterval, UTF8String} // The style of textual representation of intervals @@ -945,7 +945,6 @@ object IntervalUtils { def toYearMonthIntervalString( months: Int, style: IntervalStyle, - // TODO(SPARK-35771): Format year-month intervals using type fields startField: Byte, endField: Byte): String = { var sign = "" @@ -954,10 +953,22 @@ object IntervalUtils { sign = "-" absMonths = -absMonths } - val payload = s"$sign${absMonths / MONTHS_PER_YEAR}-${absMonths % MONTHS_PER_YEAR}" + val year = s"$sign${absMonths / MONTHS_PER_YEAR}" + val month = s"${absMonths % MONTHS_PER_YEAR}" + val yearAndMonth = s"$year-$month" style match { - case ANSI_STYLE => s"INTERVAL '$payload' YEAR TO MONTH" - case HIVE_STYLE => payload + case ANSI_STYLE => + val formatBuilder = new StringBuilder("INTERVAL '") + if (startField == endField) { + startField match { + case YearMonthIntervalType.YEAR => formatBuilder.append(s"$year' YEAR") + case YearMonthIntervalType.MONTH => formatBuilder.append(s"$month' MONTH") + } + } else { + formatBuilder.append(s"$yearAndMonth' YEAR TO MONTH") + } + formatBuilder.toString + case HIVE_STYLE => s"$yearAndMonth" } } diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/IntervalUtilsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/IntervalUtilsSuite.scala index c2ece95948..b6cf152401 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/IntervalUtilsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/IntervalUtilsSuite.scala @@ -619,4 +619,30 @@ class IntervalUtilsSuite extends SparkFunSuite with SQLHelper { assert(toDayTimeIntervalString(micros, ANSI_STYLE, SECOND, SECOND) === sec) } } + + test("SPARK-35771: Format year-month intervals using type fields") { + import org.apache.spark.sql.types.YearMonthIntervalType._ + Seq( + 0 -> + ("INTERVAL '0-0' YEAR TO MONTH", "INTERVAL '0' YEAR", "INTERVAL '0' MONTH"), + -11 -> ("INTERVAL '-0-11' YEAR TO MONTH", "INTERVAL '-0' YEAR", "INTERVAL '11' MONTH"), + 11 -> ("INTERVAL '0-11' YEAR TO MONTH", "INTERVAL '0' YEAR", "INTERVAL '11' MONTH"), + -13 -> ("INTERVAL '-1-1' YEAR TO MONTH", "INTERVAL '-1' YEAR", "INTERVAL '1' MONTH"), + 13 -> ("INTERVAL '1-1' YEAR TO MONTH", "INTERVAL '1' YEAR", "INTERVAL '1' MONTH"), + -24 -> ("INTERVAL '-2-0' YEAR TO MONTH", "INTERVAL '-2' YEAR", "INTERVAL '0' MONTH"), + 24 -> ("INTERVAL '2-0' YEAR TO MONTH", "INTERVAL '2' YEAR", "INTERVAL '0' MONTH"), + Int.MinValue -> + ("INTERVAL '-178956970-8' YEAR TO MONTH", + "INTERVAL '-178956970' YEAR", + "INTERVAL '8' MONTH"), + Int.MaxValue -> + ("INTERVAL '178956970-7' YEAR TO MONTH", + "INTERVAL '178956970' YEAR", + "INTERVAL '7' MONTH") + ).foreach { case (months, (yearToMonth, year, month)) => + assert(toYearMonthIntervalString(months, ANSI_STYLE, YEAR, MONTH) === yearToMonth) + assert(toYearMonthIntervalString(months, ANSI_STYLE, YEAR, YEAR) === year) + assert(toYearMonthIntervalString(months, ANSI_STYLE, MONTH, MONTH) === month) + } + } }