Repository navigation
[core][python] Reject case variants of main as branch names and blank branches in identifiers - #10915
Draft
plusplusjiajia wants to merge 1 commit into
Draft
plusplusjiajia wants to merge 1 commit into
plusplusjiajia wants to merge 1 commit into
Conversation
plusplusjiajia
force-pushed
the
fix/reject-main-variant-and-blank-branches
branch
from
November 1, 2026 04:59
6e23705 to
b5669c6
Compare
plusplusjiajia
force-pushed
the
fix/reject-main-variant-and-blank-branches
branch
4 times, most recently
from
November 1, 2026 13:19
658c463 to
7d4e50f
Compare
… branches in identifiers Identifier treats any case of main as the default branch and drops it from the encoded name, but branch creation only rejected the exact name main. A branch named MAIN could therefore be created, and identifiers rebuilt from its parts, such as the base table of db.t$branch_MAIN$options, lost the branch and resolved to main. An encoded identifier with a blank branch, such as db.t$branch_, resolved to the main branch wherever it was used, so getTable, alterTable and the Hive partition operations acted on main through an invalid branch identifier. In Java and PyPaimon: - creating a branch, or renaming one, to a name that equals main ignoring case, as Java String.equalsIgnoreCase compares it, is rejected with the existing "is the default branch" message, and a PyPaimon identifier drops such a branch from its encoded name as a Java one does; - Identifier rejects an encoded name with a blank branch when it is parsed, like other invalid object names, so every operation fails with IllegalArgumentException (ValueError in PyPaimon) before it resolves the branch; the PyPaimon REST client parses an identifier before it sends a request. Identifier(db, t, "") is the main table, as a blank branch option already is, so its encoded name stays valid. Java and PyPaimon decide whether a branch name is blank alike, as Character.isWhitespace does since Java 9: Java 8 also takes U+180E for whitespace, and str.strip also strips the no-break spaces. A branch named U+180E therefore resolved to main on Java 8, and a branch Java allows, such as U+00A0, resolved to main in PyPaimon branch and consumer paths and could not be created, renamed, dropped or fast-forwarded there. Identifier, the branch and consumer managers and the PyPaimon REST branch requests now share one check.
plusplusjiajia
force-pushed
the
fix/reject-main-variant-and-blank-branches
branch
from
November 2, 2026 01:50
7d4e50f to
5c8f588
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
Identifiertreats any case ofmainas the default branch and drops it from the encoded name, but branch creation only rejected the exact namemain:MAINcould be created, and identifiers rebuilt from its parts, such as the base table ofdb.t$branch_MAIN$options, lost the branch and resolved to main;db.t$branch_, resolved to the main branch wherever it was used, sogetTable,alterTableand the Hive partition operations acted on main through an invalid branch identifier.In Java and PyPaimon:
mainignoring case is rejected with the existing "is the default branch" message. PyPaimon compares names as JavaString.equalsIgnoreCasedoes, which also takes the dotted and dotless i (maİn,maın) fori, and its identifiers drop such a branch from the encoded name as Java ones do;Identifierrejects an encoded name with a blank branch when it is parsed, like other invalid object names, so every operation fails withIllegalArgumentException(ValueErrorin PyPaimon) before it resolves the branch, without a check in each catalog method. The PyPaimon REST client parses an identifier before it sends a request.Identifier(db, t, "")is the main table, as a blankbranchoption already is, so its encoded name stays valid.Java and PyPaimon decide whether a branch name is blank alike, as
Character.isWhitespacedoes since Java 9: Java 8 also takesU+180Efor whitespace, andstr.strip()also strips the no-break spaces. A branch namedU+180Etherefore resolved to main on Java 8, and a branch Java allows, such asU+00A0, resolved to main in PyPaimon branch and consumer paths and could not be created, renamed, dropped or fast-forwarded there.Identifier.isBlankBranch, used byIdentifier, the branch and consumer managers and the PyPaimon REST branch requests, is now the one check.Tests
IdentifierTest/identifier_test.py: encoded names with a blank branch are rejected when parsed, also on a second access; a blank branch argument gives the main table.FileSystemBranchManagerTest,branch_manager_test.py,filesystem_catalog_branch_test.py:MAIN,Main,maınandmaİnare rejected on create and rename in both languages.CatalogTestBase#testGetTable,filesystem_catalog_branch_test.py,jdbc_catalog_test.py,rest_catalog_test.py:getTablefails fort$branch_; the PyPaimon REST catalog fails before sending a request.FileSystemCatalogTest#testAlterTableRejectsBlankBranch:alterTablethrough a blank branch fails, also withignoreIfNotExists, and leaves the main schema unchanged.HiveCatalogTest#testPartitionOperationsRejectBlankBranch: creating, dropping and altering partitions through a blank branch fail and leave the metastore partitions unchanged.catalog_table_guard_test.py: creating aMAINbranch is now rejected.identifier_test.py:MAIN,maİnandmaınbranch arguments encode as the main table, as in Java.rest_branch_transport_test.py: creating, dropping, fast-forwarding and listing branches throughtable$branch_fail without sending a request.IdentifierTest,FileSystemBranchManagerTest,identifier_test.py,branch_manager_test.py:U+00A0,U+2007,U+202F,U+0085andU+180Eare not blank, on every JVM the build runs on;U+3000is still blank.consumer_test.py,filesystem_catalog_branch_test.py,rest_branch_test.py,rest_branch_transport_test.py: a branch namedU+00A0keeps its name in identifiers, branch and consumer paths and REST requests, reads its own data rather than main's, and can be renamed, dropped and fast-forwarded.identifier_test.py: setting a blankbranchon an identifier gives the main table, and the getter agrees with the encoded name.U+00A0,U+2007,U+202FandU+0085cases, and theU+180Ecases, which only Java 8 got wrong.IdentifierTest,RESTTokenFileIOTest,FileSystemBranchManagerTest,FileSystemCatalogTest,CachingCatalogTest,JdbcCatalogTest,MockRESTCatalogTest,ConsumerManagerTestandHiveCatalogTest(512 tests) and the related PyPaimon catalog, branch, consumer, REST, partition and system table tests (1182 passed, 256 skipped) pass locally; flake8 is clean.