diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala index 680d781b8b..d6363b53f7 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala @@ -2119,6 +2119,13 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg throw QueryParsingErrors.cannotParseValueTypeError(valueType, value, ctx) } } + + def constructTimestampLTZLiteral(value: String): Literal = { + val zoneId = getZoneId(conf.sessionLocalTimeZone) + val specialTs = convertSpecialTimestamp(value, zoneId).map(Literal(_, TimestampType)) + specialTs.getOrElse(toLiteral(stringToTimestamp(_, zoneId), TimestampType)) + } + try { valueType match { case "DATE" => @@ -2128,13 +2135,9 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg case "TIMESTAMP_NTZ" => val specialTs = convertSpecialTimestampNTZ(value).map(Literal(_, TimestampNTZType)) specialTs.getOrElse(toLiteral(stringToTimestampWithoutTimeZone, TimestampNTZType)) + case "TIMESTAMP_LTZ" => + constructTimestampLTZLiteral(value) case "TIMESTAMP" => - def constructTimestampLTZLiteral(value: String): Literal = { - val zoneId = getZoneId(conf.sessionLocalTimeZone) - val specialTs = convertSpecialTimestamp(value, zoneId).map(Literal(_, TimestampType)) - specialTs.getOrElse(toLiteral(stringToTimestamp(_, zoneId), TimestampType)) - } - SQLConf.get.timestampType match { case TimestampNTZType => val specialTs = convertSpecialTimestampNTZ(value).map(Literal(_, TimestampNTZType)) @@ -2529,6 +2532,7 @@ class AstBuilder extends SqlBaseBaseVisitor[AnyRef] with SQLConfHelper with Logg case ("date", Nil) => DateType case ("timestamp", Nil) => SQLConf.get.timestampType case ("timestamp_ntz", Nil) => TimestampNTZType + case ("timestamp_ltz", Nil) => TimestampType case ("string", Nil) => StringType case ("character" | "char", length :: Nil) => CharType(length.getText.toInt) case ("varchar", length :: Nil) => VarcharType(length.getText.toInt) diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DataTypeParserSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DataTypeParserSuite.scala index d34cccc8d2..97dd0db7b6 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DataTypeParserSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DataTypeParserSuite.scala @@ -59,6 +59,7 @@ class DataTypeParserSuite extends SparkFunSuite with SQLHelper { checkDataType("DATE", DateType) checkDataType("timestamp", TimestampType) checkDataType("timestamp_ntz", TimestampNTZType) + checkDataType("timestamp_ltz", TimestampType) checkDataType("string", StringType) checkDataType("ChaR(5)", CharType(5)) checkDataType("ChaRacter(5)", CharType(5)) 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 7b13fa9222..818afc4289 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 @@ -455,6 +455,17 @@ class ExpressionParserSuite extends AnalysisTest { } test("type constructors") { + def checkTimestampNTZAndLTZ(): Unit = { + // Timestamp with local time zone + assertEqual("tImEstAmp_LTZ '2016-03-11 20:54:00.000'", + Literal(Timestamp.valueOf("2016-03-11 20:54:00.000"))) + intercept("timestamP_LTZ '2016-33-11 20:54:00.000'", "Cannot parse the TIMESTAMP_LTZ value") + // Timestamp without time zone + assertEqual("tImEstAmp_Ntz '2016-03-11 20:54:00.000'", + Literal(LocalDateTime.parse("2016-03-11T20:54:00.000"))) + intercept("tImEstAmp_Ntz '2016-33-11 20:54:00.000'", "Cannot parse the TIMESTAMP_NTZ value") + } + // Dates. assertEqual("dAte '2016-03-11'", Literal(Date.valueOf("2016-03-11"))) intercept("DAtE 'mar 11 2016'", "Cannot parse the DATE value") @@ -464,10 +475,7 @@ class ExpressionParserSuite extends AnalysisTest { Literal(Timestamp.valueOf("2016-03-11 20:54:00.000"))) intercept("timestamP '2016-33-11 20:54:00.000'", "Cannot parse the TIMESTAMP value") - // Timestamp without time zone - assertEqual("tImEstAmp_Ntz '2016-03-11 20:54:00.000'", - Literal(LocalDateTime.parse("2016-03-11T20:54:00.000"))) - intercept("tImEstAmp_Ntz '2016-33-11 20:54:00.000'", "Cannot parse the TIMESTAMP_NTZ value") + checkTimestampNTZAndLTZ() withSQLConf(SQLConf.TIMESTAMP_TYPE.key -> TimestampTypes.TIMESTAMP_NTZ.toString) { assertEqual("tImEstAmp '2016-03-11 20:54:00.000'", Literal(LocalDateTime.parse("2016-03-11T20:54:00.000"))) @@ -477,6 +485,9 @@ class ExpressionParserSuite extends AnalysisTest { // If the timestamp string contains time zone, return a timestamp with local time zone literal assertEqual("tImEstAmp '1970-01-01 00:00:00.000 +01:00'", Literal(-3600000000L, TimestampType)) + + // The behavior of TIMESTAMP_NTZ and TIMESTAMP_LTZ is independent of SQLConf.TIMESTAMP_TYPE + checkTimestampNTZAndLTZ() } // Interval. val intervalLiteral = Literal(IntervalUtils.stringToInterval("interval 3 month 1 hour"))