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 bd5e9f9a03 [spark] Fix timestamp precision in key dynamic writes 
(#9322)
bd5e9f9a03 is described below

commit bd5e9f9a03dfa0514af2db0bccbb707ede85f76a
Author: Zouxxyy <[email protected]>
AuthorDate: Fri Aug 21 08:28:53 2026 +0800

    [spark] Fix timestamp precision in key dynamic writes (#9322)
---
 .../paimon/spark/commands/BucketProcessor.scala    | 26 +++++++++-------------
 .../paimon/spark/commands/PaimonSparkWriter.scala  | 11 +++------
 .../paimon/spark/sql/DynamicBucketTableTest.scala  | 23 +++++++++++++++++++
 3 files changed, 36 insertions(+), 24 deletions(-)

diff --git 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/BucketProcessor.scala
 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/BucketProcessor.scala
index 4a147deee9..82ab11cf43 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/BucketProcessor.scala
+++ 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/BucketProcessor.scala
@@ -22,7 +22,7 @@ import org.apache.paimon.CoreOptions
 import org.apache.paimon.bucket.BucketFunction
 import org.apache.paimon.codegen.CodeGenUtils
 import org.apache.paimon.crosspartition.{GlobalIndexAssigner, KeyPartOrRow}
-import org.apache.paimon.data.{BinaryRow, GenericRow, InternalRow => 
PaimonInternalRow, JoinedRow}
+import org.apache.paimon.data.{BinaryRow, InternalRow => PaimonInternalRow}
 import org.apache.paimon.disk.IOManager
 import org.apache.paimon.index.HashBucketAssigner
 import org.apache.paimon.schema.TableSchema
@@ -31,7 +31,6 @@ import org.apache.paimon.spark.SparkUtils.createIOManager
 import org.apache.paimon.spark.util.EncoderUtils
 import org.apache.paimon.table.FileStoreTable
 import org.apache.paimon.table.sink.RowPartitionKeyExtractor
-import org.apache.paimon.types.RowType
 import org.apache.paimon.utils.{CloseableIterator, SerializationUtils}
 
 import org.apache.spark.TaskContext
@@ -39,6 +38,7 @@ import org.apache.spark.sql.Row
 import org.apache.spark.sql.catalyst.InternalRow
 import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder
 import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder.{Deserializer, 
Serializer}
+import org.apache.spark.sql.catalyst.expressions.{GenericInternalRow, 
JoinedRow => SparkJoinedRow}
 import org.apache.spark.sql.types.StructType
 
 import java.util.UUID
@@ -173,26 +173,19 @@ case class DynamicBucketProcessor(
 
 case class GlobalDynamicBucketProcessor(
     fileStoreTable: FileStoreTable,
-    rowType: RowType,
     numAssigners: Integer,
     encoderGroup: EncoderSerDeGroup)
   extends BucketProcessor[(KeyPartOrRow, Array[Byte])] {
 
   override def processPartition(
       rowIterator: Iterator[(KeyPartOrRow, Array[Byte])]): Iterator[Row] = {
-    new GlobalIndexAssignerIterator(
-      rowIterator,
-      fileStoreTable,
-      rowType,
-      numAssigners,
-      encoderGroup)
+    new GlobalIndexAssignerIterator(rowIterator, fileStoreTable, numAssigners, 
encoderGroup)
   }
 }
 
 class GlobalIndexAssignerIterator(
     rowIterator: Iterator[(KeyPartOrRow, Array[Byte])],
     fileStoreTable: FileStoreTable,
-    rowType: RowType,
     numAssigners: Integer,
     encoderGroup: EncoderSerDeGroup)
   extends Iterator[Row]
@@ -200,6 +193,8 @@ class GlobalIndexAssignerIterator(
 
   private val queue = mutable.Queue[Row]()
 
+  private val dataRowType = fileStoreTable.rowType()
+
   val ioManager: IOManager = createIOManager
 
   var currentResult: Row = _
@@ -215,12 +210,11 @@ class GlobalIndexAssignerIterator(
       numAssigners,
       TaskContext.getPartitionId(),
       (row, bucket) => {
-        val extraRow: GenericRow = new GenericRow(2)
-        extraRow.setField(0, row.getRowKind.toByteValue)
-        extraRow.setField(1, bucket)
-        queue.enqueue(
-          encoderGroup.internalToRow(
-            DataConverter.fromPaimon(new JoinedRow(row, extraRow), rowType)))
+        val dataRow = DataConverter.fromPaimon(row, dataRowType)
+        val extraRow = new GenericInternalRow(2)
+        extraRow.setByte(0, row.getRowKind.toByteValue)
+        extraRow.setInt(1, bucket)
+        queue.enqueue(encoderGroup.internalToRow(new SparkJoinedRow(dataRow, 
extraRow)))
       }
     )
     rowIterator.foreach {
diff --git 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/PaimonSparkWriter.scala
 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/PaimonSparkWriter.scala
index 35d7098529..2bbd53b8ab 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/PaimonSparkWriter.scala
+++ 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/PaimonSparkWriter.scala
@@ -30,7 +30,7 @@ import org.apache.paimon.fs.Path
 import org.apache.paimon.index.{BucketAssigner, SimpleHashBucketAssigner}
 import org.apache.paimon.io.{CompactIncrement, DataIncrement}
 import org.apache.paimon.manifest.FileKind
-import org.apache.paimon.spark.{SparkPostponeStagedCommitter, SparkRow, 
SparkTypeUtils}
+import org.apache.paimon.spark.{SparkPostponeStagedCommitter, SparkRow}
 import org.apache.paimon.spark.catalog.functions.BucketFunction
 import org.apache.paimon.spark.schema.SparkSystemColumns.{BUCKET_COL, 
ROW_KIND_COL}
 import org.apache.paimon.spark.sort.TableSorter
@@ -40,7 +40,7 @@ import org.apache.paimon.spark.write.{PaimonDataWrite, 
WriteHelper, WriteTaskRes
 import org.apache.paimon.table.{FileStoreTable, SpecialFields}
 import org.apache.paimon.table.BucketMode._
 import org.apache.paimon.table.sink._
-import org.apache.paimon.types.{RowKind, RowType}
+import org.apache.paimon.types.RowKind
 import org.apache.paimon.utils.{SerializationUtils, UriReaderFactory}
 
 import org.apache.spark.{Partitioner, TaskContext}
@@ -247,7 +247,6 @@ case class PaimonSparkWriter(
     val written: Dataset[_ <: WriteTaskResult] = bucketMode match {
       case KEY_DYNAMIC =>
         // Topology: input -> bootstrap -> shuffle by key hash -> 
bucket-assigner -> shuffle by partition & bucket
-        val rowType = 
SparkTypeUtils.toPaimonType(withInitBucketCol.schema).asInstanceOf[RowType]
         val assignerParallelism = 
Option(coreOptions.dynamicBucketAssignerParallelism)
           .map(_.toInt)
           .getOrElse(sparkParallelism)
@@ -259,11 +258,7 @@ case class PaimonSparkWriter(
             uriReaderFactory)
 
         val globalDynamicBucketProcessor =
-          GlobalDynamicBucketProcessor(
-            table,
-            rowType,
-            assignerParallelism,
-            encoderGroupWithBucketCol)
+          GlobalDynamicBucketProcessor(table, assignerParallelism, 
encoderGroupWithBucketCol)
         val repartitioned = repartitionByPartitionsAndBucket(
           sparkSession.createDataFrame(
             
bootstrapped.mapPartitions(globalDynamicBucketProcessor.processPartition),
diff --git 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/DynamicBucketTableTest.scala
 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/DynamicBucketTableTest.scala
index fee7973aca..47c91ef460 100644
--- 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/DynamicBucketTableTest.scala
+++ 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/DynamicBucketTableTest.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
 
@@ -221,4 +224,24 @@ class DynamicBucketTableTest extends PaimonSparkTestBase {
       )
     }
   }
+
+  test("Paimon cross partition table: write timestamp with precision 3") {
+    withTable("t") {
+      val schema = Schema.newBuilder
+        .column("etl_dt", DataTypes.DATE.notNull)
+        .column("order_id", DataTypes.BIGINT.notNull)
+        .column("despatch_end", DataTypes.TIMESTAMP(3))
+        .partitionKeys("etl_dt")
+        .primaryKey("order_id")
+        .option("bucket", "-1")
+        .option("deletion-vectors.enabled", "true")
+        .option("dynamic-bucket.assigner-parallelism", "1")
+        .build
+      paimonCatalog.createTable(Identifier.create(dbName0, "t"), schema, false)
+
+      sql("INSERT INTO t VALUES (DATE '2026-08-14', 1L, TIMESTAMP '2026-08-14 
10:00:00')")
+
+      checkAnswer(sql("SELECT order_id FROM t"), Row(1L))
+    }
+  }
 }

Reply via email to