This is an automated email from the ASF dual-hosted git repository.

wenchen pushed a commit to branch branch-3.2
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-3.2 by this push:
     new eb84057  [SPARK-36428][SQL] the seconds parameter of make_timestamp 
should accept integer type
eb84057 is described below

commit eb840578f7a0153721bc85ff2c59115772b79dd8
Author: gengjiaan <[email protected]>
AuthorDate: Fri Aug 13 13:13:02 2021 +0800

    [SPARK-36428][SQL] the seconds parameter of make_timestamp should accept 
integer type
    
    ### What changes were proposed in this pull request?
    With ANSI mode, `SELECT make_timestamp(1, 1, 1, 1, 1, 1)` fails, because 
the 'seconds' parameter needs to be of type DECIMAL(8,6), and INT can't be 
implicitly casted to DECIMAL(8,6) under ANSI mode.
    
    ```
    org.apache.spark.sql.AnalysisException
    cannot resolve 'make_timestamp(1, 1, 1, 1, 1, 1)' due to data type 
mismatch: argument 6 requires decimal(8,6) type, however, '1' is of int type.; 
line 1 pos 7
    ```
    
    We should update the function `make_timestamp` to allow integer type 
'seconds' parameter.
    
    ### Why are the changes needed?
    Make `make_timestamp` could accepts integer as 'seconds' parameter.
    
    ### Does this PR introduce _any_ user-facing change?
    'Yes'.
    `make_timestamp` could accepts integer as 'seconds' parameter.
    
    ### How was this patch tested?
    New tests.
    
    Closes #33665 from beliefer/SPARK-36428.
    
    Lead-authored-by: gengjiaan <[email protected]>
    Co-authored-by: Jiaan Geng <[email protected]>
    Signed-off-by: Wenchen Fan <[email protected]>
    (cherry picked from commit 7d823367348c0235ffb212b09afcf053d070068d)
    Signed-off-by: Wenchen Fan <[email protected]>
---
 .../catalyst/expressions/datetimeExpressions.scala | 35 +++++++++++++++++-----
 .../expressions/DateExpressionsSuite.scala         |  8 +++++
 .../test/resources/sql-tests/inputs/timestamp.sql  |  4 +++
 .../sql-tests/results/ansi/timestamp.sql.out       | 35 +++++++++++++++++++++-
 .../sql-tests/results/datetime-legacy.sql.out      | 34 ++++++++++++++++++++-
 .../resources/sql-tests/results/timestamp.sql.out  | 34 ++++++++++++++++++++-
 .../results/timestampNTZ/timestamp-ansi.sql.out    | 35 +++++++++++++++++++++-
 .../results/timestampNTZ/timestamp.sql.out         | 34 ++++++++++++++++++++-
 8 files changed, 207 insertions(+), 12 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 5d42b31..5d69bbc 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
