Github user cloud-fan commented on a diff in the pull request:

    https://github.com/apache/spark/pull/20756#discussion_r176973244
  
    --- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala
 ---
    @@ -1261,8 +1261,42 @@ 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.")
    +  private lazy val resolvedSetters = {
    +    assert(beanInstance.dataType.isInstanceOf[ObjectType])
    +
    +    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.
    +        val paramTypes = 
CallMethodViaReflection.typeMapping.getOrElse(expr.dataType,
    +            Seq(expr.dataType.asInstanceOf[ObjectType].cls)) ++ 
Seq(classOf[Object])
    +        val methods = paramTypes.flatMap { fieldClass =>
    +          try {
    +            Some(beanClass.getDeclaredMethod(name, fieldClass))
    +          } catch {
    +            case e: NoSuchMethodException => None
    +          }
    +        }
    +        if (methods.isEmpty) {
    +          throw new NoSuchMethodException(s"""A method named "$name" is 
not declared """ +
    +            "in any enclosing class nor any supertype")
    +        }
    +        methods.head -> expr
    +    }
    +  }
    +
    +  override def eval(input: InternalRow): Any = {
    +    val instance = beanInstance.eval(input)
    +    if (instance != null) {
    +      val bean = instance.asInstanceOf[Object]
    +      resolvedSetters.foreach {
    +        case (setter, expr) =>
    +          setter.invoke(bean, expr.eval(input).asInstanceOf[AnyRef])
    --- End diff --
    
    Correct me if I'm wrong:
    If the setter takes primitive types, like `setAge(int i)`, and we pass a 
null via reflection, the actual passed value would be 0. This is different from 
the codegen version, seems like a potential bug. 
    
    IMO, I think the codegen version is wrong. In general we should not read 
the codegen value if it's marked as null.
    
    This doesn't cause any problem, because we only use these object 
expressions to generate encoders, which means the parameter for a primitive 
setter won't be null. But if we treat these expressions as a general DSL, we 
should be careful about this.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to