zjw1111 commented on code in PR #264:
URL: https://github.com/apache/paimon-cpp/pull/264#discussion_r3893948053


##########
src/paimon/core/catalog/catalog_utils.cpp:
##########
@@ -33,6 +36,27 @@ Status SystemTableError(const Identifier& identifier, const 
std::string& action)
                                        action, identifier.ToString()));
 }
 
+/// Rejects names that cannot be used as a single path component: such a name 
would make the
+/// path built from it escape the directory it is joined to.
+Status CheckValidIdentifierName(const std::string& kind, const std::string& 
name) {
+    const char* reason = nullptr;
+    if (StringUtils::IsNullOrWhitespaceOnly(name)) {
+        reason = "cannot be empty or whitespace";
+    } else if (name == "." || name == "..") {
+        reason = "cannot be '.' or '..'";
+    } else if (name.find('/') != std::string::npos || name.find('\\') != 
std::string::npos) {
+        reason = "cannot contain path separators";
+    } else if (std::any_of(name.begin(), name.end(), [](char c) {
+                   return std::iscntrl(static_cast<unsigned char>(c)) != 0;

Review Comment:
   `StringUtils::IsNullOrWhitespaceOnly` and `std::iscntrl` classify individual 
UTF-8 bytes under the current C locale, while the referenced Rust 
implementation uses Unicode-aware `trim` and `char::is_control`. Under the 
typical C locale, a name consisting of U+2003 EM SPACE or containing U+0085 is 
accepted here but rejected by Rust. C++ can therefore create catalog objects 
that Rust cannot operate on, and the result also depends on process locale.



##########
src/paimon/core/catalog/catalog_utils.cpp:
##########
@@ -33,6 +36,27 @@ Status SystemTableError(const Identifier& identifier, const 
std::string& action)
                                        action, identifier.ToString()));
 }
 
+/// Rejects names that cannot be used as a single path component: such a name 
would make the
+/// path built from it escape the directory it is joined to.
+Status CheckValidIdentifierName(const std::string& kind, const std::string& 
name) {
+    const char* reason = nullptr;
+    if (StringUtils::IsNullOrWhitespaceOnly(name)) {
+        reason = "cannot be empty or whitespace";
+    } else if (name == "." || name == "..") {
+        reason = "cannot be '.' or '..'";
+    } else if (name.find('/') != std::string::npos || name.find('\\') != 
std::string::npos) {
+        reason = "cannot contain path separators";
+    } else if (std::any_of(name.begin(), name.end(), [](char c) {
+                   return std::iscntrl(static_cast<unsigned char>(c)) != 0;
+               })) {
+        reason = "cannot contain control characters";
+    }
+    if (reason != nullptr) {
+        return Status::Invalid(fmt::format("{} name {}: '{}'", kind, reason, 
name));

Review Comment:
   The rejected `name` is formatted verbatim even when it contains control 
characters. A newline remains in the returned `Status` and can inject 
additional log lines when the status is propagated, while an embedded NUL can 
truncate downstream C-string output. The referenced Rust implementation escapes 
these characters through debug formatting.



##########
src/paimon/core/catalog/catalog_utils.cpp:
##########
@@ -70,4 +94,31 @@ Status CatalogUtils::CheckNotBranch(const Identifier& 
identifier, const std::str
     return Status::OK();
 }
 
+Status CatalogUtils::CheckValidDatabaseName(const std::string& db_name) {
+    return CheckValidIdentifierName("database", db_name);
+}
+
+Status CatalogUtils::CheckValidTableName(const Identifier& identifier) {
+    PAIMON_ASSIGN_OR_RAISE(std::string data_table_name, 
identifier.GetDataTableName());
+    PAIMON_RETURN_NOT_OK(CheckValidIdentifierName("table", data_table_name));
+    PAIMON_ASSIGN_OR_RAISE(std::optional<std::string> branch, 
identifier.GetBranchName());
+    if (branch) {
+        PAIMON_RETURN_NOT_OK(CheckValidIdentifierName("branch", 
branch.value()));

Review Comment:
   This path sends an identifier branch through the generic validator, so a 
whitespace-only branch is rejected. `CheckValidBranchName` below and the 
existing `BranchManager::NormalizeBranch` both treat a whitespace-only branch 
as the main branch. Consequently, `ListSnapshots(id, "   ")` is accepted while 
the equivalent `t$branch_   ` identifier is rejected, producing inconsistent 
branch semantics between entry points.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to