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 77c9f4e864 [core][spark] Fix reading newly added BLOB columns after 
MERGE (#9413)
77c9f4e864 is described below

commit 77c9f4e864f8270ee6f207f0e0c64fa0981030f1
Author: zhoulii <[email protected]>
AuthorDate: Wed Aug 26 22:44:11 2026 +0800

    [core][spark] Fix reading newly added BLOB columns after MERGE (#9413)
---
 .../paimon/operation/DataEvolutionSplitRead.java   | 31 ----------------------
 .../operation/BlobFallbackRecordReaderTest.java    | 15 +++++++++++
 .../paimon/operation/DataEvolutionReadTest.java    |  1 -
 .../paimon/spark/sql/BlobUpdateTestBase.scala      | 28 +++++++++++++++++++
 4 files changed, 43 insertions(+), 32 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/DataEvolutionSplitRead.java
 
b/paimon-core/src/main/java/org/apache/paimon/operation/DataEvolutionSplitRead.java
index 2a606ee677..9e9959a3c6 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/operation/DataEvolutionSplitRead.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/operation/DataEvolutionSplitRead.java
@@ -435,18 +435,7 @@ public class DataEvolutionSplitRead implements 
SplitRead<InternalRow> {
                     deletionVector);
         } else if (bunch instanceof BlobFileBunch) {
             // for blob bunch, fallback on placeholders
-
-            // fast path: only contains one max_seq group
             BlobFileBunch blobBunch = (BlobFileBunch) bunch;
-            if (blobBunch.sequentialReadOptimize()) {
-                return sequentialReadFiles(
-                        bunch.files(),
-                        partition,
-                        dataFilePathFactory,
-                        formatReaderMapping,
-                        rowRanges,
-                        deletionVector);
-            }
             int blobIndex = findBlobFieldIndex(readRowType);
             checkArgument(blobIndex >= 0, "Blob bunch read type should contain 
a blob field.");
             return new BlobFallbackRecordReader(
@@ -1145,26 +1134,6 @@ public class DataEvolutionSplitRead implements 
SplitRead<InternalRow> {
             return new Range(merged.get(0).from, merged.get(merged.size() - 
1).to);
         }
 
-        private boolean fullyCoversLogicalRange() {
-            List<Range> merged = Range.sortAndMergeOverlap(ranges, true);
-            return merged.size() == 1 && merged.get(0).equals(logicalRange());
-        }
-
-        public boolean sequentialReadOptimize() {
-            Preconditions.checkState(!files.isEmpty(), "Blob file bunch should 
not be empty.");
-
-            // If blob files share the same max_seq_num, we could sequentially 
read them.
-            // Files have already been sorted by first_row_id
-            long maxSeq = files.get(0).maxSequenceNumber();
-            for (int i = 1; i < files.size(); i++) {
-                if (files.get(i).maxSequenceNumber() != maxSeq) {
-                    return false;
-                }
-            }
-
-            return fullyCoversLogicalRange();
-        }
-
         @Override
         public List<DataFileMeta> files() {
             return files;
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/BlobFallbackRecordReaderTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/operation/BlobFallbackRecordReaderTest.java
index 328aaa3061..7d58ac8517 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/BlobFallbackRecordReaderTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/BlobFallbackRecordReaderTest.java
@@ -357,6 +357,21 @@ public class BlobFallbackRecordReaderTest {
         assertThat(rows.nullBlobRowIds).isEmpty();
     }
 
+    @Test
+    public void 
testBlobFallbackRecordReaderResolvesPlaceholderInSingleFullRangeGroup()
+            throws Exception {
+        DataFileMeta file = blobFile("single-full-range-file", 0, 3, 1);
+
+        ReadResult rows =
+                readFallback(Collections.singletonList(file), null, 
placeholderRows(file, 1));
+
+        assertThat(rows.rowIds).containsExactly(0L, 2L);
+        assertThat(rows.sequenceNumbers).containsExactly(1L, 1L);
+        assertThat(rows.nullBlobRowIds).containsExactly(1L);
+        assertThat(rows.nullBlobSequenceNumbers).containsExactly(-1L);
+        assertThat(rows.placeholderRowCount).isZero();
+    }
+
     @Test
     public void 
testBlobFallbackRecordReaderReturnsNullIfAllRowsArePlaceholders() throws 
Exception {
         DataFileMeta newFile = blobFile("new-placeholder-file", 0, 1, 2);
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/DataEvolutionReadTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/operation/DataEvolutionReadTest.java
index bdb9fcc935..748dbe7bdf 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/DataEvolutionReadTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/DataEvolutionReadTest.java
@@ -485,7 +485,6 @@ public class DataEvolutionReadTest {
 
         assertThat(blobBunch.rowCount()).isEqualTo(10);
         assertThat(blobBunch.logicalRange()).isEqualTo(new Range(100, 109));
-        assertThat(blobBunch.sequentialReadOptimize()).isFalse();
     }
 
     @Test
diff --git 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/BlobUpdateTestBase.scala
 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/BlobUpdateTestBase.scala
index f9d6597c94..9e76ca8bf5 100644
--- 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/BlobUpdateTestBase.scala
+++ 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/BlobUpdateTestBase.scala
@@ -60,6 +60,34 @@ class BlobUpdateTestBase extends PaimonSparkTestBase {
     }
   }
 
+  test("Blob: merge-into updates newly added BLOB column") {
+    withTable("s", "t") {
+      sql(
+        "CREATE TABLE t (id INT, name STRING) TBLPROPERTIES " +
+          "('row-tracking.enabled'='true', 'data-evolution.enabled'='true')")
+      sql(
+        "INSERT INTO t SELECT /*+ REPARTITION(1) */ * FROM VALUES " +
+          "(1, 'name1'), (2, 'name2'), (3, 'name3') AS v(id, name)")
+
+      sql("ALTER TABLE t ADD COLUMN picture BINARY COMMENT '__BLOB_FIELD'")
+      sql("INSERT INTO t VALUES (4, 'name4', CAST(NULL AS BINARY))")
+
+      sql("CREATE TABLE s (id INT, picture BINARY)")
+      sql("INSERT INTO s VALUES (1, X'4E4557')")
+      sql("""
+            |MERGE INTO t
+            |USING s
+            |ON t.id = s.id
+            |WHEN MATCHED THEN UPDATE SET t.picture = s.picture
+            |""".stripMargin)
+
+      checkAnswer(
+        sql("SELECT id, picture FROM t WHERE id <= 3 ORDER BY id"),
+        Seq(Row(1, Array[Byte](78, 69, 87)), Row(2, null), Row(3, null))
+      )
+    }
+  }
+
   test("Blob: merge-into updates raw-data array blob column") {
     withTable("s", "t") {
       sql("CREATE TABLE t (id INT, name STRING, pictures ARRAY<BINARY>) 
TBLPROPERTIES " +

Reply via email to