Skip to content

[SPARK-13456][SQL][follow-up] lazily generate the outer pointer for case class defined in REPL #11931

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class ReplSuite extends SparkFunSuite {
return out.toString
}

// Simulate the paste mode in Scala REPL.
def runInterpreterInPasteMode(master: String, input: String): String =
runInterpreter(master, ":paste\n" + input + 4.toChar) // 4 is the ascii code of CTRL + D

def assertContains(message: String, output: String) {
val isContain = output.contains(message)
assert(isContain,
Expand Down Expand Up @@ -381,4 +385,15 @@ class ReplSuite extends SparkFunSuite {
assertDoesNotContain("error:", output)
assertDoesNotContain("Exception", output)
}

test("define case class and create Dataset together with paste mode") {
val output = runInterpreterInPasteMode("local-cluster[1,1,1024]",
"""
|import sqlContext.implicits._
|case class TestClass(value: Int)
|Seq(TestClass(1)).toDS()
""".stripMargin)
assertDoesNotContain("error:", output)
assertDoesNotContain("Exception", output)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ class Analyzer(
"access to the scope that this class was defined in.\n" +
"Try moving this class out of its parent class.")
}
n.copy(outerPointer = Some(Literal.fromObject(outer)))
n.copy(outerPointer = Some(outer))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ object OuterScopes {
outerScopes.putIfAbsent(outer.getClass.getName, outer)
}

def getOuterScope(innerCls: Class[_]): AnyRef = {
/**
* Returns a function which can get the outer scope for the given inner class. By using function
* as return type, we can delay the process of getting outer pointer to execution time, which is
* useful for inner class defined in REPL.
*/
def getOuterScope(innerCls: Class[_]): () => AnyRef = {
assert(innerCls.isMemberClass)
val outerClassName = innerCls.getDeclaringClass.getName
val outer = outerScopes.get(outerClassName)
Expand All @@ -53,24 +58,30 @@ object OuterScopes {
// `INSTANCE()` method to get the single instance of class `$read`. Then call `$iw()`
// method multiply times to get the single instance of the inner most `$iw` class.
case REPLClass(baseClassName) =>
val objClass = Utils.classForName(baseClassName + "$")
val objInstance = objClass.getField("MODULE$").get(null)
val baseInstance = objClass.getMethod("INSTANCE").invoke(objInstance)
val baseClass = Utils.classForName(baseClassName)
() => {
val objClass = Utils.classForName(baseClassName + "$")
val objInstance = objClass.getField("MODULE$").get(null)
val baseInstance = objClass.getMethod("INSTANCE").invoke(objInstance)
val baseClass = Utils.classForName(baseClassName)

var getter = iwGetter(baseClass)
var obj = baseInstance
while (getter != null) {
obj = getter.invoke(obj)
getter = iwGetter(getter.getReturnType)
}
var getter = iwGetter(baseClass)
var obj = baseInstance
while (getter != null) {
obj = getter.invoke(obj)
getter = iwGetter(getter.getReturnType)
}

outerScopes.putIfAbsent(outerClassName, obj)
obj
if (obj == null) {
throw new RuntimeException(s"Failed to get outer pointer for ${innerCls.getName}")
}

outerScopes.putIfAbsent(outerClassName, obj)
obj
}
case _ => null
}
} else {
outer
() => outer
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,17 @@ object NewInstance {
* @param dataType The type of object being constructed, as a Spark SQL datatype. This allows you
* to manually specify the type when the object in question is a valid internal
* representation (i.e. ArrayData) instead of an object.
* @param outerPointer If the object being constructed is an inner class the outerPointer must
* for the containing class must be specified.
* @param outerPointer If the object being constructed is an inner class, the outerPointer for the
* containing class must be specified. This parameter is defined as an optional
* function, which allows us to get the outer pointer lazily,and it's useful if
* the inner class is defined in REPL.
*/
case class NewInstance(
cls: Class[_],
arguments: Seq[Expression],
propagateNull: Boolean,
dataType: DataType,
outerPointer: Option[Literal]) extends Expression with NonSQLExpression {
outerPointer: Option[() => AnyRef]) extends Expression with NonSQLExpression {
private val className = cls.getName

override def nullable: Boolean = propagateNull
Expand All @@ -220,12 +222,12 @@ case class NewInstance(
val argGen = arguments.map(_.gen(ctx))
val argString = argGen.map(_.value).mkString(", ")

val outer = outerPointer.map(_.gen(ctx))
val outer = outerPointer.map(func => Literal.fromObject(func()).gen(ctx))

val setup =
s"""
${argGen.map(_.code).mkString("\n")}
${outer.map(_.code.mkString("")).getOrElse("")}
${outer.map(_.code).getOrElse("")}
""".stripMargin

val constructorCall = outer.map { gen =>
Expand Down