[SPARK-29408][SQL] Support -
before interval
in interval literals
### What changes were proposed in this pull request? - `SqlBase.g4` is modified to support a negative sign `-` in the interval type constructor from a string and in interval literals - Negate interval in `AstBuilder` if a sign presents. - Interval related SQL statements are moved from `inputs/datetime.sql` to new file `inputs/interval.sql` For example: ```sql spark-sql> select -interval '-1 month 1 day -1 second'; 1 months -1 days 1 seconds spark-sql> select -interval -1 month 1 day -1 second; 1 months -1 days 1 seconds ``` ### Why are the changes needed? For feature parity with PostgreSQL which supports that: ```sql # select -interval '-1 month 1 day -1 second'; ?column? ------------------------- 1 mon -1 days +00:00:01 (1 row) ``` ### Does this PR introduce any user-facing change? No ### How was this patch tested? - Added tests to `ExpressionParserSuite` - by `interval.sql` Closes #26438 from MaxGekk/negative-interval. Authored-by: Maxim Gekk <max.gekk@gmail.com> Signed-off-by: Dongjoon Hyun <dhyun@apple.com>
This commit is contained in:
parent
e25cfc4bb8
commit
d4de01f567
|
@ -767,7 +767,7 @@ primaryExpression
|
|||
constant
|
||||
: NULL #nullLiteral
|
||||
| interval #intervalLiteral
|
||||
| identifier STRING #typeConstructor
|
||||
| negativeSign=MINUS? identifier STRING #typeConstructor
|
||||
| number #numericLiteral
|
||||
| booleanValue #booleanLiteral
|
||||
| STRING+ #stringLiteral
|
||||
|
@ -790,7 +790,7 @@ booleanValue
|
|||
;
|
||||
|
||||
interval
|
||||
: INTERVAL (errorCapturingMultiUnitsInterval | errorCapturingUnitToUnitInterval)?
|
||||
: negativeSign=MINUS? INTERVAL (errorCapturingMultiUnitsInterval | errorCapturingUnitToUnitInterval)?
|
||||
| {ansi}? (errorCapturingMultiUnitsInterval | errorCapturingUnitToUnitInterval)
|
||||
;
|
||||
|
||||
|
|
|
@ -1881,7 +1881,7 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
|
|||
ex.setStackTrace(e.getStackTrace)
|
||||
throw ex
|
||||
}
|
||||
Literal(interval, CalendarIntervalType)
|
||||
Literal(applyNegativeSign(ctx.negativeSign, interval), CalendarIntervalType)
|
||||
case "X" =>
|
||||
val padding = if (value.length % 2 != 0) "0" else ""
|
||||
Literal(DatatypeConverter.parseHexBinary(padding + value))
|
||||
|
@ -2025,6 +2025,14 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
|
|||
}
|
||||
}
|
||||
|
||||
private def applyNegativeSign(sign: Token, interval: CalendarInterval): CalendarInterval = {
|
||||
if (sign != null && sign.getText == "-") {
|
||||
IntervalUtils.negate(interval)
|
||||
} else {
|
||||
interval
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a [[CalendarInterval]] literal expression. Two syntaxes are supported:
|
||||
* - multiple unit value pairs, for instance: interval 2 months 2 days.
|
||||
|
@ -2038,7 +2046,10 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
|
|||
"Can only have a single from-to unit in the interval literal syntax",
|
||||
innerCtx.unitToUnitInterval)
|
||||
}
|
||||
Literal(visitMultiUnitsInterval(innerCtx.multiUnitsInterval), CalendarIntervalType)
|
||||
val interval = applyNegativeSign(
|
||||
ctx.negativeSign,
|
||||
visitMultiUnitsInterval(innerCtx.multiUnitsInterval))
|
||||
Literal(interval, CalendarIntervalType)
|
||||
} else if (ctx.errorCapturingUnitToUnitInterval != null) {
|
||||
val innerCtx = ctx.errorCapturingUnitToUnitInterval
|
||||
if (innerCtx.error1 != null || innerCtx.error2 != null) {
|
||||
|
@ -2047,7 +2058,8 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
|
|||
"Can only have a single from-to unit in the interval literal syntax",
|
||||
errorCtx)
|
||||
}
|
||||
Literal(visitUnitToUnitInterval(innerCtx.body), CalendarIntervalType)
|
||||
val interval = applyNegativeSign(ctx.negativeSign, visitUnitToUnitInterval(innerCtx.body))
|
||||
Literal(interval, CalendarIntervalType)
|
||||
} else {
|
||||
throw new ParseException("at least one time unit should be given for interval literal", ctx)
|
||||
}
|
||||
|
|
|
@ -438,6 +438,9 @@ class ExpressionParserSuite extends AnalysisTest {
|
|||
assertEqual("InterVal 'interval 3 month 1 hour'", intervalLiteral)
|
||||
assertEqual("INTERVAL '3 month 1 hour'", intervalLiteral)
|
||||
intercept("Interval 'interval 3 monthsss 1 hoursss'", "Cannot parse the INTERVAL value")
|
||||
assertEqual(
|
||||
"-interval '3 month 1 hour'",
|
||||
Literal(IntervalUtils.fromString("interval -3 month -1 hour")))
|
||||
|
||||
// Binary.
|
||||
assertEqual("X'A'", Literal(Array(0x0a).map(_.toByte)))
|
||||
|
@ -604,11 +607,17 @@ class ExpressionParserSuite extends AnalysisTest {
|
|||
|
||||
test("intervals") {
|
||||
def checkIntervals(intervalValue: String, expected: Literal): Unit = {
|
||||
assertEqual(s"interval $intervalValue", expected)
|
||||
Seq(
|
||||
"" -> expected,
|
||||
"-" -> expected.copy(
|
||||
value = IntervalUtils.negate(expected.value.asInstanceOf[CalendarInterval]))
|
||||
).foreach { case (sign, expectedLiteral) =>
|
||||
assertEqual(s"${sign}interval $intervalValue", expectedLiteral)
|
||||
|
||||
// SPARK-23264 Support interval values without INTERVAL clauses if ANSI SQL enabled
|
||||
withSQLConf(SQLConf.ANSI_ENABLED.key -> "true") {
|
||||
assertEqual(intervalValue, expected)
|
||||
// SPARK-23264 Support interval values without INTERVAL clauses if ANSI SQL enabled
|
||||
withSQLConf(SQLConf.ANSI_ENABLED.key -> "true") {
|
||||
assertEqual(intervalValue, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,14 +36,3 @@ 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';
|
||||
|
||||
-- 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';
|
||||
|
|
|
@ -41,3 +41,18 @@ select max(cast(v as interval)) from VALUES ('1 seconds'), ('4 seconds'), ('3 se
|
|||
|
||||
-- min
|
||||
select min(cast(v as interval)) from VALUES ('1 seconds'), ('4 seconds'), ('3 seconds') t(v);
|
||||
|
||||
-- multiply and divide an interval by a number
|
||||
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';
|
||||
|
||||
-- interval with a negative sign
|
||||
select -interval '-1 month 1 day -1 second';
|
||||
select -interval -1 month 1 day -1 second;
|
||||
|
|
|
@ -118,7 +118,7 @@ select
|
|||
interval '2-2' year to month + dateval
|
||||
from interval_arithmetic
|
||||
-- !query 9 schema
|
||||
struct<dateval:date,CAST(CAST(dateval AS TIMESTAMP) - 2 years 2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) - -2 years -2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + 2 years 2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + -2 years -2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + (- 2 years 2 months) AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + 2 years 2 months AS DATE):date>
|
||||
struct<dateval:date,CAST(CAST(dateval AS TIMESTAMP) - 2 years 2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) - -2 years -2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + 2 years 2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + -2 years -2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + -2 years -2 months AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + 2 years 2 months AS DATE):date>
|
||||
-- !query 9 output
|
||||
2012-01-01 2009-11-01 2014-03-01 2014-03-01 2009-11-01 2009-11-01 2014-03-01
|
||||
|
||||
|
@ -150,7 +150,7 @@ select
|
|||
interval '2-2' year to month + tsval
|
||||
from interval_arithmetic
|
||||
-- !query 11 schema
|
||||
struct<tsval:timestamp,CAST(tsval - 2 years 2 months AS TIMESTAMP):timestamp,CAST(tsval - -2 years -2 months AS TIMESTAMP):timestamp,CAST(tsval + 2 years 2 months AS TIMESTAMP):timestamp,CAST(tsval + -2 years -2 months AS TIMESTAMP):timestamp,CAST(tsval + (- 2 years 2 months) AS TIMESTAMP):timestamp,CAST(tsval + 2 years 2 months AS TIMESTAMP):timestamp>
|
||||
struct<tsval:timestamp,CAST(tsval - 2 years 2 months AS TIMESTAMP):timestamp,CAST(tsval - -2 years -2 months AS TIMESTAMP):timestamp,CAST(tsval + 2 years 2 months AS TIMESTAMP):timestamp,CAST(tsval + -2 years -2 months AS TIMESTAMP):timestamp,CAST(tsval + -2 years -2 months AS TIMESTAMP):timestamp,CAST(tsval + 2 years 2 months AS TIMESTAMP):timestamp>
|
||||
-- !query 11 output
|
||||
2012-01-01 00:00:00 2009-11-01 00:00:00 2014-03-01 00:00:00 2014-03-01 00:00:00 2009-11-01 00:00:00 2009-11-01 00:00:00 2014-03-01 00:00:00
|
||||
|
||||
|
@ -204,7 +204,7 @@ select
|
|||
interval '99 11:22:33.123456789' day to second + dateval
|
||||
from interval_arithmetic
|
||||
-- !query 15 schema
|
||||
struct<dateval:date,CAST(CAST(dateval AS TIMESTAMP) - 99 days 11 hours 22 minutes 33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) - -99 days -11 hours -22 minutes -33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + 99 days 11 hours 22 minutes 33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + -99 days -11 hours -22 minutes -33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + (- 99 days 11 hours 22 minutes 33.123456 seconds) AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + 99 days 11 hours 22 minutes 33.123456 seconds AS DATE):date>
|
||||
struct<dateval:date,CAST(CAST(dateval AS TIMESTAMP) - 99 days 11 hours 22 minutes 33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) - -99 days -11 hours -22 minutes -33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + 99 days 11 hours 22 minutes 33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + -99 days -11 hours -22 minutes -33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + -99 days -11 hours -22 minutes -33.123456 seconds AS DATE):date,CAST(CAST(dateval AS TIMESTAMP) + 99 days 11 hours 22 minutes 33.123456 seconds AS DATE):date>
|
||||
-- !query 15 output
|
||||
2012-01-01 2011-09-23 2012-04-09 2012-04-09 2011-09-23 2011-09-23 2012-04-09
|
||||
|
||||
|
@ -236,7 +236,7 @@ select
|
|||
interval '99 11:22:33.123456789' day to second + tsval
|
||||
from interval_arithmetic
|
||||
-- !query 17 schema
|
||||
struct<tsval:timestamp,CAST(tsval - 99 days 11 hours 22 minutes 33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval - -99 days -11 hours -22 minutes -33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval + 99 days 11 hours 22 minutes 33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval + -99 days -11 hours -22 minutes -33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval + (- 99 days 11 hours 22 minutes 33.123456 seconds) AS TIMESTAMP):timestamp,CAST(tsval + 99 days 11 hours 22 minutes 33.123456 seconds AS TIMESTAMP):timestamp>
|
||||
struct<tsval:timestamp,CAST(tsval - 99 days 11 hours 22 minutes 33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval - -99 days -11 hours -22 minutes -33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval + 99 days 11 hours 22 minutes 33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval + -99 days -11 hours -22 minutes -33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval + -99 days -11 hours -22 minutes -33.123456 seconds AS TIMESTAMP):timestamp,CAST(tsval + 99 days 11 hours 22 minutes 33.123456 seconds AS TIMESTAMP):timestamp>
|
||||
-- !query 17 output
|
||||
2012-01-01 00:00:00 2011-09-23 12:37:26.876544 2012-04-09 11:22:33.123456 2012-04-09 11:22:33.123456 2011-09-23 12:37:26.876544 2011-09-23 12:37:26.876544 2012-04-09 11:22:33.123456
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
-- Automatically generated by SQLQueryTestSuite
|
||||
-- Number of queries: 24
|
||||
-- Number of queries: 17
|
||||
|
||||
|
||||
-- !query 0
|
||||
|
@ -145,59 +145,3 @@ select timestamp'2019-10-06 10:11:12.345678' - date'2020-01-01'
|
|||
struct<subtracttimestamps(TIMESTAMP('2019-10-06 10:11:12.345678'), CAST(DATE '2020-01-01' AS TIMESTAMP)):interval>
|
||||
-- !query 16 output
|
||||
-2078 hours -48 minutes -47.654322 seconds
|
||||
|
||||
|
||||
-- !query 17
|
||||
select 3 * (timestamp'2019-10-15 10:11:12.001002' - date'2019-10-15')
|
||||
-- !query 17 schema
|
||||
struct<multiply_interval(subtracttimestamps(TIMESTAMP('2019-10-15 10:11:12.001002'), CAST(DATE '2019-10-15' AS TIMESTAMP)), CAST(3 AS DOUBLE)):interval>
|
||||
-- !query 17 output
|
||||
30 hours 33 minutes 36.003006 seconds
|
||||
|
||||
|
||||
-- !query 18
|
||||
select interval 4 month 2 weeks 3 microseconds * 1.5
|
||||
-- !query 18 schema
|
||||
struct<multiply_interval(4 months 14 days 0.000003 seconds, CAST(1.5 AS DOUBLE)):interval>
|
||||
-- !query 18 output
|
||||
6 months 21 days 0.000005 seconds
|
||||
|
||||
|
||||
-- !query 19
|
||||
select (timestamp'2019-10-15' - timestamp'2019-10-14') / 1.5
|
||||
-- !query 19 schema
|
||||
struct<divide_interval(subtracttimestamps(TIMESTAMP('2019-10-15 00:00:00'), TIMESTAMP('2019-10-14 00:00:00')), CAST(1.5 AS DOUBLE)):interval>
|
||||
-- !query 19 output
|
||||
16 hours
|
||||
|
||||
|
||||
-- !query 20
|
||||
select interval '2 seconds' / 0
|
||||
-- !query 20 schema
|
||||
struct<divide_interval(2 seconds, CAST(0 AS DOUBLE)):interval>
|
||||
-- !query 20 output
|
||||
NULL
|
||||
|
||||
|
||||
-- !query 21
|
||||
select interval '2 seconds' / null
|
||||
-- !query 21 schema
|
||||
struct<divide_interval(2 seconds, CAST(NULL AS DOUBLE)):interval>
|
||||
-- !query 21 output
|
||||
NULL
|
||||
|
||||
|
||||
-- !query 22
|
||||
select interval '2 seconds' * null
|
||||
-- !query 22 schema
|
||||
struct<multiply_interval(2 seconds, CAST(NULL AS DOUBLE)):interval>
|
||||
-- !query 22 output
|
||||
NULL
|
||||
|
||||
|
||||
-- !query 23
|
||||
select null * interval '2 seconds'
|
||||
-- !query 23 schema
|
||||
struct<multiply_interval(2 seconds, CAST(NULL AS DOUBLE)):interval>
|
||||
-- !query 23 output
|
||||
NULL
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
-- Automatically generated by SQLQueryTestSuite
|
||||
-- Number of queries: 22
|
||||
-- Number of queries: 31
|
||||
|
||||
|
||||
-- !query 0
|
||||
|
@ -178,3 +178,75 @@ select min(cast(v as interval)) from VALUES ('1 seconds'), ('4 seconds'), ('3 se
|
|||
struct<min(CAST(v AS INTERVAL)):interval>
|
||||
-- !query 21 output
|
||||
1 seconds
|
||||
|
||||
|
||||
-- !query 22
|
||||
select 3 * (timestamp'2019-10-15 10:11:12.001002' - date'2019-10-15')
|
||||
-- !query 22 schema
|
||||
struct<multiply_interval(subtracttimestamps(TIMESTAMP('2019-10-15 10:11:12.001002'), CAST(DATE '2019-10-15' AS TIMESTAMP)), CAST(3 AS DOUBLE)):interval>
|
||||
-- !query 22 output
|
||||
30 hours 33 minutes 36.003006 seconds
|
||||
|
||||
|
||||
-- !query 23
|
||||
select interval 4 month 2 weeks 3 microseconds * 1.5
|
||||
-- !query 23 schema
|
||||
struct<multiply_interval(4 months 14 days 0.000003 seconds, CAST(1.5 AS DOUBLE)):interval>
|
||||
-- !query 23 output
|
||||
6 months 21 days 0.000005 seconds
|
||||
|
||||
|
||||
-- !query 24
|
||||
select (timestamp'2019-10-15' - timestamp'2019-10-14') / 1.5
|
||||
-- !query 24 schema
|
||||
struct<divide_interval(subtracttimestamps(TIMESTAMP('2019-10-15 00:00:00'), TIMESTAMP('2019-10-14 00:00:00')), CAST(1.5 AS DOUBLE)):interval>
|
||||
-- !query 24 output
|
||||
16 hours
|
||||
|
||||
|
||||
-- !query 25
|
||||
select interval '2 seconds' / 0
|
||||
-- !query 25 schema
|
||||
struct<divide_interval(2 seconds, CAST(0 AS DOUBLE)):interval>
|
||||
-- !query 25 output
|
||||
NULL
|
||||
|
||||
|
||||
-- !query 26
|
||||
select interval '2 seconds' / null
|
||||
-- !query 26 schema
|
||||
struct<divide_interval(2 seconds, CAST(NULL AS DOUBLE)):interval>
|
||||
-- !query 26 output
|
||||
NULL
|
||||
|
||||
|
||||
-- !query 27
|
||||
select interval '2 seconds' * null
|
||||
-- !query 27 schema
|
||||
struct<multiply_interval(2 seconds, CAST(NULL AS DOUBLE)):interval>
|
||||
-- !query 27 output
|
||||
NULL
|
||||
|
||||
|
||||
-- !query 28
|
||||
select null * interval '2 seconds'
|
||||
-- !query 28 schema
|
||||
struct<multiply_interval(2 seconds, CAST(NULL AS DOUBLE)):interval>
|
||||
-- !query 28 output
|
||||
NULL
|
||||
|
||||
|
||||
-- !query 29
|
||||
select -interval '-1 month 1 day -1 second'
|
||||
-- !query 29 schema
|
||||
struct<1 months -1 days 1 seconds:interval>
|
||||
-- !query 29 output
|
||||
1 months -1 days 1 seconds
|
||||
|
||||
|
||||
-- !query 30
|
||||
select -interval -1 month 1 day -1 second
|
||||
-- !query 30 schema
|
||||
struct<1 months -1 days 1 seconds:interval>
|
||||
-- !query 30 output
|
||||
1 months -1 days 1 seconds
|
||||
|
|
Loading…
Reference in a new issue