Skip to content

Commit e09feda

Browse files
committed
[SPARK-35978][SQL] Support non-reserved keyword TIMESTAMP_LTZ
### What changes were proposed in this pull request? Support new keyword `TIMESTAMP_LTZ`, which can be used for: - timestamp with local time zone data type in DDL - timestamp with local time zone data type in Cast clause. - timestamp with local time zone data type literal ### Why are the changes needed? Users can use `TIMESTAMP_LTZ` in DDL/Cast/Literals for the timestamp with local time zone type directly. The new keyword is independent of the SQL configuration `spark.sql.timestampType`. ### Does this PR introduce _any_ user-facing change? No, the new timestamp type is not released yet. ### How was this patch tested? Unit test Closes #33224 from gengliangwang/TIMESTAMP_LTZ. Authored-by: Gengliang Wang <[email protected]> Signed-off-by: Gengliang Wang <[email protected]> (cherry picked from commit b0b9643) Signed-off-by: Gengliang Wang <[email protected]>
1 parent 22b303a commit e09feda

File tree

3 files changed

+26
-10
lines changed

3 files changed

+26
-10
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,13 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
21192119
throw QueryParsingErrors.cannotParseValueTypeError(valueType, value, ctx)
21202120
}
21212121
}
2122+
2123+
def constructTimestampLTZLiteral(value: String): Literal = {
2124+
val zoneId = getZoneId(conf.sessionLocalTimeZone)
2125+
val specialTs = convertSpecialTimestamp(value, zoneId).map(Literal(_, TimestampType))
2126+
specialTs.getOrElse(toLiteral(stringToTimestamp(_, zoneId), TimestampType))
2127+
}
2128+
21222129
try {
21232130
valueType match {
21242131
case "DATE" =>
@@ -2128,13 +2135,9 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
21282135
case "TIMESTAMP_NTZ" =>
21292136
val specialTs = convertSpecialTimestampNTZ(value).map(Literal(_, TimestampNTZType))
21302137
specialTs.getOrElse(toLiteral(stringToTimestampWithoutTimeZone, TimestampNTZType))
2138+
case "TIMESTAMP_LTZ" =>
2139+
constructTimestampLTZLiteral(value)
21312140
case "TIMESTAMP" =>
2132-
def constructTimestampLTZLiteral(value: String): Literal = {
2133-
val zoneId = getZoneId(conf.sessionLocalTimeZone)
2134-
val specialTs = convertSpecialTimestamp(value, zoneId).map(Literal(_, TimestampType))
2135-
specialTs.getOrElse(toLiteral(stringToTimestamp(_, zoneId), TimestampType))
2136-
}
2137-
21382141
SQLConf.get.timestampType match {
21392142
case TimestampNTZType =>
21402143
val specialTs = convertSpecialTimestampNTZ(value).map(Literal(_, TimestampNTZType))
@@ -2529,6 +2532,7 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg
25292532
case ("date", Nil) => DateType
25302533
case ("timestamp", Nil) => SQLConf.get.timestampType
25312534
case ("timestamp_ntz", Nil) => TimestampNTZType
2535+
case ("timestamp_ltz", Nil) => TimestampType
25322536
case ("string", Nil) => StringType
25332537
case ("character" | "char", length :: Nil) => CharType(length.getText.toInt)
25342538
case ("varchar", length :: Nil) => VarcharType(length.getText.toInt)

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DataTypeParserSuite.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class DataTypeParserSuite extends SparkFunSuite with SQLHelper {
5959
checkDataType("DATE", DateType)
6060
checkDataType("timestamp", TimestampType)
6161
checkDataType("timestamp_ntz", TimestampNTZType)
62+
checkDataType("timestamp_ltz", TimestampType)
6263
checkDataType("string", StringType)
6364
checkDataType("ChaR(5)", CharType(5))
6465
checkDataType("ChaRacter(5)", CharType(5))

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/ExpressionParserSuite.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,17 @@ class ExpressionParserSuite extends AnalysisTest {
455455
}
456456

457457
test("type constructors") {
458+
def checkTimestampNTZAndLTZ(): Unit = {
459+
// Timestamp with local time zone
460+
assertEqual("tImEstAmp_LTZ '2016-03-11 20:54:00.000'",
461+
Literal(Timestamp.valueOf("2016-03-11 20:54:00.000")))
462+
intercept("timestamP_LTZ '2016-33-11 20:54:00.000'", "Cannot parse the TIMESTAMP_LTZ value")
463+
// Timestamp without time zone
464+
assertEqual("tImEstAmp_Ntz '2016-03-11 20:54:00.000'",
465+
Literal(LocalDateTime.parse("2016-03-11T20:54:00.000")))
466+
intercept("tImEstAmp_Ntz '2016-33-11 20:54:00.000'", "Cannot parse the TIMESTAMP_NTZ value")
467+
}
468+
458469
// Dates.
459470
assertEqual("dAte '2016-03-11'", Literal(Date.valueOf("2016-03-11")))
460471
intercept("DAtE 'mar 11 2016'", "Cannot parse the DATE value")
@@ -464,10 +475,7 @@ class ExpressionParserSuite extends AnalysisTest {
464475
Literal(Timestamp.valueOf("2016-03-11 20:54:00.000")))
465476
intercept("timestamP '2016-33-11 20:54:00.000'", "Cannot parse the TIMESTAMP value")
466477

467-
// Timestamp without time zone
468-
assertEqual("tImEstAmp_Ntz '2016-03-11 20:54:00.000'",
469-
Literal(LocalDateTime.parse("2016-03-11T20:54:00.000")))
470-
intercept("tImEstAmp_Ntz '2016-33-11 20:54:00.000'", "Cannot parse the TIMESTAMP_NTZ value")
478+
checkTimestampNTZAndLTZ()
471479
withSQLConf(SQLConf.TIMESTAMP_TYPE.key -> TimestampTypes.TIMESTAMP_NTZ.toString) {
472480
assertEqual("tImEstAmp '2016-03-11 20:54:00.000'",
473481
Literal(LocalDateTime.parse("2016-03-11T20:54:00.000")))
@@ -477,6 +485,9 @@ class ExpressionParserSuite extends AnalysisTest {
477485
// If the timestamp string contains time zone, return a timestamp with local time zone literal
478486
assertEqual("tImEstAmp '1970-01-01 00:00:00.000 +01:00'",
479487
Literal(-3600000000L, TimestampType))
488+
489+
// The behavior of TIMESTAMP_NTZ and TIMESTAMP_LTZ is independent of SQLConf.TIMESTAMP_TYPE
490+
checkTimestampNTZAndLTZ()
480491
}
481492
// Interval.
482493
val intervalLiteral = Literal(IntervalUtils.stringToInterval("interval 3 month 1 hour"))

0 commit comments

Comments
 (0)