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