From c677aede0fe20f19ef66be3d4be4de19ceb27ad6 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Wed, 13 Dec 2017 10:52:55 +0000 Subject: [PATCH 1/5] Use splitExpressionsWithCurrentInputs to split codes in elt. --- .../expressions/codegen/CodeGenerator.scala | 40 ++++++++++--- .../expressions/stringExpressions.scala | 56 ++++++++++--------- 2 files changed, 60 insertions(+), 36 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala index 257c3f10fa08b..fd45ba6cc8bec 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala @@ -309,12 +309,17 @@ class CodegenContext { funcCode: String, inlineToOuterClass: Boolean = false): String = { val newFunction = addNewFunctionInternal(funcName, funcCode, inlineToOuterClass) - newFunction match { + qualifiedFunctionName(newFunction) + } + + // Returns the name of the function, qualified by class if it will be inlined to a private, + // inner class + private[this] def qualifiedFunctionName(functionSpec: NewFunctionSpec): String = + functionSpec match { case NewFunctionSpec(functionName, None, None) => functionName case NewFunctionSpec(functionName, Some(_), Some(innerClassInstance)) => innerClassInstance + "." + functionName } - } private[this] def addNewFunctionInternal( funcName: String, @@ -795,6 +800,9 @@ class CodegenContext { * @param returnType the return type of the split function. * @param makeSplitFunction makes split function body, e.g. add preparation or cleanup. * @param foldFunctions folds the split function calls. + * @param makeFunctionCallback a callback function that is called after each function split. + * The name of split function will be passed into the callback. + * @param mergeSplit When true, try to merge split methods. */ def splitExpressionsWithCurrentInputs( expressions: Seq[String], @@ -802,7 +810,9 @@ class CodegenContext { extraArguments: Seq[(String, String)] = Nil, returnType: String = "void", makeSplitFunction: String => String = identity, - foldFunctions: Seq[String] => String = _.mkString("", ";\n", ";")): String = { + foldFunctions: Seq[String] => String = _.mkString("", ";\n", ";"), + makeFunctionCallback: String => Unit = identity, + mergeSplit: Boolean = true): String = { // TODO: support whole stage codegen if (INPUT_ROW == null || currentVars != null) { expressions.mkString("\n") @@ -813,7 +823,9 @@ class CodegenContext { ("InternalRow", INPUT_ROW) +: extraArguments, returnType, makeSplitFunction, - foldFunctions) + foldFunctions, + makeFunctionCallback, + mergeSplit) } } @@ -829,6 +841,9 @@ class CodegenContext { * @param returnType the return type of the split function. * @param makeSplitFunction makes split function body, e.g. add preparation or cleanup. * @param foldFunctions folds the split function calls. + * @param makeFunctionCallback a callback function that is called after each function split. + * The name of split function will be passed into the callback. + * @param mergeSplit When true, try to merge split methods. */ def splitExpressions( expressions: Seq[String], @@ -836,7 +851,9 @@ class CodegenContext { arguments: Seq[(String, String)], returnType: String = "void", makeSplitFunction: String => String = identity, - foldFunctions: Seq[String] => String = _.mkString("", ";\n", ";")): String = { + foldFunctions: Seq[String] => String = _.mkString("", ";\n", ";"), + makeFunctionCallback: String => Unit = identity, + mergeSplit: Boolean = true): String = { val blocks = buildCodeBlocks(expressions) if (blocks.length == 1) { @@ -852,7 +869,9 @@ class CodegenContext { | ${makeSplitFunction(body)} |} """.stripMargin - addNewFunctionInternal(name, code, inlineToOuterClass = false) + val functionSpec = addNewFunctionInternal(name, code, inlineToOuterClass = false) + makeFunctionCallback(qualifiedFunctionName(functionSpec)) + functionSpec } val (outerClassFunctions, innerClassFunctions) = functions.partition(_.innerClassName.isEmpty) @@ -866,7 +885,8 @@ class CodegenContext { arguments, returnType, makeSplitFunction, - foldFunctions) + foldFunctions, + mergeSplit) foldFunctions(outerClassFunctionCalls ++ innerClassFunctionCalls) } @@ -914,6 +934,7 @@ class CodegenContext { * @param returnType the return type of the split function. * @param makeSplitFunction makes split function body, e.g. add preparation or cleanup. * @param foldFunctions folds the split function calls. + * @param mergeSplit When true, try to merge split methods. * @return an [[Iterable]] containing the methods' invocations */ private def generateInnerClassesFunctionCalls( @@ -922,7 +943,8 @@ class CodegenContext { arguments: Seq[(String, String)], returnType: String, makeSplitFunction: String => String, - foldFunctions: Seq[String] => String): Iterable[String] = { + foldFunctions: Seq[String] => String, + mergeSplit: Boolean = true): Iterable[String] = { val innerClassToFunctions = mutable.LinkedHashMap.empty[(String, String), Seq[String]] functions.foreach(f => { val key = (f.innerClassName.get, f.innerClassInstance.get) @@ -938,7 +960,7 @@ class CodegenContext { // for performance reasons, the functions are prepended, instead of appended, // thus here they are in reversed order val orderedFunctions = innerClassFunctions.reverse - if (orderedFunctions.size > CodeGenerator.MERGE_SPLIT_METHODS_THRESHOLD) { + if (mergeSplit && orderedFunctions.size > CodeGenerator.MERGE_SPLIT_METHODS_THRESHOLD) { // Adding a new function to each inner class which contains the invocation of all the // ones which have been added to that inner class. For example, // private class NestedClass { diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala index 47f0b5741f67f..15e34b7636467 100755 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala @@ -299,33 +299,35 @@ case class Elt(children: Seq[Expression]) """ } - val cases = ctx.buildCodeBlocks(assignStringValue) - val codes = if (cases.length == 1) { - s""" - UTF8String $stringVal = null; - switch ($indexVal) { - ${cases.head} - } - """ - } else { - var prevFunc = "null" - for (c <- cases.reverse) { - val funcName = ctx.freshName("eltFunc") - val funcBody = s""" - private UTF8String $funcName(InternalRow ${ctx.INPUT_ROW}, int $indexVal) { - UTF8String $stringVal = null; - switch ($indexVal) { - $c - default: - return $prevFunc; - } - return $stringVal; - } - """ - val fullFuncName = ctx.addNewFunction(funcName, funcBody) - prevFunc = s"$fullFuncName(${ctx.INPUT_ROW}, $indexVal)" - } - s"UTF8String $stringVal = $prevFunc;" + var prevFunc = "null" + var codes = ctx.splitExpressionsWithCurrentInputs( + expressions = assignStringValue, + funcName = "eltFunc", + extraArguments = ("int", indexVal) :: Nil, + returnType = "UTF8String", + makeSplitFunction = body => + s""" + |UTF8String $stringVal = null; + |switch ($indexVal) { + | $body + | default: + | return $prevFunc; + |} + |return $stringVal; + """.stripMargin, + foldFunctions = funcs => s"UTF8String $stringVal = ${funcs.last};", + makeFunctionCallback = f => prevFunc = s"$f(${ctx.INPUT_ROW}, $indexVal)", + mergeSplit = false) + + // If no any functions split, wraps all cases in a single switch. + if (prevFunc == "null") { + codes = + s""" + |UTF8String $stringVal = null; + |switch ($indexVal) { + | $codes + |} + """.stripMargin } ev.copy( From ac41620956ace61c6ea72218e52ee5f1ae60a32e Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Wed, 13 Dec 2017 14:13:38 +0000 Subject: [PATCH 2/5] Simplified version. --- .../expressions/codegen/CodeGenerator.scala | 40 +++-------- .../expressions/stringExpressions.scala | 72 ++++++++++--------- 2 files changed, 48 insertions(+), 64 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala index fd45ba6cc8bec..257c3f10fa08b 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala @@ -309,17 +309,12 @@ class CodegenContext { funcCode: String, inlineToOuterClass: Boolean = false): String = { val newFunction = addNewFunctionInternal(funcName, funcCode, inlineToOuterClass) - qualifiedFunctionName(newFunction) - } - - // Returns the name of the function, qualified by class if it will be inlined to a private, - // inner class - private[this] def qualifiedFunctionName(functionSpec: NewFunctionSpec): String = - functionSpec match { + newFunction match { case NewFunctionSpec(functionName, None, None) => functionName case NewFunctionSpec(functionName, Some(_), Some(innerClassInstance)) => innerClassInstance + "." + functionName } + } private[this] def addNewFunctionInternal( funcName: String, @@ -800,9 +795,6 @@ class CodegenContext { * @param returnType the return type of the split function. * @param makeSplitFunction makes split function body, e.g. add preparation or cleanup. * @param foldFunctions folds the split function calls. - * @param makeFunctionCallback a callback function that is called after each function split. - * The name of split function will be passed into the callback. - * @param mergeSplit When true, try to merge split methods. */ def splitExpressionsWithCurrentInputs( expressions: Seq[String], @@ -810,9 +802,7 @@ class CodegenContext { extraArguments: Seq[(String, String)] = Nil, returnType: String = "void", makeSplitFunction: String => String = identity, - foldFunctions: Seq[String] => String = _.mkString("", ";\n", ";"), - makeFunctionCallback: String => Unit = identity, - mergeSplit: Boolean = true): String = { + foldFunctions: Seq[String] => String = _.mkString("", ";\n", ";")): String = { // TODO: support whole stage codegen if (INPUT_ROW == null || currentVars != null) { expressions.mkString("\n") @@ -823,9 +813,7 @@ class CodegenContext { ("InternalRow", INPUT_ROW) +: extraArguments, returnType, makeSplitFunction, - foldFunctions, - makeFunctionCallback, - mergeSplit) + foldFunctions) } } @@ -841,9 +829,6 @@ class CodegenContext { * @param returnType the return type of the split function. * @param makeSplitFunction makes split function body, e.g. add preparation or cleanup. * @param foldFunctions folds the split function calls. - * @param makeFunctionCallback a callback function that is called after each function split. - * The name of split function will be passed into the callback. - * @param mergeSplit When true, try to merge split methods. */ def splitExpressions( expressions: Seq[String], @@ -851,9 +836,7 @@ class CodegenContext { arguments: Seq[(String, String)], returnType: String = "void", makeSplitFunction: String => String = identity, - foldFunctions: Seq[String] => String = _.mkString("", ";\n", ";"), - makeFunctionCallback: String => Unit = identity, - mergeSplit: Boolean = true): String = { + foldFunctions: Seq[String] => String = _.mkString("", ";\n", ";")): String = { val blocks = buildCodeBlocks(expressions) if (blocks.length == 1) { @@ -869,9 +852,7 @@ class CodegenContext { | ${makeSplitFunction(body)} |} """.stripMargin - val functionSpec = addNewFunctionInternal(name, code, inlineToOuterClass = false) - makeFunctionCallback(qualifiedFunctionName(functionSpec)) - functionSpec + addNewFunctionInternal(name, code, inlineToOuterClass = false) } val (outerClassFunctions, innerClassFunctions) = functions.partition(_.innerClassName.isEmpty) @@ -885,8 +866,7 @@ class CodegenContext { arguments, returnType, makeSplitFunction, - foldFunctions, - mergeSplit) + foldFunctions) foldFunctions(outerClassFunctionCalls ++ innerClassFunctionCalls) } @@ -934,7 +914,6 @@ class CodegenContext { * @param returnType the return type of the split function. * @param makeSplitFunction makes split function body, e.g. add preparation or cleanup. * @param foldFunctions folds the split function calls. - * @param mergeSplit When true, try to merge split methods. * @return an [[Iterable]] containing the methods' invocations */ private def generateInnerClassesFunctionCalls( @@ -943,8 +922,7 @@ class CodegenContext { arguments: Seq[(String, String)], returnType: String, makeSplitFunction: String => String, - foldFunctions: Seq[String] => String, - mergeSplit: Boolean = true): Iterable[String] = { + foldFunctions: Seq[String] => String): Iterable[String] = { val innerClassToFunctions = mutable.LinkedHashMap.empty[(String, String), Seq[String]] functions.foreach(f => { val key = (f.innerClassName.get, f.innerClassInstance.get) @@ -960,7 +938,7 @@ class CodegenContext { // for performance reasons, the functions are prepended, instead of appended, // thus here they are in reversed order val orderedFunctions = innerClassFunctions.reverse - if (mergeSplit && orderedFunctions.size > CodeGenerator.MERGE_SPLIT_METHODS_THRESHOLD) { + if (orderedFunctions.size > CodeGenerator.MERGE_SPLIT_METHODS_THRESHOLD) { // Adding a new function to each inner class which contains the invocation of all the // ones which have been added to that inner class. For example, // private class NestedClass { diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala index 15e34b7636467..ce4031409ee83 100755 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala @@ -289,55 +289,61 @@ case class Elt(children: Seq[Expression]) val index = indexExpr.genCode(ctx) val strings = stringExprs.map(_.genCode(ctx)) val indexVal = ctx.freshName("index") + + // -1 means the given index doesn't match indices of strings in split function. + val NOT_MATCHED = -1 + // 0 means the given index matches one of indices of strings in split function. + val MATCHED = 0 + val resultState = ctx.freshName("eltResultState") + val stringVal = ctx.freshName("stringVal") + ctx.addMutableState(ctx.javaType(dataType), stringVal) + val assignStringValue = strings.zipWithIndex.map { case (eval, index) => s""" - case ${index + 1}: - ${eval.code} - $stringVal = ${eval.isNull} ? null : ${eval.value}; - break; - """ + |if ($indexVal == ${index + 1}) { + | ${eval.code} + | $stringVal = ${eval.isNull} ? null : ${eval.value}; + | $resultState = (byte)$MATCHED; + | continue; + |} + """.stripMargin } - var prevFunc = "null" - var codes = ctx.splitExpressionsWithCurrentInputs( + val codes = ctx.splitExpressionsWithCurrentInputs( expressions = assignStringValue, funcName = "eltFunc", extraArguments = ("int", indexVal) :: Nil, - returnType = "UTF8String", + returnType = ctx.JAVA_BYTE, makeSplitFunction = body => s""" - |UTF8String $stringVal = null; - |switch ($indexVal) { + |${ctx.JAVA_BYTE} $resultState = $NOT_MATCHED; + |do { | $body - | default: - | return $prevFunc; - |} - |return $stringVal; - """.stripMargin, - foldFunctions = funcs => s"UTF8String $stringVal = ${funcs.last};", - makeFunctionCallback = f => prevFunc = s"$f(${ctx.INPUT_ROW}, $indexVal)", - mergeSplit = false) - - // If no any functions split, wraps all cases in a single switch. - if (prevFunc == "null") { - codes = + |} while (false); + |return $resultState; + """.stripMargin, + foldFunctions = _.map { funcCall => s""" - |UTF8String $stringVal = null; - |switch ($indexVal) { - | $codes + |$resultState = $funcCall; + |if ($resultState != $NOT_MATCHED) { + | continue; |} - """.stripMargin - } + """.stripMargin + }.mkString) ev.copy( s""" - ${index.code} - final int $indexVal = ${index.value}; - $codes - UTF8String ${ev.value} = $stringVal; - final boolean ${ev.isNull} = ${ev.value} == null; - """) + |${index.code} + |final int $indexVal = ${index.value}; + |${ctx.JAVA_BYTE} $resultState = $NOT_MATCHED; + |$stringVal = ${ctx.defaultValue(dataType)}; + |do { + | $codes + |} while (false); + |final UTF8String ${ev.value} = $stringVal; + |final boolean ${ev.isNull} = ${ev.value} == null; + """.stripMargin) } } From f6a4a549ff510a3a28efd2cac4ed39402b29ab38 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Wed, 13 Dec 2017 14:32:19 +0000 Subject: [PATCH 3/5] Use boolean. --- .../catalyst/expressions/stringExpressions.scala | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala index ce4031409ee83..ac2b2bde6d0eb 100755 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala @@ -289,11 +289,6 @@ case class Elt(children: Seq[Expression]) val index = indexExpr.genCode(ctx) val strings = stringExprs.map(_.genCode(ctx)) val indexVal = ctx.freshName("index") - - // -1 means the given index doesn't match indices of strings in split function. - val NOT_MATCHED = -1 - // 0 means the given index matches one of indices of strings in split function. - val MATCHED = 0 val resultState = ctx.freshName("eltResultState") val stringVal = ctx.freshName("stringVal") @@ -304,7 +299,7 @@ case class Elt(children: Seq[Expression]) |if ($indexVal == ${index + 1}) { | ${eval.code} | $stringVal = ${eval.isNull} ? null : ${eval.value}; - | $resultState = (byte)$MATCHED; + | $resultState = true; | continue; |} """.stripMargin @@ -314,10 +309,10 @@ case class Elt(children: Seq[Expression]) expressions = assignStringValue, funcName = "eltFunc", extraArguments = ("int", indexVal) :: Nil, - returnType = ctx.JAVA_BYTE, + returnType = ctx.JAVA_BOOLEAN, makeSplitFunction = body => s""" - |${ctx.JAVA_BYTE} $resultState = $NOT_MATCHED; + |${ctx.JAVA_BOOLEAN} $resultState = false; |do { | $body |} while (false); @@ -326,7 +321,7 @@ case class Elt(children: Seq[Expression]) foldFunctions = _.map { funcCall => s""" |$resultState = $funcCall; - |if ($resultState != $NOT_MATCHED) { + |if ($resultState) { | continue; |} """.stripMargin @@ -336,7 +331,7 @@ case class Elt(children: Seq[Expression]) s""" |${index.code} |final int $indexVal = ${index.value}; - |${ctx.JAVA_BYTE} $resultState = $NOT_MATCHED; + |${ctx.JAVA_BOOLEAN} $resultState = false; |$stringVal = ${ctx.defaultValue(dataType)}; |do { | $codes From 1563a467085b12373c39327ec2f603cd09a01393 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Wed, 13 Dec 2017 14:44:31 +0000 Subject: [PATCH 4/5] Rename variable. --- .../catalyst/expressions/stringExpressions.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala index ac2b2bde6d0eb..82a77b41af3ca 100755 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala @@ -289,7 +289,7 @@ case class Elt(children: Seq[Expression]) val index = indexExpr.genCode(ctx) val strings = stringExprs.map(_.genCode(ctx)) val indexVal = ctx.freshName("index") - val resultState = ctx.freshName("eltResultState") + val indexMatched = ctx.freshName("eltIndexMatched") val stringVal = ctx.freshName("stringVal") ctx.addMutableState(ctx.javaType(dataType), stringVal) @@ -299,7 +299,7 @@ case class Elt(children: Seq[Expression]) |if ($indexVal == ${index + 1}) { | ${eval.code} | $stringVal = ${eval.isNull} ? null : ${eval.value}; - | $resultState = true; + | $indexMatched = true; | continue; |} """.stripMargin @@ -312,16 +312,16 @@ case class Elt(children: Seq[Expression]) returnType = ctx.JAVA_BOOLEAN, makeSplitFunction = body => s""" - |${ctx.JAVA_BOOLEAN} $resultState = false; + |${ctx.JAVA_BOOLEAN} $indexMatched = false; |do { | $body |} while (false); - |return $resultState; + |return $indexMatched; """.stripMargin, foldFunctions = _.map { funcCall => s""" - |$resultState = $funcCall; - |if ($resultState) { + |$indexMatched = $funcCall; + |if ($indexMatched) { | continue; |} """.stripMargin @@ -331,7 +331,7 @@ case class Elt(children: Seq[Expression]) s""" |${index.code} |final int $indexVal = ${index.value}; - |${ctx.JAVA_BOOLEAN} $resultState = false; + |${ctx.JAVA_BOOLEAN} $indexMatched = false; |$stringVal = ${ctx.defaultValue(dataType)}; |do { | $codes From 69aab6109dff2b10d4dcdd6ef97587740da695a2 Mon Sep 17 00:00:00 2001 From: Liang-Chi Hsieh Date: Wed, 13 Dec 2017 15:34:38 +0000 Subject: [PATCH 5/5] Address comments. --- .../spark/sql/catalyst/expressions/codegen/CodeGenerator.scala | 2 +- .../spark/sql/catalyst/expressions/stringExpressions.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala index 257c3f10fa08b..b1d931117f99b 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/codegen/CodeGenerator.scala @@ -878,7 +878,7 @@ class CodegenContext { * * @param expressions the codes to evaluate expressions. */ - def buildCodeBlocks(expressions: Seq[String]): Seq[String] = { + private def buildCodeBlocks(expressions: Seq[String]): Seq[String] = { val blocks = new ArrayBuffer[String]() val blockBuilder = new StringBuilder() var length = 0 diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala index 82a77b41af3ca..8c4d2fd686be5 100755 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/stringExpressions.scala @@ -332,7 +332,7 @@ case class Elt(children: Seq[Expression]) |${index.code} |final int $indexVal = ${index.value}; |${ctx.JAVA_BOOLEAN} $indexMatched = false; - |$stringVal = ${ctx.defaultValue(dataType)}; + |$stringVal = null; |do { | $codes |} while (false);