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

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


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new a88e687d483 branch-4.0: [fix](inverted index) handle NULL literal to 
prevent BE crash #59916 (#60016)
a88e687d483 is described below

commit a88e687d483c1e082174090ee7b33168f31b0188
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Jan 19 19:36:39 2026 +0800

    branch-4.0: [fix](inverted index) handle NULL literal to prevent BE crash 
#59916 (#60016)
    
    Cherry-picked from #59916
    
    Co-authored-by: zzzxl <[email protected]>
---
 be/src/vec/functions/functions_comparison.h        |   3 +
 ...ion_comparison_evaluate_inverted_index_test.cpp | 125 +++++++++++++++++++++
 .../test_inverted_index_null_literal.out           |  19 ++++
 .../test_inverted_index_null_literal.groovy        |  62 ++++++++++
 4 files changed, 209 insertions(+)

diff --git a/be/src/vec/functions/functions_comparison.h 
b/be/src/vec/functions/functions_comparison.h
index f45a8d99b96..d785ea6515b 100644
--- a/be/src/vec/functions/functions_comparison.h
+++ b/be/src/vec/functions/functions_comparison.h
@@ -487,6 +487,9 @@ public:
         }
         Field param_value;
         arguments[0].column->get(0, param_value);
+        if (param_value.is_null()) {
+            return Status::OK();
+        }
         auto param_type = arguments[0].type->get_primitive_type();
         std::unique_ptr<segment_v2::InvertedIndexQueryParamFactory> 
query_param = nullptr;
         
