Skip to content

Commit 7818f04

Browse files
committed
Disable TIME type by default
1 parent bba8bb8 commit 7818f04

File tree

14 files changed

+99
-24
lines changed

14 files changed

+99
-24
lines changed

common/utils/src/main/resources/error/error-conditions.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7298,6 +7298,12 @@
72987298
],
72997299
"sqlState" : "0A001"
73007300
},
7301+
"UNSUPPORTED_TIME_TYPE" : {
7302+
"message" : [
7303+
"The data type TIME is not supported."
7304+
],
7305+
"sqlState" : "0A000"
7306+
},
73017307
"UNSUPPORTED_TYPED_LITERAL" : {
73027308
"message" : [
73037309
"Literals of the type <unsupportedType> are not supported. Supported types are <supportedTypes>."

sql/api/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBaseParser.g4

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ options { tokenVocab = SqlBaseLexer; }
4141
*/
4242
public boolean double_quoted_identifiers = false;
4343
44+
/**
45+
* When true, the TIME data type is recognized and supported.
46+
*/
47+
public boolean time_type_enabled = false;
48+
4449
/**
4550
* When false, parameter markers (? and :param) are only allowed in constant contexts.
4651
* When true, parameter markers are allowed everywhere a literal is supported.
@@ -1407,7 +1412,7 @@ nonTrivialPrimitiveType
14071412
(fromYearMonth=(YEAR | MONTH) (TO to=MONTH)? |
14081413
fromDayTime=(DAY | HOUR | MINUTE | SECOND) (TO to=(HOUR | MINUTE | SECOND))?)?
14091414
| TIMESTAMP (WITHOUT TIME ZONE)?
1410-
| TIME (LEFT_PAREN precision=integerValue RIGHT_PAREN)? (WITHOUT TIME ZONE)?
1415+
| {time_type_enabled}? TIME (LEFT_PAREN precision=integerValue RIGHT_PAREN)? (WITHOUT TIME ZONE)?
14111416
| GEOGRAPHY LEFT_PAREN (srid=integerValue | any=ANY) RIGHT_PAREN
14121417
| GEOMETRY LEFT_PAREN (srid=integerValue | any=ANY) RIGHT_PAREN
14131418
;

sql/api/src/main/scala/org/apache/spark/sql/catalyst/parser/parsers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ object AbstractParser extends Logging {
483483
parser.legacy_exponent_literal_as_decimal_enabled = conf.exponentLiteralAsDecimalEnabled
484484
parser.SQL_standard_keyword_behavior = conf.enforceReservedKeywords
485485
parser.double_quoted_identifiers = conf.doubleQuotedIdentifiers
486+
parser.time_type_enabled = conf.isTimeTypeEnabled
486487
parser.parameter_substitution_enabled = !conf.legacyParameterSubstitutionConstantsOnly
487488
parser.legacy_identifier_clause_only = conf.legacyIdentifierClauseOnly
488489
parser.single_character_pipe_operator_enabled = conf.singleCharacterPipeOperatorEnabled

sql/api/src/main/scala/org/apache/spark/sql/errors/CompilationErrors.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ private[sql] trait CompilationErrors extends DataTypeErrorsBase {
143143
errorClass = "CANNOT_MODIFY_CONFIG",
144144
messageParameters = Map("key" -> toSQLConf(key), "docroot" -> docroot))
145145
}
146+
147+
def unsupportedTimeTypeError(): Throwable = {
148+
new AnalysisException(
149+
errorClass = "UNSUPPORTED_TIME_TYPE",
150+
messageParameters = Map.empty)
151+
}
146152
}
147153

148154
private[sql] object CompilationErrors extends CompilationErrors

sql/api/src/main/scala/org/apache/spark/sql/internal/SqlApiConf.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ private[sql] trait SqlApiConf {
5353
def parserDfaCacheFlushRatio: Double
5454
def legacyParameterSubstitutionConstantsOnly: Boolean
5555
def legacyIdentifierClauseOnly: Boolean
56+
def isTimeTypeEnabled: Boolean
5657
}
5758

5859
private[sql] object SqlApiConf {
@@ -110,4 +111,5 @@ private[sql] object DefaultSqlApiConf extends SqlApiConf {
110111
override def parserDfaCacheFlushRatio: Double = -1.0
111112
override def legacyParameterSubstitutionConstantsOnly: Boolean = false
112113
override def legacyIdentifierClauseOnly: Boolean = false
114+
override def isTimeTypeEnabled: Boolean = false
113115
}

sql/api/src/main/scala/org/apache/spark/sql/types/TimeType.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
package org.apache.spark.sql.types
1919

2020
import org.apache.spark.annotation.Unstable
21-
import org.apache.spark.sql.errors.DataTypeErrors
21+
import org.apache.spark.sql.errors.{CompilationErrors, DataTypeErrors}
22+
import org.apache.spark.sql.internal.SqlApiConfHelper
2223

2324
/**
2425
* The time type represents a time value with fields hour, minute, second, up to microseconds. The
@@ -33,6 +34,10 @@ import org.apache.spark.sql.errors.DataTypeErrors
3334
@Unstable
3435
case class TimeType(precision: Int) extends AnyTimeType {
3536

37+
if (!SqlApiConfHelper.confGetter.get()().isTimeTypeEnabled) {
38+
throw CompilationErrors.unsupportedTimeTypeError()
39+
}
40+
3641
if (precision < TimeType.MIN_PRECISION || precision > TimeType.MAX_PRECISION) {
3742
throw DataTypeErrors.unsupportedTimePrecisionError(precision)
3843
}
@@ -54,5 +59,7 @@ object TimeType {
5459
val DEFAULT_PRECISION: Int = MICROS_PRECISION
5560
val NANOS_PRECISION: Int = 9
5661

57-
def apply(): TimeType = new TimeType(DEFAULT_PRECISION)
62+
def apply(): TimeType = {
63+
new TimeType(DEFAULT_PRECISION)
64+
}
5865
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/CatalystTypeConverters.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,12 @@ import java.sql.{Date, Timestamp}
2424
import java.time.{Duration, Instant, LocalDate, LocalDateTime, LocalTime, Period}
2525
import java.util.{Map => JavaMap}
2626
import javax.annotation.Nullable
27-
2827
import scala.language.existentials
29-
3028
import org.apache.spark.SparkIllegalArgumentException
3129
import org.apache.spark.sql.Row
3230
import org.apache.spark.sql.catalyst.expressions._
3331
import org.apache.spark.sql.catalyst.util._
32+
import org.apache.spark.sql.errors.{QueryCompilationErrors, QueryExecutionErrors}
3433
import org.apache.spark.sql.internal.SQLConf
3534
import org.apache.spark.sql.types._
3635
import org.apache.spark.sql.types.DayTimeIntervalType._
@@ -79,6 +78,8 @@ object CatalystTypeConverters {
7978
new GeometryConverter(g)
8079
case DateType if SQLConf.get.datetimeJava8ApiEnabled => LocalDateConverter
8180
case DateType => DateConverter
81+
case _: TimeType if !SQLConf.get.isTimeTypeEnabled =>
82+
QueryCompilationErrors.unsupportedTimeTypeError()
8283
case _: TimeType => TimeConverter
8384
case TimestampType if SQLConf.get.datetimeJava8ApiEnabled => InstantConverter
8485
case TimestampType => TimestampConverter

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/Cast.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import org.apache.spark.sql.catalyst.util._
3535
import org.apache.spark.sql.catalyst.util.DateTimeConstants._
3636
import org.apache.spark.sql.catalyst.util.DateTimeUtils._
3737
import org.apache.spark.sql.catalyst.util.IntervalUtils.{dayTimeIntervalToByte, dayTimeIntervalToDecimal, dayTimeIntervalToInt, dayTimeIntervalToLong, dayTimeIntervalToShort, yearMonthIntervalToByte, yearMonthIntervalToInt, yearMonthIntervalToShort}
38-
import org.apache.spark.sql.errors.{QueryErrorsBase, QueryExecutionErrors}
38+
import org.apache.spark.sql.errors.{QueryCompilationErrors, QueryErrorsBase, QueryExecutionErrors}
3939
import org.apache.spark.sql.internal.SQLConf
4040
import org.apache.spark.sql.types._
4141
import org.apache.spark.unsafe.types.{GeographyVal, UTF8String, VariantVal}
@@ -611,7 +611,12 @@ case class Cast(
611611
messageParameters = Map("other" -> other.toString))
612612
}
613613
if (canCast) {
614-
TypeCheckResult.TypeCheckSuccess
614+
// If the cast is to a time type, we first need to check if the time type is enabled.
615+
case _: TimeType if !SQLConf.get.isTimeTypeEnabled =>
616+
throw QueryCompilationErrors.unsupportedTimeTypeError()
617+
// If the cast is not to a time type, we can skip the time type check and continue.
618+
case _ =>
619+
TypeCheckResult.TypeCheckSuccess
615620
} else {
616621
typeCheckFailureInCast
617622
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/timeExpressions.scala

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,22 @@ import org.apache.spark.sql.catalyst.util.DateTimeUtils
3434
import org.apache.spark.sql.catalyst.util.TimeFormatter
3535
import org.apache.spark.sql.catalyst.util.TypeUtils.ordinalNumber
3636
import org.apache.spark.sql.errors.{QueryCompilationErrors, QueryExecutionErrors}
37+
import org.apache.spark.sql.internal.SQLConf
3738
import org.apache.spark.sql.internal.types.StringTypeWithCollation
3839
import org.apache.spark.sql.types.{AbstractDataType, AnyTimeType, ByteType, DataType, DayTimeIntervalType, Decimal, DecimalType, DoubleType, FloatType, IntegerType, IntegralType, LongType, NumericType, ObjectType, TimeType}
3940
import org.apache.spark.sql.types.DayTimeIntervalType.{HOUR, SECOND}
4041
import org.apache.spark.unsafe.types.UTF8String
4142

43+
trait TimeExpression extends Expression {
44+
override def checkInputDataTypes(): TypeCheckResult = {
45+
if (SQLConf.get.isTimeTypeEnabled) {
46+
super.checkInputDataTypes()
47+
} else {
48+
throw QueryCompilationErrors.unsupportedTimeTypeError()
49+
}
50+
}
51+
}
52+
4253
/**
4354
* Parses a column to a time based on the given format.
4455
*/
@@ -66,7 +77,7 @@ import org.apache.spark.unsafe.types.UTF8String
6677
since = "4.1.0")
6778
// scalastyle:on line.size.limit
6879
case class ToTime(str: Expression, format: Option[Expression])
69-
extends RuntimeReplaceable with ExpectsInputTypes {
80+
extends RuntimeReplaceable with ExpectsInputTypes with TimeExpression {
7081

7182
def this(str: Expression, format: Expression) = this(str, Option(format))
7283
def this(str: Expression) = this(str, None)
@@ -202,7 +213,7 @@ object TryToTimeExpressionBuilder extends ExpressionBuilder {
202213
// scalastyle:on line.size.limit
203214
case class MinutesOfTime(child: Expression)
204215
extends RuntimeReplaceable
205-
with ExpectsInputTypes {
216+
with ExpectsInputTypes with TimeExpression {
206217

207218
override def replacement: Expression = StaticInvoke(
208219
classOf[DateTimeUtils.type],
@@ -261,7 +272,7 @@ object MinuteExpressionBuilder extends ExpressionBuilder {
261272

262273
case class HoursOfTime(child: Expression)
263274
extends RuntimeReplaceable
264-
with ExpectsInputTypes {
275+
with ExpectsInputTypes with TimeExpression {
265276

266277
override def replacement: Expression = StaticInvoke(
267278
classOf[DateTimeUtils.type],
@@ -318,7 +329,7 @@ object HourExpressionBuilder extends ExpressionBuilder {
318329

319330
case class SecondsOfTimeWithFraction(child: Expression)
320331
extends RuntimeReplaceable
321-
with ExpectsInputTypes {
332+
with ExpectsInputTypes with TimeExpression {
322333
override def replacement: Expression = {
323334
val precision = child.dataType match {
324335
case TimeType(p) => p
@@ -344,7 +355,7 @@ case class SecondsOfTimeWithFraction(child: Expression)
344355

345356
case class SecondsOfTime(child: Expression)
346357
extends RuntimeReplaceable
347-
with ExpectsInputTypes {
358+
with ExpectsInputTypes with TimeExpression {
348359

349360
override def replacement: Expression = StaticInvoke(
350361
classOf[DateTimeUtils.type],
@@ -435,7 +446,8 @@ object SecondExpressionBuilder extends ExpressionBuilder {
435446
case class CurrentTime(
436447
child: Expression = Literal(TimeType.MICROS_PRECISION),
437448
timeZoneId: Option[String] = None) extends UnaryExpression
438-
with TimeZoneAwareExpression with ImplicitCastInputTypes with CodegenFallback {
449+
with TimeZoneAwareExpression with ImplicitCastInputTypes with CodegenFallback
450+
with TimeExpression {
439451

440452
def this() = {
441453
this(Literal(TimeType.MICROS_PRECISION), None)
@@ -547,7 +559,7 @@ case class MakeTime(
547559
secsAndMicros: Expression)
548560
extends RuntimeReplaceable
549561
with ImplicitCastInputTypes
550-
with ExpectsInputTypes {
562+
with ExpectsInputTypes with TimeExpression {
551563

552564
// Accept `sec` as DecimalType to avoid loosing precision of microseconds while converting
553565
// it to the fractional part of `sec`. If `sec` is an IntegerType, it can be cast into decimal
@@ -572,7 +584,8 @@ case class MakeTime(
572584
* Adds day-time interval to time.
573585
*/
574586
case class TimeAddInterval(time: Expression, interval: Expression)
575-
extends BinaryExpression with RuntimeReplaceable with ExpectsInputTypes {
587+
extends BinaryExpression with RuntimeReplaceable with ExpectsInputTypes
588+
with TimeExpression {
576589
override def nullIntolerant: Boolean = true
577590

578591
override def left: Expression = time
@@ -613,7 +626,8 @@ case class TimeAddInterval(time: Expression, interval: Expression)
613626
* Returns a day-time interval between time values.
614627
*/
615628
case class SubtractTimes(left: Expression, right: Expression)
616-
extends BinaryExpression with RuntimeReplaceable with ExpectsInputTypes {
629+
extends BinaryExpression with RuntimeReplaceable with ExpectsInputTypes
630+
with TimeExpression {
617631
override def nullIntolerant: Boolean = true
618632
override def inputTypes: Seq[AbstractDataType] = Seq(AnyTimeType, AnyTimeType)
619633

@@ -670,7 +684,8 @@ case class TimeDiff(
670684
end: Expression)
671685
extends TernaryExpression
672686
with RuntimeReplaceable
673-
with ImplicitCastInputTypes {
687+
with ImplicitCastInputTypes
688+
with TimeExpression {
674689

675690
override def first: Expression = unit
676691
override def second: Expression = start
@@ -725,7 +740,8 @@ case class TimeDiff(
725740
since = "4.1.0")
726741
// scalastyle:on line.size.limit
727742
case class TimeTrunc(unit: Expression, time: Expression)
728-
extends BinaryExpression with RuntimeReplaceable with ImplicitCastInputTypes {
743+
extends BinaryExpression with RuntimeReplaceable with ImplicitCastInputTypes
744+
with TimeExpression {
729745

730746
override def left: Expression = unit
731747
override def right: Expression = time
@@ -753,7 +769,8 @@ case class TimeTrunc(unit: Expression, time: Expression)
753769
}
754770

755771
abstract class IntegralToTimeBase
756-
extends UnaryExpression with ExpectsInputTypes with CodegenFallback {
772+
extends UnaryExpression with ExpectsInputTypes with CodegenFallback
773+
with TimeExpression {
757774
protected def upScaleFactor: Long
758775

759776
override def inputTypes: Seq[AbstractDataType] = Seq(IntegralType)
@@ -772,7 +789,8 @@ abstract class IntegralToTimeBase
772789
}
773790
}
774791

775-
abstract class TimeToLongBase extends UnaryExpression with ExpectsInputTypes {
792+
abstract class TimeToLongBase extends UnaryExpression with ExpectsInputTypes
793+
with TimeExpression {
776794
protected def scaleFactor: Long
777795

778796
override def inputTypes: Seq[AbstractDataType] = Seq(AnyTimeType)
@@ -819,7 +837,8 @@ abstract class TimeToLongBase extends UnaryExpression with ExpectsInputTypes {
819837
group = "datetime_funcs")
820838
// scalastyle:on line.size.limit
821839
case class TimeFromSeconds(child: Expression)
822-
extends UnaryExpression with ExpectsInputTypes with CodegenFallback {
840+
extends UnaryExpression with ExpectsInputTypes with CodegenFallback
841+
with TimeExpression {
823842
override def inputTypes: Seq[AbstractDataType] = Seq(NumericType)
824843
override def dataType: DataType = TimeType(TimeType.MICROS_PRECISION)
825844
override def nullable: Boolean = true

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3499,7 +3499,7 @@ class AstBuilder extends DataTypeAstBuilder
34993499
val zoneId = getZoneId(conf.sessionLocalTimeZone)
35003500
val specialDate = convertSpecialDate(value, zoneId).map(Literal(_, DateType))
35013501
specialDate.getOrElse(toLiteral(stringToDate, DateType))
3502-
case TIME => toLiteral(stringToTime, TimeType())
3502+
case TIME if conf.isTimeTypeEnabled => toLiteral(stringToTime, TimeType())
35033503
case TIMESTAMP_NTZ =>
35043504
convertSpecialTimestampNTZ(value, getZoneId(conf.sessionLocalTimeZone))
35053505
.map(Literal(_, TimestampNTZType))
@@ -3559,7 +3559,7 @@ class AstBuilder extends DataTypeAstBuilder
35593559
throw QueryParsingErrors.literalValueTypeUnsupportedError(
35603560
unsupportedType = ctx.literalType.getText,
35613561
supportedTypes =
3562-
Seq("DATE", "TIMESTAMP_NTZ", "TIMESTAMP_LTZ", "TIMESTAMP", "INTERVAL", "X", "TIME"),
3562+
Seq("DATE", "TIMESTAMP_NTZ", "TIMESTAMP_LTZ", "TIMESTAMP", "INTERVAL", "X"),
35633563
ctx)
35643564
}
35653565
}

0 commit comments

Comments
 (0)