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

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 567c8401c2 [spark] Fix bucketed scan reporting for unsupported 
timestamp precision (#9216)
567c8401c2 is described below

commit 567c8401c2d17933ba7fb69080aab6dc8b04ab32
Author: Xiangyi Zhu <[email protected]>
AuthorDate: Sat Aug 15 20:18:23 2026 +0800

    [spark] Fix bucketed scan reporting for unsupported timestamp precision 
(#9216)
---
 .../scala/org/apache/paimon/spark/PaimonScan.scala | 10 ++++++
 .../paimon/spark/sql/BucketedTableQueryTest.scala  | 38 ++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/PaimonScan.scala
 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/PaimonScan.scala
index 7b376c2f79..b6d89cf6e8 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/PaimonScan.scala
+++ 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/PaimonScan.scala
@@ -21,6 +21,7 @@ package org.apache.paimon.spark
 import org.apache.paimon.CoreOptions.BucketFunctionType
 import org.apache.paimon.partition.PartitionPredicate
 import org.apache.paimon.predicate.{FullTextSearch, HybridSearch, Predicate, 
TopN, VectorSearch}
+import org.apache.paimon.spark.catalog.functions.BucketFunction
 import org.apache.paimon.spark.commands.BucketExpression.quote
 import org.apache.paimon.spark.read.VariantExtractionInfo
 import org.apache.paimon.table.{BucketMode, FileStoreTable, InnerTable}
@@ -67,6 +68,15 @@ case class PaimonScan(
             .bucketFunctionType() != BucketFunctionType.DEFAULT
         ) {
           None
+        } else if (!BucketFunction.supportsTable(fileStoreTable)) {
+          // Spark tells two scans apart by the canonical name of the bound 
bucket function, which
+          // is derived from Spark types. Spark's timestamp precision is fixed 
to 6, so bucket keys
+          // carrying any other precision are indistinguishable there, while 
Paimon lays their
+          // BinaryRow out differently and thus puts the same value into a 
different bucket.
+          // Reporting a bucket transform would let Spark treat such tables as 
co-partitioned and
+          // drop a shuffle that is actually required. This mirrors the same 
check the write side
+          // already does in `PaimonSparkWriter`.
+          None
         } else if (bucketSpec.getBucketKeys.size() > 1) {
           None
         } else {
diff --git 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/BucketedTableQueryTest.scala
 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/BucketedTableQueryTest.scala
index 16b932ed7a..745d498b6f 100644
--- 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/BucketedTableQueryTest.scala
+++ 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/BucketedTableQueryTest.scala
@@ -18,7 +18,10 @@
 
 package org.apache.paimon.spark.sql
 
+import org.apache.paimon.catalog.Identifier
+import org.apache.paimon.schema.Schema
 import org.apache.paimon.spark.PaimonSparkTestBase
+import org.apache.paimon.types.DataTypes
 
 import org.apache.spark.sql.Row
 import org.apache.spark.sql.execution.SortExec
@@ -189,6 +192,41 @@ class BucketedTableQueryTest extends PaimonSparkTestBase 
with AdaptiveSparkPlanH
     }
   }
 
+  test("Query on a bucketed table - join - bucket key of an unsupported 
timestamp precision") {
+    assume(gteqSpark3_3)
+
+    // Spark cannot express a parameterized timestamp in DDL, so go through 
the table API. Spark's
+    // timestamp precision is fixed to 6, hence a bucket key of another 
precision is not
+    // distinguishable in the reported transform while Paimon buckets it 
differently.
+    Seq(("ts3", DataTypes.TIMESTAMP_MILLIS()), ("ts6", 
DataTypes.TIMESTAMP())).foreach {
+      case (name, tsType) =>
+        paimonCatalog.createTable(
+          Identifier.create(dbName0, name),
+          Schema.newBuilder
+            .column("ts", tsType)
+            .column("c", DataTypes.STRING())
+            .option("bucket-key", "ts")
+            .option("bucket", "2")
+            .build,
+          false
+        )
+        spark.sql(s"""
+                     |INSERT INTO $name VALUES
+                     |(timestamp'2024-01-01 00:00:01', 'x1'), 
(timestamp'2024-01-01 00:00:02', 'x2'),
+                     |(timestamp'2024-01-01 00:00:03', 'x3'), 
(timestamp'2024-01-01 00:00:04', 'x4'),
+                     |(timestamp'2024-01-01 00:00:05', 'x5'), 
(timestamp'2024-01-01 00:00:06', 'x6'),
+                     |(timestamp'2024-01-01 00:00:07', 'x7'), 
(timestamp'2024-01-01 00:00:08', 'x8')
+                     |""".stripMargin)
+    }
+
+    try {
+      checkAnswerAndShuffleSorts("SELECT * FROM ts3 JOIN ts6 on ts3.ts = 
ts6.ts", 2, 2)
+    } finally {
+      spark.sql("DROP TABLE IF EXISTS ts3")
+      spark.sql("DROP TABLE IF EXISTS ts6")
+    }
+  }
+
   test("Query on a bucketed table - other operators") {
     assume(gteqSpark3_3)
 

Reply via email to