diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala index 2a4e84241a941..fbbbda33538d1 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala @@ -906,7 +906,18 @@ trait ScalaReflection extends Logging { * only defines a constructor via `apply` method. */ private def getCompanionConstructor(tpe: Type): Symbol = { - tpe.typeSymbol.asClass.companion.asTerm.typeSignature.member(universe.TermName("apply")) + def throwUnsupportedOperation = { + throw new UnsupportedOperationException(s"Unable to find constructor for $tpe. " + + s"This could happen if $tpe is an interface, or a trait without companion object " + + "constructor.") + } + tpe.typeSymbol.asClass.companion match { + case NoSymbol => throwUnsupportedOperation + case sym => sym.asTerm.typeSignature.member(universe.TermName("apply")) match { + case NoSymbol => throwUnsupportedOperation + case constructorSym => constructorSym + } + } } protected def constructParams(tpe: Type): Seq[Symbol] = { diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala index a2e58c3eaa0bd..f9cd9c3c398f6 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala @@ -138,6 +138,14 @@ trait ScroogeLikeExample extends Product1[Int] with Serializable { override def hashCode: Int = x } +/** Counter-examples to [[ScroogeLikeExample]] as a trait without a companion object constructor */ +trait TraitProductWithoutCompanion extends Product1[Int] {} + +/** Counter-examples to [[ScroogeLikeExample]] as a trait with no-constructor companion object */ +object TraitProductWithNoConstructorCompanion {} + +trait TraitProductWithNoConstructorCompanion extends Product1[Int] {} + class ScalaReflectionSuite extends SparkFunSuite { import org.apache.spark.sql.catalyst.ScalaReflection._ @@ -404,6 +412,20 @@ class ScalaReflectionSuite extends SparkFunSuite { StructField("x", IntegerType, nullable = false))), nullable = true)) } + test("SPARK-29026: schemaFor for trait without companion object throws exception ") { + val e = intercept[UnsupportedOperationException] { + schemaFor[TraitProductWithoutCompanion] + } + assert(e.getMessage.contains("Unable to find constructor")) + } + + test("SPARK-29026: schemaFor for trait with no-constructor companion throws exception ") { + val e = intercept[UnsupportedOperationException] { + schemaFor[TraitProductWithNoConstructorCompanion] + } + assert(e.getMessage.contains("Unable to find constructor")) + } + test("SPARK-27625: annotated data types") { assert(serializerFor[FooWithAnnotation].dataType == StructType(Seq( StructField("f1", StringType),