This is an automated email from the ASF dual-hosted git repository.

mrhhsg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new dc7b50ce99e [fix](be) Avoid unsigned underflow in JSON modify path 
(#63579)
dc7b50ce99e is described below

commit dc7b50ce99ea5c1af6404c8d0dbc957bc51cba4a
Author: Jerry Hu <[email protected]>
AuthorDate: Mon Jun 8 09:08:58 2026 +0800

    [fix](be) Avoid unsigned underflow in JSON modify path (#63579)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Problem Summary: JSON modify functions build a parent path by iterating
    to the element before the last JSON path leg. The loop used
    `get_leg_vector_size() - 1` on an unsigned size, which can underflow for
    an empty path expression even though normal root-path execution resolves
    to the root value before that branch. This patch caches the leg count
    once, asserts the non-empty invariant for the insert-parent branch, and
    uses `j + 1 < legs_count` to avoid unsigned subtraction while preserving
    behavior.
    
    The patch also adds BE unit coverage for missing-path JSONB modify cases
    where the parent path has to be rebuilt, including single-leg object
    insertion, nested object insertion, and array append behavior for both
    `jsonb_insert` and `jsonb_set`.
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test:
        - Unit Test: added `FunctionJsonbTEST.JsonbModifyMissingPathParent`
    - Build check: `DORIS_HOME=/mnt/disk7/hushenggang/doris-fix-spill ninja
    -C be/ut_build_ASAN
    test/CMakeFiles/doris_be_test.dir/exprs/function/function_jsonb_test.cpp.o`
    - Format check: `build-support/clang-format.sh`;
    `build-support/check-format.sh`; `git diff --check`
    - Behavior changed: No
    - Does this need documentation: No
---
 be/src/exprs/function/function_jsonb.cpp       |  5 +++--
 be/test/exprs/function/function_jsonb_test.cpp | 24 ++++++++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/be/src/exprs/function/function_jsonb.cpp 
b/be/src/exprs/function/function_jsonb.cpp
index 84238c6d688..3941a0a6fc7 100644
--- a/be/src/exprs/function/function_jsonb.cpp
+++ b/be/src/exprs/function/function_jsonb.cpp
@@ -2048,6 +2048,7 @@ public:
 
                 bool replace = false;
                 parents.emplace_back(json_documents[row_idx]->getValue());
+                const auto legs_count = 
json_path[path_index].get_leg_vector_size();
                 if (find_result.value) {
                     // find target path, replace it with the new value.
                     replace = true;
@@ -2058,7 +2059,8 @@ public:
                 } else {
                     // does not find target path, insert the new value.
                     JsonbPath new_path;
-                    for (size_t j = 0; j < 
json_path[path_index].get_leg_vector_size() - 1; ++j) {
+                    DCHECK_GT(legs_count, 0);
+                    for (size_t j = 0; j + 1 < legs_count; ++j) {
                         auto* current_leg = 
json_path[path_index].get_leg_from_leg_vector(j);
                         std::unique_ptr<leg_info> leg = 
std::make_unique<leg_info>(
                                 current_leg->leg_ptr, current_leg->leg_len,
@@ -2072,7 +2074,6 @@ public:
                     }
                 }
 
-                const auto legs_count = 
json_path[path_index].get_leg_vector_size();
                 leg_info* last_leg =
                         legs_count > 0
                                 ? 
json_path[path_index].get_leg_from_leg_vector(legs_count - 1)
diff --git a/be/test/exprs/function/function_jsonb_test.cpp 
b/be/test/exprs/function/function_jsonb_test.cpp
index 25377a0bce9..8d9df92d402 100644
--- a/be/test/exprs/function/function_jsonb_test.cpp
+++ b/be/test/exprs/function/function_jsonb_test.cpp
@@ -986,4 +986,28 @@ TEST(FunctionJsonbTEST, JsonContains) {
     ASSERT_TRUE(st.ok()) << "execute failed: " << st.to_string();
 }
 
+TEST(FunctionJsonbTEST, JsonbModifyMissingPathParent) {
+    InputTypeSet input_types = {PrimitiveType::TYPE_JSONB, 
PrimitiveType::TYPE_VARCHAR,
+                                PrimitiveType::TYPE_JSONB};
+
+    {
+        DataSet data_set = {
+                {{STRING("{}"), STRING("$.a"), STRING("1")}, 
STRING(R"({"a":1})")},
+                {{STRING(R"({"a":{}})"), STRING("$.a.b"), STRING("2")}, 
STRING(R"({"a":{"b":2}})")},
+                {{STRING(R"({"a":[1]})"), STRING("$.a[1]"), STRING("2")}, 
STRING(R"({"a":[1,2]})")},
+        };
+        static_cast<void>(
+                check_function<DataTypeJsonb, true>("jsonb_insert", 
input_types, data_set));
+    }
+
+    {
+        DataSet data_set = {
+                {{STRING("{}"), STRING("$.a"), STRING("1")}, 
STRING(R"({"a":1})")},
+                {{STRING(R"({"a":{}})"), STRING("$.a.b"), STRING("2")}, 
STRING(R"({"a":{"b":2}})")},
+                {{STRING(R"({"a":[1]})"), STRING("$.a[1]"), STRING("2")}, 
STRING(R"({"a":[1,2]})")},
+        };
+        static_cast<void>(check_function<DataTypeJsonb, true>("jsonb_set", 
input_types, data_set));
+    }
+}
+
 } // namespace doris


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to