From 1e88df7303e54be84bfab646e673bdf5d13d623e Mon Sep 17 00:00:00 2001 From: Yuming Wang Date: Fri, 31 Mar 2023 13:34:01 +0800 Subject: [PATCH 1/4] First commit --- .../sql/catalyst/analysis/Analyzer.scala | 106 ------------------ .../catalyst/analysis/AnsiTypeCoercion.scala | 76 +++++++++++++ .../sql/catalyst/analysis/TypeCoercion.scala | 102 +++++++++++++++++ 3 files changed, 178 insertions(+), 106 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala index 8821e652a31f0..9e94bcb59f73c 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala @@ -54,7 +54,6 @@ import org.apache.spark.sql.internal.SQLConf import org.apache.spark.sql.internal.SQLConf.{PartitionOverwriteMode, StoreAssignmentPolicy} import org.apache.spark.sql.internal.connector.V1Function import org.apache.spark.sql.types._ -import org.apache.spark.sql.types.DayTimeIntervalType.DAY import org.apache.spark.sql.util.{CaseInsensitiveStringMap, SchemaUtils} import org.apache.spark.util.collection.{Utils => CUtils} @@ -318,7 +317,6 @@ class Analyzer(override val catalogManager: CatalogManager) extends RuleExecutor ResolveLambdaVariables :: ResolveTimeZone :: ResolveRandomSeed :: - ResolveBinaryArithmetic :: ResolveUnion :: RewriteDeleteFromTable :: typeCoercionRules ++ @@ -347,110 +345,6 @@ class Analyzer(override val catalogManager: CatalogManager) extends RuleExecutor HandleSpecialCommand) ) - /** - * For [[Add]]: - * 1. if both side are interval, stays the same; - * 2. else if one side is date and the other is interval, - * turns it to [[DateAddInterval]]; - * 3. else if one side is interval, turns it to [[TimeAdd]]; - * 4. else if one side is date, turns it to [[DateAdd]] ; - * 5. else stays the same. - * - * For [[Subtract]]: - * 1. if both side are interval, stays the same; - * 2. else if the left side is date and the right side is interval, - * turns it to [[DateAddInterval(l, -r)]]; - * 3. else if the right side is an interval, turns it to [[TimeAdd(l, -r)]]; - * 4. else if one side is timestamp, turns it to [[SubtractTimestamps]]; - * 5. else if the right side is date, turns it to [[DateDiff]]/[[SubtractDates]]; - * 6. else if the left side is date, turns it to [[DateSub]]; - * 7. else turns it to stays the same. - * - * For [[Multiply]]: - * 1. If one side is interval, turns it to [[MultiplyInterval]]; - * 2. otherwise, stays the same. - * - * For [[Divide]]: - * 1. If the left side is interval, turns it to [[DivideInterval]]; - * 2. otherwise, stays the same. - */ - object ResolveBinaryArithmetic extends Rule[LogicalPlan] { - override def apply(plan: LogicalPlan): LogicalPlan = plan.resolveOperatorsUpWithPruning( - _.containsPattern(BINARY_ARITHMETIC), ruleId) { - case p: LogicalPlan => p.transformExpressionsUpWithPruning( - _.containsPattern(BINARY_ARITHMETIC), ruleId) { - case a @ Add(l, r, mode) if a.childrenResolved => (l.dataType, r.dataType) match { - case (DateType, DayTimeIntervalType(DAY, DAY)) => DateAdd(l, ExtractANSIIntervalDays(r)) - case (DateType, _: DayTimeIntervalType) => TimeAdd(Cast(l, TimestampType), r) - case (DayTimeIntervalType(DAY, DAY), DateType) => DateAdd(r, ExtractANSIIntervalDays(l)) - case (_: DayTimeIntervalType, DateType) => TimeAdd(Cast(r, TimestampType), l) - case (DateType, _: YearMonthIntervalType) => DateAddYMInterval(l, r) - case (_: YearMonthIntervalType, DateType) => DateAddYMInterval(r, l) - case (TimestampType | TimestampNTZType, _: YearMonthIntervalType) => - TimestampAddYMInterval(l, r) - case (_: YearMonthIntervalType, TimestampType | TimestampNTZType) => - TimestampAddYMInterval(r, l) - case (CalendarIntervalType, CalendarIntervalType) | - (_: DayTimeIntervalType, _: DayTimeIntervalType) => a - case (_: NullType, _: AnsiIntervalType) => - a.copy(left = Cast(a.left, a.right.dataType)) - case (_: AnsiIntervalType, _: NullType) => - a.copy(right = Cast(a.right, a.left.dataType)) - case (DateType, CalendarIntervalType) => - DateAddInterval(l, r, ansiEnabled = mode == EvalMode.ANSI) - case (_, CalendarIntervalType | _: DayTimeIntervalType) => Cast(TimeAdd(l, r), l.dataType) - case (CalendarIntervalType, DateType) => - DateAddInterval(r, l, ansiEnabled = mode == EvalMode.ANSI) - case (CalendarIntervalType | _: DayTimeIntervalType, _) => Cast(TimeAdd(r, l), r.dataType) - case (DateType, dt) if dt != StringType => DateAdd(l, r) - case (dt, DateType) if dt != StringType => DateAdd(r, l) - case _ => a - } - case s @ Subtract(l, r, mode) if s.childrenResolved => (l.dataType, r.dataType) match { - case (DateType, DayTimeIntervalType(DAY, DAY)) => - DateAdd(l, UnaryMinus(ExtractANSIIntervalDays(r), mode == EvalMode.ANSI)) - case (DateType, _: DayTimeIntervalType) => - DatetimeSub(l, r, TimeAdd(Cast(l, TimestampType), UnaryMinus(r, mode == EvalMode.ANSI))) - case (DateType, _: YearMonthIntervalType) => - DatetimeSub(l, r, DateAddYMInterval(l, UnaryMinus(r, mode == EvalMode.ANSI))) - case (TimestampType | TimestampNTZType, _: YearMonthIntervalType) => - DatetimeSub(l, r, TimestampAddYMInterval(l, UnaryMinus(r, mode == EvalMode.ANSI))) - case (CalendarIntervalType, CalendarIntervalType) | - (_: DayTimeIntervalType, _: DayTimeIntervalType) => s - case (_: NullType, _: AnsiIntervalType) => - s.copy(left = Cast(s.left, s.right.dataType)) - case (_: AnsiIntervalType, _: NullType) => - s.copy(right = Cast(s.right, s.left.dataType)) - case (DateType, CalendarIntervalType) => - DatetimeSub(l, r, DateAddInterval(l, - UnaryMinus(r, mode == EvalMode.ANSI), ansiEnabled = mode == EvalMode.ANSI)) - case (_, CalendarIntervalType | _: DayTimeIntervalType) => - Cast(DatetimeSub(l, r, TimeAdd(l, UnaryMinus(r, mode == EvalMode.ANSI))), l.dataType) - case _ if AnyTimestampType.unapply(l) || AnyTimestampType.unapply(r) => - SubtractTimestamps(l, r) - case (_, DateType) => SubtractDates(l, r) - case (DateType, dt) if dt != StringType => DateSub(l, r) - case _ => s - } - case m @ Multiply(l, r, mode) if m.childrenResolved => (l.dataType, r.dataType) match { - case (CalendarIntervalType, _) => MultiplyInterval(l, r, mode == EvalMode.ANSI) - case (_, CalendarIntervalType) => MultiplyInterval(r, l, mode == EvalMode.ANSI) - case (_: YearMonthIntervalType, _) => MultiplyYMInterval(l, r) - case (_, _: YearMonthIntervalType) => MultiplyYMInterval(r, l) - case (_: DayTimeIntervalType, _) => MultiplyDTInterval(l, r) - case (_, _: DayTimeIntervalType) => MultiplyDTInterval(r, l) - case _ => m - } - case d @ Divide(l, r, mode) if d.childrenResolved => (l.dataType, r.dataType) match { - case (CalendarIntervalType, _) => DivideInterval(l, r, mode == EvalMode.ANSI) - case (_: YearMonthIntervalType, _) => DivideYMInterval(l, r) - case (_: DayTimeIntervalType, _) => DivideDTInterval(l, r) - case _ => d - } - } - } - } - /** * Substitute child plan with WindowSpecDefinitions. */ diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala index 56dbb2a85907d..258acd1ef9d01 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala @@ -22,6 +22,7 @@ import org.apache.spark.sql.catalyst.expressions._ import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan import org.apache.spark.sql.catalyst.rules.Rule import org.apache.spark.sql.types._ +import org.apache.spark.sql.types.DayTimeIntervalType.DAY /** * In Spark ANSI mode, the type coercion rules are based on the type precedence lists of the input @@ -77,6 +78,7 @@ object AnsiTypeCoercion extends TypeCoercionBase { UnpivotCoercion :: WidenSetOperationTypes :: new AnsiCombinedTypeCoercionRule( + ResolveBinaryArithmetic :: InConversion :: PromoteStrings :: DecimalPrecision :: @@ -288,6 +290,80 @@ object AnsiTypeCoercion extends TypeCoercionBase { } } + // Please see the comments in `TypeCoercion.ResolveBinaryArithmetic`. + object ResolveBinaryArithmetic extends TypeCoercionRule { + override val transform: PartialFunction[Expression, Expression] = { + case a @ Add(l, r, mode) if a.childrenResolved => (l.dataType, r.dataType) match { + case (DateType, DayTimeIntervalType(DAY, DAY)) => DateAdd(l, ExtractANSIIntervalDays(r)) + case (DateType, _: DayTimeIntervalType) => TimeAdd(Cast(l, TimestampType), r) + case (DayTimeIntervalType(DAY, DAY), DateType) => DateAdd(r, ExtractANSIIntervalDays(l)) + case (_: DayTimeIntervalType, DateType) => TimeAdd(Cast(r, TimestampType), l) + case (DateType, _: YearMonthIntervalType) => DateAddYMInterval(l, r) + case (_: YearMonthIntervalType, DateType) => DateAddYMInterval(r, l) + case (TimestampType | TimestampNTZType, _: YearMonthIntervalType) => + TimestampAddYMInterval(l, r) + case (_: YearMonthIntervalType, TimestampType | TimestampNTZType) => + TimestampAddYMInterval(r, l) + case (CalendarIntervalType, CalendarIntervalType) | + (_: DayTimeIntervalType, _: DayTimeIntervalType) => a + case (_: NullType, _: AnsiIntervalType) => + a.copy(left = Cast(a.left, a.right.dataType)) + case (_: AnsiIntervalType, _: NullType) => + a.copy(right = Cast(a.right, a.left.dataType)) + case (DateType, CalendarIntervalType) => + DateAddInterval(l, r, ansiEnabled = mode == EvalMode.ANSI) + case (_, CalendarIntervalType | _: DayTimeIntervalType) => Cast(TimeAdd(l, r), l.dataType) + case (CalendarIntervalType, DateType) => + DateAddInterval(r, l, ansiEnabled = mode == EvalMode.ANSI) + case (CalendarIntervalType | _: DayTimeIntervalType, _) => Cast(TimeAdd(r, l), r.dataType) + case (DateType, dt) if dt != StringType => DateAdd(l, r) + case (dt, DateType) if dt != StringType => DateAdd(r, l) + case _ => a + } + case s @ Subtract(l, r, mode) if s.childrenResolved => (l.dataType, r.dataType) match { + case (DateType, DayTimeIntervalType(DAY, DAY)) => + DateAdd(l, UnaryMinus(ExtractANSIIntervalDays(r), mode == EvalMode.ANSI)) + case (DateType, _: DayTimeIntervalType) => + DatetimeSub(l, r, TimeAdd(Cast(l, TimestampType), UnaryMinus(r, mode == EvalMode.ANSI))) + case (DateType, _: YearMonthIntervalType) => + DatetimeSub(l, r, DateAddYMInterval(l, UnaryMinus(r, mode == EvalMode.ANSI))) + case (TimestampType | TimestampNTZType, _: YearMonthIntervalType) => + DatetimeSub(l, r, TimestampAddYMInterval(l, UnaryMinus(r, mode == EvalMode.ANSI))) + case (CalendarIntervalType, CalendarIntervalType) | + (_: DayTimeIntervalType, _: DayTimeIntervalType) => s + case (_: NullType, _: AnsiIntervalType) => + s.copy(left = Cast(s.left, s.right.dataType)) + case (_: AnsiIntervalType, _: NullType) => + s.copy(right = Cast(s.right, s.left.dataType)) + case (DateType, CalendarIntervalType) => + DatetimeSub(l, r, DateAddInterval(l, + UnaryMinus(r, mode == EvalMode.ANSI), ansiEnabled = mode == EvalMode.ANSI)) + case (_, CalendarIntervalType | _: DayTimeIntervalType) => + Cast(DatetimeSub(l, r, TimeAdd(l, UnaryMinus(r, mode == EvalMode.ANSI))), l.dataType) + case _ if AnyTimestampType.unapply(l) || AnyTimestampType.unapply(r) => + SubtractTimestamps(l, r) + case (_, DateType) => SubtractDates(l, r) + case (DateType, dt) if dt != StringType => DateSub(l, r) + case _ => s + } + case m @ Multiply(l, r, mode) if m.childrenResolved => (l.dataType, r.dataType) match { + case (CalendarIntervalType, _) => MultiplyInterval(l, r, mode == EvalMode.ANSI) + case (_, CalendarIntervalType) => MultiplyInterval(r, l, mode == EvalMode.ANSI) + case (_: YearMonthIntervalType, _) => MultiplyYMInterval(l, r) + case (_, _: YearMonthIntervalType) => MultiplyYMInterval(r, l) + case (_: DayTimeIntervalType, _) => MultiplyDTInterval(l, r) + case (_, _: DayTimeIntervalType) => MultiplyDTInterval(r, l) + case _ => m + } + case d @ Divide(l, r, mode) if d.childrenResolved => (l.dataType, r.dataType) match { + case (CalendarIntervalType, _) => DivideInterval(l, r, mode == EvalMode.ANSI) + case (_: YearMonthIntervalType, _) => DivideYMInterval(l, r) + case (_: DayTimeIntervalType, _) => DivideDTInterval(l, r) + case _ => d + } + } + } + object DateTimeOperations extends TypeCoercionRule { override val transform: PartialFunction[Expression, Expression] = { // Skip nodes who's children have not been resolved yet. diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala index 059c36c4f9044..5847d5bee0779 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala @@ -31,6 +31,7 @@ import org.apache.spark.sql.catalyst.types.DataTypeUtils import org.apache.spark.sql.errors.QueryCompilationErrors import org.apache.spark.sql.internal.SQLConf import org.apache.spark.sql.types._ +import org.apache.spark.sql.types.DayTimeIntervalType.DAY abstract class TypeCoercionBase { /** @@ -837,6 +838,7 @@ object TypeCoercion extends TypeCoercionBase { UnpivotCoercion :: WidenSetOperationTypes :: new CombinedTypeCoercionRule( + ResolveBinaryArithmetic :: InConversion :: PromoteStrings :: DecimalPrecision :: @@ -1171,6 +1173,106 @@ object TypeCoercion extends TypeCoercionBase { } } + /** + * For [[Add]]: + * 1. if both side are interval, stays the same; + * 2. else if one side is date and the other is interval, + * turns it to [[DateAddInterval]]; + * 3. else if one side is interval, turns it to [[TimeAdd]]; + * 4. else if one side is date, turns it to [[DateAdd]] ; + * 5. else stays the same. + * + * For [[Subtract]]: + * 1. if both side are interval, stays the same; + * 2. else if the left side is date and the right side is interval, + * turns it to [[DateAddInterval(l, -r)]]; + * 3. else if the right side is an interval, turns it to [[TimeAdd(l, -r)]]; + * 4. else if one side is timestamp, turns it to [[SubtractTimestamps]]; + * 5. else if the right side is date, turns it to [[DateDiff]]/[[SubtractDates]]; + * 6. else if the left side is date, turns it to [[DateSub]]; + * 7. else turns it to stays the same. + * + * For [[Multiply]]: + * 1. If one side is interval, turns it to [[MultiplyInterval]]; + * 2. otherwise, stays the same. + * + * For [[Divide]]: + * 1. If the left side is interval, turns it to [[DivideInterval]]; + * 2. otherwise, stays the same. + */ + object ResolveBinaryArithmetic extends TypeCoercionRule { + override val transform: PartialFunction[Expression, Expression] = { + case a @ Add(l, r, mode) if a.childrenResolved => (l.dataType, r.dataType) match { + case (DateType, DayTimeIntervalType(DAY, DAY)) => DateAdd(l, ExtractANSIIntervalDays(r)) + case (DateType, _: DayTimeIntervalType) => TimeAdd(Cast(l, TimestampType), r) + case (DayTimeIntervalType(DAY, DAY), DateType) => DateAdd(r, ExtractANSIIntervalDays(l)) + case (_: DayTimeIntervalType, DateType) => TimeAdd(Cast(r, TimestampType), l) + case (DateType, _: YearMonthIntervalType) => DateAddYMInterval(l, r) + case (_: YearMonthIntervalType, DateType) => DateAddYMInterval(r, l) + case (TimestampType | TimestampNTZType, _: YearMonthIntervalType) => + TimestampAddYMInterval(l, r) + case (_: YearMonthIntervalType, TimestampType | TimestampNTZType) => + TimestampAddYMInterval(r, l) + case (CalendarIntervalType, CalendarIntervalType) | + (_: DayTimeIntervalType, _: DayTimeIntervalType) => a + case (_: NullType, _: AnsiIntervalType) => + a.copy(left = Cast(a.left, a.right.dataType)) + case (_: AnsiIntervalType, _: NullType) => + a.copy(right = Cast(a.right, a.left.dataType)) + case (DateType, CalendarIntervalType) => + DateAddInterval(l, r, ansiEnabled = mode == EvalMode.ANSI) + case (_, CalendarIntervalType | _: DayTimeIntervalType) => Cast(TimeAdd(l, r), l.dataType) + case (CalendarIntervalType, DateType) => + DateAddInterval(r, l, ansiEnabled = mode == EvalMode.ANSI) + case (CalendarIntervalType | _: DayTimeIntervalType, _) => Cast(TimeAdd(r, l), r.dataType) + case (DateType, dt) if dt != StringType => DateAdd(l, r) + case (dt, DateType) if dt != StringType => DateAdd(r, l) + case _ => a + } + case s @ Subtract(l, r, mode) if s.childrenResolved => (l.dataType, r.dataType) match { + case (DateType, DayTimeIntervalType(DAY, DAY)) => + DateAdd(l, UnaryMinus(ExtractANSIIntervalDays(r), mode == EvalMode.ANSI)) + case (DateType, _: DayTimeIntervalType) => + DatetimeSub(l, r, TimeAdd(Cast(l, TimestampType), UnaryMinus(r, mode == EvalMode.ANSI))) + case (DateType, _: YearMonthIntervalType) => + DatetimeSub(l, r, DateAddYMInterval(l, UnaryMinus(r, mode == EvalMode.ANSI))) + case (TimestampType | TimestampNTZType, _: YearMonthIntervalType) => + DatetimeSub(l, r, TimestampAddYMInterval(l, UnaryMinus(r, mode == EvalMode.ANSI))) + case (CalendarIntervalType, CalendarIntervalType) | + (_: DayTimeIntervalType, _: DayTimeIntervalType) => s + case (_: NullType, _: AnsiIntervalType) => + s.copy(left = Cast(s.left, s.right.dataType)) + case (_: AnsiIntervalType, _: NullType) => + s.copy(right = Cast(s.right, s.left.dataType)) + case (DateType, CalendarIntervalType) => + DatetimeSub(l, r, DateAddInterval(l, + UnaryMinus(r, mode == EvalMode.ANSI), ansiEnabled = mode == EvalMode.ANSI)) + case (_, CalendarIntervalType | _: DayTimeIntervalType) => + Cast(DatetimeSub(l, r, TimeAdd(l, UnaryMinus(r, mode == EvalMode.ANSI))), l.dataType) + case _ if AnyTimestampType.unapply(l) || AnyTimestampType.unapply(r) => + SubtractTimestamps(l, r) + case (_, DateType) => SubtractDates(l, r) + case (DateType, dt) if dt != StringType => DateSub(l, r) + case _ => s + } + case m @ Multiply(l, r, mode) if m.childrenResolved => (l.dataType, r.dataType) match { + case (CalendarIntervalType, _) => MultiplyInterval(l, r, mode == EvalMode.ANSI) + case (_, CalendarIntervalType) => MultiplyInterval(r, l, mode == EvalMode.ANSI) + case (_: YearMonthIntervalType, _) => MultiplyYMInterval(l, r) + case (_, _: YearMonthIntervalType) => MultiplyYMInterval(r, l) + case (_: DayTimeIntervalType, _) => MultiplyDTInterval(l, r) + case (_, _: DayTimeIntervalType) => MultiplyDTInterval(r, l) + case _ => m + } + case d @ Divide(l, r, mode) if d.childrenResolved => (l.dataType, r.dataType) match { + case (CalendarIntervalType, _) => DivideInterval(l, r, mode == EvalMode.ANSI) + case (_: YearMonthIntervalType, _) => DivideYMInterval(l, r) + case (_: DayTimeIntervalType, _) => DivideDTInterval(l, r) + case _ => d + } + } + } + object DateTimeOperations extends TypeCoercionRule { override val transform: PartialFunction[Expression, Expression] = { // Skip nodes who's children have not been resolved yet. From e907a6822da561413f0c71281e1ec848f7f5146f Mon Sep 17 00:00:00 2001 From: Yuming Wang Date: Fri, 31 Mar 2023 14:41:14 +0800 Subject: [PATCH 2/4] Disable string +/- interval in ANSI mode --- .../catalyst/analysis/AnsiTypeCoercion.scala | 8 +- .../sql-tests/results/ansi/interval.sql.out | 102 ++++++++---------- .../results/ansi/try_arithmetic.sql.out | 10 +- 3 files changed, 53 insertions(+), 67 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala index 258acd1ef9d01..b769040f7a473 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala @@ -312,10 +312,12 @@ object AnsiTypeCoercion extends TypeCoercionBase { a.copy(right = Cast(a.right, a.left.dataType)) case (DateType, CalendarIntervalType) => DateAddInterval(l, r, ansiEnabled = mode == EvalMode.ANSI) - case (_, CalendarIntervalType | _: DayTimeIntervalType) => Cast(TimeAdd(l, r), l.dataType) + case (_: DatetimeType, CalendarIntervalType | _: DayTimeIntervalType) => + Cast(TimeAdd(l, r), l.dataType) case (CalendarIntervalType, DateType) => DateAddInterval(r, l, ansiEnabled = mode == EvalMode.ANSI) - case (CalendarIntervalType | _: DayTimeIntervalType, _) => Cast(TimeAdd(r, l), r.dataType) + case (CalendarIntervalType | _: DayTimeIntervalType, _: DatetimeType) => + Cast(TimeAdd(r, l), r.dataType) case (DateType, dt) if dt != StringType => DateAdd(l, r) case (dt, DateType) if dt != StringType => DateAdd(r, l) case _ => a @@ -338,7 +340,7 @@ object AnsiTypeCoercion extends TypeCoercionBase { case (DateType, CalendarIntervalType) => DatetimeSub(l, r, DateAddInterval(l, UnaryMinus(r, mode == EvalMode.ANSI), ansiEnabled = mode == EvalMode.ANSI)) - case (_, CalendarIntervalType | _: DayTimeIntervalType) => + case (_: DatetimeType, CalendarIntervalType | _: DayTimeIntervalType) => Cast(DatetimeSub(l, r, TimeAdd(l, UnaryMinus(r, mode == EvalMode.ANSI))), l.dataType) case _ if AnyTimestampType.unapply(l) || AnyTimestampType.unapply(r) => SubtractTimestamps(l, r) 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 index c5c73002a1a91..d710914101239 100644 --- 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 @@ -1945,15 +1945,14 @@ select '4 11:11' - interval '4 22:12' day to minute -- !query schema struct<> -- !query output -org.apache.spark.SparkDateTimeException +org.apache.spark.sql.AnalysisException { - "errorClass" : "CAST_INVALID_INPUT", - "sqlState" : "22018", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "sqlState" : "42K09", "messageParameters" : { - "ansiConfig" : "\"spark.sql.ansi.enabled\"", - "expression" : "'4 11:11'", - "sourceType" : "\"STRING\"", - "targetType" : "\"TIMESTAMP\"" + "left" : "\"STRING\"", + "right" : "\"INTERVAL DAY TO MINUTE\"", + "sqlExpr" : "\"(4 11:11 - INTERVAL '4 22:12' DAY TO MINUTE)\"" }, "queryContext" : [ { "objectType" : "", @@ -1970,15 +1969,14 @@ select '4 12:12:12' + interval '4 22:12' day to minute -- !query schema struct<> -- !query output -org.apache.spark.SparkDateTimeException +org.apache.spark.sql.AnalysisException { - "errorClass" : "CAST_INVALID_INPUT", - "sqlState" : "22018", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "sqlState" : "42K09", "messageParameters" : { - "ansiConfig" : "\"spark.sql.ansi.enabled\"", - "expression" : "'4 12:12:12'", - "sourceType" : "\"STRING\"", - "targetType" : "\"TIMESTAMP\"" + "left" : "\"STRING\"", + "right" : "\"INTERVAL DAY TO MINUTE\"", + "sqlExpr" : "\"(4 12:12:12 + INTERVAL '4 22:12' DAY TO MINUTE)\"" }, "queryContext" : [ { "objectType" : "", @@ -2051,15 +2049,14 @@ select str - interval '4 22:12' day to minute from interval_view -- !query schema struct<> -- !query output -org.apache.spark.SparkDateTimeException +org.apache.spark.sql.AnalysisException { - "errorClass" : "CAST_INVALID_INPUT", - "sqlState" : "22018", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "sqlState" : "42K09", "messageParameters" : { - "ansiConfig" : "\"spark.sql.ansi.enabled\"", - "expression" : "'1'", - "sourceType" : "\"STRING\"", - "targetType" : "\"TIMESTAMP\"" + "left" : "\"STRING\"", + "right" : "\"INTERVAL DAY TO MINUTE\"", + "sqlExpr" : "\"(str - INTERVAL '4 22:12' DAY TO MINUTE)\"" }, "queryContext" : [ { "objectType" : "", @@ -2076,15 +2073,14 @@ select str + interval '4 22:12' day to minute from interval_view -- !query schema struct<> -- !query output -org.apache.spark.SparkDateTimeException +org.apache.spark.sql.AnalysisException { - "errorClass" : "CAST_INVALID_INPUT", - "sqlState" : "22018", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "sqlState" : "42K09", "messageParameters" : { - "ansiConfig" : "\"spark.sql.ansi.enabled\"", - "expression" : "'1'", - "sourceType" : "\"STRING\"", - "targetType" : "\"TIMESTAMP\"" + "left" : "\"STRING\"", + "right" : "\"INTERVAL DAY TO MINUTE\"", + "sqlExpr" : "\"(str + INTERVAL '4 22:12' DAY TO MINUTE)\"" }, "queryContext" : [ { "objectType" : "", @@ -2103,14 +2099,12 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", "sqlState" : "42K09", "messageParameters" : { - "inputSql" : "\"INTERVAL '2-2' YEAR TO MONTH\"", - "inputType" : "\"INTERVAL YEAR TO MONTH\"", - "paramIndex" : "1", - "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", - "sqlExpr" : "\"INTERVAL '2-2' YEAR TO MONTH + INTERVAL '3' DAY\"" + "left" : "\"INTERVAL YEAR TO MONTH\"", + "right" : "\"INTERVAL DAY\"", + "sqlExpr" : "\"(INTERVAL '2-2' YEAR TO MONTH + INTERVAL '3' DAY)\"" }, "queryContext" : [ { "objectType" : "", @@ -2129,14 +2123,12 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", "sqlState" : "42K09", "messageParameters" : { - "inputSql" : "\"INTERVAL '2-2' YEAR TO MONTH\"", - "inputType" : "\"INTERVAL YEAR TO MONTH\"", - "paramIndex" : "1", - "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", - "sqlExpr" : "\"INTERVAL '2-2' YEAR TO MONTH + INTERVAL '3' DAY\"" + "left" : "\"INTERVAL DAY\"", + "right" : "\"INTERVAL YEAR TO MONTH\"", + "sqlExpr" : "\"(INTERVAL '3' DAY + INTERVAL '2-2' YEAR TO MONTH)\"" }, "queryContext" : [ { "objectType" : "", @@ -2155,14 +2147,12 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", "sqlState" : "42K09", "messageParameters" : { - "inputSql" : "\"INTERVAL '2-2' YEAR TO MONTH\"", - "inputType" : "\"INTERVAL YEAR TO MONTH\"", - "paramIndex" : "1", - "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", - "sqlExpr" : "\"INTERVAL '2-2' YEAR TO MONTH + (- INTERVAL '3' DAY)\"" + "left" : "\"INTERVAL YEAR TO MONTH\"", + "right" : "\"INTERVAL DAY\"", + "sqlExpr" : "\"(INTERVAL '2-2' YEAR TO MONTH - INTERVAL '3' DAY)\"" }, "queryContext" : [ { "objectType" : "", @@ -2205,14 +2195,12 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", "sqlState" : "42K09", "messageParameters" : { - "inputSql" : "\"1\"", - "inputType" : "\"INT\"", - "paramIndex" : "1", - "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", - "sqlExpr" : "\"1 + (- INTERVAL '02' SECOND)\"" + "left" : "\"INT\"", + "right" : "\"INTERVAL SECOND\"", + "sqlExpr" : "\"(1 - INTERVAL '02' SECOND)\"" }, "queryContext" : [ { "objectType" : "", @@ -2255,14 +2243,12 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", "sqlState" : "42K09", "messageParameters" : { - "inputSql" : "\"1\"", - "inputType" : "\"INT\"", - "paramIndex" : "1", - "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", - "sqlExpr" : "\"1 + INTERVAL '02' SECOND\"" + "left" : "\"INTERVAL SECOND\"", + "right" : "\"INT\"", + "sqlExpr" : "\"(INTERVAL '02' SECOND + 1)\"" }, "queryContext" : [ { "objectType" : "", diff --git a/sql/core/src/test/resources/sql-tests/results/ansi/try_arithmetic.sql.out b/sql/core/src/test/resources/sql-tests/results/ansi/try_arithmetic.sql.out index cf29eff19fbb8..70dd59300fffa 100644 --- a/sql/core/src/test/resources/sql-tests/results/ansi/try_arithmetic.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/ansi/try_arithmetic.sql.out @@ -212,14 +212,12 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", "sqlState" : "42K09", "messageParameters" : { - "inputSql" : "\"INTERVAL '2' YEAR\"", - "inputType" : "\"INTERVAL YEAR\"", - "paramIndex" : "1", - "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", - "sqlExpr" : "\"INTERVAL '2' YEAR + INTERVAL '02' SECOND\"" + "left" : "\"INTERVAL YEAR\"", + "right" : "\"INTERVAL SECOND\"", + "sqlExpr" : "\"(INTERVAL '2' YEAR + INTERVAL '02' SECOND)\"" }, "queryContext" : [ { "objectType" : "", From 3865db3ef82ecbdb12e9592478587f88593ff689 Mon Sep 17 00:00:00 2001 From: Yuming Wang Date: Mon, 3 Apr 2023 14:01:31 +0800 Subject: [PATCH 3/4] Update --- .../catalyst/analysis/AnsiTypeCoercion.scala | 37 +++++- .../sql/catalyst/analysis/TypeCoercion.scala | 116 +++--------------- .../sql-tests/results/ansi/interval.sql.out | 30 +++-- .../results/ansi/try_arithmetic.sql.out | 10 +- 4 files changed, 76 insertions(+), 117 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala index b769040f7a473..b74fd4fcac854 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala @@ -290,7 +290,33 @@ object AnsiTypeCoercion extends TypeCoercionBase { } } - // Please see the comments in `TypeCoercion.ResolveBinaryArithmetic`. + /** + * For [[Add]]: + * 1. if both side are interval, stays the same; + * 2. else if one side is date and the other is interval, + * turns it to [[DateAddInterval]]; + * 3. else if one side is interval, turns it to [[TimeAdd]]; + * 4. else if one side is date, turns it to [[DateAdd]] ; + * 5. else stays the same. + * + * For [[Subtract]]: + * 1. if both side are interval, stays the same; + * 2. else if the left side is date and the right side is interval, + * turns it to [[DateAddInterval(l, -r)]]; + * 3. else if the right side is an interval, turns it to [[TimeAdd(l, -r)]]; + * 4. else if one side is timestamp, turns it to [[SubtractTimestamps]]; + * 5. else if the right side is date, turns it to [[DateDiff]]/[[SubtractDates]]; + * 6. else if the left side is date, turns it to [[DateSub]]; + * 7. else turns it to stays the same. + * + * For [[Multiply]]: + * 1. If one side is interval, turns it to [[MultiplyInterval]]; + * 2. otherwise, stays the same. + * + * For [[Divide]]: + * 1. If the left side is interval, turns it to [[DivideInterval]]; + * 2. otherwise, stays the same. + */ object ResolveBinaryArithmetic extends TypeCoercionRule { override val transform: PartialFunction[Expression, Expression] = { case a @ Add(l, r, mode) if a.childrenResolved => (l.dataType, r.dataType) match { @@ -312,11 +338,13 @@ object AnsiTypeCoercion extends TypeCoercionBase { a.copy(right = Cast(a.right, a.left.dataType)) case (DateType, CalendarIntervalType) => DateAddInterval(l, r, ansiEnabled = mode == EvalMode.ANSI) - case (_: DatetimeType, CalendarIntervalType | _: DayTimeIntervalType) => + case (_: DatetimeType | _: AnsiIntervalType, + CalendarIntervalType | _: DayTimeIntervalType) => Cast(TimeAdd(l, r), l.dataType) case (CalendarIntervalType, DateType) => DateAddInterval(r, l, ansiEnabled = mode == EvalMode.ANSI) - case (CalendarIntervalType | _: DayTimeIntervalType, _: DatetimeType) => + case (CalendarIntervalType | _: DayTimeIntervalType, + _: DatetimeType | _: AnsiIntervalType) => Cast(TimeAdd(r, l), r.dataType) case (DateType, dt) if dt != StringType => DateAdd(l, r) case (dt, DateType) if dt != StringType => DateAdd(r, l) @@ -340,7 +368,8 @@ object AnsiTypeCoercion extends TypeCoercionBase { case (DateType, CalendarIntervalType) => DatetimeSub(l, r, DateAddInterval(l, UnaryMinus(r, mode == EvalMode.ANSI), ansiEnabled = mode == EvalMode.ANSI)) - case (_: DatetimeType, CalendarIntervalType | _: DayTimeIntervalType) => + case (_: DatetimeType | _: AnsiIntervalType, + CalendarIntervalType | _: DayTimeIntervalType) => Cast(DatetimeSub(l, r, TimeAdd(l, UnaryMinus(r, mode == EvalMode.ANSI))), l.dataType) case _ if AnyTimestampType.unapply(l) || AnyTimestampType.unapply(r) => SubtractTimestamps(l, r) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala index 5847d5bee0779..98d965669ebe0 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala @@ -31,7 +31,6 @@ import org.apache.spark.sql.catalyst.types.DataTypeUtils import org.apache.spark.sql.errors.QueryCompilationErrors import org.apache.spark.sql.internal.SQLConf import org.apache.spark.sql.types._ -import org.apache.spark.sql.types.DayTimeIntervalType.DAY abstract class TypeCoercionBase { /** @@ -1173,104 +1172,27 @@ object TypeCoercion extends TypeCoercionBase { } } - /** - * For [[Add]]: - * 1. if both side are interval, stays the same; - * 2. else if one side is date and the other is interval, - * turns it to [[DateAddInterval]]; - * 3. else if one side is interval, turns it to [[TimeAdd]]; - * 4. else if one side is date, turns it to [[DateAdd]] ; - * 5. else stays the same. - * - * For [[Subtract]]: - * 1. if both side are interval, stays the same; - * 2. else if the left side is date and the right side is interval, - * turns it to [[DateAddInterval(l, -r)]]; - * 3. else if the right side is an interval, turns it to [[TimeAdd(l, -r)]]; - * 4. else if one side is timestamp, turns it to [[SubtractTimestamps]]; - * 5. else if the right side is date, turns it to [[DateDiff]]/[[SubtractDates]]; - * 6. else if the left side is date, turns it to [[DateSub]]; - * 7. else turns it to stays the same. - * - * For [[Multiply]]: - * 1. If one side is interval, turns it to [[MultiplyInterval]]; - * 2. otherwise, stays the same. - * - * For [[Divide]]: - * 1. If the left side is interval, turns it to [[DivideInterval]]; - * 2. otherwise, stays the same. - */ + // For legacy support. For example: string type +/- interval. object ResolveBinaryArithmetic extends TypeCoercionRule { - override val transform: PartialFunction[Expression, Expression] = { - case a @ Add(l, r, mode) if a.childrenResolved => (l.dataType, r.dataType) match { - case (DateType, DayTimeIntervalType(DAY, DAY)) => DateAdd(l, ExtractANSIIntervalDays(r)) - case (DateType, _: DayTimeIntervalType) => TimeAdd(Cast(l, TimestampType), r) - case (DayTimeIntervalType(DAY, DAY), DateType) => DateAdd(r, ExtractANSIIntervalDays(l)) - case (_: DayTimeIntervalType, DateType) => TimeAdd(Cast(r, TimestampType), l) - case (DateType, _: YearMonthIntervalType) => DateAddYMInterval(l, r) - case (_: YearMonthIntervalType, DateType) => DateAddYMInterval(r, l) - case (TimestampType | TimestampNTZType, _: YearMonthIntervalType) => - TimestampAddYMInterval(l, r) - case (_: YearMonthIntervalType, TimestampType | TimestampNTZType) => - TimestampAddYMInterval(r, l) - case (CalendarIntervalType, CalendarIntervalType) | - (_: DayTimeIntervalType, _: DayTimeIntervalType) => a - case (_: NullType, _: AnsiIntervalType) => - a.copy(left = Cast(a.left, a.right.dataType)) - case (_: AnsiIntervalType, _: NullType) => - a.copy(right = Cast(a.right, a.left.dataType)) - case (DateType, CalendarIntervalType) => - DateAddInterval(l, r, ansiEnabled = mode == EvalMode.ANSI) - case (_, CalendarIntervalType | _: DayTimeIntervalType) => Cast(TimeAdd(l, r), l.dataType) - case (CalendarIntervalType, DateType) => - DateAddInterval(r, l, ansiEnabled = mode == EvalMode.ANSI) - case (CalendarIntervalType | _: DayTimeIntervalType, _) => Cast(TimeAdd(r, l), r.dataType) - case (DateType, dt) if dt != StringType => DateAdd(l, r) - case (dt, DateType) if dt != StringType => DateAdd(r, l) - case _ => a - } - case s @ Subtract(l, r, mode) if s.childrenResolved => (l.dataType, r.dataType) match { - case (DateType, DayTimeIntervalType(DAY, DAY)) => - DateAdd(l, UnaryMinus(ExtractANSIIntervalDays(r), mode == EvalMode.ANSI)) - case (DateType, _: DayTimeIntervalType) => - DatetimeSub(l, r, TimeAdd(Cast(l, TimestampType), UnaryMinus(r, mode == EvalMode.ANSI))) - case (DateType, _: YearMonthIntervalType) => - DatetimeSub(l, r, DateAddYMInterval(l, UnaryMinus(r, mode == EvalMode.ANSI))) - case (TimestampType | TimestampNTZType, _: YearMonthIntervalType) => - DatetimeSub(l, r, TimestampAddYMInterval(l, UnaryMinus(r, mode == EvalMode.ANSI))) - case (CalendarIntervalType, CalendarIntervalType) | - (_: DayTimeIntervalType, _: DayTimeIntervalType) => s - case (_: NullType, _: AnsiIntervalType) => - s.copy(left = Cast(s.left, s.right.dataType)) - case (_: AnsiIntervalType, _: NullType) => - s.copy(right = Cast(s.right, s.left.dataType)) - case (DateType, CalendarIntervalType) => - DatetimeSub(l, r, DateAddInterval(l, - UnaryMinus(r, mode == EvalMode.ANSI), ansiEnabled = mode == EvalMode.ANSI)) - case (_, CalendarIntervalType | _: DayTimeIntervalType) => - Cast(DatetimeSub(l, r, TimeAdd(l, UnaryMinus(r, mode == EvalMode.ANSI))), l.dataType) - case _ if AnyTimestampType.unapply(l) || AnyTimestampType.unapply(r) => - SubtractTimestamps(l, r) - case (_, DateType) => SubtractDates(l, r) - case (DateType, dt) if dt != StringType => DateSub(l, r) - case _ => s - } - case m @ Multiply(l, r, mode) if m.childrenResolved => (l.dataType, r.dataType) match { - case (CalendarIntervalType, _) => MultiplyInterval(l, r, mode == EvalMode.ANSI) - case (_, CalendarIntervalType) => MultiplyInterval(r, l, mode == EvalMode.ANSI) - case (_: YearMonthIntervalType, _) => MultiplyYMInterval(l, r) - case (_, _: YearMonthIntervalType) => MultiplyYMInterval(r, l) - case (_: DayTimeIntervalType, _) => MultiplyDTInterval(l, r) - case (_, _: DayTimeIntervalType) => MultiplyDTInterval(r, l) - case _ => m - } - case d @ Divide(l, r, mode) if d.childrenResolved => (l.dataType, r.dataType) match { - case (CalendarIntervalType, _) => DivideInterval(l, r, mode == EvalMode.ANSI) - case (_: YearMonthIntervalType, _) => DivideYMInterval(l, r) - case (_: DayTimeIntervalType, _) => DivideDTInterval(l, r) - case _ => d + override val transform: PartialFunction[Expression, Expression] = + AnsiTypeCoercion.ResolveBinaryArithmetic.transform.andThen { + case a @ Add(l, r, _) if a.childrenResolved => (l.dataType, r.dataType) match { + case (StringType | _: NumericType | BinaryType | BooleanType, + CalendarIntervalType | _: DayTimeIntervalType) => + Cast(TimeAdd(l, r), l.dataType) + case (CalendarIntervalType | _: DayTimeIntervalType, + StringType | _: NumericType | BinaryType | BooleanType) => + Cast(TimeAdd(r, l), r.dataType) + case _ => a + } + case s @ Subtract(l, r, mode) if s.childrenResolved => (l.dataType, r.dataType) match { + case (StringType | _: NumericType | BinaryType | BooleanType, + CalendarIntervalType | _: DayTimeIntervalType) => + Cast(DatetimeSub(l, r, TimeAdd(l, UnaryMinus(r, mode == EvalMode.ANSI))), l.dataType) + case _ => s + } + case other => other } - } } object DateTimeOperations extends TypeCoercionRule { 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 index d710914101239..5f7722e4e2f84 100644 --- 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 @@ -2099,12 +2099,14 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", "sqlState" : "42K09", "messageParameters" : { - "left" : "\"INTERVAL YEAR TO MONTH\"", - "right" : "\"INTERVAL DAY\"", - "sqlExpr" : "\"(INTERVAL '2-2' YEAR TO MONTH + INTERVAL '3' DAY)\"" + "inputSql" : "\"INTERVAL '2-2' YEAR TO MONTH\"", + "inputType" : "\"INTERVAL YEAR TO MONTH\"", + "paramIndex" : "1", + "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", + "sqlExpr" : "\"INTERVAL '2-2' YEAR TO MONTH + INTERVAL '3' DAY\"" }, "queryContext" : [ { "objectType" : "", @@ -2123,12 +2125,14 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", "sqlState" : "42K09", "messageParameters" : { - "left" : "\"INTERVAL DAY\"", - "right" : "\"INTERVAL YEAR TO MONTH\"", - "sqlExpr" : "\"(INTERVAL '3' DAY + INTERVAL '2-2' YEAR TO MONTH)\"" + "inputSql" : "\"INTERVAL '2-2' YEAR TO MONTH\"", + "inputType" : "\"INTERVAL YEAR TO MONTH\"", + "paramIndex" : "1", + "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", + "sqlExpr" : "\"INTERVAL '2-2' YEAR TO MONTH + INTERVAL '3' DAY\"" }, "queryContext" : [ { "objectType" : "", @@ -2147,12 +2151,14 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", "sqlState" : "42K09", "messageParameters" : { - "left" : "\"INTERVAL YEAR TO MONTH\"", - "right" : "\"INTERVAL DAY\"", - "sqlExpr" : "\"(INTERVAL '2-2' YEAR TO MONTH - INTERVAL '3' DAY)\"" + "inputSql" : "\"INTERVAL '2-2' YEAR TO MONTH\"", + "inputType" : "\"INTERVAL YEAR TO MONTH\"", + "paramIndex" : "1", + "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", + "sqlExpr" : "\"INTERVAL '2-2' YEAR TO MONTH + (- INTERVAL '3' DAY)\"" }, "queryContext" : [ { "objectType" : "", diff --git a/sql/core/src/test/resources/sql-tests/results/ansi/try_arithmetic.sql.out b/sql/core/src/test/resources/sql-tests/results/ansi/try_arithmetic.sql.out index 70dd59300fffa..cf29eff19fbb8 100644 --- a/sql/core/src/test/resources/sql-tests/results/ansi/try_arithmetic.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/ansi/try_arithmetic.sql.out @@ -212,12 +212,14 @@ struct<> -- !query output org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", "sqlState" : "42K09", "messageParameters" : { - "left" : "\"INTERVAL YEAR\"", - "right" : "\"INTERVAL SECOND\"", - "sqlExpr" : "\"(INTERVAL '2' YEAR + INTERVAL '02' SECOND)\"" + "inputSql" : "\"INTERVAL '2' YEAR\"", + "inputType" : "\"INTERVAL YEAR\"", + "paramIndex" : "1", + "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", + "sqlExpr" : "\"INTERVAL '2' YEAR + INTERVAL '02' SECOND\"" }, "queryContext" : [ { "objectType" : "", From 0e828567079a840880202e37c6b4e8b6027de077 Mon Sep 17 00:00:00 2001 From: Yuming Wang Date: Mon, 3 Apr 2023 17:23:22 +0800 Subject: [PATCH 4/4] fix --- .../analyzer-results/ansi/interval.sql.out | 104 +++++++++++++----- 1 file changed, 76 insertions(+), 28 deletions(-) diff --git a/sql/core/src/test/resources/sql-tests/analyzer-results/ansi/interval.sql.out b/sql/core/src/test/resources/sql-tests/analyzer-results/ansi/interval.sql.out index 19c77c8de9071..2943b8896950f 100644 --- a/sql/core/src/test/resources/sql-tests/analyzer-results/ansi/interval.sql.out +++ b/sql/core/src/test/resources/sql-tests/analyzer-results/ansi/interval.sql.out @@ -1596,15 +1596,45 @@ org.apache.spark.sql.AnalysisException -- !query select '4 11:11' - interval '4 22:12' day to minute -- !query analysis -Project [cast(4 11:11 - INTERVAL '4 22:12' DAY TO MINUTE as string) AS 4 11:11 - INTERVAL '4 22:12' DAY TO MINUTE#x] -+- OneRowRelation +org.apache.spark.sql.AnalysisException +{ + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "sqlState" : "42K09", + "messageParameters" : { + "left" : "\"STRING\"", + "right" : "\"INTERVAL DAY TO MINUTE\"", + "sqlExpr" : "\"(4 11:11 - INTERVAL '4 22:12' DAY TO MINUTE)\"" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 51, + "fragment" : "'4 11:11' - interval '4 22:12' day to minute" + } ] +} -- !query select '4 12:12:12' + interval '4 22:12' day to minute -- !query analysis -Project [cast(cast(4 12:12:12 as timestamp) + INTERVAL '4 22:12' DAY TO MINUTE as string) AS 4 12:12:12 + INTERVAL '4 22:12' DAY TO MINUTE#x] -+- OneRowRelation +org.apache.spark.sql.AnalysisException +{ + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "sqlState" : "42K09", + "messageParameters" : { + "left" : "\"STRING\"", + "right" : "\"INTERVAL DAY TO MINUTE\"", + "sqlExpr" : "\"(4 12:12:12 + INTERVAL '4 22:12' DAY TO MINUTE)\"" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 54, + "fragment" : "'4 12:12:12' + interval '4 22:12' day to minute" + } ] +} -- !query @@ -1662,23 +1692,45 @@ org.apache.spark.sql.AnalysisException -- !query select str - interval '4 22:12' day to minute from interval_view -- !query analysis -Project [cast(str#x - INTERVAL '4 22:12' DAY TO MINUTE as string) AS str - INTERVAL '4 22:12' DAY TO MINUTE#x] -+- SubqueryAlias interval_view - +- View (`interval_view`, [str#x]) - +- Project [cast(str#x as string) AS str#x] - +- Project [1 AS str#x] - +- OneRowRelation +org.apache.spark.sql.AnalysisException +{ + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "sqlState" : "42K09", + "messageParameters" : { + "left" : "\"STRING\"", + "right" : "\"INTERVAL DAY TO MINUTE\"", + "sqlExpr" : "\"(str - INTERVAL '4 22:12' DAY TO MINUTE)\"" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 45, + "fragment" : "str - interval '4 22:12' day to minute" + } ] +} -- !query select str + interval '4 22:12' day to minute from interval_view -- !query analysis -Project [cast(cast(str#x as timestamp) + INTERVAL '4 22:12' DAY TO MINUTE as string) AS str + INTERVAL '4 22:12' DAY TO MINUTE#x] -+- SubqueryAlias interval_view - +- View (`interval_view`, [str#x]) - +- Project [cast(str#x as string) AS str#x] - +- Project [1 AS str#x] - +- OneRowRelation +org.apache.spark.sql.AnalysisException +{ + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", + "sqlState" : "42K09", + "messageParameters" : { + "left" : "\"STRING\"", + "right" : "\"INTERVAL DAY TO MINUTE\"", + "sqlExpr" : "\"(str + INTERVAL '4 22:12' DAY TO MINUTE)\"" + }, + "queryContext" : [ { + "objectType" : "", + "objectName" : "", + "startIndex" : 8, + "stopIndex" : 45, + "fragment" : "str + interval '4 22:12' day to minute" + } ] +} -- !query @@ -1780,14 +1832,12 @@ select 1 - interval '2' second -- !query analysis org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", "sqlState" : "42K09", "messageParameters" : { - "inputSql" : "\"1\"", - "inputType" : "\"INT\"", - "paramIndex" : "1", - "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", - "sqlExpr" : "\"1 + (- INTERVAL '02' SECOND)\"" + "left" : "\"INT\"", + "right" : "\"INTERVAL SECOND\"", + "sqlExpr" : "\"(1 - INTERVAL '02' SECOND)\"" }, "queryContext" : [ { "objectType" : "", @@ -1826,14 +1876,12 @@ select interval '2' second + 1 -- !query analysis org.apache.spark.sql.AnalysisException { - "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE", + "errorClass" : "DATATYPE_MISMATCH.BINARY_OP_DIFF_TYPES", "sqlState" : "42K09", "messageParameters" : { - "inputSql" : "\"1\"", - "inputType" : "\"INT\"", - "paramIndex" : "1", - "requiredType" : "\"(TIMESTAMP OR TIMESTAMP WITHOUT TIME ZONE)\"", - "sqlExpr" : "\"1 + INTERVAL '02' SECOND\"" + "left" : "\"INTERVAL SECOND\"", + "right" : "\"INT\"", + "sqlExpr" : "\"(INTERVAL '02' SECOND + 1)\"" }, "queryContext" : [ { "objectType" : "",