github-actions[bot] commented on code in PR #67796:
URL: https://github.com/apache/doris/pull/67796#discussion_r4002355934


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownTopNDistinctThroughJoin.java:
##########
@@ -197,6 +201,31 @@ private List<OrderKey> getPushedOrderKeys(Set<Slot> 
groupBySlots, Set<Slot> join
                 notFound = true;
             }
         }
-        return pushedOrderKeys.build();
+        List<OrderKey> pushedOrderKeyList = pushedOrderKeys.build();
+        if (pushedOrderKeyList.size() == orderKeys.size()
+                || isOrderKeyPrefixUniqueAfterDistinct(joinChild, 
pushedOrderKeyList)) {
+            return pushedOrderKeyList;
+        }
+        return ImmutableList.of();
+    }
+
+    /**
+     * A partial order-key prefix is safe for a hard limit only when it 
uniquely orders the rows produced by
+     * {@link PlanUtils#distinct(Plan)}. This is true when a leading part of 
the prefix either is already a
+     * non-null unique key, covers every child output, or functionally 
determines every remaining child output.
+     */
+    private boolean isOrderKeyPrefixUniqueAfterDistinct(Plan joinChild, 
List<OrderKey> orderKeyPrefix) {
+        Set<Slot> childOutput = joinChild.getOutputSet();
+        DataTrait childTrait = joinChild.getLogicalProperties().getTrait();
+        Set<Slot> prefixSlots = new HashSet<>();
+        for (OrderKey orderKey : orderKeyPrefix) {
+            prefixSlots.add((Slot) orderKey.getExpr());
+            if (prefixSlots.containsAll(childOutput) || 
childTrait.isUniqueAndNotNull(prefixSlots)
+                    || childOutput.stream().allMatch(slot -> 
prefixSlots.contains(slot)

Review Comment:
   [P2] Apply FD augmentation to the accumulated prefix
   
   For a child `Project(a, b, a + 1 AS c)`, the trait records `{a} -> {c}`. 
With final order `(a, b, right.d)`, the accumulated child prefix `{a,b}` 
therefore determines the full `DISTINCT(a,b,c)` row and is safe to push. This 
check nevertheless asks `isDependent({a,b}, {c})`, while `FuncDeps.isFuncDeps` 
only matches the exact stored determinant `{a}`, so the advertised safe 
optimization is skipped. Please make this proof augmentation/closure-aware (for 
example, accept a valid determinant contained in the prefix) and add the 
augmented-prefix rule test.



##########
fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownTopNDistinctThroughJoinTest.java:
##########
@@ -0,0 +1,99 @@
+// 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.nereids.properties.OrderKey;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.util.PlanConstructor;
+import org.apache.doris.qe.ConnectContext;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Set;
+
+class PushDownTopNDistinctThroughJoinTest {
+    private static final PushDownTopNDistinctThroughJoin RULE = new 
PushDownTopNDistinctThroughJoin();
+    private static final LogicalOlapScan LEFT_SCAN = 
PlanConstructor.newLogicalOlapScan(0, "t1", 0);
+    private static final LogicalOlapScan RIGHT_SCAN = 
PlanConstructor.newLogicalOlapScan(1, "t2", 0);
+
+    @BeforeEach
+    void setUp() {
+        new ConnectContext().setThreadLocalInfo();
+    }
+
+    @AfterEach
+    void tearDown() {
+        ConnectContext.remove();
+    }
+
+    @Test
+    void rejectNonUniquePartialOrderKeys() {

Review Comment:
   [P2] Exercise the registered rewrites, not only the helper
   
   All four tests enter production code only through `getPushedOrderKeys`, so 
they can pass even if either `TopN -> DISTINCT -> Join` rule shape fails to 
match or installs the child TopN incorrectly. They also use DUP-key scans, 
leaving the new `isUniqueAndNotNull` acceptance (and nullable-unique rejection) 
untested. Please add `PlanChecker`/actual-rewrite cases for the direct and 
all-slots-project shapes, including a non-null unique prefix and its nullable 
negative, and assert presence or absence of `TopN(DISTINCT(child))`. This also 
lets the helper remain private.



##########
regression-test/suites/nereids_rules_p0/push_down_top_n/push_down_top_n_distinct_through_join.groovy:
##########
@@ -67,4 +67,61 @@ suite("push_down_top_n_distinct_through_join") {
     qt_push_down_topn_through_join_data """
         select distinct * from (select t1.id from table_join t1 cross join 
table_join t2) t order by id limit 10;
     """
+
+    sql "DROP TABLE IF EXISTS topn_distinct_left"
+    sql "DROP TABLE IF EXISTS topn_distinct_right"
+    sql """
+        CREATE TABLE topn_distinct_left (
+            k INT NOT NULL,
+            id INT NOT NULL
+        ) DUPLICATE KEY(k, id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES("replication_num" = "1")
+    """
+    sql """
+        CREATE TABLE topn_distinct_right (
+            id INT NOT NULL,
+            s INT NOT NULL
+        ) DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES("replication_num" = "1")
+    """
+    sql """
+        INSERT INTO topn_distinct_left VALUES
+            (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8)
+    """
+    sql """
+        INSERT INTO topn_distinct_right VALUES
+            (1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60), (7, 70), (8, 
80)
+    """
+
+    test {

Review Comment:
   [P2] Record these deterministic results through the generated oracle
   
   These are fixed expected result sets, but both cases hand-code 
`test/check/assertEquals` and the PR does not update the suite's generated 
`.out` file. Doris's regression contract requires determined results to use 
named `qt`/`order_qt` cases and have their outputs generated by the regression 
runner. Please convert both queries and commit the generated output; keep a 
separate plan assertion if you want to prove that the unsafe child TopN is 
absent.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to