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 bcdd0d6f69 [python] Fix empty Ray overwrite commit (#8041)
bcdd0d6f69 is described below

commit bcdd0d6f6953e71406a234e26fc132c7c65f7167
Author: QuakeWang <[email protected]>
AuthorDate: Sun May 31 10:16:48 2026 +0800

    [python] Fix empty Ray overwrite commit (#8041)
    
    `PaimonDatasink.on_write_complete()` previously returned early when all
    Ray write tasks produced empty commit messages. This made
    `write_paimon(empty_ds, table, overwrite=True)` a silent no-op, so
    existing data was kept even though Paimon overwrite commit semantics
    require empty overwrite commits to reach `TableCommit`.
    
    This PR keeps the empty-message fast path only for append writes. For
    overwrite writes, Ray now calls `TableCommit.commit([])`, allowing
    `FileStoreCommit.overwrite()` to delete the target range for
    static/unpartitioned overwrite and preserve dynamic-partition empty
    overwrite no-op semantics.
---
 .../pypaimon/tests/ray_integration_test.py         | 27 ++++++++++++++++++++++
 paimon-python/pypaimon/tests/ray_sink_test.py      | 16 +++++++++++++
 paimon-python/pypaimon/write/ray_datasink.py       |  2 +-
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/paimon-python/pypaimon/tests/ray_integration_test.py 
b/paimon-python/pypaimon/tests/ray_integration_test.py
index 10464f1653..2ad95f610b 100644
--- a/paimon-python/pypaimon/tests/ray_integration_test.py
+++ b/paimon-python/pypaimon/tests/ray_integration_test.py
@@ -306,6 +306,33 @@ class RayIntegrationTest(unittest.TestCase):
         df = result.to_pandas()
         self.assertEqual(list(df['id']), [3])
 
+    def test_write_paimon_empty_overwrite_unpartitioned(self):
+        """write_paimon(overwrite=True) with empty data clears an 
unpartitioned table."""
+        from pypaimon.ray import read_paimon, write_paimon
+
+        pa_schema = pa.schema([
+            ('id', pa.int32()),
+            ('val', pa.int64()),
+        ])
+        identifier = 'default.test_write_empty_overwrite_unpartitioned'
+        catalog = CatalogFactory.create(self.catalog_options)
+        schema = Schema.from_pyarrow_schema(pa_schema)
+        catalog.create_table(identifier, schema, False)
+
+        initial = ray.data.from_arrow(
+            pa.Table.from_pydict({'id': [1, 2], 'val': [10, 20]}, 
schema=pa_schema)
+        )
+        write_paimon(initial, identifier, self.catalog_options)
+        self.assertEqual(read_paimon(identifier, 
self.catalog_options).count(), 2)
+
+        empty = ray.data.from_arrow(
+            pa.Table.from_pydict({'id': [], 'val': []}, schema=pa_schema)
+        )
+        write_paimon(empty, identifier, self.catalog_options, overwrite=True)
+
+        result = read_paimon(identifier, self.catalog_options)
+        self.assertEqual(result.count(), 0)
+
     def test_read_paimon_primary_key(self):
         """read_paimon() merges PK rows correctly after an upsert."""
         from pypaimon.ray import read_paimon
diff --git a/paimon-python/pypaimon/tests/ray_sink_test.py 
b/paimon-python/pypaimon/tests/ray_sink_test.py
index fab77ecf87..ca51b05d1b 100644
--- a/paimon-python/pypaimon/tests/ray_sink_test.py
+++ b/paimon-python/pypaimon/tests/ray_sink_test.py
@@ -225,6 +225,22 @@ class RaySinkTest(unittest.TestCase):
         )
         datasink.on_write_complete(write_result)
 
+        # Empty overwrite must still reach TableCommit so overwrite semantics
+        # can delete the target range.
+        datasink = PaimonDatasink(self.table, overwrite=True)
+        datasink.on_write_start()
+        write_result = WriteResult(
+            num_rows=0,
+            size_bytes=0,
+            write_returns=[[], []]
+        )
+        mock_commit = Mock()
+        datasink._writer_builder.new_commit = Mock(return_value=mock_commit)
+        datasink.on_write_complete(write_result)
+
+        mock_commit.commit.assert_called_once_with([])
+        mock_commit.close.assert_called_once()
+
         # Test with messages and filtering empty messages
         datasink = PaimonDatasink(self.table, overwrite=False)
         datasink.on_write_start()
diff --git a/paimon-python/pypaimon/write/ray_datasink.py 
b/paimon-python/pypaimon/write/ray_datasink.py
index a01387b327..989ec42dc4 100644
--- a/paimon-python/pypaimon/write/ray_datasink.py
+++ b/paimon-python/pypaimon/write/ray_datasink.py
@@ -163,7 +163,7 @@ class PaimonDatasink(_DatasinkBase):
 
             self._pending_commit_messages = non_empty_messages
 
-            if not non_empty_messages:
+            if not non_empty_messages and not self.overwrite:
                 logger.info("No data to commit (all commit messages are 
empty)")
                 self._pending_commit_messages = []
                 return

Reply via email to