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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubqueryToApply.java:
##########
@@ -503,6 +535,46 @@ private Pair<LogicalPlan, Optional<Expression>> 
addApply(SubqueryExpr subquery,
         return Pair.of(logicalProject, newCorrelatedOuterExpr);
     }
 
+    /**
+     * simplify the conjunct that contains mark join slots and infer the 
behavior of each
+     * mark join slot, return the rewritten conjunct together with the mark 
slots info.
+     *
+     * for each mark slot, the pair in the returned map has:
+     * Pair.first: whether the null and false values of the mark slot are 
indistinguishable,
+     *             i.e. the mark slot can be treated as a non-nullable 
boolean. it only affects
+     *             how the mark value is computed (treating null as false) and 
never changes the
+     *             number of output rows, so it's safe for every join type and 
the filter.
+     * Pair.second: whether the original mark join can be directly eliminated 
and turned into a
+     *              plain semi join. a plain semi join only outputs the 
matched rows, while a
+     *              mark join keeps all original rows and adds a mark column, 
so eliminating the
+     *              mark join is only safe when discarding the unmatched rows 
is already part of
+     *              the containing join's semantics (inner, cross and semi 
joins).
+     *
+     * when Pair.second is true, the mark slot is replaced by the true literal 
in the returned
+     * conjunct, and the caller can drop the mark join slot to turn the mark 
join into a plain
+     * semi join.
+     */
+    private Pair<Expression, Map<MarkJoinSlotReference, Pair<Boolean, 
Boolean>>> simplifyConjunctWithMarkJoinSlot(
+            Expression conjunct, Plan plan, CascadesContext cascadesContext) {
+        ExpressionRewriteContext rewriteContext = new 
ExpressionRewriteContext(plan, cascadesContext);
+        Map<MarkJoinSlotReference, Pair<Boolean, Boolean>> markSlotsInfo;
+        if (conjunct.containsType(MarkJoinSlotReference.class)) {
+            markSlotsInfo = ExpressionUtils.inferMarkSlotNotNullMap(conjunct, 
rewriteContext);
+        } else {
+            markSlotsInfo = Maps.newHashMap();
+        }
+        Map<MarkJoinSlotReference, BooleanLiteral> replaceMap = 
Maps.newHashMap();
+        for (Map.Entry<MarkJoinSlotReference, Pair<Boolean, Boolean>> entry : 
markSlotsInfo.entrySet()) {
+            if (entry.getValue().second) {

Review Comment:
   [P1] Do not eliminate a mark join across non-movable evaluation
   
   Pair.second is a row-truth proof, but dropping the Apply also changes which 
rows reach other expressions. For example, after subquery replacement:
   
   ```text
   Filter(ifnull(ifnull(M,FALSE) AND assert_true(guard,'bad'), FALSE))
     Project(..., M)
       Apply(IN, mark=M)
         Scan(t)
         Scan(s)
   ```
   
   Both M=FALSE and M=NULL fold to FALSE, so this loop replaces M with TRUE and 
`addApply` turns the mark Apply into a semi join. In the original plan a mixed 
match/nonmatch batch reaches the filter; `VCompoundPred` evaluates the RHS for 
that batch, so `assert_true` can throw on an unmatched `guard=FALSE` row. The 
semi join prunes that row first and suppresses the error, violating 
`AssertTrue`'s `NoneMovableFunction` contract. Please fence Pair.second when 
the conjunct contains `NoneMovableFunction` (and audit volatile expressions for 
the same evaluation-domain issue), with an error-behavior regression.
   



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubqueryToApply.java:
##########
@@ -118,20 +118,15 @@ public List<Rule> buildRules() {
                                 ctx.statementContext, 
shouldOutputMarkJoinSlot.get(i));
                         SubqueryContext context = new 
SubqueryContext(subqueryExprs);
                         Expression conjunct = 
replaceSubquery.replace(oldConjuncts.get(i), context);
-                        // TODO: The way to optimize null aware mark join is 
not right.
-                        //   remove it temporary until we refactor it.
-                        // ExpressionRewriteContext rewriteContext = new 
ExpressionRewriteContext(ctx.cascadesContext);
-                        // boolean isMarkSlotNotNull = 
conjunct.containsType(MarkJoinSlotReference.class)
-                        //                 ? 
ExpressionUtils.canInferNotNullForMarkSlot(
-                        //                         
TrySimplifyPredicateWithMarkJoinSlot.INSTANCE.rewrite(conjunct,
-                        //                                 rewriteContext), 
rewriteContext)
-                        //                 : false;
-                        boolean isMarkSlotNotNull = false;
+                        Pair<Expression, Map<MarkJoinSlotReference, 
Pair<Boolean, Boolean>>> simplifyResult =

Review Comment:
   [P1] Preserve mark-free subtrees below NULL-observing wrappers
   
   This enables Pair.first for arbitrary filter expressions, but the 
simplifier's local `And`/`Or` neutral elements are not sound when a parent 
observes their value. A reduced failing tree is:
   
   ```text
   Filter(ifnull(M, flag OR FALSE))
     Project(key, flag, M)
       Apply(IN, mark=M)
         Scan(t)
         Scan(s)
   ```
   
   `TrySimplifyPredicateWithMarkJoinSlot` rewrites the mark-free `flag OR 
FALSE` subtree to FALSE, so inference sees `ifnull(M,FALSE)` and returns 
Pair.first=true. For an outer row with `key=NULL`, `flag=TRUE`, and a nonempty 
`s`, SQL has `M=NULL` and keeps the row (`ifnull(NULL,TRUE)=TRUE`). Pair.first 
then lets `InApplyToJoin` move the nullable equality out of `markConjuncts`, 
computing M as FALSE, and the same filter rejects the row. Please restrict this 
proof to contexts where no wrapper can observe NULL (or conservatively model 
the non-mark inputs through the whole expression), and add this NULL-bearing 
result regression.
   



-- 
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