My code compiled with Spark 3.1.2:
private def work(plan: LogicalPlan): LogicalPlan = {
val result = plan.transformDown {
// irrelevant details
}
}
When run with Spark 3.3.0, I run into:
java.lang.NoSuchMethodError: org.apache.spark.sql.catalyst.plans.logical.LogicalPlan.transformDown(Lscala/PartialFunction;)Lorg/apache/spark/sql/catalyst/plans/logical/LogicalPlan;
which makes sense because transformDown doesn't exist anymore in Spark 3.3.0 and seems to now be transformDownWithPruning.
I want to make this work via reflection with the logic:
if (sparkVersion = 3.1.2) plan.transformDown
else if (sparkVersion = 3.3.0) plan.transformDownWithPruning
I know you can call methods by exact name via reflection, but is there any way to get the method to call based on the method name containing a string? In this case it would if it contains "transformDown". I've been writing up the code below, but something like this:
private def transformWithReflection(plan: LogicalPlan) = {
val runtime = scala.reflect.runtime.universe
val mirror = runtime.runtimeMirror(getClass.getClassLoader)
val instanceMirror = mirror.reflect(plan)
//We target the transformDown method
val transformMethodAlternatives = runtime
.typeOf[LogicalPlan]
.decl(runtime.TermName("transformDown")) // this looks for exact name right?
.asTerm
.alternatives
...
// call reflected method
}
Or maybe I can get a list of all the methods under this class and filter them by "contains transformDown" which should only be 1 per list and then call that?
I guess there is some misunderstanding.
The class
org.apache.spark.sql.catalyst.plans.logical.LogicalPlanitself doesn't have methodtransformDowneither in Spark 3.1.2 or 3.3.0https://www.diffchecker.com/gvueXinY/
It's an inherited member and exists both in 3.1.2 and 3.3.0
https://www.diffchecker.com/gKx1ZcYM/
What changed in 3.3.0 in comparison with 3.1.2 is the signature of the method (since the method is inherited you need
.memberrather than.decl)The method is inherited from the class
org.apache.spark.sql.catalyst.trees.TreeNodehttps://github.com/apache/spark/blob/v3.1.2/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/trees/TreeNode.scala#L316
https://github.com/apache/spark/blob/v3.3.0/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/trees/TreeNode.scala#L559-L577
What should work both in 3.1.2 and 3.3.0 is static upcasting (with type ascription) to
TreeNodeRuntime reflection also should work both in 3.1.2 and 3.3.0
In principle, when you'd like to call different methods (this doesn't seem to be your case) you can do this for example with runtime reflection
or runtime compilation
NoSuchMethodError: scala.tools.nsc.Settings.usejavacp()Lscala/tools/nsc/settings/AbsSettings$AbsSetting;