-
Notifications
You must be signed in to change notification settings - Fork 28.6k
[SPARK-16622][SQL] Fix NullPointerException when the returned value of the called method in Invoke is null #14259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…d method is null.
@@ -147,12 +158,12 @@ case class Invoke( | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is is better to pass $funcValIsNull
to ${ev.isNull}
? This is because $funcValIsNull
determines whether ${ev.value}
is null
or not instead of ${ev.isNull}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here ${ev.value}
will be null
only when ctx.defaultValue(javaType)
is null
. There is a postNullCheck
to do the extra check for this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes. You are right. Sorry for my misunderstanding.
Test build #62520 has finished for PR 14259 at commit
|
Test build #62527 has finished for PR 14259 at commit
|
Test build #62535 has finished for PR 14259 at commit
|
cc @cloud-fan @rxin Can you review this? Please take a look. Thanks. |
Can you explain a bit more about this? Why a method can't return null? |
Use the added test case as example, the generated java code to access the second element in a Tuple2
To assign a null to int will cause |
val code = s""" | ||
${obj.code} | ||
${argGen.map(_.code).mkString("\n")} | ||
$setIsNull | ||
$callFunc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking of something like
val returnPrimitive = method.isDefined && method.get.getReturnType.isPrimitive
val needTryCatch = method.isDefined && method.get.getExceptionTypes.nonEmpty
def getFuncResult(resultVal: String, funcCall: String): String = if (needTryCatch) {
"""
try {
$resultVal = $funcCall;
} catch (Exception e) {
org.apache.spark.unsafe.Platform.throwException(e);
}
"""
} else {
s"$resultVal = $funcCall;"
}
val evaluate = if (returnPrimitive) {
getFuncResult(ev.value, s"${obj.value}.$functionName($argString)")
} else {
val funcResult = ctx.freshName("funcResult")
s"""
Object $funcResult = null;
$getFuncResult(funcResult, s"${obj.value}.$functionName($argString)")
if ($funcResult == null) {
${ev.isNull} = true;
} else {
${ev.value} = (${ctx.boxedType(javaType)}) $funcResult;
}
"""
}
val code = s"""
...
$javaType ${ev.value} = ${ctx.defaultValue(dataType)};
$evaluete
...
"""
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uh. Looks good. Let me update this. Thanks!
@@ -314,4 +343,14 @@ class DatasetAggregatorSuite extends QueryTest with SharedSQLContext { | |||
val ds3 = sql("SELECT 'Some String' AS b, 1279869254 AS a").as[AggData] | |||
assert(ds3.select(NameAgg.toColumn).schema.head.nullable === true) | |||
} | |||
|
|||
test("Aggregator on empty dataset") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should have a unit test for this bug instead of this complex end-to-end test, e.g. we can create Invoke
directly and call a method returning null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I will update this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another problem is this test should not be in DatasetAggregatorSuite
. I don't find a proper file for this test. Which one you would suggest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can create a ObjectExpressionsSuite
under org.apache.spark.sql.catalyst.expressions
Test build #62665 has finished for PR 14259 at commit
|
Test build #62720 has finished for PR 14259 at commit
|
@cloud-fan The unit test is added. Please take a look if this PR is good for you now. Thanks. |
|
||
test("SPARK-16622: The returned value of the called method in Invoke can be null") { | ||
val inputRow = InternalRow.fromSeq(Seq((false, null))) | ||
val cls = classOf[Tuple2[Boolean, Int]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
java.lang.Integer
? Int
can not be null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Test build #62728 has finished for PR 14259 at commit
|
thanks, merging to master! |
Thanks for reviewing this. |
What changes were proposed in this pull request?
Currently we don't check the value returned by called method in
Invoke
. When the returned value is null and is assigned to a variable of primitive type,NullPointerException
will be thrown.How was this patch tested?
Jenkins tests.