Skip to content

Commit 2a5b5fd

Browse files
jkbradleymarmbrus
authored andcommitted
[SPARK-4791] [sql] Infer schema from case class with multiple constructors
Modified ScalaReflection.schemaFor to take primary constructor of Product when there are multiple constructors. Added test to suite which failed before but works now. Needed for [#3637] CC: marmbrus Author: Joseph K. Bradley <[email protected]> Closes #3646 from jkbradley/sql-reflection and squashes the following commits: 796b2e4 [Joseph K. Bradley] Modified ScalaReflection.schemaFor to take primary constructor of Product when there are multiple constructors. Added test to suite which failed before but works now.
1 parent 57d37f9 commit 2a5b5fd

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,19 @@ trait ScalaReflection {
118118
case t if t <:< typeOf[Product] =>
119119
val formalTypeArgs = t.typeSymbol.asClass.typeParams
120120
val TypeRef(_, _, actualTypeArgs) = t
121-
val params = t.member(nme.CONSTRUCTOR).asMethod.paramss
121+
val constructorSymbol = t.member(nme.CONSTRUCTOR)
122+
val params = if (constructorSymbol.isMethod) {
123+
constructorSymbol.asMethod.paramss
124+
} else {
125+
// Find the primary constructor, and use its parameter ordering.
126+
val primaryConstructorSymbol: Option[Symbol] = constructorSymbol.asTerm.alternatives.find(
127+
s => s.isMethod && s.asMethod.isPrimaryConstructor)
128+
if (primaryConstructorSymbol.isEmpty) {
129+
sys.error("Internal SQL error: Product object did not have a primary constructor.")
130+
} else {
131+
primaryConstructorSymbol.get.asMethod.paramss
132+
}
133+
}
122134
Schema(StructType(
123135
params.head.map { p =>
124136
val Schema(dataType, nullable) =

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ case class ComplexData(
6868
case class GenericData[A](
6969
genericField: A)
7070

71+
case class MultipleConstructorsData(a: Int, b: String, c: Double) {
72+
def this(b: String, a: Int) = this(a, b, c = 1.0)
73+
}
74+
7175
class ScalaReflectionSuite extends FunSuite {
7276
import ScalaReflection._
7377

@@ -253,4 +257,14 @@ class ScalaReflectionSuite extends FunSuite {
253257
Row(1, 1, 1, 1, 1, 1, true))
254258
assert(convertToCatalyst(data, dataType) === convertedData)
255259
}
260+
261+
test("infer schema from case class with multiple constructors") {
262+
val dataType = schemaFor[MultipleConstructorsData].dataType
263+
dataType match {
264+
case s: StructType =>
265+
// Schema should have order: a: Int, b: String, c: Double
266+
assert(s.fieldNames === Seq("a", "b", "c"))
267+
assert(s.fields.map(_.dataType) === Seq(IntegerType, StringType, DoubleType))
268+
}
269+
}
256270
}

0 commit comments

Comments
 (0)