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

yiguolei 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 7bfda3179b6 [fix](be) Fix null query crash in inverted index 
predicates (#65138)
7bfda3179b6 is described below

commit 7bfda3179b68ffa923c522067cfd6452f6220af9
Author: liangj777 <[email protected]>
AuthorDate: Fri Jul 3 21:28:25 2026 +0800

    [fix](be) Fix null query crash in inverted index predicates (#65138)
    
    ### What problem does this PR solve?
    
    Issue Number: close #xxx
    
    Related PR: #xxx
    
    Problem Summary:
    
    When SQL contains a null literal in an inverted index predicate, such as
    `col = NULL`, `MATCH_ALL NULL`, or `multi_match(col, 'phrase', NULL)`,
    the null const column value can reach the BE inverted index evaluation
    path. The previous code read literal arguments with `get_data_at(0)` and
    then converted the returned `StringRef` to `std::string` directly. For a
    null value, `ColumnNullable::get_data_at()` returns `{nullptr, 0}`, so
    the subsequent string construction may crash or trigger undefined
    behavior.
    
    This PR checks the literal query type and query string with `get()`
    before calling `get_data_at()`. If either argument is null, the inverted
    index evaluation returns an empty bitmap result, meaning the predicate
    matches no rows instead of crashing.
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [x] Regression test
        - [ ] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [x] No.
        - [ ] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [x] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 be/src/exprs/function/function_multi_match.cpp     | 17 +++--
 .../test_multi_match_null_query.out                | 28 ++++++++
 .../test_multi_match_null_query.groovy             | 76 ++++++++++++++++++++++
 3 files changed, 117 insertions(+), 4 deletions(-)

diff --git a/be/src/exprs/function/function_multi_match.cpp 
b/be/src/exprs/function/function_multi_match.cpp
index ecc10e593ef..3f73c258c51 100644
--- a/be/src/exprs/function/function_multi_match.cpp
+++ b/be/src/exprs/function/function_multi_match.cpp
@@ -65,8 +65,12 @@ Status FunctionMultiMatch::evaluate_inverted_index(
     std::shared_ptr<roaring::Roaring> null_bitmap = 
std::make_shared<roaring::Roaring>();
 
     // type
-    auto query_type_value = arguments[0].column->get_data_at(0);
-    auto query_type = get_query_type(query_type_value.to_string());
+    Field query_type_value;
+    arguments[0].column->get(0, query_type_value);
+    if (query_type_value.is_null()) {
+        return Status::RuntimeError("query_type can not be NULL");
+    }
+    auto query_type = get_query_type(query_type_value.get<TYPE_STRING>());
     if (query_type == InvertedIndexQueryType::UNKNOWN_QUERY) {
         return Status::RuntimeError(
                 "parameter query type incorrect for function multi_match: 
query_type = {}",
@@ -74,7 +78,12 @@ Status FunctionMultiMatch::evaluate_inverted_index(
     }
 
     // query
-    auto query_str_ref = arguments[1].column->get_data_at(0);
+    Field query_str_value;
+    arguments[1].column->get(0, query_str_value);
+    if (query_str_value.is_null()) {
+        bitmap_result = segment_v2::InvertedIndexResultBitmap(roaring, 
null_bitmap);
+        return Status::OK();
+    }
     auto param_type = arguments[1].type->get_primitive_type();
     if (!is_string_type(param_type)) {
         return Status::Error<ErrorCode::INDEX_INVALID_PARAMETERS>(
@@ -82,7 +91,7 @@ Status FunctionMultiMatch::evaluate_inverted_index(
     }
     // search
     InvertedIndexParam param;
-    param.query_value = 
Field::create_field<TYPE_STRING>(query_str_ref.to_string());
+    param.query_value = query_str_value;
     param.query_type = query_type;
     param.num_rows = num_rows;
     for (size_t i = 0; i < data_type_with_names.size(); i++) {
diff --git 
a/regression-test/data/inverted_index_p0/test_multi_match_null_query.out 
b/regression-test/data/inverted_index_p0/test_multi_match_null_query.out
new file mode 100644
index 00000000000..07bb1c8ee4e
--- /dev/null
+++ b/regression-test/data/inverted_index_p0/test_multi_match_null_query.out
@@ -0,0 +1,28 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !sql --
+0
+
+-- !sql --
+0
+
+-- !sql --
+0
+
+-- !sql --
+0
+
+-- !sql --
+0
+
+-- !sql --
+0
+
+-- !sql --
+0
+
+-- !sql --
+1
+
+-- !sql --
+0
+
diff --git 
a/regression-test/suites/inverted_index_p0/test_multi_match_null_query.groovy 
b/regression-test/suites/inverted_index_p0/test_multi_match_null_query.groovy
new file mode 100644
index 00000000000..7b516ca8517
--- /dev/null
+++ 
b/regression-test/suites/inverted_index_p0/test_multi_match_null_query.groovy
@@ -0,0 +1,76 @@
+// 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.
+
+// Regression test for: NULL query string reaches inverted index 
evaluate_inverted_index.
+// Without FE constant folding, NULL propagates to the BE inverted index path.
+// Expected: returns 0 rows without error (null query matches nothing).
+
+suite("test_multi_match_null_query", "p0") {
+    sql "DROP TABLE IF EXISTS test_multi_match_null_query"
+    sql """
+        CREATE TABLE test_multi_match_null_query (
+            id INT,
+            body TEXT,
+            INDEX body_idx (body) USING INVERTED PROPERTIES("parser" = 
"english")
+        ) ENGINE=OLAP
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_allocation" = "tag.location.default: 1")
+    """
+    sql "INSERT INTO test_multi_match_null_query VALUES (1, 'hello world'), 
(2, 'foo bar')"
+
+    // Disable FE constant folding so NULL is not optimized to VEMPTYSET,
+    // forcing the predicate to reach the BE inverted index path 
(pushAggOp=COUNT_ON_INDEX).
+    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_sql "SELECT count() FROM test_multi_match_null_query WHERE body 
MATCH_ALL NULL"
+    qt_sql "SELECT count() FROM test_multi_match_null_query WHERE body 
MATCH_ANY NULL"
+    qt_sql "SELECT count() FROM test_multi_match_null_query WHERE body 
MATCH_PHRASE NULL"
+    qt_sql "SELECT count() FROM test_multi_match_null_query WHERE 
multi_match(body, 'phrase_prefix', NULL)"
+    qt_sql "SELECT count() FROM test_multi_match_null_query WHERE 
multi_match(body, 'phrase', NULL)"
+    qt_sql "SELECT count() FROM test_multi_match_null_query WHERE 
multi_match(body, 'any', NULL)"
+    qt_sql "SELECT count() FROM test_multi_match_null_query WHERE 
multi_match(body, 'all', NULL)"
+
+    test {
+        sql "SELECT count() FROM test_multi_match_null_query WHERE 
multi_match(body, NULL, 'abc')"
+        exception "query_type can not be NULL"
+    }
+
+    sql "DROP TABLE IF EXISTS test_multi_match_null_query_parser_none"
+    sql """
+        CREATE TABLE test_multi_match_null_query_parser_none (
+            id INT,
+            body TEXT,
+            INDEX body_idx (body) USING INVERTED PROPERTIES("parser" = "none")
+        ) ENGINE=OLAP
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_allocation" = "tag.location.default: 1")
+    """
+    sql "INSERT INTO test_multi_match_null_query_parser_none VALUES (1, ''), 
(2, NULL), (3, 'hello')"
+    qt_sql """
+        SELECT id FROM test_multi_match_null_query_parser_none
+        WHERE multi_match(body, 'any', '')
+        ORDER BY id
+    """
+    qt_sql """
+        SELECT count() FROM test_multi_match_null_query_parser_none
+        WHERE multi_match(body, 'any', NULL)
+    """
+}


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

Reply via email to