@@ -2494,8 +2494,9 @@ case class MakeTimestampLTZ(
       * day - the day-of-month to represent, from 1 to 31
       * hour - the hour-of-day to represent, from 0 to 23
       * min - the minute-of-hour to represent, from 0 to 59
-      * sec - the second-of-minute and its micro-fraction to represent, from
-              0 to 60. If the sec argument equals to 60, the seconds field is 
set
+      * sec - the second-of-minute and its micro-fraction to represent, from 0 
to 60.
+              The value can be either an integer like 13 , or a fraction like 
13.123.
+              If the sec argument equals to 60, the seconds field is set
               to 0 and 1 minute is added to the final timestamp.
       * timezone - the time zone identifier. For example, CET, UTC and etc.
   """,
@@ -2507,6 +2508,8 @@ case class MakeTimestampLTZ(
        2014-12-27 21:30:45.887
       > SELECT _FUNC_(2019, 6, 30, 23, 59, 60);
        2019-07-01 00:00:00
+      > SELECT _FUNC_(2019, 6, 30, 23, 59, 1);
+       2019-06-30 23:59:01
       > SELECT _FUNC_(2019, 13, 1, 10, 11, 12, 'PST');
        NULL
       > SELECT _FUNC_(null, 7, 22, 15, 30, 0);
@@ -2556,13 +2559,20 @@ case class MakeTimestamp(
   // Accept `sec` as DecimalType to avoid loosing precision of microseconds 
while converting
   // them to the fractional part of `sec`.
   override def inputTypes: Seq[AbstractDataType] =
-    Seq(IntegerType, IntegerType, IntegerType, IntegerType, IntegerType, 
DecimalType(8, 6)) ++
-    timezone.map(_ => StringType)
+    Seq(IntegerType, IntegerType, IntegerType, IntegerType, IntegerType,
+      TypeCollection(DecimalType(8, 6), IntegerType, NullType)) ++ 
timezone.map(_ => StringType)
   override def nullable: Boolean = if (failOnError) 
children.exists(_.nullable) else true
 
   override def withTimeZone(timeZoneId: String): TimeZoneAwareExpression =
     copy(timeZoneId = Option(timeZoneId))
 
+  private lazy val toDecimal = sec.dataType match {
+    case DecimalType() =>
+      (secEval: Any) => secEval.asInstanceOf[Decimal]
+    case IntegerType =>
+      (secEval: Any) => Decimal(BigDecimal(secEval.asInstanceOf[Int]), 8, 6)
+  }
+
   private def toMicros(
       year: Int,
       month: Int,
@@ -2617,7 +2627,7 @@ case class MakeTimestamp(
       day.asInstanceOf[Int],
       hour.asInstanceOf[Int],
       min.asInstanceOf[Int],
-      sec.asInstanceOf[Decimal],
+      toDecimal(sec),
       zid)
   }
 
@@ -2625,6 +2635,7 @@ case class MakeTimestamp(
     val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
     val zid = ctx.addReferenceObj("zoneId", zoneId, classOf[ZoneId].getName)
     val d = Decimal.getClass.getName.stripSuffix("$")
+    val decimalValue = ctx.freshName("decimalValue")
     val failOnErrorBranch = if (failOnError) "throw e;" else s"${ev.isNull} = 
true;"
     nullSafeCodeGen(ctx, ev, (year, month, day, hour, min, secAndNanos, 
timezone) => {
       val zoneId = timezone.map(tz => 
s"$dtu.getZoneId(${tz}.toString())").getOrElse(zid)
@@ -2636,11 +2647,21 @@ case class MakeTimestamp(
       } else {
         s"${ev.value} = $dtu.localDateTimeToMicros(ldt);"
       }
+      val toDecimalCode = sec.dataType match {
+        case DecimalType() =>
+          s"org.apache.spark.sql.types.Decimal $decimalValue = $secAndNanos;"
+        case IntegerType =>
+          s"""
+             |org.apache.spark.sql.types.Decimal $decimalValue =
+             |$d$$.MODULE$$.apply(new java.math.BigDecimal($secAndNanos), 8, 
6);
+           """.stripMargin
+      }
       s"""
       try {
-        org.apache.spark.sql.types.Decimal secFloor = $secAndNanos.floor();
+        $toDecimalCode
+        org.apache.spark.sql.types.Decimal secFloor = $decimalValue.floor();
         org.apache.spark.sql.types.Decimal nanosPerSec = 
$d$$.MODULE$$.apply(1000000000L, 10, 0);
-        int nanos = 
(($secAndNanos.$$minus(secFloor)).$$times(nanosPerSec)).toInt();
+        int nanos = 
(($decimalValue.$$minus(secFloor)).$$times(nanosPerSec)).toInt();
         int seconds = secFloor.toInt();
         java.time.LocalDateTime ldt;
         if (seconds == 60) {
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 eecaa93..0026f9a 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
@@ -1222,6 +1222,14 @@ class DateExpressionsSuite extends SparkFunSuite with 
ExpressionEvalHelper {
             checkEvaluation(makeTimestampExpr, expectedAnswer("2019-08-12 
00:00:58.000001"))
           }
         }
+
+        Seq(true, false).foreach { ansi =>
+          withSQLConf(SQLConf.ANSI_ENABLED.key -> ansi.toString) {
+            val makeTimestampExpr = MakeTimestamp(Literal(2019), Literal(8), 
Literal(12),
+              Literal(0), Literal(0), Literal(1))
+            checkEvaluation(makeTimestampExpr, expectedAnswer("2019-08-12 
00:00:01"))
+          }
+        }
       }
     }
   }
diff --git a/sql/core/src/test/resources/sql-tests/inputs/timestamp.sql 
b/sql/core/src/test/resources/sql-tests/inputs/timestamp.sql
index f8a3b3c..a55adb0 100644
--- a/sql/core/src/test/resources/sql-tests/inputs/timestamp.sql
+++ b/sql/core/src/test/resources/sql-tests/inputs/timestamp.sql
@@ -18,6 +18,10 @@ select localtimestamp() = localtimestamp();
 SELECT make_timestamp(2021, 07, 11, 6, 30, 45.678);
 SELECT make_timestamp(2021, 07, 11, 6, 30, 45.678, 'CET');
 SELECT make_timestamp(2021, 07, 11, 6, 30, 60.007);
+SELECT make_timestamp(1, 1, 1, 1, 1, 1);
+SELECT make_timestamp(1, 1, 1, 1, 1, 60);
+SELECT make_timestamp(1, 1, 1, 1, 1, 61);
+SELECT make_timestamp(1, 1, 1, 1, 1, null);
 
 -- [SPARK-31710] TIMESTAMP_SECONDS, TIMESTAMP_MILLISECONDS and 
TIMESTAMP_MICROSECONDS that always create timestamp_ltz
 select 
TIMESTAMP_SECONDS(1230219000),TIMESTAMP_SECONDS(-1230219000),TIMESTAMP_SECONDS(null);
diff --git 
a/sql/core/src/test/resources/sql-tests/results/ansi/timestamp.sql.out 
b/sql/core/src/test/resources/sql-tests/results/ansi/timestamp.sql.out
index 948a790..02138a6 100644
--- a/sql/core/src/test/resources/sql-tests/results/ansi/timestamp.sql.out
+++ b/sql/core/src/test/resources/sql-tests/results/ansi/timestamp.sql.out
@@ -1,5 +1,5 @@
 -- Automatically generated by SQLQueryTestSuite
--- Number of queries: 82
+-- Number of queries: 86
 
 
 -- !query
@@ -108,6 +108,39 @@ The fraction of sec must be zero. Valid range is [0, 60].
 
 
 -- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 1)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 1):timestamp>
+-- !query output
+0001-01-01 01:01:01
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 60)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 60):timestamp>
+-- !query output
+0001-01-01 01:02:00
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 61)
+-- !query schema
+struct<>
+-- !query output
+java.time.DateTimeException
+Invalid value for SecondOfMinute (valid values 0 - 59): 61
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, null)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, NULL):timestamp>
+-- !query output
+NULL
+
+
+-- !query
 select 
