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 90e2402a5d7da..e87ff8f19d09a 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 @@ -17,6 +17,7 @@ package org.apache.spark.sql.catalyst.util +import java.math.{BigDecimal, RoundingMode} import java.util.concurrent.TimeUnit import scala.util.control.NonFatal @@ -255,26 +256,50 @@ object IntervalUtils { try { units(i) match { case "year" => - months = Math.addExact(months, Math.multiplyExact(values(i).toInt, 12)) + months = new BigDecimal(values(i)).multiply(new BigDecimal(MONTHS_PER_YEAR)) + .setScale(0, RoundingMode.DOWN) + .intValueExact case "month" => - months = Math.addExact(months, values(i).toInt) + val m = new BigDecimal(values(i)) + microseconds = m.remainder(BigDecimal.ONE).multiply(new BigDecimal(MICROS_PER_MONTH)) + .setScale(0, RoundingMode.DOWN) + .longValueExact + months = Math.addExact(months, m.setScale(0, RoundingMode.DOWN).intValueExact) case "week" => - val weeksUs = Math.multiplyExact(values(i).toLong, 7 * DateTimeUtils.MICROS_PER_DAY) - microseconds = Math.addExact(microseconds, weeksUs) + microseconds = Math.addExact( + microseconds, + new BigDecimal(values(i)).multiply(new BigDecimal(7 * DateTimeUtils.MICROS_PER_DAY)) + .setScale(0, RoundingMode.DOWN) + .longValueExact) case "day" => - val daysUs = Math.multiplyExact(values(i).toLong, DateTimeUtils.MICROS_PER_DAY) - microseconds = Math.addExact(microseconds, daysUs) + microseconds = Math.addExact( + microseconds, + new BigDecimal(values(i)).multiply(new BigDecimal(DateTimeUtils.MICROS_PER_DAY)) + .setScale(0, RoundingMode.DOWN) + .longValueExact) case "hour" => - val hoursUs = Math.multiplyExact(values(i).toLong, MICROS_PER_HOUR) - microseconds = Math.addExact(microseconds, hoursUs) + microseconds = Math.addExact( + microseconds, + new BigDecimal(values(i)).multiply(new BigDecimal(MICROS_PER_HOUR)) + .setScale(0, RoundingMode.DOWN) + .longValueExact) case "minute" => - val minutesUs = Math.multiplyExact(values(i).toLong, MICROS_PER_MINUTE) - microseconds = Math.addExact(microseconds, minutesUs) + microseconds = Math.addExact( + microseconds, + new BigDecimal(values(i)).multiply(new BigDecimal(MICROS_PER_MINUTE)) + .setScale(0, RoundingMode.DOWN) + .longValueExact) case "second" => - microseconds = Math.addExact(microseconds, parseSecondNano(values(i))) + microseconds = Math.addExact( + microseconds, + new BigDecimal(values(i)).multiply(new BigDecimal(DateTimeUtils.MICROS_PER_SECOND)) + .setScale(0, RoundingMode.DOWN).longValueExact) case "millisecond" => - val millisUs = Math.multiplyExact(values(i).toLong, DateTimeUtils.MICROS_PER_MILLIS) - microseconds = Math.addExact(microseconds, millisUs) + microseconds = Math.addExact( + microseconds, + new BigDecimal(values(i)).multiply(new BigDecimal(DateTimeUtils.MICROS_PER_MILLIS)) + .setScale(0, RoundingMode.DOWN) + .longValueExact) case "microsecond" => microseconds = Math.addExact(microseconds, values(i).toLong) } diff --git a/sql/core/src/test/resources/sql-tests/inputs/datetime.sql b/sql/core/src/test/resources/sql-tests/inputs/datetime.sql index 0e22af1fbdf29..601c104249620 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/datetime.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/datetime.sql @@ -36,3 +36,27 @@ select date '2001-10-01' - 7; select date '2001-10-01' - date '2001-09-28'; select date'2020-01-01' - timestamp'2019-10-06 10:11:12.345678'; select timestamp'2019-10-06 10:11:12.345678' - date'2020-01-01'; + +-- year interval only affect year and month if any +select interval '1.41 years'; + +-- month interval will affect microseconds +select interval '2.512345678 months'; +select interval '2.51 months'; + +select interval '2.21 weeks'; +select interval '15.24 days'; +select interval '3.31 hours'; +select interval '5.38 minutes'; +select interval '12.3456789 seconds'; +select interval '-12.3456789 seconds'; + +select interval '6.66 milliseconds'; + +select interval '1.41 years 2.51 months 2.21 weeks 15.24 days 3.31 hours 5.38 minutes 12.3456789 seconds'; +select interval '1.42 years 2.51 months 2.21 weeks 15.24 days 3.31 hours 5.38 minutes 12.3456789 seconds'; + +select interval '-1.41 years 2.51 months 2.21 weeks 15.24 days 3.31 hours 5.38 minutes 12.3456789 seconds'; +select interval '-1.42 years 2.51 months 2.21 weeks 15.24 days 3.31 hours 5.38 minutes 12.3456789 seconds'; + +select interval '.1111111111' second; diff --git a/sql/core/src/test/resources/sql-tests/inputs/postgreSQL/interval.sql b/sql/core/src/test/resources/sql-tests/inputs/postgreSQL/interval.sql index eb8cc34419519..04fd22cf5afa4 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/postgreSQL/interval.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/postgreSQL/interval.sql @@ -19,9 +19,10 @@ -- SELECT INTERVAL '-1 +02:03' AS `22 hours ago...`; -- SELECT INTERVAL '-1 days +02:03' AS `22 hours ago...`; -- [SPARK-29371] Support interval field values with fractional parts --- SELECT INTERVAL '1.5 weeks' AS `Ten days twelve hours`; --- SELECT INTERVAL '1.5 months' AS `One month 15 days`; +SELECT INTERVAL '1.5 weeks' AS `Ten days twelve hours`; +SELECT INTERVAL '1.5 months' AS `One month 15 days`; -- SELECT INTERVAL '10 years -11 month -12 days +13:14' AS `9 years...`; +SELECT INTERVAL '10 years -11 month -12 days +13 hours 14 minutes' AS `9 years...`; -- [SPARK-29382] Support writing `INTERVAL` type to datasource table -- CREATE TABLE INTERVAL_TBL (f1 interval); diff --git a/sql/core/src/test/resources/sql-tests/results/datetime.sql.out b/sql/core/src/test/resources/sql-tests/results/datetime.sql.out index 0f4036cad6125..5393037eb7aee 100644 --- a/sql/core/src/test/resources/sql-tests/results/datetime.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/datetime.sql.out @@ -1,5 +1,5 @@ -- Automatically generated by SQLQueryTestSuite --- Number of queries: 17 +-- Number of queries: 32 -- !query 0 @@ -145,3 +145,123 @@ select timestamp'2019-10-06 10:11:12.345678' - date'2020-01-01' struct -- !query 16 output interval -12 weeks -2 days -14 hours -48 minutes -47 seconds -654 milliseconds -322 microseconds + + +-- !query 17 +select interval '1.41 years' +-- !query 17 schema +struct +-- !query 17 output +interval 1 years 4 months + + +-- !query 18 +select interval '2.512345678 months' +-- !query 18 schema +struct +-- !query 18 output +interval 2 months 2 weeks 1 days 8 hours 53 minutes 19 seconds 997 milliseconds 376 microseconds + + +-- !query 19 +select interval '2.51 months' +-- !query 19 schema +struct +-- !query 19 output +interval 2 months 2 weeks 1 days 7 hours 12 minutes + + +-- !query 20 +select interval '2.21 weeks' +-- !query 20 schema +struct +-- !query 20 output +interval 2 weeks 1 days 11 hours 16 minutes 48 seconds + + +-- !query 21 +select interval '15.24 days' +-- !query 21 schema +struct +-- !query 21 output +interval 2 weeks 1 days 5 hours 45 minutes 36 seconds + + +-- !query 22 +select interval '3.31 hours' +-- !query 22 schema +struct +-- !query 22 output +interval 3 hours 18 minutes 36 seconds + + +-- !query 23 +select interval '5.38 minutes' +-- !query 23 schema +struct +-- !query 23 output +interval 5 minutes 22 seconds 800 milliseconds + + +-- !query 24 +select interval '12.3456789 seconds' +-- !query 24 schema +struct +-- !query 24 output +interval 12 seconds 345 milliseconds 678 microseconds + + +-- !query 25 +select interval '-12.3456789 seconds' +-- !query 25 schema +struct +-- !query 25 output +interval -12 seconds -345 milliseconds -678 microseconds + + +-- !query 26 +select interval '6.66 milliseconds' +-- !query 26 schema +struct +-- !query 26 output +interval 6 milliseconds 660 microseconds + + +-- !query 27 +select interval '1.41 years 2.51 months 2.21 weeks 15.24 days 3.31 hours 5.38 minutes 12.3456789 seconds' +-- !query 27 schema +struct +-- !query 27 output +interval 1 years 6 months 6 weeks 4 days 3 hours 38 minutes 35 seconds 145 milliseconds 678 microseconds + + +-- !query 28 +select interval '1.42 years 2.51 months 2.21 weeks 15.24 days 3.31 hours 5.38 minutes 12.3456789 seconds' +-- !query 28 schema +struct +-- !query 28 output +interval 1 years 7 months 6 weeks 4 days 3 hours 38 minutes 35 seconds 145 milliseconds 678 microseconds + + +-- !query 29 +select interval '-1.41 years 2.51 months 2.21 weeks 15.24 days 3.31 hours 5.38 minutes 12.3456789 seconds' +-- !query 29 schema +struct +-- !query 29 output +interval -1 years -2 months 6 weeks 4 days 3 hours 38 minutes 35 seconds 145 milliseconds 678 microseconds + + +-- !query 30 +select interval '-1.42 years 2.51 months 2.21 weeks 15.24 days 3.31 hours 5.38 minutes 12.3456789 seconds' +-- !query 30 schema +struct +-- !query 30 output +interval -1 years -3 months 6 weeks 4 days 3 hours 38 minutes 35 seconds 145 milliseconds 678 microseconds + + +-- !query 31 +select interval '.1111111111' second +-- !query 31 schema +struct +-- !query 31 output +interval 111 milliseconds 111 microseconds diff --git a/sql/core/src/test/resources/sql-tests/results/postgreSQL/interval.sql.out b/sql/core/src/test/resources/sql-tests/results/postgreSQL/interval.sql.out index bed5d7a56c1f8..5df8e5734802b 100644 --- a/sql/core/src/test/resources/sql-tests/results/postgreSQL/interval.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/postgreSQL/interval.sql.out @@ -1,153 +1,153 @@ -- Automatically generated by SQLQueryTestSuite --- Number of queries: 24 +-- Number of queries: 27 -- !query 0 -SELECT interval '999' second +SELECT INTERVAL '1.5 weeks' AS `Ten days twelve hours` -- !query 0 schema -struct +struct -- !query 0 output -interval 16 minutes 39 seconds +interval 1 weeks 3 days 12 hours -- !query 1 -SELECT interval '999' minute +SELECT INTERVAL '1.5 months' AS `One month 15 days` -- !query 1 schema -struct +struct -- !query 1 output -interval 16 hours 39 minutes +interval 1 months 2 weeks 1 days -- !query 2 -SELECT interval '999' hour +SELECT INTERVAL '10 years -11 month -12 days +13 hours 14 minutes' AS `9 years...` -- !query 2 schema -struct +struct<9 years...:interval> -- !query 2 output -interval 5 weeks 6 days 15 hours +interval 9 years 1 months -1 weeks -4 days -10 hours -46 minutes -- !query 3 -SELECT interval '999' day +SELECT interval '999' second -- !query 3 schema -struct +struct -- !query 3 output -interval 142 weeks 5 days +interval 16 minutes 39 seconds -- !query 4 -SELECT interval '999' month +SELECT interval '999' minute -- !query 4 schema -struct +struct -- !query 4 output -interval 83 years 3 months +interval 16 hours 39 minutes -- !query 5 -SELECT interval '1' year +SELECT interval '999' hour -- !query 5 schema -struct +struct -- !query 5 output -interval 1 years +interval 5 weeks 6 days 15 hours -- !query 6 -SELECT interval '2' month +SELECT interval '999' day -- !query 6 schema -struct +struct -- !query 6 output -interval 2 months +interval 142 weeks 5 days -- !query 7 -SELECT interval '3' day +SELECT interval '999' month -- !query 7 schema -struct +struct -- !query 7 output -interval 3 days +interval 83 years 3 months -- !query 8 -SELECT interval '4' hour +SELECT interval '1' year -- !query 8 schema -struct +struct -- !query 8 output -interval 4 hours +interval 1 years -- !query 9 -SELECT interval '5' minute +SELECT interval '2' month -- !query 9 schema -struct +struct -- !query 9 output -interval 5 minutes +interval 2 months -- !query 10 -SELECT interval '6' second +SELECT interval '3' day -- !query 10 schema -struct +struct -- !query 10 output -interval 6 seconds +interval 3 days -- !query 11 -SELECT interval '1-2' year to month +SELECT interval '4' hour -- !query 11 schema -struct +struct -- !query 11 output -interval 1 years 2 months +interval 4 hours -- !query 12 -SELECT interval '1 2:03' day to hour +SELECT interval '5' minute -- !query 12 schema -struct +struct -- !query 12 output -interval 1 days 2 hours +interval 5 minutes -- !query 13 -SELECT interval '1 2:03:04' day to hour +SELECT interval '6' second -- !query 13 schema -struct +struct -- !query 13 output -interval 1 days 2 hours +interval 6 seconds -- !query 14 -SELECT interval '1 2:03' day to minute +SELECT interval '1-2' year to month -- !query 14 schema -struct +struct -- !query 14 output -interval 1 days 2 hours 3 minutes +interval 1 years 2 months -- !query 15 -SELECT interval '1 2:03:04' day to minute +SELECT interval '1 2:03' day to hour -- !query 15 schema -struct +struct -- !query 15 output -interval 1 days 2 hours 3 minutes +interval 1 days 2 hours -- !query 16 -SELECT interval '1 2:03' day to second +SELECT interval '1 2:03:04' day to hour -- !query 16 schema -struct +struct -- !query 16 output -interval 1 days 2 hours 3 minutes +interval 1 days 2 hours -- !query 17 -SELECT interval '1 2:03:04' day to second +SELECT interval '1 2:03' day to minute -- !query 17 schema -struct +struct -- !query 17 output -interval 1 days 2 hours 3 minutes 4 seconds +interval 1 days 2 hours 3 minutes -- !query 18 -SELECT interval '1 2:03' hour to minute +SELECT interval '1 2:03:04' day to minute -- !query 18 schema struct -- !query 18 output @@ -155,7 +155,7 @@ interval 1 days 2 hours 3 minutes -- !query 19 -SELECT interval '1 2:03:04' hour to minute +SELECT interval '1 2:03' day to second -- !query 19 schema struct -- !query 19 output @@ -163,32 +163,56 @@ interval 1 days 2 hours 3 minutes -- !query 20 -SELECT interval '1 2:03' hour to second +SELECT interval '1 2:03:04' day to second -- !query 20 schema -struct +struct -- !query 20 output -interval 1 days 2 hours 3 minutes +interval 1 days 2 hours 3 minutes 4 seconds -- !query 21 -SELECT interval '1 2:03:04' hour to second +SELECT interval '1 2:03' hour to minute -- !query 21 schema -struct +struct -- !query 21 output -interval 1 days 2 hours 3 minutes 4 seconds +interval 1 days 2 hours 3 minutes -- !query 22 -SELECT interval '1 2:03' minute to second +SELECT interval '1 2:03:04' hour to minute -- !query 22 schema -struct +struct -- !query 22 output -interval 1 days 2 minutes 3 seconds +interval 1 days 2 hours 3 minutes -- !query 23 -SELECT interval '1 2:03:04' minute to second +SELECT interval '1 2:03' hour to second -- !query 23 schema -struct +struct -- !query 23 output +interval 1 days 2 hours 3 minutes + + +-- !query 24 +SELECT interval '1 2:03:04' hour to second +-- !query 24 schema +struct +-- !query 24 output +interval 1 days 2 hours 3 minutes 4 seconds + + +-- !query 25 +SELECT interval '1 2:03' minute to second +-- !query 25 schema +struct +-- !query 25 output +interval 1 days 2 minutes 3 seconds + + +-- !query 26 +SELECT interval '1 2:03:04' minute to second +-- !query 26 schema +struct +-- !query 26 output interval 1 days 2 hours 3 minutes 4 seconds diff --git a/sql/core/src/test/scala/org/apache/spark/sql/execution/command/DDLParserSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/execution/command/DDLParserSuite.scala index 07935641efdf5..6f86dce51347a 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/execution/command/DDLParserSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/execution/command/DDLParserSuite.scala @@ -971,8 +971,6 @@ class DDLParserSuite extends AnalysisTest with SharedSparkSession { "hour 49 outside range [0, 23]") assertError("select interval '23:61:15' hour to second", "minute 61 outside range [0, 59]") - assertError("select interval '.1111111111' second", - "nanosecond 1111111111 outside range") } test("use native json_tuple instead of hive's UDTF in LATERAL VIEW") {