You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by yi...@apache.org on 2026/04/08 08:20:53 UTC
(doris) branch branch-4.1 updated: branch-4.1: [fix](fe) Fix NoSuchElementException when count with MATCH_ALL expression #62172 (#62198)
This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 0e8e985119a branch-4.1: [fix](fe) Fix NoSuchElementException when count with MATCH_ALL expression #62172 (#62198)
0e8e985119a is described below
commit 0e8e985119ab2bc3cb18a404b57425cd517c4b73
Author: github-actions[bot] <41...@users.noreply.github.com>
AuthorDate: Wed Apr 8 16:20:45 2026 +0800
branch-4.1: [fix](fe) Fix NoSuchElementException when count with MATCH_ALL expression #62172 (#62198)
Cherry-picked from #62172
Co-authored-by: HappenLee <ha...@selectdb.com>
---
.../rules/implementation/AggregateStrategies.java | 14 +++-
.../test_count_match_all_pushdown.out | 16 +++++
.../test_count_match_all_pushdown.groovy | 83 ++++++++++++++++++++++
3 files changed, 111 insertions(+), 2 deletions(-)
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/AggregateStrategies.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/AggregateStrategies.java
index ca2f115f096..22356c98e76 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/AggregateStrategies.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/AggregateStrategies.java
@@ -513,7 +513,11 @@ public class AggregateStrategies implements ImplementationRuleFactory {
SlotReference.class::isInstance);
List<SlotReference> usedSlotInTable = (List<SlotReference>) Project.findProject(aggUsedSlots, outPutSlots);
for (SlotReference slot : usedSlotInTable) {
- Column column = slot.getOriginalColumn().get();
+ Optional<Column> optionalColumn = slot.getOriginalColumn();
+ if (!optionalColumn.isPresent()) {
+ return false;
+ }
+ Column column = optionalColumn.get();
PrimitiveType colType = column.getType().getPrimitiveType();
if (colType.isComplexType() || colType.isHllType() || colType.isBitmapType()) {
return false;
@@ -687,7 +691,13 @@ public class AggregateStrategies implements ImplementationRuleFactory {
logicalScan.getOutput());
for (SlotReference slot : usedSlotInTable) {
- Column column = slot.getOriginalColumn().get();
+ Optional<Column> optionalColumn = slot.getOriginalColumn();
+ if (!optionalColumn.isPresent()) {
+ // virtual columns (e.g., generated from MATCH_ALL expressions) do not have
+ // an original column and cannot be pushed down to storage layer aggregate
+ return canNotPush;
+ }
+ Column column = optionalColumn.get();
if (column.isAggregated()) {
return canNotPush;
}
diff --git a/regression-test/data/inverted_index_p0/test_count_match_all_pushdown.out b/regression-test/data/inverted_index_p0/test_count_match_all_pushdown.out
new file mode 100644
index 00000000000..c6578995170
--- /dev/null
+++ b/regression-test/data/inverted_index_p0/test_count_match_all_pushdown.out
@@ -0,0 +1,16 @@
+-- This file is automatically generated. You should know what you did if you want to edit this
+-- !match_all_count_1 --
+3
+
+-- !match_all_count_2 --
+3
+
+-- !match_any_count --
+3
+
+-- !match_phrase_count --
+3
+
+-- !match_all_with_where --
+2
+
diff --git a/regression-test/suites/inverted_index_p0/test_count_match_all_pushdown.groovy b/regression-test/suites/inverted_index_p0/test_count_match_all_pushdown.groovy
new file mode 100644
index 00000000000..0a185acf8bb
--- /dev/null
+++ b/regression-test/suites/inverted_index_p0/test_count_match_all_pushdown.groovy
@@ -0,0 +1,83 @@
+// 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_count_match_all_pushdown", "p0") {
+ // Test that count(col MATCH_ALL 'xxx') should not push down storage layer aggregate.
+ // MATCH_ALL generates a virtual column in OlapScan without originalColumn,
+ // which previously caused NoSuchElementException on Optional.get().
+
+ sql "DROP TABLE IF EXISTS test_count_match_all_pushdown"
+ sql """
+ CREATE TABLE test_count_match_all_pushdown (
+ `id` INT NOT NULL,
+ `content` TEXT NOT NULL,
+ `nullable_content` TEXT NULL,
+ INDEX content_idx (`content`) USING INVERTED PROPERTIES("parser" = "english") COMMENT '',
+ INDEX nullable_content_idx (`nullable_content`) USING INVERTED PROPERTIES("parser" = "english") COMMENT ''
+ ) ENGINE=OLAP
+ DUPLICATE KEY(`id`)
+ DISTRIBUTED BY HASH(`id`) BUCKETS 1
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1"
+ );
+ """
+
+ sql """
+ INSERT INTO test_count_match_all_pushdown VALUES
+ (1, 'hello world', 'foo bar'),
+ (2, 'hello doris', 'foo baz'),
+ (3, 'world doris', NULL);
+ """
+
+ sql "SET disable_nereids_rules='REWRITE_SIMPLE_AGG_TO_CONSTANT'"
+
+ // count(MATCH_ALL expr) should not be pushed down to storage layer
+ explain {
+ sql("select count(content MATCH_ALL 'hello world') from test_count_match_all_pushdown;")
+ contains "pushAggOp=NONE"
+ }
+
+ explain {
+ sql("select count(content MATCH_ALL 'hello') from test_count_match_all_pushdown;")
+ contains "pushAggOp=NONE"
+ }
+
+ // count(MATCH_ANY expr) should not be pushed down either
+ explain {
+ sql("select count(content MATCH_ANY 'hello world') from test_count_match_all_pushdown;")
+ contains "pushAggOp=NONE"
+ }
+
+ // count(MATCH_PHRASE expr) should not be pushed down either
+ explain {
+ sql("select count(content MATCH_PHRASE 'hello world') from test_count_match_all_pushdown;")
+ contains "pushAggOp=NONE"
+ }
+
+ // Verify actual execution returns correct results
+ order_qt_match_all_count_1 "select count(content MATCH_ALL 'hello world') from test_count_match_all_pushdown;"
+ order_qt_match_all_count_2 "select count(content MATCH_ALL 'hello') from test_count_match_all_pushdown;"
+ order_qt_match_any_count "select count(content MATCH_ANY 'hello world') from test_count_match_all_pushdown;"
+ order_qt_match_phrase_count "select count(content MATCH_PHRASE 'hello world') from test_count_match_all_pushdown;"
+
+ // Test with complex WHERE clause (the original bug scenario)
+ order_qt_match_all_with_where """
+ select count(content MATCH_ALL 'hello') from test_count_match_all_pushdown
+ where (case nullable_content when 'foo bar' then 1 end) = 1
+ or not ((case id when 1 then 1 end) = 1 and (nullable_content is null));
+ """
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org