TIMESTAMP_SECONDS(1230219000),TIMESTAMP_SECONDS(-1230219000),TIMESTAMP_SECONDS(null)
 -- !query schema
 
struct<timestamp_seconds(1230219000):timestamp,timestamp_seconds(-1230219000):timestamp,timestamp_seconds(NULL):timestamp>
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 59797bc..a820bf6 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
@@ -1,5 +1,5 @@
 -- Automatically generated by SQLQueryTestSuite
--- Number of queries: 159
+-- Number of queries: 163
 
 
 -- !query
@@ -759,6 +759,38 @@ NULL
 
 
 -- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 1)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 1):timestamp>
+-- !query output
+0001-01-01 01:01:01
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 60)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 60):timestamp>
+-- !query output
+0001-01-01 01:02:00
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 61)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 61):timestamp>
+-- !query output
+NULL
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, null)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, NULL):timestamp>
+-- !query output
+NULL
+
+
+-- !query
 select 
TIMESTAMP_SECONDS(1230219000),TIMESTAMP_SECONDS(-1230219000),TIMESTAMP_SECONDS(null)
 -- !query schema
 
struct<timestamp_seconds(1230219000):timestamp,timestamp_seconds(-1230219000):timestamp,timestamp_seconds(NULL):timestamp>
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 b3b7306..cf8fb9e 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
@@ -1,5 +1,5 @@
 -- Automatically generated by SQLQueryTestSuite
