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

morrySnow 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 f461a2858b6 [fix](point query) Keep LogicalOlapScan for short-circuit 
point query on empty table (#62948)
f461a2858b6 is described below

commit f461a2858b6c0539d0056edb902ea29d7fdeb55e
Author: HonestManXin <[email protected]>
AuthorDate: Wed May 6 14:51:42 2026 +0800

    [fix](point query) Keep LogicalOlapScan for short-circuit point query on 
empty table (#62948)
    
    point query will not take effect if table has no data which will cause
    bad query performance
---
 .../doris/nereids/jobs/executor/Rewriter.java      |  6 +-
 .../nereids/rules/rewrite/PruneEmptyPartition.java |  6 ++
 .../rules/rewrite/ShortCircuitPointQueryTest.java  | 78 ++++++++++++++++++++++
 .../data/point_query_p0/test_point_query.out       |  2 +
 .../suites/point_query_p0/test_point_query.groovy  |  5 ++
 5 files changed, 95 insertions(+), 2 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
index 7996c987c11..d078f4c3fe0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
@@ -722,6 +722,10 @@ public class Rewriter extends AbstractBatchJobExecutor {
                 topic("Table/Physical optimization",
                         cascadesContext -> 
cascadesContext.rewritePlanContainsTypes(LogicalCatalogRelation.class),
                         topDown(
+                                // Mark short-circuit point query before 
pruning empty partitions,
+                                // otherwise PRUNE_EMPTY_PARTITION may replace 
LogicalOlapScan with LogicalEmptyRelation
+                                // and short-circuit flag can never be set.
+                                new 
LogicalResultSinkToShortCircuitPointQuery(),
                                 new PruneOlapScanPartition(),
                                 new PruneEmptyPartition(),
                                 new PruneFileScanPartition(),
@@ -734,8 +738,6 @@ public class Rewriter extends AbstractBatchJobExecutor {
                 topic("adjust preagg status",
                         custom(RuleType.SET_PREAGG_STATUS, 
SetPreAggStatus::new)
                 ),
-                topic("Point query short circuit",
-                        topDown(new 
LogicalResultSinkToShortCircuitPointQuery())),
                 topic("eliminate",
                         // SORT_PRUNING should be applied after mergeLimit
                         custom(RuleType.ELIMINATE_SORT, EliminateSort::new),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneEmptyPartition.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneEmptyPartition.java
index 3347518b165..aadd48bbd52 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneEmptyPartition.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneEmptyPartition.java
@@ -40,6 +40,12 @@ public class PruneEmptyPartition extends 
OneRewriteRuleFactory {
     @Override
     public Rule build() {
         return logicalOlapScan().thenApply(ctx -> {
+            // We still want to keep LogicalOlapScan even if partitions are 
empty,
+            // so that the planner can build a scan node and the 
PreparedStatement can cache ShortCircuitQueryContext.
+            if (ctx.connectContext != null && 
ctx.connectContext.getStatementContext() != null
+                    && 
ctx.connectContext.getStatementContext().isShortCircuitQuery()) {
+                return null;
+            }
             LogicalOlapScan scan = ctx.root;
             OlapTable table = scan.getTable();
             List<Long> partitionIdsToPrune = scan.getSelectedPartitionIds();
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ShortCircuitPointQueryTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ShortCircuitPointQueryTest.java
new file mode 100644
index 00000000000..e036b3a7c49
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ShortCircuitPointQueryTest.java
@@ -0,0 +1,78 @@
+// 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.
+
+package org.apache.doris.nereids.rules.rewrite;
+
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.util.MemoPatternMatchSupported;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Regression test:
+ * For short-circuit point query, we should not rewrite LogicalOlapScan to 
LogicalEmptyRelation
+ * even if the table partitions are empty. Otherwise PreparedStatement could 
not cache
+ * ShortCircuitQueryContext and may downgrade to normal planning repeatedly.
+ */
+class ShortCircuitPointQueryTest extends TestWithFeService
+        implements MemoPatternMatchSupported {
+
+    @Override
+    protected void runBeforeAll() throws Exception {
+        createDatabase("test");
+        useDatabase("test");
+        createTable("CREATE TABLE `tbl_point_query` (\n"
+                + "  `key` int(11) NULL,\n"
+                + "  `v1` varchar(30) NULL\n"
+                + ") ENGINE=OLAP\n"
+                + "UNIQUE KEY(`key`)\n"
+                + "DISTRIBUTED BY HASH(`key`) BUCKETS 1\n"
+                + "PROPERTIES (\n"
+                + "  \"replication_num\" = \"1\",\n"
+                + "  \"enable_unique_key_merge_on_write\" = \"true\",\n"
+                + "  \"light_schema_change\" = \"true\",\n"
+                + "  \"store_row_column\" = \"true\"\n"
+                + ");");
+    }
+
+    @Test
+    void testShortCircuitPointQueryKeepOlapScanWhenTableEmpty() {
+        boolean originRunningUnitTest = FeConstants.runningUnitTest;
+        FeConstants.runningUnitTest = false;
+        try {
+            String sql = "select * from tbl_point_query where `key` = 1";
+            Plan plan = PlanChecker.from(connectContext)
+                    .analyze(sql)
+                    .rewrite()
+                    .getPlan();
+
+            // short-circuit flag should be set
+            
Assertions.assertTrue(connectContext.getStatementContext().isShortCircuitQuery());
+            // should still keep scan node for point query path
+            Assertions.assertTrue(plan.anyMatch(p -> p instanceof 
LogicalOlapScan));
+            Assertions.assertFalse(plan.anyMatch(p -> p instanceof 
LogicalEmptyRelation));
+        } finally {
+            FeConstants.runningUnitTest = originRunningUnitTest;
+        }
+    }
+}
diff --git a/regression-test/data/point_query_p0/test_point_query.out 
b/regression-test/data/point_query_p0/test_point_query.out
index 5fafa2fb91d..e03c0741865 100644
--- a/regression-test/data/point_query_p0/test_point_query.out
+++ b/regression-test/data/point_query_p0/test_point_query.out
@@ -152,6 +152,8 @@
 -- !sql --
 0      1111111
 
+-- !sql_empty --
+
 -- !sql --
 10     20      aabc    value
 
diff --git a/regression-test/suites/point_query_p0/test_point_query.groovy 
b/regression-test/suites/point_query_p0/test_point_query.groovy
index 90c6bacf599..431687c63a7 100644
--- a/regression-test/suites/point_query_p0/test_point_query.groovy
+++ b/regression-test/suites/point_query_p0/test_point_query.groovy
@@ -328,6 +328,11 @@ suite("test_point_query") {
         DISTRIBUTED BY HASH(`col1`, `col2`, `loc3`) BUCKETS 1
         PROPERTIES ( "replication_allocation" = "tag.location.default: 1", 
"bloom_filter_columns" = "col1", "store_row_column" = "true", 
"enable_mow_light_delete" = "false" );
     """
+    explain {
+        sql("select * from table_3821461 where col1 = -10 and col2 = 20 and 
loc3 = 'aabc'")
+        contains "SHORT-CIRCUIT"
+    }
+    qt_sql_empty "select * from table_3821461 where col1 = 10 and col2 = 20 
and loc3 = 'aabc';"
     sql "insert into table_3821461 values (-10, 20, 'aabc', 'value')"
     sql "insert into table_3821461 values (10, 20, 'aabc', 'value');"
     sql "insert into table_3821461 values (20, 30, 'aabc', 'value');"


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

Reply via email to