-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-29783][SQL] Support SQL Standard/ISO_8601 output style for interval type #26418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
88418e0
02be22b
429ee49
da119c5
fff17d5
2af8593
b3c9e08
005a28d
5ea0ff8
fa9c41e
f89e7c1
981eae5
be65a4d
748fbad
3aa723c
0f54bac
3cf71e9
8d18fac
0c530cc
08b7359
7641e5e
0f54af8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
|
|
||
| package org.apache.spark.sql.catalyst.util | ||
|
|
||
| import java.math.BigDecimal | ||
| import java.util.concurrent.TimeUnit | ||
|
|
||
| import scala.util.control.NonFatal | ||
|
|
@@ -378,6 +379,43 @@ object IntervalUtils { | |
| fromDoubles(interval.months / num, interval.days / num, interval.microseconds / num) | ||
| } | ||
|
|
||
| def toSqlStandardString(interval: CalendarInterval): String = { | ||
| val yearMonthPart = if (interval.months != 0) { | ||
| interval.months / 12 + "-" + math.abs(interval.months) % 12 | ||
| } else { | ||
| "" | ||
| } | ||
|
|
||
| val dayPart = if (interval.days != 0) interval.days.toString else "" | ||
|
|
||
| val timePart = if (interval.microseconds != 0) { | ||
| val sb = new StringBuilder() | ||
| var rest = interval.microseconds | ||
| sb.append(rest / MICROS_PER_HOUR) | ||
| sb.append(':') | ||
| rest = math.abs(rest % MICROS_PER_HOUR) | ||
| val minutes = rest / MICROS_PER_MINUTE; | ||
| if (minutes < 10) { | ||
| sb.append(0) | ||
| } | ||
| sb.append(minutes) | ||
| sb.append(':') | ||
| rest %= MICROS_PER_MINUTE | ||
| val db = BigDecimal.valueOf(rest, 6) | ||
| if (db.compareTo(new BigDecimal(10)) < 0) { | ||
| sb.append(0) | ||
| } | ||
| val s = db.stripTrailingZeros().toPlainString | ||
| sb.append(s) | ||
| sb.toString() | ||
| } else { | ||
| "" | ||
| } | ||
|
|
||
| val intervalList = Seq(yearMonthPart, dayPart, timePart).filter(_.nonEmpty) | ||
| if (intervalList.nonEmpty) intervalList.mkString(" ") else "0" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wow, a single
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. postgres=# set IntervalStyle=sql_standard;
SET
postgres=# select interval '0';
interval
----------
0
(1 row)
postgres=# set IntervalStyle=postgres;
SET
postgres=# select interval '0';
interval
----------
00:00:00
(1 row) |
||
| } | ||
|
|
||
| private object ParseState extends Enumeration { | ||
| val PREFIX, | ||
| BEGIN_VALUE, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,7 +37,6 @@ import org.apache.spark.sql.catalyst.expressions.CodegenObjectFactoryMode | |
| import org.apache.spark.sql.catalyst.expressions.codegen.CodeGenerator | ||
| import org.apache.spark.sql.catalyst.plans.logical.HintErrorHandler | ||
| import org.apache.spark.sql.connector.catalog.CatalogManager.SESSION_CATALOG_NAME | ||
| import org.apache.spark.sql.internal.SQLConf.StoreAssignmentPolicy | ||
| import org.apache.spark.unsafe.array.ByteArrayMethods | ||
| import org.apache.spark.util.Utils | ||
|
|
||
|
|
@@ -1774,6 +1773,19 @@ object SQLConf { | |
| .booleanConf | ||
| .createWithDefault(false) | ||
|
|
||
| object IntervalStyle extends Enumeration { | ||
| val SQL_STANDARD, MULTI_UNITS = Value | ||
| } | ||
|
|
||
| val INTERVAL_STYLE = buildConf("spark.sql.IntervalOutputStyle") | ||
| .doc("Display format for interval values. The value SQL_STANDARD will produce output" + | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. display is not accurate as this controls the cast behavior as well. How about
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I will make it much clearer |
||
| " matching SQL standard interval literals. The value MULTI_UNITS (which is the default)" + | ||
| " will produce output in form of value unit pairs, i.e. '3 year 2 months 10 days'") | ||
| .stringConf | ||
| .transform(_.toUpperCase(Locale.ROOT)) | ||
| .checkValues(IntervalStyle.values.map(_.toString)) | ||
| .createWithDefault(IntervalStyle.MULTI_UNITS.toString) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I personally think
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I guess some users may already rely on the output string |
||
|
|
||
| val SORT_BEFORE_REPARTITION = | ||
| buildConf("spark.sql.execution.sortBeforeRepartition") | ||
| .internal() | ||
|
|
@@ -2502,6 +2514,8 @@ class SQLConf extends Serializable with Logging { | |
| def storeAssignmentPolicy: StoreAssignmentPolicy.Value = | ||
| StoreAssignmentPolicy.withName(getConf(STORE_ASSIGNMENT_POLICY)) | ||
|
|
||
| def intervalOutputStyle: IntervalStyle.Value = IntervalStyle.withName(getConf(INTERVAL_STYLE)) | ||
|
|
||
| def ansiEnabled: Boolean = getConf(ANSI_ENABLED) | ||
|
|
||
| def usePostgreSQLDialect: Boolean = getConf(DIALECT) == Dialect.POSTGRESQL.toString() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| -- | ||
|
yaooqinn marked this conversation as resolved.
Outdated
|
||
| -- Licensed to the Apache Software Foundation (ASF) under one or more | ||
| -- contributor license agreements. See the NOTICE file distributed with | ||
| -- this work for additional information regarding copyright ownership. | ||
| -- The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| -- (the "License"); you may not use this file except in compliance with | ||
| -- the License. You may obtain a copy of the License at | ||
| -- | ||
| -- http://www.apache.org/licenses/LICENSE-2.0 | ||
| -- | ||
| -- Unless required by applicable law or agreed to in writing, software | ||
| -- distributed under the License is distributed on an "AS IS" BASIS, | ||
| -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| -- See the License for the specific language governing permissions and | ||
| -- limitations under the License. | ||
| -- | ||
|
|
||
| -- test for interacting with intervals | ||
|
|
||
| -- using SQL_STANDARD output style | ||
| set spark.sql.IntervalOutputStyle=SQL_STANDARD; | ||
| select interval 4 month 2 weeks 3 microseconds; | ||
| select interval '1 year 20 month'; | ||
| select interval '-1 year -20 month'; | ||
| select interval '20 month 30 days -21 hours 10 minutes 999 milliseconds'; | ||
| select date'2019-10-15' - timestamp'2019-10-15 10:11:12.001002'; | ||
|
|
||
| -- using MULTI_UNITS (which is default) output style | ||
| set spark.sql.IntervalOutputStyle=MULTI_UNITS; | ||
|
|
||
| -- interval operations | ||
| select 3 * (timestamp'2019-10-15 10:11:12.001002' - date'2019-10-15'); | ||
| select interval 4 month 2 weeks 3 microseconds * 1.5; | ||
| select (timestamp'2019-10-15' - timestamp'2019-10-14') / 1.5; | ||
|
|
||
| -- interval operation with null and zero case | ||
| select interval '2 seconds' / 0; | ||
| select interval '2 seconds' / null; | ||
| select interval '2 seconds' * null; | ||
| select null * interval '2 seconds'; | ||
Uh oh!
There was an error while loading. Please reload this page.