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

csun5285 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 7ff8c69da8f [fix](fe) Do not use short-circuit point query when 
reading hidden columns  (#70162)
7ff8c69da8f is described below

commit 7ff8c69da8ffb2907a9d3a9adcbe5564fd13308d
Author: Chenyang Sun <[email protected]>
AuthorDate: Wed Nov 4 17:31:26 2026 +0800

    [fix](fe) Do not use short-circuit point query when reading hidden columns  
(#70162)
    
    Problem Summary:
    
    A singleton rowset stores only a placeholder for `__DORIS_VERSION_COL__`
    and `__DORIS_COMMIT_TSO_COL__`; the real value comes from the rowset
    metadata at read time. The short-circuit point query reads the JSONB row
    store, which holds that placeholder, and the BE fast path deliberately
    does not resolve these columns (see `point_query_executor.cpp`). So on a
    MOW table with `store_row_column = true`:
    
    ```sql
    CREATE TABLE b (id INT NOT NULL, v INT) UNIQUE KEY(id) DISTRIBUTED BY 
HASH(id) BUCKETS 1
    PROPERTIES ('replication_num'='1', 
'enable_unique_key_merge_on_write'='true',
                'store_row_column'='true', 'changelog.enable'='true');
    INSERT INTO b VALUES (1, 7), (2, 8);
    SELECT __DORIS_VERSION_COL__, __DORIS_COMMIT_TSO_COL__, v FROM b WHERE id = 
1;
    -- before: 0, 0, 7          after: 2, <commit tso>, 7
    ```
---
 .../LogicalResultSinkToShortCircuitPointQuery.java | 10 +++-
 .../rules/rewrite/ShortCircuitPointQueryTest.java  | 53 ++++++++++++++++++
 .../test_short_circuit_rowset_hidden_columns.out   | 10 ++++
 ...test_short_circuit_rowset_hidden_columns.groovy | 65 ++++++++++++++++++++++
 4 files changed, 137 insertions(+), 1 deletion(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/LogicalResultSinkToShortCircuitPointQuery.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/LogicalResultSinkToShortCircuitPointQuery.java
index e0bb0ea45b9..235f203a0cb 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/LogicalResultSinkToShortCircuitPointQuery.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/LogicalResultSinkToShortCircuitPointQuery.java
@@ -26,6 +26,7 @@ import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
@@ -95,6 +96,11 @@ public class LogicalResultSinkToShortCircuitPointQuery 
implements RewriteRuleFac
                         && olapTable.storeRowColumn();
     }
 
+    // Queries reading hidden columns do not use the short circuit.
+    private boolean readsHiddenColumn(Set<Slot> slots) {
+        return slots.stream().anyMatch(slot -> !((SlotReference) 
slot).isVisible());
+    }
+
     // set short circuit flag and return the original plan
     private Plan shortCircuit(Plan root, OlapTable olapTable,
                 Set<Expression> conjuncts, StatementContext statementContext) {
@@ -119,7 +125,8 @@ public class LogicalResultSinkToShortCircuitPointQuery 
implements RewriteRuleFac
                 RuleType.SHOR_CIRCUIT_POINT_QUERY.build(
                         
logicalResultSink(logicalProject(logicalFilter(logicalOlapScan()
                             .when(this::scanMatchShortCircuitCondition)
-                    ).when(this::filterMatchShortCircuitCondition)))
+                    ).when(this::filterMatchShortCircuitCondition))
+                        .when(project -> 
!readsHiddenColumn(project.getInputSlots())))
                         .thenApply(ctx -> {
                             return shortCircuit(ctx.root, 
ctx.root.child().child().child().getTable(),
 
@@ -129,6 +136,7 @@ public class LogicalResultSinkToShortCircuitPointQuery 
implements RewriteRuleFac
                         logicalResultSink(logicalFilter(logicalOlapScan()
                                 .when(this::scanMatchShortCircuitCondition)
                         ).when(this::filterMatchShortCircuitCondition))
+                                .when(sink -> 
!readsHiddenColumn(sink.getInputSlots()))
                                 .thenApply(ctx -> {
                                     return shortCircuit(ctx.root, 
ctx.root.child().child().getTable(),
                                             ctx.root.child().getConjuncts(), 
ctx.statementContext);
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
index 9511e8d4bb9..5232dbfc4df 100644
--- 
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
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.Tablet;
+import org.apache.doris.common.Config;
 import org.apache.doris.common.FeConstants;
 import org.apache.doris.datasource.doris.RemoteOlapTable;
 import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
@@ -48,6 +49,7 @@ class ShortCircuitPointQueryTest extends TestWithFeService
 
     @Override
     protected void runBeforeAll() throws Exception {
+        Config.enable_feature_changelog = true;
         createDatabase("test");
         useDatabase("test");
         createTable("CREATE TABLE `tbl_point_query` (\n"
@@ -78,6 +80,57 @@ class ShortCircuitPointQueryTest extends TestWithFeService
                 + "  \"light_schema_change\" = \"true\",\n"
                 + "  \"store_row_column\" = \"true\"\n"
                 + ");");
+        // Carries both __DORIS_VERSION_COL__ and __DORIS_COMMIT_TSO_COL__.
+        createTable("CREATE TABLE `tbl_changelog_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"
+                + "  \"changelog.enable\" = \"true\"\n"
+                + ");");
+    }
+
+    @Test
+    void testHiddenColumnsDoNotUseShortCircuit() {
+        // The row store returned 0 for __DORIS_VERSION_COL__ and 
__DORIS_COMMIT_TSO_COL__ instead
+        // of the rowset version and commit TSO.
+        rewrite("select __DORIS_VERSION_COL__ from tbl_changelog_point_query 
where `key` = 1");
+        
Assertions.assertFalse(connectContext.getStatementContext().isShortCircuitQuery());
+
+        rewrite("select __DORIS_COMMIT_TSO_COL__, v1 from 
tbl_changelog_point_query where `key` = 1");
+        
Assertions.assertFalse(connectContext.getStatementContext().isShortCircuitQuery());
+
+        rewrite("select __DORIS_COMMIT_TSO_COL__ + 1 from 
tbl_changelog_point_query where `key` = 1");
+        
Assertions.assertFalse(connectContext.getStatementContext().isShortCircuitQuery());
+
+        rewrite("select __DORIS_DELETE_SIGN__, v1 from 
tbl_changelog_point_query where `key` = 1");
+        
Assertions.assertFalse(connectContext.getStatementContext().isShortCircuitQuery());
+
+        // show_hidden_columns makes `select *` return hidden columns as well.
+        boolean originalShowHidden = 
connectContext.getSessionVariable().showHiddenColumns();
+        try {
+            connectContext.getSessionVariable().setShowHiddenColumns(true);
+            rewrite("select * from tbl_changelog_point_query where `key` = 1");
+            
Assertions.assertFalse(connectContext.getStatementContext().isShortCircuitQuery());
+        } finally {
+            
connectContext.getSessionVariable().setShowHiddenColumns(originalShowHidden);
+        }
+    }
+
+    @Test
+    void testQueryWithoutHiddenColumnsUsesShortCircuit() {
+        // `select *` skips hidden columns by default.
+        rewrite("select * from tbl_changelog_point_query where `key` = 1");
+        
Assertions.assertTrue(connectContext.getStatementContext().isShortCircuitQuery());
+
+        rewrite("select v1 from tbl_changelog_point_query where `key` = 1");
+        
Assertions.assertTrue(connectContext.getStatementContext().isShortCircuitQuery());
     }
 
     @Test
diff --git 
a/regression-test/data/point_query_p0/test_short_circuit_rowset_hidden_columns.out
 
b/regression-test/data/point_query_p0/test_short_circuit_rowset_hidden_columns.out
new file mode 100644
index 00000000000..d7cbd41dd1b
--- /dev/null
+++ 
b/regression-test/data/point_query_p0/test_short_circuit_rowset_hidden_columns.out
@@ -0,0 +1,10 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !version --
+2      7
+
+-- !commit_tso --
+true   7
+
+-- !select_star --
+1      7
+
diff --git 
a/regression-test/suites/point_query_p0/test_short_circuit_rowset_hidden_columns.groovy
 
b/regression-test/suites/point_query_p0/test_short_circuit_rowset_hidden_columns.groovy
new file mode 100644
index 00000000000..e9755540b03
--- /dev/null
+++ 
b/regression-test/suites/point_query_p0/test_short_circuit_rowset_hidden_columns.groovy
@@ -0,0 +1,65 @@
+// 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_short_circuit_rowset_hidden_columns", "p0") {
+    // Queries reading hidden columns do not use the short circuit: it used to 
return 0 for
+    // __DORIS_VERSION_COL__ and __DORIS_COMMIT_TSO_COL__ instead of version 2 
and the commit TSO.
+    sql "DROP TABLE IF EXISTS short_circuit_rowset_hidden_columns"
+    sql """
+        CREATE TABLE short_circuit_rowset_hidden_columns (
+            `id` INT NOT NULL,
+            `v` INT NULL
+        ) ENGINE=OLAP
+        UNIQUE KEY(`id`)
+        DISTRIBUTED BY HASH(`id`) BUCKETS 1
+        PROPERTIES (
+            "replication_num" = "1",
+            "enable_unique_key_merge_on_write" = "true",
+            "light_schema_change" = "true",
+            "store_row_column" = "true",
+            "changelog.enable" = "true"
+        )
+    """
+    sql "INSERT INTO short_circuit_rowset_hidden_columns VALUES (1, 7), (2, 8)"
+
+    explain {
+        sql "SELECT __DORIS_VERSION_COL__, v FROM 
short_circuit_rowset_hidden_columns WHERE id = 1"
+        notContains "SHORT-CIRCUIT"
+    }
+    qt_version "SELECT __DORIS_VERSION_COL__, v FROM 
short_circuit_rowset_hidden_columns WHERE id = 1"
+
+    explain {
+        sql "SELECT __DORIS_COMMIT_TSO_COL__ > 0, v FROM 
short_circuit_rowset_hidden_columns WHERE id = 1"
+        notContains "SHORT-CIRCUIT"
+    }
+    qt_commit_tso "SELECT __DORIS_COMMIT_TSO_COL__ > 0, v FROM 
short_circuit_rowset_hidden_columns WHERE id = 1"
+
+    // show_hidden_columns makes `select *` return hidden columns as well.
+    sql "SET show_hidden_columns = true"
+    explain {
+        sql "SELECT * FROM short_circuit_rowset_hidden_columns WHERE id = 1"
+        notContains "SHORT-CIRCUIT"
+    }
+    sql "SET show_hidden_columns = false"
+
+    // Queries that do not read hidden columns keep the short circuit.
+    explain {
+        sql "SELECT * FROM short_circuit_rowset_hidden_columns WHERE id = 1"
+        contains "SHORT-CIRCUIT"
+    }
+    qt_select_star "SELECT * FROM short_circuit_rowset_hidden_columns WHERE id 
= 1"
+}


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

Reply via email to