Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 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 @@ -123,7 +123,8 @@ statement
| CREATE TEMPORARY? FUNCTION qualifiedName AS className=STRING
(USING resource (',' resource)*)? #createFunction
| DROP TEMPORARY? FUNCTION (IF EXISTS)? qualifiedName #dropFunction
| EXPLAIN (LOGICAL | FORMATTED | EXTENDED | CODEGEN)? statement #explain
| EXPLAIN (LOGICAL | FORMATTED | EXTENDED | CODEGEN | COST)?
statement #explain
| SHOW TABLES ((FROM | IN) db=identifier)?
(LIKE? pattern=STRING)? #showTables
| SHOW TABLE EXTENDED ((FROM | IN) db=identifier)?
Expand Down Expand Up @@ -794,6 +795,7 @@ EXPLAIN: 'EXPLAIN';
FORMAT: 'FORMAT';
LOGICAL: 'LOGICAL';
CODEGEN: 'CODEGEN';
COST: 'COST';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also put in it nonReserved

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Also please update the hiveNonReservedKeyword in TableIdentifierParserSuite

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Updated.

CAST: 'CAST';
SHOW: 'SHOW';
TABLES: 'TABLES';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ abstract class LogicalPlan extends QueryPlan[LogicalPlan] with Logging {
Statistics(sizeInBytes = children.map(_.stats(conf).sizeInBytes).product)
}

override def verboseStringWithSuffix: String = {
super.verboseString + statsCache.map(", " + _.toString).getOrElse("")
}

