You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by ch...@apache.org on 2022/05/26 09:19:19 UTC

[incubator-kyuubi] branch master updated: [KYUUBI #2664] Kyuubi Spark TPC-H Connector - SupportsReportStatistics

This is an automated email from the ASF dual-hosted git repository.

chengpan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-kyuubi.git


The following commit(s) were added to refs/heads/master by this push:
     new dbe315e8a [KYUUBI #2664] Kyuubi Spark TPC-H Connector - SupportsReportStatistics
dbe315e8a is described below

commit dbe315e8a2ac51c2c81e88cab3d62924d3196a65
Author: jiaoqingbo <11...@qq.com>
AuthorDate: Thu May 26 17:19:10 2022 +0800

    [KYUUBI #2664] Kyuubi Spark TPC-H Connector - SupportsReportStatistics
    
    ### _Why are the changes needed?_
    
    fix #2664
    
    ### _How was this patch tested?_
    - [x] Add some test cases that check the changes thoroughly including negative and positive cases if possible
    
    - [ ] Add screenshots for manual tests if appropriate
    
    - [x] [Run test](https://kyuubi.apache.org/docs/latest/develop_tools/testing.html#running-tests) locally before make a pull request
    
    Closes #2740 from jiaoqingbo/kyuubi-2664.
    
    Closes #2664
    
    ebac8adb [jiaoqingbo] fix ut failed
    6532cc5d [jiaoqingbo] [KYUUBI #2664] Kyuubi Spark TPC-H Connector - SupportsReportStatistics
    
    Authored-by: jiaoqingbo <11...@qq.com>
    Signed-off-by: Cheng Pan <ch...@apache.org>
---
 .../kyuubi/spark/connector/tpch/SparkUtils.scala   | 78 ++++++++++++++++++++++
 .../spark/connector/tpch/TPCHBatchScan.scala       | 10 ++-
 .../spark/connector/tpch/TPCHStatisticsUtils.scala |  2 +-
 .../spark/connector/tpch/TPCHCatalogSuite.scala    | 30 ++++++++-
 4 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/SparkUtils.scala b/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/SparkUtils.scala
new file mode 100644
index 000000000..84b2835ba
--- /dev/null
+++ b/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/SparkUtils.scala
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.spark.connector.tpch
+
+import org.apache.spark.SPARK_VERSION
+
+object SparkUtils {
+
+  /**
+   * Given a Kyuubi/Spark/Hive version string,
+   * return the (major version number, minor version number).
+   * E.g., for 2.0.1-SNAPSHOT, return (2, 0).
+   */
+  def majorMinorVersion(version: String): (Int, Int) = {
+    """^(\d+)\.(\d+)(\..*)?$""".r.findFirstMatchIn(version) match {
+      case Some(m) =>
+        (m.group(1).toInt, m.group(2).toInt)
+      case None =>
+        throw new IllegalArgumentException(s"Tried to parse '$version' as a project" +
+          s" version string, but it could not find the major and minor version numbers.")
+    }
+  }
+
+  /**
+   * Given a Kyuubi/Spark/Hive version string, return the major version number.
+   * E.g., for 2.0.1-SNAPSHOT, return 2.
+   */
+  def majorVersion(version: String): Int = majorMinorVersion(version)._1
+
+  /**
+   * Given a Kyuubi/Spark/Hive version string, return the minor version number.
+   * E.g., for 2.0.1-SNAPSHOT, return 0.
+   */
+  def minorVersion(version: String): Int = majorMinorVersion(version)._2
+
+  def isSparkVersionAtMost(ver: String): Boolean = {
+    val runtimeMajor = majorVersion(SPARK_VERSION)
+    val targetMajor = majorVersion(ver)
+    (runtimeMajor < targetMajor) || {
+      val runtimeMinor = minorVersion(SPARK_VERSION)
+      val targetMinor = minorVersion(ver)
+      runtimeMajor == targetMajor && runtimeMinor <= targetMinor
+    }
+  }
+
+  def isSparkVersionAtLeast(ver: String): Boolean = {
+    val runtimeMajor = majorVersion(SPARK_VERSION)
+    val targetMajor = majorVersion(ver)
+    (runtimeMajor > targetMajor) || {
+      val runtimeMinor = minorVersion(SPARK_VERSION)
+      val targetMinor = minorVersion(ver)
+      runtimeMajor == targetMajor && runtimeMinor >= targetMinor
+    }
+  }
+
+  def isSparkVersionEqualTo(ver: String): Boolean = {
+    val runtimeMajor = majorVersion(SPARK_VERSION)
+    val targetMajor = majorVersion(ver)
+    val runtimeMinor = minorVersion(SPARK_VERSION)
+    val targetMinor = minorVersion(ver)
+    runtimeMajor == targetMajor && runtimeMinor == targetMinor
+  }
+}
diff --git a/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/TPCHBatchScan.scala b/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/TPCHBatchScan.scala
index 4c9e8c13e..939e7c29e 100644
--- a/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/TPCHBatchScan.scala
+++ b/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/TPCHBatchScan.scala
@@ -19,6 +19,7 @@ package org.apache.kyuubi.spark.connector.tpch
 
 import java.time.LocalDate
 import java.time.format.DateTimeFormatter
+import java.util.OptionalLong
 
 import scala.collection.mutable.ArrayBuffer
 
@@ -38,7 +39,9 @@ class TPCHBatchScan(
     @transient table: TpchTable[_],
     scale: Int,
     schema: StructType) extends ScanBuilder
-  with Scan with Batch with Serializable {
+  with SupportsReportStatistics with Batch with Serializable {
+
+  private val _sizeInBytes: Long = TPCHStatisticsUtils.sizeInBytes(table, scale)
 
   private val _numRows: Long = TPCHStatisticsUtils.numRows(table, scale)
 
@@ -69,6 +72,11 @@ class TPCHBatchScan(
     new TPCHPartitionReader(chuck.table, chuck.scale, chuck.parallelism, chuck.index, schema)
   }
 
+  override def estimateStatistics: Statistics = new Statistics {
+    override def sizeInBytes: OptionalLong = OptionalLong.of(_sizeInBytes)
+    override def numRows: OptionalLong = OptionalLong.of(_numRows)
+  }
+
 }
 
 class TPCHPartitionReader(
diff --git a/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/TPCHStatisticsUtils.scala b/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/TPCHStatisticsUtils.scala
index 1108829d8..1137521fe 100644
--- a/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/TPCHStatisticsUtils.scala
+++ b/extensions/spark/kyuubi-spark-connector-tpch/src/main/scala/org/apache/kyuubi/spark/connector/tpch/TPCHStatisticsUtils.scala
@@ -46,7 +46,7 @@ object TPCHStatisticsUtils {
       case (PART_SUPPLIER, scale) => 800000L * scale
       case (SUPPLIER, scale) => 10000L * scale
       case (NATION, _) => 25L
-      case (REGION, _) => 25L
+      case (REGION, _) => 5L
     }
   }
 
diff --git a/extensions/spark/kyuubi-spark-connector-tpch/src/test/scala/org/apache/kyuubi/spark/connector/tpch/TPCHCatalogSuite.scala b/extensions/spark/kyuubi-spark-connector-tpch/src/test/scala/org/apache/kyuubi/spark/connector/tpch/TPCHCatalogSuite.scala
index 2496a0cd8..a831745b0 100644
--- a/extensions/spark/kyuubi-spark-connector-tpch/src/test/scala/org/apache/kyuubi/spark/connector/tpch/TPCHCatalogSuite.scala
+++ b/extensions/spark/kyuubi-spark-connector-tpch/src/test/scala/org/apache/kyuubi/spark/connector/tpch/TPCHCatalogSuite.scala
@@ -17,7 +17,7 @@
 
 package org.apache.kyuubi.spark.connector.tpch
 
-import org.apache.spark.sql.SparkSession
+import org.apache.spark.sql.{AnalysisException, SparkSession}
 
 import org.apache.kyuubi.KyuubiFunSuite
 
@@ -51,6 +51,34 @@ class TPCHCatalogSuite extends KyuubiFunSuite {
     assert(spark.table("tpch.sf1.region").count === 5)
   }
 
+  test("tpch.sf1 stats") {
+    def assertStats(tableName: String, sizeInBytes: BigInt, rowCount: BigInt): Unit = {
+      val stats = spark.table(tableName).queryExecution.analyzed.stats
+      assert(stats.sizeInBytes == sizeInBytes)
+      // stats.rowCount only has value after SPARK-33954
+      if (SparkUtils.isSparkVersionAtLeast("3.2")) {
+        assert(stats.rowCount.contains(rowCount), tableName)
+      }
+    }
+
+    assertStats("tpch.sf1.customer", 26850000, 150000)
+    assertStats("tpch.sf1.orders", 156000000, 1500000)
+    assertStats("tpch.sf1.lineitem", 672136080, 6001215)
+    assertStats("tpch.sf1.part", 31000000, 200000)
+    assertStats("tpch.sf1.partsupp", 115200000, 800000)
+    assertStats("tpch.sf1.supplier", 1590000, 10000)
+    assertStats("tpch.sf1.nation", 3200, 25)
+    assertStats("tpch.sf1.region", 620, 5)
+
+  }
+
+  test("nonexistent table") {
+    val exception = intercept[AnalysisException] {
+      spark.table("tpch.sf1.nonexistent_table")
+    }
+    assert(exception.message === "Table or view not found: tpch.sf1.nonexistent_table")
+  }
+
   override def afterAll(): Unit = {
     super.afterAll()
     spark.stop()