Skip to content

Commit f9fde2d

Browse files
[python] Reject dropping the main branch (#10855)
1 parent 9895baa commit f9fde2d

4 files changed

Lines changed: 58 additions & 1 deletion

File tree

‎paimon-python/pypaimon/branch/filesystem_branch_manager.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,10 @@ def drop_branch(self, branch_name: str) -> None:
180180
branch_name: Name of the branch to drop
181181
182182
Raises:
183-
ValueError: If branch doesn't exist
183+
ValueError: If branch is main or doesn't exist
184184
"""
185+
if BranchManager.is_main_branch(branch_name):
186+
raise ValueError("The main branch cannot be deleted.")
185187
if not self.branch_exists(branch_name):
186188
raise ValueError(f"Branch name '{branch_name}' doesn't exist.")
187189

‎paimon-python/pypaimon/tests/branch_manager_test.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,25 @@ def test_branch_exists(self):
184184
self.assertTrue(manager.branch_exists("feature"))
185185
self.assertFalse(manager.branch_exists("develop"))
186186

187+
def test_main_branch_validation_before_file_access(self):
188+
from unittest.mock import Mock
189+
190+
file_io = Mock()
191+
manager = FileSystemBranchManager(file_io, self.table_path, None, None, None)
192+
for operation, args, message in (
193+
(manager.drop_branch, ("main",), "The main branch cannot be deleted."),
194+
(manager.create_branch, ("main",),
195+
"Branch name 'main' is the default branch and cannot be used."),
196+
(manager.create_branch, ("main", "absent"),
197+
"Branch name 'main' is the default branch and cannot be used."),
198+
(manager.fast_forward, ("main",), "Branch name 'main' do not use in fast-forward."),
199+
):
200+
with self.subTest(operation=operation.__name__, args=args):
201+
with self.assertRaises(ValueError) as cm:
202+
operation(*args)
203+
self.assertEqual(str(cm.exception), message)
204+
self.assertEqual(file_io.mock_calls, [])
205+
187206

188207
class SnapshotManagerBranchAwarenessTest(unittest.TestCase):
189208
"""Pin down SnapshotManager's branch-aware path computation.

‎paimon-python/pypaimon/tests/filesystem_catalog_branch_test.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,16 @@ def test_drop_branch_missing_raises(self):
238238
self.catalog.drop_branch(self.identifier, "absent")
239239
self.assertEqual(cm.exception.branch, "absent")
240240

241+
def test_drop_main_branch_preserves_table(self):
242+
table = self.catalog.get_table(self.identifier)
243+
with self.assertRaisesRegex(ValueError, r"^The main branch cannot be deleted\.$"):
244+
self.catalog.drop_branch(self.identifier, "main")
245+
self.assertTrue(os.path.isdir(table.table_path))
246+
self.assertEqual(
247+
self._read(self.catalog.get_table(self.identifier)).to_pydict(),
248+
{"id": [1, 2, 3], "value": ["a", "b", "c"]},
249+
)
250+
241251
# -- fast_forward ---------------------------------------------------------
242252

243253
def test_fast_forward_missing_raises(self):

‎paimon-python/pypaimon/tests/jdbc_catalog_test.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import tempfile
2323
import unittest
2424

25+
import pyarrow as pa
26+
2527
from pypaimon import CatalogFactory, Schema
2628
from pypaimon.catalog.catalog_exception import (
2729
DatabaseAlreadyExistException,
@@ -190,6 +192,30 @@ def fail_insert_table_properties(identifier, properties):
190192
self.assertEqual(table_count, 0)
191193
self.assertFalse(os.path.exists(os.path.join(self.warehouse, "test_db.db", "test_table")))
192194

195+
def test_drop_main_branch_preserves_table(self):
196+
with CatalogFactory.create(self.options) as catalog:
197+
catalog.create_database("test_db", False)
198+
data = pa.table({"id": [1]})
199+
catalog.create_table("test_db.test_table", Schema.from_pyarrow_schema(data.schema), False)
200+
table = catalog.get_table("test_db.test_table")
201+
builder = table.new_batch_write_builder()
202+
writer = builder.new_write()
203+
try:
204+
writer.write_arrow(data)
205+
builder.new_commit().commit(writer.prepare_commit())
206+
finally:
207+
writer.close()
208+
209+
with self.assertRaisesRegex(ValueError, r"^The main branch cannot be deleted\.$"):
210+
table.branch_manager().drop_branch("main")
211+
self.assertTrue(os.path.isdir(table.table_path))
212+
table = catalog.get_table("test_db.test_table")
213+
reader = table.new_read_builder()
214+
self.assertEqual(
215+
reader.new_read().to_arrow(reader.new_scan().plan().splits()).to_pydict(),
216+
{"id": [1]},
217+
)
218+
193219
def test_rename_table_keeps_metadata_when_file_move_fails(self):
194220
fields = [DataField.from_dict({"id": 1, "name": "f0", "type": "INT"})]
195221
catalog = CatalogFactory.create(self.options)

0 commit comments

Comments
 (0)