/**
* Returns the maximum number of rows that this plan may compute.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@

package org.apache.spark.sql.catalyst.plans.logical

import java.math.{MathContext, RoundingMode}

import scala.util.control.NonFatal

import org.apache.spark.internal.Logging
import org.apache.spark.sql.{AnalysisException, Row}
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.catalyst.expressions.aggregate._
import org.apache.spark.sql.types._
import org.apache.spark.util.Utils


/**
Expand Down Expand Up @@ -54,11 +57,29 @@ case class Statistics(

/** Readable string representation for the Statistics. */
def simpleString: String = {
Seq(s"sizeInBytes=$sizeInBytes",
if (rowCount.isDefined) s"rowCount=${rowCount.get}" else "",
Seq(s"sizeInBytes=${format(sizeInBytes, isSize = true)}",
if (rowCount.isDefined) s"rowCount=${format(rowCount.get, isSize = false)}" else "",
s"isBroadcastable=$isBroadcastable"
).filter(_.nonEmpty).mkString(", ")
}

/** Show the given number in a readable format. */
def format(number: BigInt, isSize: Boolean): String = {
val decimalValue = BigDecimal(number, new MathContext(3, RoundingMode.HALF_UP))
if (isSize) {
// The largest unit in Utils.bytesToString is TB

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about improving bytesToString and make it support PB or higher?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, I also think TB is a little small

val PB = 1L << 50
if (number < 2 * PB) {
// The number is not very large, so we can use Utils.bytesToString to show it.
Utils.bytesToString(number.toLong)
} else {
// The number is too large, show it in scientific notation.
decimalValue.toString() + " B"
}
} else {
decimalValue.toString()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always represent it using scientific notation? Or only do it when the number is too large?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://en.wikipedia.org/wiki/Metric_prefix

Even if we do not have a unit, we still can use K, M, G, T, P, E?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, will that be more readable than scientific notation if no unit?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With or without units, the readability is the same, right? If we make them consistent, the impl of def format(number: BigInt) will look much cleaner.

@wzhfy wzhfy Feb 22, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't make them consistent here, because unit string is added inside Utils.bytesToString.
How about move the logic for size into Utils.bytesToString and make it support BigInt?
Then we can remove def format:

  def simpleString: String = {
    Seq(s"sizeInBytes=${Utils.bytesToString(sizeInBytes)}",
      if (rowCount.isDefined) {
        // Show row count in scientific notation.
        s"rowCount=${BigDecimal(rowCount.get, new MathContext(3, RoundingMode.HALF_UP)).toString()}"
      } else {
        ""
      },
      s"isBroadcastable=$isBroadcastable"
    ).filter(_.nonEmpty).mkString(", ")
  }

}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,13 +453,16 @@ abstract class TreeNode[BaseType <: TreeNode[BaseType]] extends Product {
/** ONE line description of this node with more information */
def verboseString: String

/** ONE line description of this node with some suffix information */
def verboseStringWithSuffix: String = verboseString

override def toString: String = treeString

/** Returns a string representation of the nodes in this tree */
def treeString: String = treeString(verbose = true)

def treeString(verbose: Boolean): String = {
generateTreeString(0, Nil, new StringBuilder, verbose).toString
def treeString(verbose: Boolean, addSuffix: Boolean = false): String = {
generateTreeString(0, Nil, new StringBuilder, verbose = verbose, addSuffix = addSuffix).toString
}

/**
Expand Down Expand Up @@ -524,7 +527,8 @@ abstract class TreeNode[BaseType <: TreeNode[BaseType]] extends Product {
lastChildren: Seq[Boolean],
builder: StringBuilder,
verbose: Boolean,
prefix: String = ""): StringBuilder = {
prefix: String = "",
addSuffix: Boolean = false): StringBuilder = {

if (depth > 0) {
lastChildren.init.foreach { isLast =>
Expand All @@ -533,22 +537,29 @@ abstract class TreeNode[BaseType <: TreeNode[BaseType]] extends Product {
builder.append(if (lastChildren.last) "+- " else ":- ")
}

val str = if (verbose) {
if (addSuffix) verboseStringWithSuffix else verboseString
} else {
simpleString
}
builder.append(prefix)
builder.append(if (verbose) verboseString else simpleString)
builder.append(str)
builder.append("\n")

if (innerChildren.nonEmpty) {
innerChildren.init.foreach(_.generateTreeString(
depth + 2, lastChildren :+ children.isEmpty :+ false, builder, verbose))
depth + 2, lastChildren :+ children.isEmpty :+ false, builder, verbose,
addSuffix = addSuffix))
innerChildren.last.generateTreeString(
depth + 2, lastChildren :+ children.isEmpty :+ true, builder, verbose)
depth + 2, lastChildren :+ children.isEmpty :+ true, builder, verbose,
addSuffix = addSuffix)
}

if (children.nonEmpty) {
children.init.foreach(_.generateTreeString(
depth + 1, lastChildren :+ false, builder, verbose, prefix))
depth + 1, lastChildren :+ false, builder, verbose, prefix, addSuffix))
children.last.generateTreeString(
depth + 1, lastChildren :+ true, builder, verbose, prefix)
depth + 1, lastChildren :+ true, builder, verbose, prefix, addSuffix)
}

builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,20 +197,32 @@ class QueryExecution(val sparkSession: SparkSession, val logical: LogicalPlan) {
""".stripMargin.trim
}

override def toString: String = {
override def toString: String = completeString(appendStats = false)

def toStringWithStats: String = completeString(appendStats = true)

def completeString(appendStats: Boolean): String = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private?

def output = Utils.truncatedString(
analyzed.output.map(o => s"${o.name}: ${o.dataType.simpleString}"), ", ")
val analyzedPlan = Seq(
stringOrError(output),
stringOrError(analyzed.treeString(verbose = true))
).filter(_.nonEmpty).mkString("\n")

val optimizedPlanString = if (appendStats) {
// trigger to compute stats for logical plans
optimizedPlan.stats(sparkSession.sessionState.conf)
optimizedPlan.treeString(verbose = true, addSuffix = true)
} else {
optimizedPlan.treeString(verbose = true)
}

s"""== Parsed Logical Plan ==
|${stringOrError(logical.treeString(verbose = true))}
|== Analyzed Logical Plan ==
|$analyzedPlan
|== Optimized Logical Plan ==
|${stringOrError(optimizedPlan.treeString(verbose = true))}
|${stringOrError(optimizedPlanString)}
|== Physical Plan ==
|${stringOrError(executedPlan.treeString(verbose = true))}
""".stripMargin.trim
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@ class SparkSqlAstBuilder(conf: SQLConf) extends AstBuilder {
if (statement == null) {
null // This is enough since ParseException will raise later.
} else if (isExplainableStatement(statement)) {
ExplainCommand(statement, extended = ctx.EXTENDED != null, codegen = ctx.CODEGEN != null)
ExplainCommand(statement, extended = ctx.EXTENDED != null, codegen = ctx.CODEGEN != null,
cost = ctx.COST != null)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to fix the style.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give a clue on the style?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

      ExplainCommand(
        statement,
        extended = ctx.EXTENDED != null,
        codegen = ctx.CODEGEN != null,
        cost = ctx.COST != null)

} else {
ExplainCommand(OneRowRelation)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ case class InputAdapter(child: SparkPlan) extends UnaryExecNode with CodegenSupp
lastChildren: Seq[Boolean],
builder: StringBuilder,
verbose: Boolean,
prefix: String = ""): StringBuilder = {
prefix: String = "",
addSuffix: Boolean = false): StringBuilder = {
child.generateTreeString(depth, lastChildren, builder, verbose, "")
}
}
Expand Down Expand Up @@ -428,7 +429,8 @@ case class WholeStageCodegenExec(child: SparkPlan) extends UnaryExecNode with Co
lastChildren: Seq[Boolean],
builder: StringBuilder,
verbose: Boolean,
prefix: String = ""): StringBuilder = {
prefix: String = "",
addSuffix: Boolean = false): StringBuilder = {
child.generateTreeString(depth, lastChildren, builder, verbose, "*")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ case class ExecutedCommandExec(cmd: RunnableCommand) extends SparkPlan {
case class ExplainCommand(
logicalPlan: LogicalPlan,
extended: Boolean = false,
codegen: Boolean = false)
codegen: Boolean = false,
cost: Boolean = false)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add @parm like the other parameters

extends RunnableCommand {

override val output: Seq[Attribute] =
Expand All @@ -113,6 +114,8 @@ case class ExplainCommand(
codegenString(queryExecution.executedPlan)
} else if (extended) {
queryExecution.toString
} else if (cost) {
queryExecution.toStringWithStats
} else {
queryExecution.simpleString
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,25 @@ class StatisticsCollectionSuite extends StatisticsCollectionTestBase with Shared
}
checkColStats(df, mutable.LinkedHashMap(expectedColStats: _*))
}

test("number format in statistics") {
val numbers = Seq(
"0" -> ("0.0 B", "0"),
"100" -> ("100.0 B", "100"),
"2047" -> ("2047.0 B", "2.05E+3"),
"2048" -> ("2.0 KB", "2.05E+3"),
"3333333" -> ("3.2 MB", "3.33E+6"),
"4444444444" -> ("4.1 GB", "4.44E+9"),
"5555555555555" -> ("5.1 TB", "5.56E+12"),
"6666666666666666" -> ("6.67E+15 B", "6.67E+15")
)
numbers.foreach { case (input, (expectedSize, expectedRows)) =>
val stats = Statistics(sizeInBytes = BigInt(input), rowCount = Some(BigInt(input)))
val expectedString = s"sizeInBytes=$expectedSize, rowCount=$expectedRows," +
s" isBroadcastable=${stats.isBroadcastable}"
assert(stats.simpleString == expectedString)
}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ import org.apache.spark.sql.test.SQLTestUtils
*/
class HiveExplainSuite extends QueryTest with SQLTestUtils with TestHiveSingleton {

test("show cost in explain command") {
// Only has sizeInBytes before ANALYZE command
checkKeywordsExist(sql("EXPLAIN COST SELECT * FROM src "), "sizeInBytes")
checkKeywordsNotExist(sql("EXPLAIN COST SELECT * FROM src "), "rowCount")

// Has both sizeInBytes and rowCount after ANALYZE command
sql("ANALYZE TABLE src COMPUTE STATISTICS")
checkKeywordsExist(sql("EXPLAIN COST SELECT * FROM src "), "sizeInBytes", "rowCount")

// No cost information
checkKeywordsNotExist(sql("EXPLAIN SELECT * FROM src "), "sizeInBytes", "rowCount")
}

test("explain extended command") {
checkKeywordsExist(sql(" explain select * from src where key=123 "),
"== Physical Plan ==")
Expand Down