From 978080bd8f5a1b095fd0d58ff529e16dd9cbadba Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Wed, 7 Mar 2018 02:56:53 +0000 Subject: [PATCH 01/11] Add interpreted execution for InitializeJavaBean expression. --- .../expressions/objects/objects.scala | 20 +++++++++++++++++-- .../expressions/ExpressionEvalHelper.scala | 3 ++- .../expressions/ObjectExpressionsSuite.scala | 11 +++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index 80618af1e859..a8535b322dfa 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1254,8 +1254,24 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp override def children: Seq[Expression] = beanInstance +: setters.values.toSeq override def dataType: DataType = beanInstance.dataType - override def eval(input: InternalRow): Any = - throw new UnsupportedOperationException("Only code-generated evaluation is supported.") + override def eval(input: InternalRow): Any = { + val instance = beanInstance.eval(input).asInstanceOf[Object] + if (instance != null) { + setters.foreach { case (setterMethod, fieldExpr) => + val fieldValue = fieldExpr.eval(input).asInstanceOf[Object] + + val foundMethods = instance.getClass.getMethods.filter { method => + method.getName == setterMethod && Modifier.isPublic(method.getModifiers) && + method.getParameterTypes.length == 1 + } + assert(foundMethods.length == 1, + throw new RuntimeException("The Java Bean instance should have only one " + + s"setter $setterMethod method, but ${foundMethods.length} methods found.")) + foundMethods.head.invoke(instance, fieldValue) + } + } + instance + } override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = { val instanceGen = beanInstance.genCode(ctx) diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ExpressionEvalHelper.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ExpressionEvalHelper.scala index b4c8eab19c5c..b365c7ba68f4 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ExpressionEvalHelper.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ExpressionEvalHelper.scala @@ -48,7 +48,8 @@ trait ExpressionEvalHelper extends GeneratorDrivenPropertyChecks { expression: => Expression, expected: Any, inputRow: InternalRow = EmptyRow): Unit = { val serializer = new JavaSerializer(new SparkConf()).newInstance val resolver = ResolveTimeZone(new SQLConf) - val expr = resolver.resolveTimeZones(serializer.deserialize(serializer.serialize(expression))) + // Make it as method to obtain fresh expression everytime. + def expr = resolver.resolveTimeZones(serializer.deserialize(serializer.serialize(expression))) val catalystValue = CatalystTypeConverters.convertToCatalyst(expected) checkEvaluationWithoutCodegen(expr, catalystValue, inputRow) checkEvaluationWithGeneratedMutableProjection(expr, catalystValue, inputRow) diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala index 3edcc02f1526..50d50a8a9b56 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala @@ -20,7 +20,7 @@ package org.apache.spark.sql.catalyst.expressions import org.apache.spark.SparkFunSuite import org.apache.spark.sql.catalyst.InternalRow import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder -import org.apache.spark.sql.catalyst.expressions.objects.Invoke +import org.apache.spark.sql.catalyst.expressions.objects._ import org.apache.spark.sql.catalyst.util.{ArrayBasedMapData, GenericArrayData} import org.apache.spark.sql.types.{IntegerType, ObjectType} @@ -66,4 +66,13 @@ class ObjectExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper { checkEvalutionWithUnsafeProjection( mapEncoder.serializer.head, mapExpected, mapInputRow) } + + test("SPARK-23593: InitializeJavaBean should support interpreted execution") { + val list = new java.util.LinkedList[Int]() + list.add(1) + + val initializeBean = InitializeJavaBean(Literal.fromObject(new java.util.LinkedList[Int]), + Map("add" -> Literal(1))) + checkEvaluation(initializeBean, list, InternalRow.fromSeq(Seq())) + } } From b8f171e5492f3156767589ad4c6ed458cb24615c Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Wed, 7 Mar 2018 03:24:48 +0000 Subject: [PATCH 02/11] Test error message of interpreted execution. --- .../sql/catalyst/expressions/ObjectExpressionsSuite.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala index c5daf1672572..8cf3018f44b7 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala @@ -75,6 +75,14 @@ class ObjectExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper { val initializeBean = InitializeJavaBean(Literal.fromObject(new java.util.LinkedList[Int]), Map("add" -> Literal(1))) checkEvaluation(initializeBean, list, InternalRow.fromSeq(Seq())) + + val errMsg = intercept[RuntimeException] { + val initializeWithNonexistingMethod = InitializeJavaBean( + Literal.fromObject(new java.util.LinkedList[Int]), + Map("nonexisting" -> Literal(1))) + evaluate(initializeWithNonexistingMethod, InternalRow.fromSeq(Seq())) + }.getMessage + assert(errMsg.contains("but 0 methods found.")) } test("SPARK-23585: UnwrapOption should support interpreted execution") { From b51303a49c66a178e9cdd717605e3ef0d41e6a3a Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Wed, 7 Mar 2018 13:03:07 +0000 Subject: [PATCH 03/11] Resolving setters outside eval. --- .../expressions/objects/objects.scala | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index 69987778103e..5f2f0e5e3084 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1261,20 +1261,27 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp override def children: Seq[Expression] = beanInstance +: setters.values.toSeq override def dataType: DataType = beanInstance.dataType + private lazy val resolvedSetters = { + val ObjectType(beanClass) = beanInstance.dataType + + setters.map { case (setterMethod, fieldExpr) => + val foundMethods = beanClass.getMethods.filter { method => + method.getName == setterMethod && Modifier.isPublic(method.getModifiers) && + method.getParameterTypes.length == 1 + } + assert(foundMethods.length == 1, + throw new RuntimeException("The Java Bean class should have only one " + + s"setter $setterMethod method, but ${foundMethods.length} methods found.")) + (foundMethods.head, fieldExpr) + } + } + override def eval(input: InternalRow): Any = { val instance = beanInstance.eval(input).asInstanceOf[Object] if (instance != null) { - setters.foreach { case (setterMethod, fieldExpr) => + resolvedSetters.foreach { case (setterMethod, fieldExpr) => val fieldValue = fieldExpr.eval(input).asInstanceOf[Object] - - val foundMethods = instance.getClass.getMethods.filter { method => - method.getName == setterMethod && Modifier.isPublic(method.getModifiers) && - method.getParameterTypes.length == 1 - } - assert(foundMethods.length == 1, - throw new RuntimeException("The Java Bean instance should have only one " + - s"setter $setterMethod method, but ${foundMethods.length} methods found.")) - foundMethods.head.invoke(instance, fieldValue) + setterMethod.invoke(instance, fieldValue) } } instance From 970ed6c6497c148d6eba10fe65f947c05b58d642 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Thu, 8 Mar 2018 04:40:10 +0000 Subject: [PATCH 04/11] Simplifying it. --- .../catalyst/expressions/objects/objects.scala | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index 47e3b9fadfc8..52cbe7e47c96 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1265,19 +1265,15 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp val ObjectType(beanClass) = beanInstance.dataType setters.map { case (name, expr) => - val methods = CallMethodViaReflection.typeMapping.getOrElse(expr.dataType, - Seq(expr.dataType.asInstanceOf[ObjectType].cls)).flatMap { fieldClass => + // Looking for known type mapping first, then using Class attached in `ObjectType`. + // Finally also looking for general `Object`-type parameter for generic methods. + val paramTypes = CallMethodViaReflection.typeMapping.getOrElse(expr.dataType, + Seq(expr.dataType.asInstanceOf[ObjectType].cls)) ++ Seq(classOf[Object]) + val methods = paramTypes.flatMap { fieldClass => try { - // Looking up for specified parameter type first. Some(beanClass.getDeclaredMethod(name, fieldClass)) } catch { - case e: NoSuchMethodException => - try { - // Looking up for general `Object`-type parameter for generic methods. - Some(beanClass.getDeclaredMethod(name, classOf[Object])) - } catch { - case e: NoSuchMethodException => None - } + case e: NoSuchMethodException => None } } if (methods.isEmpty) { From e20c207801613b45fea34520c05f59e9cb453351 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Thu, 8 Mar 2018 08:45:47 +0000 Subject: [PATCH 05/11] Address comment. --- .../spark/sql/catalyst/expressions/objects/objects.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index 52cbe7e47c96..c720244a4d9f 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1262,6 +1262,8 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp override def dataType: DataType = beanInstance.dataType private lazy val resolvedSetters = { + assert(beanInstance.dataType.isInstanceOf[ObjectType]) + val ObjectType(beanClass) = beanInstance.dataType setters.map { case (name, expr) => @@ -1285,11 +1287,12 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp } override def eval(input: InternalRow): Any = { - val instance = beanInstance.eval(input).asInstanceOf[Object] + val instance = beanInstance.eval(input) if (instance != null) { + val bean = instance.asInstanceOf[Object] resolvedSetters.foreach { case (setter, expr) => - setter.invoke(instance, expr.eval(input).asInstanceOf[Object]) + setter.invoke(bean, expr.eval(input).asInstanceOf[Object]) } } instance From f3fdf575c599a5d0d8fdaacebcd67bfbc89d39fd Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Thu, 8 Mar 2018 13:43:20 +0000 Subject: [PATCH 06/11] Change error message a bit. --- .../apache/spark/sql/catalyst/expressions/objects/objects.scala | 2 +- .../spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index c720244a4d9f..322432b4da7b 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1280,7 +1280,7 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp } if (methods.isEmpty) { throw new NoSuchMethodException(s"""A method named "$name" is not declared """ + - "in any enclosing class nor any supertype, nor through a static import") + "in any enclosing class nor any supertype") } methods.head -> expr } diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala index a69854551bff..66bcec77e015 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala @@ -82,7 +82,7 @@ class ObjectExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper { checkExceptionInExpression[Exception](initializeWithNonexistingMethod, InternalRow.fromSeq(Seq()), """A method named "nonexisting" is not declared in any enclosing class """ + - "nor any supertype, nor through a static import") + "nor any supertype") } test("SPARK-23585: UnwrapOption should support interpreted execution") { From 1a783346a14e93e5dd755185071ca2e93360f387 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Fri, 9 Mar 2018 04:37:38 +0000 Subject: [PATCH 07/11] Use AnyRef. --- .../apache/spark/sql/catalyst/expressions/objects/objects.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index 322432b4da7b..39d69602be7e 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1292,7 +1292,7 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp val bean = instance.asInstanceOf[Object] resolvedSetters.foreach { case (setter, expr) => - setter.invoke(bean, expr.eval(input).asInstanceOf[Object]) + setter.invoke(bean, expr.eval(input).asInstanceOf[AnyRef]) } } instance From e7640e14ac8eea8ba5521d80f640664e5753bb9f Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Tue, 27 Mar 2018 03:57:20 +0000 Subject: [PATCH 08/11] Add a test. --- .../expressions/ObjectExpressionsSuite.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala index 66bcec77e015..bfb960370b1d 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala @@ -83,6 +83,15 @@ class ObjectExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper { InternalRow.fromSeq(Seq()), """A method named "nonexisting" is not declared in any enclosing class """ + "nor any supertype") + + val initializeWithWrongParamType = InitializeJavaBean( + Literal.fromObject(new TestBean), + Map("setX" -> Literal("1"))) + intercept[Exception] { + evaluateWithoutCodegen(initializeWithWrongParamType, InternalRow.fromSeq(Seq())) + }.getMessage.contains( + """A method named "setX" is not declared in any enclosing class """ + + "nor any supertype") } test("SPARK-23585: UnwrapOption should support interpreted execution") { @@ -127,3 +136,9 @@ class ObjectExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper { "The 0th field 'c0' of input row cannot be null.") } } + +class TestBean extends Serializable { + private var x: Int = 0 + + def setX(i: Int): Unit = x = i +} From 0d6b3d355276210ad6a0967646f30d95744ddfcb Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Thu, 5 Apr 2018 06:03:47 +0000 Subject: [PATCH 09/11] Prevent null input to setters. --- .../sql/catalyst/expressions/objects/objects.scala | 12 +++++++++++- .../expressions/ObjectExpressionsSuite.scala | 9 +++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index 39d69602be7e..af1c8be68492 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1292,7 +1292,13 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp val bean = instance.asInstanceOf[Object] resolvedSetters.foreach { case (setter, expr) => - setter.invoke(bean, expr.eval(input).asInstanceOf[AnyRef]) + val paramVal = expr.eval(input) + if (paramVal == null) { + throw new NullPointerException("The parameter value for setters in " + + "`InitializeJavaBean` can not be null") + } else { + setter.invoke(bean, paramVal.asInstanceOf[AnyRef]) + } } } instance @@ -1309,6 +1315,10 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp val fieldGen = fieldValue.genCode(ctx) s""" |${fieldGen.code} + |if (${fieldGen.isNull}) { + | throw new NullPointerException("The parameter value for setters in " + + | "`InitializeJavaBean` can not be null"); + |} |$javaBeanInstance.$setterMethod(${fieldGen.value}); """.stripMargin } diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala index bfb960370b1d..f4bfcd128b6d 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala @@ -94,6 +94,13 @@ class ObjectExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper { "nor any supertype") } + test("Can not pass in null into setters in InitializeJavaBean") { + val initializeBean = InitializeJavaBean( + Literal.fromObject(new TestBean), + Map("setNonPrimitive" -> Literal(null))) + evaluateWithoutCodegen(initializeBean, InternalRow.fromSeq(Seq())) + } + test("SPARK-23585: UnwrapOption should support interpreted execution") { val cls = classOf[Option[Int]] val inputObject = BoundReference(0, ObjectType(cls), nullable = true) @@ -141,4 +148,6 @@ class TestBean extends Serializable { private var x: Int = 0 def setX(i: Int): Unit = x = i + def setNonPrimitive(i: AnyRef): Unit = + assert(i != null, "this setter should not be called with null.") } From 96a8fe6102d8d175003392a14d67cd74c0b86319 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Thu, 5 Apr 2018 06:30:17 +0000 Subject: [PATCH 10/11] Prevent null input to setters. --- .../sql/catalyst/expressions/objects/objects.scala | 3 +-- .../expressions/ObjectExpressionsSuite.scala | 13 ++++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index efc13436a08e..cc12fc53432a 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1418,8 +1418,7 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp case (name, expr) => // Looking for known type mapping first, then using Class attached in `ObjectType`. // Finally also looking for general `Object`-type parameter for generic methods. - val paramTypes = CallMethodViaReflection.typeMapping.getOrElse(expr.dataType, - Seq(expr.dataType.asInstanceOf[ObjectType].cls)) ++ Seq(classOf[Object]) + val paramTypes = ScalaReflection.expressionJavaClasses(Seq(expr)) ++ Seq(classOf[Object]) val methods = paramTypes.flatMap { fieldClass => try { Some(beanClass.getDeclaredMethod(name, fieldClass)) diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala index 08cc7581696f..44fecd602e85 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala @@ -158,7 +158,18 @@ class ObjectExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper { val initializeBean = InitializeJavaBean( Literal.fromObject(new TestBean), Map("setNonPrimitive" -> Literal(null))) - evaluateWithoutCodegen(initializeBean, InternalRow.fromSeq(Seq())) + intercept[NullPointerException] { + evaluateWithoutCodegen(initializeBean, InternalRow.fromSeq(Seq())) + }.getMessage.contains("The parameter value for setters in `InitializeJavaBean` can not be null") + intercept[NullPointerException] { + evaluateWithGeneratedMutableProjection(initializeBean, InternalRow.fromSeq(Seq())) + }.getMessage.contains("The parameter value for setters in `InitializeJavaBean` can not be null") + + val initializeBean2 = InitializeJavaBean( + Literal.fromObject(new TestBean), + Map("setNonPrimitive" -> Literal("string"))) + evaluateWithoutCodegen(initializeBean2, InternalRow.fromSeq(Seq())) + evaluateWithGeneratedMutableProjection(initializeBean2, InternalRow.fromSeq(Seq())) } test("SPARK-23585: UnwrapOption should support interpreted execution") { From 573db595ee84e894b7d7093884d7fdef2c040150 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Thu, 5 Apr 2018 06:33:27 +0000 Subject: [PATCH 11/11] Fix out-of-date comment. --- .../spark/sql/catalyst/expressions/objects/objects.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala index cc12fc53432a..20c4f4c7324f 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala @@ -1416,8 +1416,8 @@ case class InitializeJavaBean(beanInstance: Expression, setters: Map[String, Exp val ObjectType(beanClass) = beanInstance.dataType setters.map { case (name, expr) => - // Looking for known type mapping first, then using Class attached in `ObjectType`. - // Finally also looking for general `Object`-type parameter for generic methods. + // Looking for known type mapping. + // But also looking for general `Object`-type parameter for generic methods. val paramTypes = ScalaReflection.expressionJavaClasses(Seq(expr)) ++ Seq(classOf[Object]) val methods = paramTypes.flatMap { fieldClass => try {