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

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git

commit 1b3e2a585dbdf6a14756bd612c08e3bdf7424ed4
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Sep 28 09:29:49 2026 +0800

    branch-4.1: [fix](be) Fix constant nullable JSON path evaluation #68427 
(#68525)
    
    Cherry-picked from #68427
    
    Co-authored-by: Jerry Hu <[email protected]>
---
 be/src/exprs/function/function_jsonb.cpp           |  10 +-
 be/test/exprs/function/function_jsonb_test.cpp     | 111 ++++++++++++++++++++-
 .../test_json_const_nullable_path.out              |   6 ++
 .../test_json_const_nullable_path.groovy           |  43 ++++++++
 4 files changed, 164 insertions(+), 6 deletions(-)

diff --git a/be/src/exprs/function/function_jsonb.cpp 
b/be/src/exprs/function/function_jsonb.cpp
index e572573a3c8..bfee90dc1a9 100644
--- a/be/src/exprs/function/function_jsonb.cpp
+++ b/be/src/exprs/function/function_jsonb.cpp
@@ -1405,8 +1405,10 @@ struct JsonbLengthUtil {
         }
 
         for (size_t i = 0; i < input_rows_count; ++i) {
-            if (jsonb_data_column->is_null_at(i) || path_column->is_null_at(i) 
||
-                (jsonb_data_column->get_data_at(i).size == 0)) {
+            const auto jsonb_index = index_check_const(i, jsonb_data_const);
+            if (jsonb_data_column->is_null_at(jsonb_index) ||
+                path_column->is_null_at(index_check_const(i, is_const)) ||
+                (jsonb_data_column->get_data_at(jsonb_index).size == 0)) {
                 null_map->get_data()[i] = 1;
                 res->insert_data(nullptr, 0);
                 continue;
@@ -1421,7 +1423,7 @@ struct JsonbLengthUtil {
                                              path_value.size));
                 }
             }
-            auto jsonb_value = jsonb_data_column->get_data_at(i);
+            auto jsonb_value = jsonb_data_column->get_data_at(jsonb_index);
             // doc is NOT necessary to be deleted since JsonbDocument will not 
allocate memory
             const JsonbDocument* doc = nullptr;
             
RETURN_IF_ERROR(JsonbDocument::checkAndCreateDocument(jsonb_value.data,
@@ -1520,7 +1522,7 @@ struct JsonbContainsUtil {
 
         for (size_t i = 0; i < input_rows_count; ++i) {
             if (jsonb_data1_column->is_null_at(i) || 
jsonb_data2_column->is_null_at(i) ||
-                path_column->is_null_at(i)) {
+                path_column->is_null_at(index_check_const(i, is_const))) {
                 null_map->get_data()[i] = 1;
                 res->insert_data(nullptr, 0);
                 continue;
diff --git a/be/test/exprs/function/function_jsonb_test.cpp 
b/be/test/exprs/function/function_jsonb_test.cpp
index 8d9df92d402..7736362271c 100644
--- a/be/test/exprs/function/function_jsonb_test.cpp
+++ b/be/test/exprs/function/function_jsonb_test.cpp
@@ -67,6 +67,12 @@ ColumnPtr create_const_path_column(const std::string& path, 
size_t rows) {
     return ColumnConst::create(create_path_column({path}), rows);
 }
 
+ColumnPtr create_const_nullable_path_column(const std::string& path, size_t 
rows) {
+    auto nullable_path = ColumnNullable::create(ColumnString::create(), 
ColumnUInt8::create());
+    nullable_path->insert_data(path.data(), path.size());
+    return ColumnConst::create(std::move(nullable_path), rows);
+}
+
 Status execute_json_keys_with_path(ColumnPtr path_column, size_t rows) {
     auto jsonb_type = std::make_shared<DataTypeJsonb>();
     auto path_type = std::make_shared<DataTypeString>();
@@ -902,7 +908,7 @@ TEST(FunctionJsonbTEST, JsonLength) {
     arg_types.emplace_back(json_data_type);
     
arg_types.emplace_back(std::make_shared<DataTypeNullable>(std::make_shared<DataTypeString>()));
 
-    FunctionUtils fn_utils(return_type, arg_types, 0);
+    FunctionUtils fn_utils(return_type, arg_types, false);
     auto* fn_ctx = fn_utils.get_fn_ctx();
     //     fn_ctx->set_constant_cols(constant_cols);
     auto st = func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL);
@@ -917,6 +923,70 @@ TEST(FunctionJsonbTEST, JsonLength) {
     ASSERT_TRUE(st.ok()) << "execute failed: " << st.to_string();
 }
 
+TEST(FunctionJsonbTEST, JsonLengthConstNullablePathMultiRows) {
+    constexpr size_t rows = 3;
+    auto jsonb_type = std::make_shared<DataTypeJsonb>();
+    auto path_type = make_nullable(std::make_shared<DataTypeString>());
+    auto return_type = make_nullable(std::make_shared<DataTypeInt32>());
+
+    auto jsonb_column = jsonb_type->create_column();
+    ASSERT_TRUE(insert_cell(jsonb_column, jsonb_type, 
STRING(R"({"a":[1,2]})")));
+    ASSERT_TRUE(insert_cell(jsonb_column, jsonb_type, 
STRING(R"({"a":[3,4,5]})")));
+    ASSERT_TRUE(insert_cell(jsonb_column, jsonb_type, STRING(R"({"a":[]})")));
+
+    Block block;
+    block.insert({std::move(jsonb_column), jsonb_type, "jsonb"});
+    block.insert({create_const_nullable_path_column("$.a", rows), path_type, 
"path"});
+
+    FunctionBasePtr func = SimpleFunctionFactory::instance().get_function(
+            "json_length", block.get_columns_with_type_and_name(), 
return_type);
+    ASSERT_NE(func, nullptr);
+
+    FunctionUtils fn_utils(return_type, {jsonb_type, path_type}, false);
+    auto* fn_ctx = fn_utils.get_fn_ctx();
+    ASSERT_TRUE(func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok());
+    ASSERT_TRUE(func->open(fn_ctx, FunctionContext::THREAD_LOCAL).ok());
+
+    block.insert({nullptr, return_type, "result"});
+    ASSERT_TRUE(func->execute(fn_ctx, block, {0, 1}, 2, rows).ok());
+
+    const auto& result = assert_cast<const 
ColumnNullable&>(*block.get_by_position(2).column);
+    EXPECT_EQ(result.get_null_map_data(), NullMap({0, 0, 0}));
+    EXPECT_EQ(assert_cast<const 
ColumnInt32&>(result.get_nested_column()).get_data(),
+              PaddedPODArray<Int32>({2, 3, 0}));
+}
+
+TEST(FunctionJsonbTEST, JsonLengthConstJsonVaryingPathMultiRows) {
+    constexpr size_t rows = 3;
+    auto jsonb_type = std::make_shared<DataTypeJsonb>();
+    auto path_type = std::make_shared<DataTypeString>();
+    auto return_type = make_nullable(std::make_shared<DataTypeInt32>());
+
+    auto jsonb_column = jsonb_type->create_column();
+    ASSERT_TRUE(insert_cell(jsonb_column, jsonb_type, 
STRING(R"({"a":[1,2]})")));
+
+    Block block;
+    block.insert({ColumnConst::create(std::move(jsonb_column), rows), 
jsonb_type, "jsonb"});
+    block.insert({create_path_column({"$.a", "$.a[0]", "$.missing"}), 
path_type, "path"});
+
+    FunctionBasePtr func = SimpleFunctionFactory::instance().get_function(
+            "json_length", block.get_columns_with_type_and_name(), 
return_type);
+    ASSERT_NE(func, nullptr);
+
+    FunctionUtils fn_utils(return_type, {jsonb_type, path_type}, false);
+    auto* fn_ctx = fn_utils.get_fn_ctx();
+    ASSERT_TRUE(func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok());
+    ASSERT_TRUE(func->open(fn_ctx, FunctionContext::THREAD_LOCAL).ok());
+
+    block.insert({nullptr, return_type, "result"});
+    ASSERT_TRUE(func->execute(fn_ctx, block, {0, 1}, 2, rows).ok());
+
+    const auto& result = assert_cast<const 
ColumnNullable&>(*block.get_by_position(2).column);
+    EXPECT_EQ(result.get_null_map_data(), NullMap({0, 0, 1}));
+    EXPECT_EQ(assert_cast<const 
ColumnInt32&>(result.get_nested_column()).get_data(),
+              PaddedPODArray<Int32>({2, 1, 0}));
+}
+
 TEST(FunctionJsonbTEST, JsonContains) {
     std::string func_name = "json_contains";
 
@@ -971,7 +1041,7 @@ TEST(FunctionJsonbTEST, JsonContains) {
     arg_types.emplace_back(json_data_type);
     
arg_types.emplace_back(std::make_shared<DataTypeNullable>(std::make_shared<DataTypeString>()));
 
-    FunctionUtils fn_utils(return_type, arg_types, 0);
+    FunctionUtils fn_utils(return_type, arg_types, false);
     auto* fn_ctx = fn_utils.get_fn_ctx();
     //     fn_ctx->set_constant_cols(constant_cols);
     auto st = func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL);
@@ -986,6 +1056,43 @@ TEST(FunctionJsonbTEST, JsonContains) {
     ASSERT_TRUE(st.ok()) << "execute failed: " << st.to_string();
 }
 
+TEST(FunctionJsonbTEST, JsonContainsConstNullablePathMultiRows) {
+    constexpr size_t rows = 3;
+    auto jsonb_type = std::make_shared<DataTypeJsonb>();
+    auto path_type = make_nullable(std::make_shared<DataTypeString>());
+    auto return_type = make_nullable(std::make_shared<DataTypeBool>());
+
+    auto jsonb_column = jsonb_type->create_column();
+    ASSERT_TRUE(insert_cell(jsonb_column, jsonb_type, 
STRING(R"({"a":[1,2]})")));
+    ASSERT_TRUE(insert_cell(jsonb_column, jsonb_type, 
STRING(R"({"a":[3,4,5]})")));
+    ASSERT_TRUE(insert_cell(jsonb_column, jsonb_type, STRING(R"({"a":[]})")));
+
+    auto candidate_column = jsonb_type->create_column();
+    ASSERT_TRUE(insert_cell(candidate_column, jsonb_type, STRING("[3]")));
+
+    Block block;
+    block.insert({std::move(jsonb_column), jsonb_type, "jsonb"});
+    block.insert({ColumnConst::create(std::move(candidate_column), rows), 
jsonb_type, "candidate"});
+    block.insert({create_const_nullable_path_column("$.a", rows), path_type, 
"path"});
+
+    FunctionBasePtr func = SimpleFunctionFactory::instance().get_function(
+            "json_contains", block.get_columns_with_type_and_name(), 
return_type);
+    ASSERT_NE(func, nullptr);
+
+    FunctionUtils fn_utils(return_type, {jsonb_type, jsonb_type, path_type}, 
false);
+    auto* fn_ctx = fn_utils.get_fn_ctx();
+    ASSERT_TRUE(func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok());
+    ASSERT_TRUE(func->open(fn_ctx, FunctionContext::THREAD_LOCAL).ok());
+
+    block.insert({nullptr, return_type, "result"});
+    ASSERT_TRUE(func->execute(fn_ctx, block, {0, 1, 2}, 3, rows).ok());
+
+    const auto& result = assert_cast<const 
ColumnNullable&>(*block.get_by_position(3).column);
+    EXPECT_EQ(result.get_null_map_data(), NullMap({0, 0, 0}));
+    EXPECT_EQ(assert_cast<const 
ColumnUInt8&>(result.get_nested_column()).get_data(),
+              PaddedPODArray<UInt8>({0, 1, 0}));
+}
+
 TEST(FunctionJsonbTEST, JsonbModifyMissingPathParent) {
     InputTypeSet input_types = {PrimitiveType::TYPE_JSONB, 
PrimitiveType::TYPE_VARCHAR,
                                 PrimitiveType::TYPE_JSONB};
diff --git 
a/regression-test/data/query_p0/sql_functions/json_functions/test_json_const_nullable_path.out
 
b/regression-test/data/query_p0/sql_functions/json_functions/test_json_const_nullable_path.out
new file mode 100644
index 00000000000..1b6a587b084
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/json_functions/test_json_const_nullable_path.out
@@ -0,0 +1,6 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !const_nullable_path --
+1      2       false
+2      3       true
+3      0       false
+
diff --git 
a/regression-test/suites/query_p0/sql_functions/json_functions/test_json_const_nullable_path.groovy
 
b/regression-test/suites/query_p0/sql_functions/json_functions/test_json_const_nullable_path.groovy
new file mode 100644
index 00000000000..dc55a99d7a2
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/json_functions/test_json_const_nullable_path.groovy
@@ -0,0 +1,43 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_json_const_nullable_path", "p0") {
+    sql "DROP TABLE IF EXISTS test_json_const_nullable_path"
+    sql """
+        CREATE TABLE test_json_const_nullable_path (
+            id INT,
+            js JSON
+        )
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_num" = "1")
+    """
+    sql """
+        INSERT INTO test_json_const_nullable_path VALUES
+            (1, '{"a":[1,2]}'),
+            (2, '{"a":[3,4,5]}'),
+            (3, '{"a":[]}')
+    """
+
+    order_qt_const_nullable_path """
+        SELECT id,
+               json_length(js, nullable('\$.a')),
+               json_contains(js, CAST('[3]' AS JSON), nullable('\$.a'))
+        FROM test_json_const_nullable_path
+        ORDER BY id
+    """
+}


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

Reply via email to