diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/Cast.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/Cast.scala index 969128838eba4..85bad74850dc1 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/Cast.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/Cast.scala @@ -131,7 +131,8 @@ object Cast { case (from: DecimalType, to: NumericType) if from.isTighterThan(to) => true case (f, t) if legalNumericPrecedence(f, t) => true case (DateType, TimestampType) => true - case (_, StringType) => true + case (_: AtomicType, StringType) => true + case (_: CalendarIntervalType, StringType) => true // Spark supports casting between long and timestamp, please see `longToTimestamp` and // `timestampToLong` for details. diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/encoders/EncoderResolutionSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/encoders/EncoderResolutionSuite.scala index da1b695919dec..53cb8bce0a52d 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/encoders/EncoderResolutionSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/encoders/EncoderResolutionSuite.scala @@ -196,6 +196,21 @@ class EncoderResolutionSuite extends PlanTest { encoder.resolveAndBind(attrs) } + test("SPARK-28497: complex type is not compatible with string encoder schema") { + val encoder = ExpressionEncoder[String] + + Seq('a.struct('x.long), 'a.array(StringType), 'a.map(StringType, StringType)).foreach { attr => + val attrs = Seq(attr) + assert(intercept[AnalysisException](encoder.resolveAndBind(attrs)).message == + s""" + |Cannot up cast `a` from ${attr.dataType.catalogString} to string. + |The type path of the target object is: + |- root class: "java.lang.String" + |You can either add an explicit cast to the input data or choose a higher precision type + """.stripMargin.trim + " of the field in the target object") + } + } + test("throw exception if real type is not compatible with encoder schema") { val msg1 = intercept[AnalysisException] { ExpressionEncoder[StringIntClass].resolveAndBind(Seq('a.string, 'b.long)) diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/CastSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/CastSuite.scala index 4d667fd61ae01..44825c79781d9 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/CastSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/CastSuite.scala @@ -1002,6 +1002,11 @@ class CastSuite extends SparkFunSuite with ExpressionEvalHelper { } } } + numericTypes.foreach { dt => + makeComplexTypes(dt, true).foreach { complexType => + assert(!Cast.canUpCast(complexType, StringType)) + } + } } test("SPARK-27671: cast from nested null type in struct") { diff --git a/sql/core/src/test/scala/org/apache/spark/sql/UserDefinedTypeSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/UserDefinedTypeSuite.scala index 6628d36ffc702..49f0000212554 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/UserDefinedTypeSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/UserDefinedTypeSuite.scala @@ -272,4 +272,9 @@ class UserDefinedTypeSuite extends QueryTest with SharedSQLContext with ParquetT val ret = Cast(Literal(data, udt), StringType, None) checkEvaluation(ret, "(1.0, 3.0, 5.0, 7.0, 9.0)") } + + test("SPARK-28497 Can't up cast UserDefinedType to string") { + val udt = new TestUDT.MyDenseVectorUDT() + assert(!Cast.canUpCast(udt, StringType)) + } }