RETURN_IF_ERROR(segment_v2::InvertedIndexQueryParamFactory::create_query_value(
diff --git 
a/be/test/vec/function/function_comparison_evaluate_inverted_index_test.cpp 
b/be/test/vec/function/function_comparison_evaluate_inverted_index_test.cpp
new file mode 100644
index 00000000000..f414412be06
--- /dev/null
+++ b/be/test/vec/function/function_comparison_evaluate_inverted_index_test.cpp
@@ -0,0 +1,125 @@
+// 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.
+
+#include "gtest/gtest.h"
+#include "olap/rowset/segment_v2/index_iterator.h"
+#include "olap/rowset/segment_v2/inverted_index_reader.h"
+#include "vec/columns/column_const.h"
+#include "vec/columns/column_nullable.h"
+#include "vec/data_types/data_type_nullable.h"
+#include "vec/data_types/data_type_number.h"
+#include "vec/data_types/data_type_string.h"
+#include "vec/functions/functions_comparison.h"
+
+namespace doris::vectorized {
+
+class MockInvertedIndexReader : public segment_v2::InvertedIndexReader {
+public:
+    MockInvertedIndexReader(const TabletIndex& index_meta)
+            : segment_v2::InvertedIndexReader(&index_meta, nullptr) {}
+    ~MockInvertedIndexReader() override = default;
+
+    segment_v2::InvertedIndexReaderType type() override {
+        return segment_v2::InvertedIndexReaderType::BKD;
+    }
+
+    Status query(const segment_v2::IndexQueryContextPtr& context, const 
std::string& column_name,
+                 const void* query_value, segment_v2::InvertedIndexQueryType 
query_type,
+                 std::shared_ptr<roaring::Roaring>& bit_map,
+                 const InvertedIndexAnalyzerCtx* analyzer_ctx = nullptr) 
override {
+        return Status::OK();
+    }
+
+    Status try_query(const segment_v2::IndexQueryContextPtr& context,
+                     const std::string& column_name, const void* query_value,
+                     segment_v2::InvertedIndexQueryType query_type, size_t* 
count) override {
+        return Status::OK();
+    }
+
+    Status new_iterator(std::unique_ptr<segment_v2::IndexIterator>* iterator) 
override {
+        return Status::OK();
+    }
+};
+
+class MockComparisonIndexIterator : public segment_v2::IndexIterator {
+public:
+    MockComparisonIndexIterator(std::shared_ptr<MockInvertedIndexReader> 
reader)
+            : _reader(reader) {}
+    ~MockComparisonIndexIterator() override = default;
+
+    segment_v2::IndexReaderPtr get_reader(segment_v2::IndexReaderType 
reader_type) const override {
+        if 
(std::holds_alternative<segment_v2::InvertedIndexReaderType>(reader_type)) {
+            if (std::get<segment_v2::InvertedIndexReaderType>(reader_type) ==
+                segment_v2::InvertedIndexReaderType::BKD) {
+                return _reader;
+            }
+        }
+        return nullptr;
+    }
+
+    Status read_from_index(const segment_v2::IndexParam& param) override {
+        auto* p = std::get<segment_v2::InvertedIndexParam*>(param);
+        p->roaring->addRange(0, 10);
+        return Status::OK();
+    }
+
+    Status read_null_bitmap(segment_v2::InvertedIndexQueryCacheHandle* 
cache_handle) override {
+        return Status::OK();
+    }
+
+    Result<bool> has_null() override { return false; }
+
+private:
+    std::shared_ptr<MockInvertedIndexReader> _reader;
+};
+
+TEST(FunctionComparisonTest, evaluate_inverted_index_with_null_param) {
+    FunctionComparison<EqualsOp, NameEquals> func;
+
+    auto nested_col = ColumnInt32::create();
+    nested_col->insert_default();
+
+    auto null_map = ColumnUInt8::create();
+    null_map->insert_value(1);
+
+    auto nullable_col = ColumnNullable::create(std::move(nested_col), 
std::move(null_map));
+
+    auto const_nullable_col = ColumnConst::create(std::move(nullable_col), 1);
+
+    auto nullable_type = 
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>());
+    ColumnsWithTypeAndName arguments = {
+            {std::move(const_nullable_col), nullable_type, "null_param"}};
+
+    std::vector<IndexFieldNameAndTypePair> data_type_with_names = {
+            {"test_col", std::make_shared<DataTypeInt32>()}};
+
+    TabletIndex index_meta;
+    auto reader = std::make_shared<MockInvertedIndexReader>(index_meta);
+    auto iter = std::make_unique<MockComparisonIndexIterator>(reader);
+    std::vector<segment_v2::IndexIterator*> iterators = {iter.get()};
+
+    segment_v2::InvertedIndexResultBitmap bitmap_result;
+    auto status = func.evaluate_inverted_index(arguments, 
data_type_with_names, iterators, 100,
+                                               nullptr, bitmap_result);
+
+    ASSERT_TRUE(status.ok()) << "Status should be OK when param is NULL";
+
+    ASSERT_EQ(bitmap_result.get_data_bitmap(), nullptr)
+            << "bitmap_result should not be set when param is NULL";
+}
+
+} // namespace doris::vectorized
diff --git 
a/regression-test/data/inverted_index_p0/test_inverted_index_null_literal.out 
b/regression-test/data/inverted_index_p0/test_inverted_index_null_literal.out
new file mode 100644
index 00000000000..55bc0b2e54e
--- /dev/null
+++ 
b/regression-test/data/inverted_index_p0/test_inverted_index_null_literal.out
@@ -0,0 +1,19 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select_null_literal --
+1
+2
+3
+
+-- !select_null_eq --
+
+-- !select_null_ne --
+
+-- !select_null_between --
+
+-- !select_is_null --
+3
+
+-- !select_is_not_null --
+1
+2
+
diff --git 
a/regression-test/suites/inverted_index_p0/test_inverted_index_null_literal.groovy
 
b/regression-test/suites/inverted_index_p0/test_inverted_index_null_literal.groovy
new file mode 100644
index 00000000000..1170a4a28ff
--- /dev/null
+++ 
b/regression-test/suites/inverted_index_p0/test_inverted_index_null_literal.groovy
@@ -0,0 +1,62 @@
+// 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_inverted_index_null_literal") {
+
+    def tableName = "test_inverted_index_null_literal"
+
+    sql "DROP TABLE IF EXISTS ${tableName}"
+
+    sql """
+        CREATE TABLE ${tableName} (
+            pk INT,
+            col_varchar VARCHAR(20),
+            INDEX idx_col_varchar (`col_varchar`) USING INVERTED
+        )
+        DUPLICATE KEY(pk)
+        DISTRIBUTED BY HASH(pk) BUCKETS 1
+        PROPERTIES (
+            "replication_allocation" = "tag.location.default: 1"
+        )
+    """
+
+    sql """
+        INSERT INTO ${tableName} VALUES 
+        (1, 'hello'),
+        (2, 'world'),
+        (3, NULL)
+    """
+
+    sql """ SET disable_nereids_expression_rules = "FOLD_CONSTANT_ON_FE" """
+    sql """ SET enable_fold_constant_by_be = "true" """
+    sql """ SET enable_sql_cache = 0 """
+
+    qt_select_null_literal """
+        SELECT pk FROM ${tableName} 
+        WHERE col_varchar = NULL OR pk >= 0
+        ORDER BY pk
+    """
+
+    qt_select_null_eq """SELECT pk FROM ${tableName} WHERE col_varchar = NULL 
ORDER BY pk"""
+    qt_select_null_ne """SELECT pk FROM ${tableName} WHERE col_varchar != NULL 
ORDER BY pk"""
+    qt_select_null_between """SELECT pk FROM ${tableName} WHERE col_varchar 
BETWEEN NULL AND NULL ORDER BY pk"""
+
+    qt_select_is_null """SELECT pk FROM ${tableName} WHERE col_varchar IS NULL 
ORDER BY pk"""
+    qt_select_is_not_null """SELECT pk FROM ${tableName} WHERE col_varchar IS 
NOT NULL ORDER BY pk"""
+
+    sql "DROP TABLE IF EXISTS ${tableName}"
+}


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

Reply via email to