-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-32877][SQL] Fix Hive UDF not support decimal type in complex type #29749
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
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,7 +47,8 @@ private[hive] case class HiveSimpleUDF( | |
| with HiveInspectors | ||
| with CodegenFallback | ||
| with Logging | ||
| with UserDefinedExpression { | ||
| with UserDefinedExpression | ||
| with ImplicitCastInputTypes { | ||
|
|
||
| override lazy val deterministic: Boolean = isUDFDeterministic && children.forall(_.deterministic) | ||
|
|
||
|
|
@@ -69,6 +70,23 @@ private[hive] case class HiveSimpleUDF( | |
| udfType != null && udfType.deterministic() && !udfType.stateful() | ||
| } | ||
|
|
||
| override def inputTypes: Seq[AbstractDataType] = { | ||
| val inTypes = children.map(_.dataType) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused. The expected type should be defined by the function signature, but not the actual function inputs. What are we doing here? As an example,
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Let's say we have a udf But Hive udf can't enjoy it, now we only use Hive
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I misread the code. We get children data type only to skip decimal. |
||
| if (!inTypes.exists(_.existsRecursively(_.isInstanceOf[DecimalType]))) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need special handling for decimal types?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a compatible issue. In normal case, data type is converted by Hive ObjectInspector at running time. But Hive not support input decimal type when method required double type. Unfortunately the default type of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see. Could you describe it in the PR description?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I didn't get it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In first commit I did it for all types but test not passed. The reason is a UDF required an Object type. We convert Java Object Type to NullType in So we can't reflect the UDF method using a NullType, the error msg is:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if a hive udf requires
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about this ?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems we should check data type and replace I will do an another check if we can change |
||
| inTypes | ||
| } else { | ||
| val expectTypes = method.getGenericParameterTypes.map(javaTypeToDataType) | ||
| // check decimal | ||
| inTypes.zip(expectTypes).map { case (in, expect) => | ||
| if (in.existsRecursively(_.isInstanceOf[DecimalType])) { | ||
| expect | ||
| } else { | ||
| in | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| override def foldable: Boolean = isUDFDeterministic && children.forall(_.foldable) | ||
|
|
||
| // Create parameter converters | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -658,6 +658,24 @@ class HiveUDFSuite extends QueryTest with TestHiveSingleton with SQLTestUtils { | |
| } | ||
| } | ||
|
|
||
| test("SPARK-32877: Fix Hive UDF not support decimal type in complex type") { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is not covered in any other test case, it looks worth of having this. |
||
| withUserDefinedFunction("testArraySum" -> false) { | ||
| sql(s"CREATE FUNCTION testArraySum AS '${classOf[ArraySumUDF].getName}'") | ||
| checkAnswer( | ||
| sql("SELECT testArraySum(array(1, 1.1, 1.2))"), | ||
| Seq(Row(3.3))) | ||
|
|
||
| val msg = intercept[AnalysisException] { | ||
| sql("SELECT testArraySum(1)") | ||
| }.getMessage | ||
| assert(msg.contains(s"No handler for UDF/UDAF/UDTF '${classOf[ArraySumUDF].getName}'")) | ||
|
|
||
| val msg2 = intercept[AnalysisException] { | ||
| sql("SELECT testArraySum(1, 2)") | ||
| }.getMessage | ||
| assert(msg2.contains(s"No handler for UDF/UDAF/UDTF '${classOf[ArraySumUDF].getName}'")) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| class TestPair(x: Int, y: Int) extends Writable with Serializable { | ||
|
|
@@ -741,3 +759,14 @@ class StatelessUDF extends UDF { | |
| result | ||
| } | ||
| } | ||
|
|
||
| class ArraySumUDF extends UDF { | ||
| import scala.collection.JavaConverters._ | ||
| def evaluate(values: java.util.List[java.lang.Double]): java.lang.Double = { | ||
| var r = 0d | ||
| for (v <- values.asScala) { | ||
| r += v | ||
| } | ||
| r | ||
| } | ||
| } | ||
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.
ScalaUDFdoesn't extendImplicitCastInputTypeseither. Does it have the same problem?Uh oh!
There was an error while loading. Please reload this page.
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.
No,
ImplicitTypeCastshas checkedScalaUDFascase udf: ScalaUDF if udf.inputTypes.nonEmpty =>