diff --git a/docs/sql-reserved-and-non-reserved-keywords.md b/docs/sql-reserved-and-non-reserved-keywords.md
index 53eb9988f6c8..b1561fb593f3 100644
--- a/docs/sql-reserved-and-non-reserved-keywords.md
+++ b/docs/sql-reserved-and-non-reserved-keywords.md
@@ -137,7 +137,8 @@ The list of reserved and non-reserved keywords can change according to the confi
| DATABASE | non-reserved | non-reserved | non-reserved |
| DATABASES | non-reserved | non-reserved | non-reserved |
| DATE | non-reserved | non-reserved | reserved |
- | DAY | non-reserved | non-reserved | reserved |
+ | DAY | reserved | non-reserved | reserved |
+ | DAYS | non-reserved | non-reserved | non-reserved |
| DBPROPERTIES | non-reserved | non-reserved | non-reserved |
| DEALLOCATE | non-reserved | non-reserved | reserved |
| DEC | non-reserved | non-reserved | reserved |
@@ -230,7 +231,8 @@ The list of reserved and non-reserved keywords can change according to the confi
| HANDLER | non-reserved | non-reserved | reserved |
| HAVING | reserved | non-reserved | reserved |
| HOLD | non-reserved | non-reserved | reserved |
- | HOUR | non-reserved | non-reserved | reserved |
+ | HOUR | reserved | non-reserved | reserved |
+ | HOURS | non-reserved | non-reserved | non-reserved |
| IDENTITY | non-reserved | non-reserved | reserved |
| IF | non-reserved | non-reserved | reserved |
| IGNORE | non-reserved | non-reserved | non-reserved |
@@ -313,13 +315,19 @@ The list of reserved and non-reserved keywords can change according to the confi
| MEMBER | non-reserved | non-reserved | reserved |
| MERGE | non-reserved | non-reserved | reserved |
| METHOD | non-reserved | non-reserved | reserved |
+ | MICROSECOND | non-reserved | non-reserved | non-reserved |
+ | MICROSECONDS | non-reserved | non-reserved | non-reserved |
+ | MILLISECOND | non-reserved | non-reserved | non-reserved |
+ | MILLISECONDS | non-reserved | non-reserved | non-reserved |
| MIN | non-reserved | non-reserved | reserved |
| MINUS | reserved | reserved | non-reserved |
- | MINUTE | non-reserved | non-reserved | reserved |
+ | MINUTE | reserved | non-reserved | reserved |
+ | MINUTES | non-reserved | non-reserved | non-reserved |
| MOD | non-reserved | non-reserved | reserved |
| MODIFIES | non-reserved | non-reserved | reserved |
| MODULE | non-reserved | non-reserved | reserved |
- | MONTH | non-reserved | non-reserved | reserved |
+ | MONTH | reserved | non-reserved | reserved |
+ | MONTHS | non-reserved | non-reserved | non-reserved |
| MSCK | non-reserved | non-reserved | non-reserved |
| MULTISET | non-reserved | non-reserved | reserved |
| NAMES | non-reserved | non-reserved | non-reserved |
@@ -448,7 +456,8 @@ The list of reserved and non-reserved keywords can change according to the confi
| SCOPE | non-reserved | non-reserved | reserved |
| SCROLL | non-reserved | non-reserved | reserved |
| SEARCH | non-reserved | non-reserved | reserved |
- | SECOND | non-reserved | non-reserved | reserved |
+ | SECOND | reserved | non-reserved | reserved |
+ | SECONDS | non-reserved | non-reserved | non-reserved |
| SECTION | non-reserved | non-reserved | non-reserved |
| SEEK | non-reserved | non-reserved | non-reserved |
| SELECT | reserved | non-reserved | reserved |
@@ -568,8 +577,11 @@ The list of reserved and non-reserved keywords can change according to the confi
| WITH | reserved | non-reserved | reserved |
| WITHIN | non-reserved | non-reserved | reserved |
| WITHOUT | non-reserved | non-reserved | reserved |
+ | WEEK | non-reserved | non-reserved | non-reserved |
+ | WEEKS | non-reserved | non-reserved | non-reserved |
| WORK | non-reserved | non-reserved | non-reserved |
| WRITE | non-reserved | non-reserved | non-reserved |
- | YEAR | non-reserved | non-reserved | reserved |
+ | YEAR | reserved | non-reserved | reserved |
+ | YEARS | non-reserved | non-reserved | non-reserved |
| ZONE | non-reserved | non-reserved | non-reserved |
diff --git a/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 b/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4
index d11c28c18494..be36aaa538b4 100644
--- a/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4
+++ b/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4
@@ -655,11 +655,12 @@ booleanValue
;
interval
- : INTERVAL intervalField*
+ : {ansi}? INTERVAL? intervalField+
+ | {!ansi}? INTERVAL intervalField*
;
intervalField
- : value=intervalValue unit=identifier (TO to=identifier)?
+ : value=intervalValue unit=intervalUnit (TO to=intervalUnit)?
;
intervalValue
@@ -667,6 +668,27 @@ intervalValue
| STRING
;
+intervalUnit
+ : DAY
+ | DAYS
+ | HOUR
+ | HOURS
+ | MICROSECOND
+ | MICROSECONDS
+ | MILLISECOND
+ | MILLISECONDS
+ | MINUTE
+ | MINUTES
+ | MONTH
+ | MONTHS
+ | SECOND
+ | SECONDS
+ | WEEK
+ | WEEKS
+ | YEAR
+ | YEARS
+ ;
+
colPosition
: FIRST | AFTER identifier
;
@@ -795,6 +817,7 @@ ansiNonReserved
| DATA
| DATABASE
| DATABASES
+ | DAYS
| DBPROPERTIES
| DEFINED
| DELETE
@@ -825,6 +848,7 @@ ansiNonReserved
| FUNCTIONS
| GLOBAL
| GROUPING
+ | HOURS
| IF
| IGNORE
| IMPORT
@@ -851,6 +875,12 @@ ansiNonReserved
| LOGICAL
| MACRO
| MAP
+ | MICROSECOND
+ | MICROSECONDS
+ | MILLISECOND
+ | MILLISECONDS
+ | MINUTES
+ | MONTHS
| MSCK
| NO
| NULLS
@@ -891,6 +921,7 @@ ansiNonReserved
| ROW
| ROWS
| SCHEMA
+ | SECONDS
| SEPARATED
| SERDE
| SERDEPROPERTIES
@@ -924,7 +955,10 @@ ansiNonReserved
| USE
| VALUES
| VIEW
+ | WEEK
+ | WEEKS
| WINDOW
+ | YEARS
;
defaultReserved
@@ -996,6 +1030,8 @@ nonReserved
| DATA
| DATABASE
| DATABASES
+ | DAY
+ | DAYS
| DBPROPERTIES
| DEFINED
| DELETE
@@ -1037,6 +1073,8 @@ nonReserved
| GROUP
| GROUPING
| HAVING
+ | HOUR
+ | HOURS
| IF
| IGNORE
| IMPORT
@@ -1067,6 +1105,14 @@ nonReserved
| LOGICAL
| MACRO
| MAP
+ | MICROSECOND
+ | MICROSECONDS
+ | MILLISECOND
+ | MILLISECONDS
+ | MINUTE
+ | MINUTES
+ | MONTH
+ | MONTHS
| MSCK
| NO
| NOT
@@ -1115,6 +1161,8 @@ nonReserved
| ROLLUP
| ROW
| ROWS
+ | SECOND
+ | SECONDS
| SELECT
| SEPARATED
| SERDE
@@ -1157,10 +1205,14 @@ nonReserved
| USER
| VALUES
| VIEW
+ | WEEK
+ | WEEKS
| WHEN
| WHERE
| WINDOW
| WITH
+ | YEAR
+ | YEARS
;
SELECT: 'SELECT';
@@ -1199,6 +1251,24 @@ ASC: 'ASC';
DESC: 'DESC';
FOR: 'FOR';
INTERVAL: 'INTERVAL';
+YEAR: 'YEAR';
+YEARS: 'YEARS';
+MONTH: 'MONTH';
+MONTHS: 'MONTHS';
+WEEK: 'WEEK';
+WEEKS: 'WEEKS';
+DAY: 'DAY';
+DAYS: 'DAYS';
+HOUR: 'HOUR';
+HOURS: 'HOURS';
+MINUTE: 'MINUTE';
+MINUTES: 'MINUTES';
+SECOND: 'SECOND';
+SECONDS: 'SECONDS';
+MILLISECOND: 'MILLISECOND';
+MILLISECONDS: 'MILLISECONDS';
+MICROSECOND: 'MICROSECOND';
+MICROSECONDS: 'MICROSECONDS';
CASE: 'CASE';
WHEN: 'WHEN';
THEN: 'THEN';
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/ExpressionParserSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/ExpressionParserSuite.scala
index 7ecad5927dc5..d1d0d38f40d9 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/ExpressionParserSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/ExpressionParserSuite.scala
@@ -39,7 +39,6 @@ import org.apache.spark.unsafe.types.CalendarInterval
* CheckAnalysis classes.
*/
class ExpressionParserSuite extends PlanTest {
- import CatalystSqlParser._
import org.apache.spark.sql.catalyst.dsl.expressions._
import org.apache.spark.sql.catalyst.dsl.plans._
@@ -585,49 +584,60 @@ class ExpressionParserSuite extends PlanTest {
}
}
+ val intervalUnits = Seq(
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond")
+
+ def intervalLiteral(u: String, s: String): Literal = {
+ Literal(CalendarInterval.fromSingleUnitString(u, s))
+ }
+
test("intervals") {
- def intervalLiteral(u: String, s: String): Literal = {
- Literal(CalendarInterval.fromSingleUnitString(u, s))
+ def checkIntervals(intervalValue: String, expected: Literal): Unit = {
+ assertEqual(s"interval $intervalValue", expected)
+
+ // SPARK-23264 Support interval values without INTERVAL clauses if ANSI SQL enabled
+ withSQLConf(SQLConf.ANSI_SQL_PARSER.key -> "true") {
+ assertEqual(intervalValue, expected)
+ }
}
// Empty interval statement
intercept("interval", "at least one time unit should be given for interval literal")
// Single Intervals.
- val units = Seq(
- "year",
- "month",
- "week",
- "day",
- "hour",
- "minute",
- "second",
- "millisecond",
- "microsecond")
val forms = Seq("", "s")
val values = Seq("0", "10", "-7", "21")
- units.foreach { unit =>
+ intervalUnits.foreach { unit =>
forms.foreach { form =>
values.foreach { value =>
val expected = intervalLiteral(unit, value)
- assertEqual(s"interval $value $unit$form", expected)
- assertEqual(s"interval '$value' $unit$form", expected)
+ checkIntervals(s"$value $unit$form", expected)
+ checkIntervals(s"'$value' $unit$form", expected)
}
}
}
// Hive nanosecond notation.
- assertEqual("interval 13.123456789 seconds", intervalLiteral("second", "13.123456789"))
- assertEqual("interval -13.123456789 second", intervalLiteral("second", "-13.123456789"))
+ checkIntervals("13.123456789 seconds", intervalLiteral("second", "13.123456789"))
+ checkIntervals("-13.123456789 second", intervalLiteral("second", "-13.123456789"))
// Non Existing unit
- intercept("interval 10 nanoseconds", "No interval can be constructed")
+ intercept("interval 10 nanoseconds",
+ "no viable alternative at input 'interval 10 nanoseconds'")
// Year-Month intervals.
val yearMonthValues = Seq("123-10", "496-0", "-2-3", "-123-0")
yearMonthValues.foreach { value =>
val result = Literal(CalendarInterval.fromYearMonthString(value))
- assertEqual(s"interval '$value' year to month", result)
+ checkIntervals(s"'$value' year to month", result)
}
// Day-Time intervals.
@@ -640,22 +650,51 @@ class ExpressionParserSuite extends PlanTest {
"1 0:0:1")
datTimeValues.foreach { value =>
val result = Literal(CalendarInterval.fromDayTimeString(value))
- assertEqual(s"interval '$value' day to second", result)
+ checkIntervals(s"'$value' day to second", result)
}
// Unknown FROM TO intervals
- intercept("interval 10 month to second", "Intervals FROM month TO second are not supported.")
+ intercept("interval 10 month to second",
+ "Intervals FROM month TO second are not supported.")
// Composed intervals.
- assertEqual(
- "interval 3 months 22 seconds 1 millisecond",
+ checkIntervals(
+ "3 months 22 seconds 1 millisecond",
Literal(new CalendarInterval(3, 22001000L)))
- assertEqual(
- "interval 3 years '-1-10' year to month 3 weeks '1 0:0:2' day to second",
+ checkIntervals(
+ "3 years '-1-10' year to month 3 weeks '1 0:0:2' day to second",
Literal(new CalendarInterval(14,
22 * CalendarInterval.MICROS_PER_DAY + 2 * CalendarInterval.MICROS_PER_SECOND)))
}
+ test("SPARK-23264 Interval Compatibility tests") {
+ def checkIntervals(intervalValue: String, expected: Literal): Unit = {
+ withSQLConf(SQLConf.ANSI_SQL_PARSER.key -> "true") {
+ assertEqual(intervalValue, expected)
+ }
+
+ // Compatibility tests: If ANSI SQL disabled, `intervalValue` should be parsed as an alias
+ withSQLConf(SQLConf.ANSI_SQL_PARSER.key -> "false") {
+ val aliases = defaultParser.parseExpression(intervalValue).collect {
+ case a @ Alias(_: Literal, name)
+ if intervalUnits.exists { unit => name.startsWith(unit) } => a
+ }
+ assert(aliases.size === 1)
+ }
+ }
+ val forms = Seq("", "s")
+ val values = Seq("5", "1", "-11", "8")
+ intervalUnits.foreach { unit =>
+ forms.foreach { form =>
+ values.foreach { value =>
+ val expected = intervalLiteral(unit, value)
+ checkIntervals(s"$value $unit$form", expected)
+ checkIntervals(s"'$value' $unit$form", expected)
+ }
+ }
+ }
+ }
+
test("composed expressions") {
assertEqual("1 + r.r As q", (Literal(1) + UnresolvedAttribute("r.r")).as("q"))
assertEqual("1 - f('o', o(bar))", Literal(1) - 'f.function("o", 'o.function('bar)))
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/TableIdentifierParserSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/TableIdentifierParserSuite.scala
index 2725deb1d209..4dfd8172cb2b 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/TableIdentifierParserSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/TableIdentifierParserSuite.scala
@@ -74,6 +74,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"date",
"datetime",
"day",
+ "days",
"dbproperties",
"decimal",
"deferred",
@@ -113,6 +114,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"grouping",
"hold_ddltime",
"hour",
+ "hours",
"idxproperties",
"ignore",
"import",
@@ -148,9 +150,15 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"mapjoin",
"materialized",
"metadata",
+ "microsecond",
+ "microseconds",
+ "millisecond",
+ "milliseconds",
"minus",
"minute",
+ "minutes",
"month",
+ "months",
"msck",
"no_drop",
"none",
@@ -205,6 +213,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"rows",
"schemas",
"second",
+ "seconds",
"serde",
"serdeproperties",
"server",
@@ -253,11 +262,14 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"utctimestamp",
"values",
"view",
+ "week",
+ "weeks",
"while",
"with",
"work",
"write",
- "year")
+ "year",
+ "years")
val hiveStrictNonReservedKeyword = Seq(
"anti",
@@ -408,6 +420,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"databases",
"date",
"day",
+ "days",
"dbproperties",
"deallocate",
"dec",
@@ -500,6 +513,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"having",
"hold",
"hour",
+ "hours",
"identity",
"if",
"ignore",
@@ -582,13 +596,19 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"member",
"merge",
"method",
+ "microsecond",
+ "microseconds",
+ "millisecond",
+ "milliseconds",
"min",
"minus",
"minute",
+ "minutes",
"mod",
"modifies",
"module",
"month",
+ "months",
"msck",
"multiset",
"names",
@@ -716,6 +736,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"scroll",
"search",
"second",
+ "seconds",
"section",
"seek",
"select",
@@ -826,6 +847,8 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"varying",
"versioning",
"view",
+ "week",
+ "weeks",
"when",
"whenever",
"where",
@@ -838,6 +861,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"work",
"write",
"year",
+ "years",
"zone")
val reservedKeywordsInAnsiMode = Set(
@@ -860,6 +884,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"current_time",
"current_timestamp",
"current_user",
+ "day",
"distinct",
"else",
"end",
@@ -873,6 +898,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"grant",
"group",
"having",
+ "hour",
"in",
"inner",
"intersect",
@@ -881,6 +907,8 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"is",
"leading",
"left",
+ "minute",
+ "month",
"natural",
"not",
"null",
@@ -897,6 +925,7 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"semi",
"session_user",
"minus",
+ "second",
"some",
"table",
"then",
@@ -908,7 +937,8 @@ class TableIdentifierParserSuite extends SparkFunSuite with SQLHelper {
"using",
"when",
"where",
- "with")
+ "with",
+ "year")
val nonReservedKeywordsInAnsiMode = allCandidateKeywords -- reservedKeywordsInAnsiMode
diff --git a/sql/core/src/test/resources/sql-tests/inputs/ansi/interval.sql b/sql/core/src/test/resources/sql-tests/inputs/ansi/interval.sql
new file mode 100644
index 000000000000..f2f4b02c8634
--- /dev/null
+++ b/sql/core/src/test/resources/sql-tests/inputs/ansi/interval.sql
@@ -0,0 +1,188 @@
+-- Turns on ANSI mode
+SET spark.sql.parser.ansi.enabled=true;
+
+select
+ '1' second,
+ 2 seconds,
+ '1' minute,
+ 2 minutes,
+ '1' hour,
+ 2 hours,
+ '1' day,
+ 2 days,
+ '1' month,
+ 2 months,
+ '1' year,
+ 2 years;
+
+select
+ interval '10-11' year to month,
+ interval '10' year,
+ interval '11' month;
+
+select
+ '10-11' year to month,
+ '10' year,
+ '11' month;
+
+select
+ interval '10 9:8:7.987654321' day to second,
+ interval '10' day,
+ interval '11' hour,
+ interval '12' minute,
+ interval '13' second,
+ interval '13.123456789' second;
+
+select
+ '10 9:8:7.987654321' day to second,
+ '10' day,
+ '11' hour,
+ '12' minute,
+ '13' second,
+ '13.123456789' second;
+
+select map(1, interval 1 day, 2, interval 3 week);
+
+select map(1, 1 day, 2, 3 week);
+
+-- Interval year-month arithmetic
+
+create temporary view interval_arithmetic as
+ select CAST(dateval AS date), CAST(tsval AS timestamp) from values
+ ('2012-01-01', '2012-01-01')
+ as interval_arithmetic(dateval, tsval);
+
+select
+ dateval,
+ dateval - interval '2-2' year to month,
+ dateval - interval '-2-2' year to month,
+ dateval + interval '2-2' year to month,
+ dateval + interval '-2-2' year to month,
+ - interval '2-2' year to month + dateval,
+ interval '2-2' year to month + dateval
+from interval_arithmetic;
+
+select
+ dateval,
+ dateval - '2-2' year to month,
+ dateval - '-2-2' year to month,
+ dateval + '2-2' year to month,
+ dateval + '-2-2' year to month,
+ - '2-2' year to month + dateval,
+ '2-2' year to month + dateval
+from interval_arithmetic;
+
+select
+ tsval,
+ tsval - interval '2-2' year to month,
+ tsval - interval '-2-2' year to month,
+ tsval + interval '2-2' year to month,
+ tsval + interval '-2-2' year to month,
+ - interval '2-2' year to month + tsval,
+ interval '2-2' year to month + tsval
+from interval_arithmetic;
+
+select
+ tsval,
+ tsval - '2-2' year to month,
+ tsval - '-2-2' year to month,
+ tsval + '2-2' year to month,
+ tsval + '-2-2' year to month,
+ - '2-2' year to month + tsval,
+ '2-2' year to month + tsval
+from interval_arithmetic;
+
+select
+ interval '2-2' year to month + interval '3-3' year to month,
+ interval '2-2' year to month - interval '3-3' year to month
+from interval_arithmetic;
+
+select
+ '2-2' year to month + '3-3' year to month,
+ '2-2' year to month - '3-3' year to month
+from interval_arithmetic;
+
+-- Interval day-time arithmetic
+
+select
+ dateval,
+ dateval - interval '99 11:22:33.123456789' day to second,
+ dateval - interval '-99 11:22:33.123456789' day to second,
+ dateval + interval '99 11:22:33.123456789' day to second,
+ dateval + interval '-99 11:22:33.123456789' day to second,
+ -interval '99 11:22:33.123456789' day to second + dateval,
+ interval '99 11:22:33.123456789' day to second + dateval
+from interval_arithmetic;
+
+select
+ dateval,
+ dateval - '99 11:22:33.123456789' day to second,
+ dateval - '-99 11:22:33.123456789' day to second,
+ dateval + '99 11:22:33.123456789' day to second,
+ dateval + '-99 11:22:33.123456789' day to second,
+ - '99 11:22:33.123456789' day to second + dateval,
+ '99 11:22:33.123456789' day to second + dateval
+from interval_arithmetic;
+
+select
+ tsval,
+ tsval - interval '99 11:22:33.123456789' day to second,
+ tsval - interval '-99 11:22:33.123456789' day to second,
+ tsval + interval '99 11:22:33.123456789' day to second,
+ tsval + interval '-99 11:22:33.123456789' day to second,
+ -interval '99 11:22:33.123456789' day to second + tsval,
+ interval '99 11:22:33.123456789' day to second + tsval
+from interval_arithmetic;
+
+select
+ tsval,
+ tsval - '99 11:22:33.123456789' day to second,
+ tsval - '-99 11:22:33.123456789' day to second,
+ tsval + '99 11:22:33.123456789' day to second,
+ tsval + '-99 11:22:33.123456789' day to second,
+ - '99 11:22:33.123456789' day to second + tsval,
+ '99 11:22:33.123456789' day to second + tsval
+from interval_arithmetic;
+
+select
+ interval '99 11:22:33.123456789' day to second + interval '10 9:8:7.123456789' day to second,
+ interval '99 11:22:33.123456789' day to second - interval '10 9:8:7.123456789' day to second
+from interval_arithmetic;
+
+select
+ '99 11:22:33.123456789' day to second + '10 9:8:7.123456789' day to second,
+ '99 11:22:33.123456789' day to second - '10 9:8:7.123456789' day to second
+from interval_arithmetic;
+
+-- More tests for interval syntax alternatives
+
+select 30 day;
+
+select 30 day day;
+
+select 30 day day day;
+
+select date '2012-01-01' - 30 day;
+
+select date '2012-01-01' - 30 day day;
+
+select date '2012-01-01' - 30 day day day;
+
+select date '2012-01-01' + '-30' day;
+
+select date '2012-01-01' + interval '-30' day;
+
+-- Unsupported syntax for intervals
+
+select date '2012-01-01' + interval (-30) day;
+
+select date '2012-01-01' + (-30) day;
+
+create temporary view t as select * from values (1), (2) as t(a);
+
+select date '2012-01-01' + interval (a + 1) day from t;
+
+select date '2012-01-01' + (a + 1) day from t;
+
+-- Turns off ANSI mode
+SET spark.sql.parser.ansi.enabled=false;
diff --git a/sql/core/src/test/resources/sql-tests/results/ansi/interval.sql.out b/sql/core/src/test/resources/sql-tests/results/ansi/interval.sql.out
new file mode 100644
index 000000000000..1f8b5b6ebee7
--- /dev/null
+++ b/sql/core/src/test/resources/sql-tests/results/ansi/interval.sql.out
@@ -0,0 +1,439 @@
+-- Automatically generated by SQLQueryTestSuite
+-- Number of queries: 35
+
+
+-- !query 0
+SET spark.sql.parser.ansi.enabled=true
+-- !query 0 schema
+struct
+-- !query 0 output
+spark.sql.parser.ansi.enabled true
+
+
+-- !query 1
+select
+ '1' second,
+ 2 seconds,
+ '1' minute,
+ 2 minutes,
+ '1' hour,
+ 2 hours,
+ '1' day,
+ 2 days,
+ '1' month,
+ 2 months,
+ '1' year,
+ 2 years
+-- !query 1 schema
+struct
+-- !query 1 output
+interval 1 seconds interval 2 seconds interval 1 minutes interval 2 minutes interval 1 hours interval 2 hours interval 1 days interval 2 days interval 1 months interval 2 months interval 1 years interval 2 years
+
+
+-- !query 2
+select
+ interval '10-11' year to month,
+ interval '10' year,
+ interval '11' month
+-- !query 2 schema
+struct
+-- !query 2 output
+interval 10 years 11 months interval 10 years interval 11 months
+
+
+-- !query 3
+select
+ '10-11' year to month,
+ '10' year,
+ '11' month
+-- !query 3 schema
+struct
+-- !query 3 output
+interval 10 years 11 months interval 10 years interval 11 months
+
+
+-- !query 4
+select
+ interval '10 9:8:7.987654321' day to second,
+ interval '10' day,
+ interval '11' hour,
+ interval '12' minute,
+ interval '13' second,
+ interval '13.123456789' second
+-- !query 4 schema
+struct
+-- !query 4 output
+interval 1 weeks 3 days 9 hours 8 minutes 7 seconds 987 milliseconds 654 microseconds interval 1 weeks 3 days interval 11 hours interval 12 minutes interval 13 seconds interval 13 seconds 123 milliseconds 456 microseconds
+
+
+-- !query 5
+select
+ '10 9:8:7.987654321' day to second,
+ '10' day,
+ '11' hour,
+ '12' minute,
+ '13' second,
+ '13.123456789' second
+-- !query 5 schema
+struct
+-- !query 5 output
+interval 1 weeks 3 days 9 hours 8 minutes 7 seconds 987 milliseconds 654 microseconds interval 1 weeks 3 days interval 11 hours interval 12 minutes interval 13 seconds interval 13 seconds 123 milliseconds 456 microseconds
+
+
+-- !query 6
+select map(1, interval 1 day, 2, interval 3 week)
+-- !query 6 schema
+struct