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]