--- Number of queries: 82
+-- Number of queries: 86
 
 
 -- !query
@@ -101,6 +101,38 @@ NULL
 
 
 -- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 1)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 1):timestamp>
+-- !query output
+0001-01-01 01:01:01
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 60)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 60):timestamp>
+-- !query output
+0001-01-01 01:02:00
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 61)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 61):timestamp>
+-- !query output
+NULL
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, null)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, NULL):timestamp>
+-- !query output
+NULL
+
+
+-- !query
 select 
TIMESTAMP_SECONDS(1230219000),TIMESTAMP_SECONDS(-1230219000),TIMESTAMP_SECONDS(null)
 -- !query schema
 
struct<timestamp_seconds(1230219000):timestamp,timestamp_seconds(-1230219000):timestamp,timestamp_seconds(NULL):timestamp>
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 a355c2d..5d0f9aa 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
@@ -1,5 +1,5 @@
 -- Automatically generated by SQLQueryTestSuite
--- Number of queries: 82
+-- Number of queries: 86
 
 
 -- !query
@@ -108,6 +108,39 @@ The fraction of sec must be zero. Valid range is [0, 60].
 
 
 -- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 1)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 1):timestamp_ntz>
+-- !query output
+0001-01-01 01:01:01
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 60)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 60):timestamp_ntz>
+-- !query output
+0001-01-01 01:02:00
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 61)
+-- !query schema
+struct<>
+-- !query output
+java.time.DateTimeException
+Invalid value for SecondOfMinute (valid values 0 - 59): 61
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, null)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, NULL):timestamp_ntz>
+-- !query output
+NULL
+
+
+-- !query
 select 
TIMESTAMP_SECONDS(1230219000),TIMESTAMP_SECONDS(-1230219000),TIMESTAMP_SECONDS(null)
 -- !query schema
 
struct<timestamp_seconds(1230219000):timestamp,timestamp_seconds(-1230219000):timestamp,timestamp_seconds(NULL):timestamp>
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 6c02a5a..0b24c1e 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
@@ -1,5 +1,5 @@
 -- Automatically generated by SQLQueryTestSuite
--- Number of queries: 82
+-- Number of queries: 86
 
 
 -- !query
@@ -101,6 +101,38 @@ NULL
 
 
 -- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 1)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 1):timestamp_ntz>
+-- !query output
+0001-01-01 01:01:01
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 60)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 60):timestamp_ntz>
+-- !query output
+0001-01-01 01:02:00
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, 61)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, 61):timestamp_ntz>
+-- !query output
+NULL
+
+
+-- !query
+SELECT make_timestamp(1, 1, 1, 1, 1, null)
+-- !query schema
+struct<make_timestamp(1, 1, 1, 1, 1, NULL):timestamp_ntz>
+-- !query output
+NULL
+
+
+-- !query
 select 
TIMESTAMP_SECONDS(1230219000),TIMESTAMP_SECONDS(-1230219000),TIMESTAMP_SECONDS(null)
 -- !query schema
 
struct<timestamp_seconds(1230219000):timestamp,timestamp_seconds(-1230219000):timestamp,timestamp_seconds(NULL):timestamp>

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to