-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-30764][SQL] Improve the readability of EXPLAIN FORMATTED style #27509
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 2 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 |
|---|---|---|
|
|
@@ -76,7 +76,7 @@ trait DataSourceScanExec extends LeafExecNode { | |
|
|
||
| s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |Output: ${producedAttributes.mkString("[", ", ", "]")} | ||
| |${ExplainUtils.generateFieldString("Output", producedAttributes)} | ||
| |${metadataStr.mkString("\n")} | ||
|
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. These changes are only for DSV1. Could we make the corresponding changes when using DSV2? Open the ticket https://issues.apache.org/jira/browse/SPARK-31480 Also, please check the output when the schema is very long. For example, containing 250+ columns. |
||
| """.stripMargin | ||
| } | ||
|
|
@@ -377,7 +377,7 @@ case class FileSourceScanExec( | |
|
|
||
| s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |Output: ${producedAttributes.mkString("[", ", ", "]")} | ||
| |${ExplainUtils.generateFieldString("Output", producedAttributes)} | ||
| |${metadataStr.mkString("\n")} | ||
| """.stripMargin | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,6 @@ import scala.collection.mutable.ArrayBuffer | |
| import org.apache.spark.sql.AnalysisException | ||
| import org.apache.spark.sql.catalyst.expressions.{Expression, PlanExpression} | ||
| import org.apache.spark.sql.catalyst.plans.QueryPlan | ||
| import org.apache.spark.sql.catalyst.trees.TreeNodeTag | ||
|
|
||
| object ExplainUtils { | ||
| /** | ||
|
|
@@ -171,7 +170,7 @@ object ExplainUtils { | |
| var currentCodegenId = -1 | ||
| plan.foreach { | ||
| case p: WholeStageCodegenExec => currentCodegenId = p.codegenStageId | ||
| case p: InputAdapter => currentCodegenId = -1 | ||
| case _: InputAdapter => currentCodegenId = -1 | ||
| case other: QueryPlan[_] => | ||
| if (currentCodegenId != -1) { | ||
| other.setTagValue(QueryPlan.CODEGEN_ID_TAG, currentCodegenId) | ||
|
|
@@ -182,6 +181,17 @@ object ExplainUtils { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Generate detailed field string with different format based on type of input value | ||
| */ | ||
| def generateFieldString(fieldName: String, values: Any): String = values match { | ||
| case iter: Iterable[_] if (iter.size == 0) => s"${fieldName}: []" | ||
| case iter: Iterable[_] => s"${fieldName} [${iter.size}]: ${iter.mkString("[", ", ", "]")}" | ||
| case str: String if (str == null || str.isEmpty) => s"${fieldName}: None" | ||
| case str: String => s"${fieldName}: ${str}" | ||
| case _ => s"${fieldName}: Unknown" | ||
|
||
| } | ||
|
|
||
| /** | ||
| * Given a input plan, returns an array of tuples comprising of : | ||
| * 1. Hosting opeator id. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,7 @@ import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan | |
| import org.apache.spark.sql.catalyst.plans.physical._ | ||
| import org.apache.spark.sql.catalyst.trees.TreeNodeTag | ||
| import org.apache.spark.sql.execution.metric.SQLMetric | ||
| import org.apache.spark.sql.internal.SQLConf | ||
| import org.apache.spark.sql.vectorized.ColumnarBatch | ||
|
|
||
| object SparkPlan { | ||
|
|
@@ -512,10 +513,16 @@ trait LeafExecNode extends SparkPlan { | |
| override final def children: Seq[SparkPlan] = Nil | ||
| override def producedAttributes: AttributeSet = outputSet | ||
| override def verboseStringWithOperatorId(): String = { | ||
| s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |Output: ${producedAttributes.mkString("[", ", ", "]")} | ||
| """.stripMargin | ||
| val argumentString = argString(SQLConf.get.maxToStringFields) | ||
| val result = s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |${ExplainUtils.generateFieldString("Output", producedAttributes)} | ||
| """.stripMargin | ||
| if (argumentString != null && !argumentString.isEmpty) { | ||
| s"""${result} |Arguments: $argumentString\n""".stripMargin | ||
|
||
| } else { | ||
| s"${result}" | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -531,10 +538,16 @@ trait UnaryExecNode extends SparkPlan { | |
|
|
||
| override final def children: Seq[SparkPlan] = child :: Nil | ||
| override def verboseStringWithOperatorId(): String = { | ||
| s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |Input: ${child.output.mkString("[", ", ", "]")} | ||
| """.stripMargin | ||
| val argumentString = argString(SQLConf.get.maxToStringFields) | ||
| val result = s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |${ExplainUtils.generateFieldString("Input", child.output)} | ||
| """.stripMargin | ||
| if (argumentString != null && !argumentString.isEmpty) { | ||
| s"""${result} |Arguments: $argumentString\n""".stripMargin | ||
|
||
| } else { | ||
| s"${result}" | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -544,10 +557,16 @@ trait BinaryExecNode extends SparkPlan { | |
|
|
||
| override final def children: Seq[SparkPlan] = Seq(left, right) | ||
| override def verboseStringWithOperatorId(): String = { | ||
| s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |Left output: ${left.output.mkString("[", ", ", "]")} | ||
| |Right output: ${right.output.mkString("[", ", ", "]")} | ||
| """.stripMargin | ||
| val argumentString = argString(SQLConf.get.maxToStringFields) | ||
| val result = s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |${ExplainUtils.generateFieldString("Left output", left.output)} | ||
| |${ExplainUtils.generateFieldString("Right output", right.output)} | ||
| """.stripMargin | ||
| if (argumentString != null && !argumentString.isEmpty) { | ||
| s"""${result} |Arguments: $argumentString\n""".stripMargin | ||
| } else { | ||
| s"${result}" | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -86,8 +86,8 @@ case class ProjectExec(projectList: Seq[NamedExpression], child: SparkPlan) | |
| override def verboseStringWithOperatorId(): String = { | ||
| s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |Output : ${projectList.mkString("[", ", ", "]")} | ||
| |Input : ${child.output.mkString("[", ", ", "]")} | ||
| |${ExplainUtils.generateFieldString("Output", projectList)} | ||
| |${ExplainUtils.generateFieldString("Input", child.output)} | ||
| """.stripMargin | ||
| } | ||
| } | ||
|
|
@@ -243,7 +243,7 @@ case class FilterExec(condition: Expression, child: SparkPlan) | |
| override def verboseStringWithOperatorId(): String = { | ||
| s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |Input : ${child.output.mkString("[", ", ", "]")} | ||
| |${ExplainUtils.generateFieldString("Input", child.output)} | ||
| |Condition : ${condition} | ||
|
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. Can we remove the space before ":"? |
||
| """.stripMargin | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,9 +63,9 @@ case class SortMergeJoinExec( | |
| } else "None" | ||
| s""" | ||
| |(${ExplainUtils.getOpId(this)}) $nodeName ${ExplainUtils.getCodegenId(this)} | ||
| |Left keys : ${leftKeys} | ||
| |Right keys: ${rightKeys} | ||
| |Join condition : ${joinCondStr} | ||
| |${ExplainUtils.generateFieldString("Left keys", leftKeys)} | ||
|
||
| |${ExplainUtils.generateFieldString("Right keys", rightKeys)} | ||
| |${ExplainUtils.generateFieldString("Join condition", joinCondStr)} | ||
| """.stripMargin | ||
| } | ||
|
|
||
|
|
||
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.
This is too hard to read. How about
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 see, thanks!
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, I just remember why I tried so complicated writing here.
result + s"Arguments: $argumentString\n"will lead to unexpected padding before Arguments.Thus to avoid the padding and not using improper multiline string maybe we can use
s"${result} |Arguments: $argumentString\n".stripMargin?Any other suggestions? Thanks @cloud-fan
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.
how about
result.trim + s"Arguments: $argumentString\n"?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.
It seems trim will break the first line padding of
result.result.trim + s"\nArguments: $argumentString\n"Shows
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.
how about
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.
Yea, it works with better code style. Thanks a lot!