seawinde commented on code in PR #59013:
URL: https://github.com/apache/doris/pull/59013#discussion_r2622401523


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java:
##########
@@ -129,167 +124,179 @@ public static Map<Expression, ExpressionInfo> 
compensateCouldNotPullUpPredicates
     }
 
     /**
-     * compensate equivalence predicates
+     * Compensate equivalence predicates based on equivalence classes.
+     * Collects uncovered equivalence predicates into uncoveredEquals for 
residual compensation.
      */
     public static Map<Expression, ExpressionInfo> 
compensateEquivalence(StructInfo queryStructInfo,
             StructInfo viewStructInfo,
             SlotMapping viewToQuerySlotMapping,
-            ComparisonResult comparisonResult) {
+            ComparisonResult comparisonResult,
+            Set<Expression> uncoveredEquals) {
         EquivalenceClass queryEquivalenceClass = 
queryStructInfo.getEquivalenceClass();
         EquivalenceClass viewEquivalenceClass = 
viewStructInfo.getEquivalenceClass();
         Map<SlotReference, SlotReference> viewToQuerySlotMap = 
viewToQuerySlotMapping.toSlotReferenceMap();
         EquivalenceClass viewEquivalenceClassQueryBased = 
viewEquivalenceClass.permute(viewToQuerySlotMap);
         if (viewEquivalenceClassQueryBased == null) {
             return null;
         }
-        final Map<Expression, ExpressionInfo> equalCompensateConjunctions = 
new HashMap<>();
         if (queryEquivalenceClass.isEmpty() && viewEquivalenceClass.isEmpty()) 
{
             return ImmutableMap.of();
         }
-        if (queryEquivalenceClass.isEmpty() && 
!viewEquivalenceClass.isEmpty()) {
-            return null;
-        }
         EquivalenceClassMapping queryToViewEquivalenceMapping =
                 EquivalenceClassMapping.generate(queryEquivalenceClass, 
viewEquivalenceClassQueryBased);
-        // can not map all target equivalence class, can not compensate
         if (queryToViewEquivalenceMapping.getEquivalenceClassSetMap().size()
                 < viewEquivalenceClass.getEquivalenceSetList().size()) {
             return null;
         }
-        // do equal compensate
+        Map<Expression, ExpressionInfo> compensations = new HashMap<>();
         Set<List<SlotReference>> mappedQueryEquivalenceSet =
                 
queryToViewEquivalenceMapping.getEquivalenceClassSetMap().keySet();
 
         for (List<SlotReference> queryEquivalenceSet : 
queryEquivalenceClass.getEquivalenceSetList()) {
-            // compensate the equivalence in query but not in view
             if (!mappedQueryEquivalenceSet.contains(queryEquivalenceSet)) {
-                Iterator<SlotReference> iterator = 
queryEquivalenceSet.iterator();
-                SlotReference first = iterator.next();
-                while (iterator.hasNext()) {
-                    Expression equals = new EqualTo(first, iterator.next());
-                    if (equals.anyMatch(AggregateFunction.class::isInstance)) {
-                        return null;
-                    }
-                    equalCompensateConjunctions.put(equals, 
ExpressionInfo.EMPTY);
+                SlotReference first = queryEquivalenceSet.get(0);
+                for (int i = 1; i < queryEquivalenceSet.size(); i++) {
+                    uncoveredEquals.add(new EqualTo(first, 
queryEquivalenceSet.get(i)));
                 }
             } else {
-                // compensate the equivalence both in query and view, but 
query has more equivalence
                 List<SlotReference> viewEquivalenceSet =
                         
queryToViewEquivalenceMapping.getEquivalenceClassSetMap().get(queryEquivalenceSet);
-                List<SlotReference> copiedQueryEquivalenceSet = new 
ArrayList<>(queryEquivalenceSet);
-                copiedQueryEquivalenceSet.removeAll(viewEquivalenceSet);
-                SlotReference first = viewEquivalenceSet.iterator().next();
-                for (SlotReference slotReference : copiedQueryEquivalenceSet) {
-                    Expression equals = new EqualTo(first, slotReference);
+                List<SlotReference> queryExtraSlots = new 
ArrayList<>(queryEquivalenceSet);
+                queryExtraSlots.removeAll(viewEquivalenceSet);
+
+                SlotReference firstViewSlot = viewEquivalenceSet.get(0);
+                for (SlotReference extraSlot : queryExtraSlots) {
+                    Expression equals = new EqualTo(firstViewSlot, extraSlot);
                     if (equals.anyMatch(AggregateFunction.class::isInstance)) {
                         return null;
                     }
-                    equalCompensateConjunctions.put(equals, 
ExpressionInfo.EMPTY);
+                    compensations.put(equals, ExpressionInfo.EMPTY);
                 }
             }
         }
-        return equalCompensateConjunctions;
+        return compensations;
     }
 
     /**
-     * compensate range predicates
+     * Compensate range predicates.
+     * Collects uncovered range predicates into uncoveredRanges for residual 
compensation.
      */
     public static Map<Expression, ExpressionInfo> 
compensateRangePredicate(StructInfo queryStructInfo,
             StructInfo viewStructInfo,
             SlotMapping viewToQuerySlotMapping,
             ComparisonResult comparisonResult,
-            CascadesContext cascadesContext) {
+            CascadesContext cascadesContext,
+            Set<Expression> uncoveredRanges) {
         SplitPredicate querySplitPredicate = 
queryStructInfo.getSplitPredicate();
         SplitPredicate viewSplitPredicate = viewStructInfo.getSplitPredicate();
 
-        Set<Expression> viewRangeQueryBasedSet = new HashSet<>();
-        for (Expression viewExpression : 
viewSplitPredicate.getRangePredicateMap().keySet()) {
-            viewRangeQueryBasedSet.add(
-                    ExpressionUtils.replace(viewExpression, 
viewToQuerySlotMapping.toSlotReferenceMap()));
-        }
-        viewRangeQueryBasedSet.remove(BooleanLiteral.TRUE);
-
-        Set<Expression> queryRangeSet = 
querySplitPredicate.getRangePredicateMap().keySet();
-        queryRangeSet.remove(BooleanLiteral.TRUE);
+        Set<Expression> viewRangeQueryBasedSet = mapExpressionsToQueryContext(
+                viewSplitPredicate.getRangePredicateMap().keySet(), 
viewToQuerySlotMapping);
+        Set<Expression> queryRangeSet = new 
HashSet<>(querySplitPredicate.getRangePredicateMap().keySet());
 
-        Set<Expression> differentExpressions = new HashSet<>();
-        Sets.difference(queryRangeSet, 
viewRangeQueryBasedSet).copyInto(differentExpressions);
-        Sets.difference(viewRangeQueryBasedSet, 
queryRangeSet).copyInto(differentExpressions);
-        // the range predicate in query and view is same, don't need to 
compensate
-        if (differentExpressions.isEmpty()) {
-            return ImmutableMap.of();
-        }
-        // try to normalize the different expressions
-        Set<Expression> normalizedExpressions =
-                normalizeExpression(ExpressionUtils.and(differentExpressions), 
cascadesContext);
-        if (!queryRangeSet.containsAll(normalizedExpressions)) {
-            // normalized expressions is not in query, can not compensate
+        Set<Expression> mvExtraRange = Sets.difference(viewRangeQueryBasedSet, 
queryRangeSet);
+        if (!mvExtraRange.isEmpty()) {
             return null;
         }
-        Map<Expression, ExpressionInfo> normalizedExpressionsWithLiteral = new 
HashMap<>();
-        for (Expression expression : normalizedExpressions) {
-            Set<Literal> literalSet = expression.collect(expressionTreeNode -> 
expressionTreeNode instanceof Literal);
-            if (!(expression instanceof ComparisonPredicate)
-                    || (expression instanceof GreaterThan || expression 
instanceof LessThanEqual)
-                    || literalSet.size() != 1) {
-                if (expression.anyMatch(AggregateFunction.class::isInstance)) {
-                    return null;
-                }
-                normalizedExpressionsWithLiteral.put(expression, 
ExpressionInfo.EMPTY);
-                continue;
-            }
-            if (expression.anyMatch(AggregateFunction.class::isInstance)) {
-                return null;
-            }
-            normalizedExpressionsWithLiteral.put(expression, new 
ExpressionInfo(literalSet.iterator().next()));
-        }
-        return normalizedExpressionsWithLiteral;
-    }
 
-    private static Set<Expression> normalizeExpression(Expression expression, 
CascadesContext cascadesContext) {
-        ExpressionNormalization expressionNormalization = new 
ExpressionNormalization();
-        ExpressionOptimization expressionOptimization = new 
ExpressionOptimization();
-        ExpressionRewriteContext context = new 
ExpressionRewriteContext(cascadesContext);
-        expression = expressionNormalization.rewrite(expression, context);
-        expression = expressionOptimization.rewrite(expression, context);
-        return ExpressionUtils.extractConjunctionToSet(expression);
+        uncoveredRanges.addAll(Sets.difference(queryRangeSet, 
viewRangeQueryBasedSet));
+        return ImmutableMap.of();
     }
 
     /**
-     * compensate residual predicates
+     * Compensate residual predicates with extra query residuals (uncovered 
equal/range predicates).
+     * Supports OR branch matching. For example, if MV has predicate (ds >= 
'202531' OR ds = '202529')
+     * and query has (ds = '202529'), the query matches one branch of MV's OR 
predicate.
+     * Compensation NOT(ds >= '202531') is generated to exclude the extra MV 
branch.
      */
     public static Map<Expression, ExpressionInfo> 
compensateResidualPredicate(StructInfo queryStructInfo,
             StructInfo viewStructInfo,
             SlotMapping viewToQuerySlotMapping,
-            ComparisonResult comparisonResult) {
-        // TODO Residual predicates compensate, simplify implementation 
currently.
+            ComparisonResult comparisonResult,
+            Set<Expression> extraQueryResiduals) {
         SplitPredicate querySplitPredicate = 
queryStructInfo.getSplitPredicate();
         SplitPredicate viewSplitPredicate = viewStructInfo.getSplitPredicate();
 
-        Set<Expression> viewResidualQueryBasedSet = new HashSet<>();
-        for (Expression viewExpression : 
viewSplitPredicate.getResidualPredicateMap().keySet()) {
-            viewResidualQueryBasedSet.add(
-                    ExpressionUtils.replace(viewExpression, 
viewToQuerySlotMapping.toSlotReferenceMap()));
+        Set<Expression> viewResidualQueryBasedSet = 
mapExpressionsToQueryContext(
+                viewSplitPredicate.getResidualPredicateMap().keySet(), 
viewToQuerySlotMapping);
+        Set<Expression> queryResidualSet = new 
HashSet<>(querySplitPredicate.getResidualPredicateMap().keySet());
+        if (extraQueryResiduals != null) {
+            queryResidualSet.addAll(extraQueryResiduals);
         }
-        viewResidualQueryBasedSet.remove(BooleanLiteral.TRUE);
-
-        Set<Expression> queryResidualSet = 
querySplitPredicate.getResidualPredicateMap().keySet();
-        // remove unnecessary literal BooleanLiteral.TRUE
-        queryResidualSet.remove(BooleanLiteral.TRUE);
-        // query residual predicate can not contain all view residual 
predicate when view have residual predicate,
-        // bail out
-        if (!queryResidualSet.containsAll(viewResidualQueryBasedSet)) {
+
+        Set<Expression> compensations = 
coverResidualSets(viewResidualQueryBasedSet, queryResidualSet);
+        if (compensations == null) {
             return null;
         }
-        queryResidualSet.removeAll(viewResidualQueryBasedSet);
-        Map<Expression, ExpressionInfo> expressionExpressionInfoMap = new 
HashMap<>();
-        for (Expression needCompensate : queryResidualSet) {
-            if (needCompensate.anyMatch(AggregateFunction.class::isInstance)) {
+
+        Map<Expression, ExpressionInfo> compensationMap = new HashMap<>();
+        for (Expression compensation : compensations) {
+            if (compensation.anyMatch(AggregateFunction.class::isInstance)) {
                 return null;
             }
-            expressionExpressionInfoMap.put(needCompensate, 
ExpressionInfo.EMPTY);
+            compensationMap.put(compensation, ExpressionInfo.EMPTY);
+        }
+        return compensationMap;
+    }
+
+    private static Set<Expression> coverResidualSets(Set<Expression> 
viewResidualSet,
+            Set<Expression> queryResidualSet) {
+        Set<Expression> coveredQueryExprs = new HashSet<>();
+        Set<Expression> compensations = new HashSet<>();
+
+        for (Expression viewExpr : viewResidualSet) {
+            Pair<Expression, Set<Expression>> result = 
coverSingleResidual(viewExpr, queryResidualSet);
+            if (result == null) {
+                return null;
+            }
+            coveredQueryExprs.add(result.first);
+            compensations.addAll(result.second);
+        }
+
+        Set<Expression> uncoveredQueryResiduals = 
Sets.difference(queryResidualSet, coveredQueryExprs);
+        compensations.addAll(uncoveredQueryResiduals);
+        return compensations;
+    }
+
+    private static Pair<Expression, Set<Expression>> coverSingleResidual(
+            Expression viewExpr, Set<Expression> queryResidualSet) {
+        Set<Expression> mvBranches = new 
HashSet<>(ExpressionUtils.extractDisjunction(viewExpr));
+
+        for (Expression queryExpr : queryResidualSet) {
+            Set<Expression> queryBranches = new 
HashSet<>(ExpressionUtils.extractDisjunction(queryExpr));
+            Set<Expression> intersection = new HashSet<>(mvBranches);
+            intersection.retainAll(queryBranches);
+
+            if (!intersection.isEmpty()) {
+                return Pair.of(queryExpr, 
buildResidualCompensation(mvBranches, queryBranches, intersection));
+            }
+        }
+        return null;
+    }
+
+    private static Set<Expression> mapExpressionsToQueryContext(

Review Comment:
   ditto



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java:
##########
@@ -129,167 +124,179 @@ public static Map<Expression, ExpressionInfo> 
compensateCouldNotPullUpPredicates
     }
 
     /**
-     * compensate equivalence predicates
+     * Compensate equivalence predicates based on equivalence classes.
+     * Collects uncovered equivalence predicates into uncoveredEquals for 
residual compensation.
      */
     public static Map<Expression, ExpressionInfo> 
compensateEquivalence(StructInfo queryStructInfo,
             StructInfo viewStructInfo,
             SlotMapping viewToQuerySlotMapping,
-            ComparisonResult comparisonResult) {
+            ComparisonResult comparisonResult,
+            Set<Expression> uncoveredEquals) {
         EquivalenceClass queryEquivalenceClass = 
queryStructInfo.getEquivalenceClass();
         EquivalenceClass viewEquivalenceClass = 
viewStructInfo.getEquivalenceClass();
         Map<SlotReference, SlotReference> viewToQuerySlotMap = 
viewToQuerySlotMapping.toSlotReferenceMap();
         EquivalenceClass viewEquivalenceClassQueryBased = 
viewEquivalenceClass.permute(viewToQuerySlotMap);
         if (viewEquivalenceClassQueryBased == null) {
             return null;
         }
-        final Map<Expression, ExpressionInfo> equalCompensateConjunctions = 
new HashMap<>();
         if (queryEquivalenceClass.isEmpty() && viewEquivalenceClass.isEmpty()) 
{
             return ImmutableMap.of();
         }
-        if (queryEquivalenceClass.isEmpty() && 
!viewEquivalenceClass.isEmpty()) {
-            return null;
-        }
         EquivalenceClassMapping queryToViewEquivalenceMapping =
                 EquivalenceClassMapping.generate(queryEquivalenceClass, 
viewEquivalenceClassQueryBased);
-        // can not map all target equivalence class, can not compensate
         if (queryToViewEquivalenceMapping.getEquivalenceClassSetMap().size()
                 < viewEquivalenceClass.getEquivalenceSetList().size()) {
             return null;
         }
-        // do equal compensate
+        Map<Expression, ExpressionInfo> compensations = new HashMap<>();
         Set<List<SlotReference>> mappedQueryEquivalenceSet =
                 
queryToViewEquivalenceMapping.getEquivalenceClassSetMap().keySet();
 
         for (List<SlotReference> queryEquivalenceSet : 
queryEquivalenceClass.getEquivalenceSetList()) {
-            // compensate the equivalence in query but not in view
             if (!mappedQueryEquivalenceSet.contains(queryEquivalenceSet)) {
-                Iterator<SlotReference> iterator = 
queryEquivalenceSet.iterator();
-                SlotReference first = iterator.next();
-                while (iterator.hasNext()) {
-                    Expression equals = new EqualTo(first, iterator.next());
-                    if (equals.anyMatch(AggregateFunction.class::isInstance)) {
-                        return null;
-                    }
-                    equalCompensateConjunctions.put(equals, 
ExpressionInfo.EMPTY);
+                SlotReference first = queryEquivalenceSet.get(0);
+                for (int i = 1; i < queryEquivalenceSet.size(); i++) {
+                    uncoveredEquals.add(new EqualTo(first, 
queryEquivalenceSet.get(i)));
                 }
             } else {
-                // compensate the equivalence both in query and view, but 
query has more equivalence
                 List<SlotReference> viewEquivalenceSet =
                         
queryToViewEquivalenceMapping.getEquivalenceClassSetMap().get(queryEquivalenceSet);
-                List<SlotReference> copiedQueryEquivalenceSet = new 
ArrayList<>(queryEquivalenceSet);
-                copiedQueryEquivalenceSet.removeAll(viewEquivalenceSet);
-                SlotReference first = viewEquivalenceSet.iterator().next();
-                for (SlotReference slotReference : copiedQueryEquivalenceSet) {
-                    Expression equals = new EqualTo(first, slotReference);
+                List<SlotReference> queryExtraSlots = new 
ArrayList<>(queryEquivalenceSet);
+                queryExtraSlots.removeAll(viewEquivalenceSet);
+
+                SlotReference firstViewSlot = viewEquivalenceSet.get(0);
+                for (SlotReference extraSlot : queryExtraSlots) {
+                    Expression equals = new EqualTo(firstViewSlot, extraSlot);
                     if (equals.anyMatch(AggregateFunction.class::isInstance)) {
                         return null;
                     }
-                    equalCompensateConjunctions.put(equals, 
ExpressionInfo.EMPTY);
+                    compensations.put(equals, ExpressionInfo.EMPTY);
                 }
             }
         }
-        return equalCompensateConjunctions;
+        return compensations;
     }
 
     /**
-     * compensate range predicates
+     * Compensate range predicates.
+     * Collects uncovered range predicates into uncoveredRanges for residual 
compensation.
      */
     public static Map<Expression, ExpressionInfo> 
compensateRangePredicate(StructInfo queryStructInfo,
             StructInfo viewStructInfo,
             SlotMapping viewToQuerySlotMapping,
             ComparisonResult comparisonResult,
-            CascadesContext cascadesContext) {
+            CascadesContext cascadesContext,
+            Set<Expression> uncoveredRanges) {
         SplitPredicate querySplitPredicate = 
queryStructInfo.getSplitPredicate();
         SplitPredicate viewSplitPredicate = viewStructInfo.getSplitPredicate();
 
-        Set<Expression> viewRangeQueryBasedSet = new HashSet<>();
-        for (Expression viewExpression : 
viewSplitPredicate.getRangePredicateMap().keySet()) {
-            viewRangeQueryBasedSet.add(
-                    ExpressionUtils.replace(viewExpression, 
viewToQuerySlotMapping.toSlotReferenceMap()));
-        }
-        viewRangeQueryBasedSet.remove(BooleanLiteral.TRUE);
-
-        Set<Expression> queryRangeSet = 
querySplitPredicate.getRangePredicateMap().keySet();
-        queryRangeSet.remove(BooleanLiteral.TRUE);
+        Set<Expression> viewRangeQueryBasedSet = mapExpressionsToQueryContext(
+                viewSplitPredicate.getRangePredicateMap().keySet(), 
viewToQuerySlotMapping);
+        Set<Expression> queryRangeSet = new 
HashSet<>(querySplitPredicate.getRangePredicateMap().keySet());
 
-        Set<Expression> differentExpressions = new HashSet<>();
-        Sets.difference(queryRangeSet, 
viewRangeQueryBasedSet).copyInto(differentExpressions);
-        Sets.difference(viewRangeQueryBasedSet, 
queryRangeSet).copyInto(differentExpressions);
-        // the range predicate in query and view is same, don't need to 
compensate
-        if (differentExpressions.isEmpty()) {
-            return ImmutableMap.of();
-        }
-        // try to normalize the different expressions
-        Set<Expression> normalizedExpressions =
-                normalizeExpression(ExpressionUtils.and(differentExpressions), 
cascadesContext);
-        if (!queryRangeSet.containsAll(normalizedExpressions)) {
-            // normalized expressions is not in query, can not compensate
+        Set<Expression> mvExtraRange = Sets.difference(viewRangeQueryBasedSet, 
queryRangeSet);
+        if (!mvExtraRange.isEmpty()) {
             return null;
         }
-        Map<Expression, ExpressionInfo> normalizedExpressionsWithLiteral = new 
HashMap<>();
-        for (Expression expression : normalizedExpressions) {
-            Set<Literal> literalSet = expression.collect(expressionTreeNode -> 
expressionTreeNode instanceof Literal);
-            if (!(expression instanceof ComparisonPredicate)
-                    || (expression instanceof GreaterThan || expression 
instanceof LessThanEqual)
-                    || literalSet.size() != 1) {
-                if (expression.anyMatch(AggregateFunction.class::isInstance)) {
-                    return null;
-                }
-                normalizedExpressionsWithLiteral.put(expression, 
ExpressionInfo.EMPTY);
-                continue;
-            }
-            if (expression.anyMatch(AggregateFunction.class::isInstance)) {
-                return null;
-            }
-            normalizedExpressionsWithLiteral.put(expression, new 
ExpressionInfo(literalSet.iterator().next()));
-        }
-        return normalizedExpressionsWithLiteral;
-    }
 
-    private static Set<Expression> normalizeExpression(Expression expression, 
CascadesContext cascadesContext) {
-        ExpressionNormalization expressionNormalization = new 
ExpressionNormalization();
-        ExpressionOptimization expressionOptimization = new 
ExpressionOptimization();
-        ExpressionRewriteContext context = new 
ExpressionRewriteContext(cascadesContext);
-        expression = expressionNormalization.rewrite(expression, context);
-        expression = expressionOptimization.rewrite(expression, context);
-        return ExpressionUtils.extractConjunctionToSet(expression);
+        uncoveredRanges.addAll(Sets.difference(queryRangeSet, 
viewRangeQueryBasedSet));
+        return ImmutableMap.of();
     }
 
     /**
-     * compensate residual predicates
+     * Compensate residual predicates with extra query residuals (uncovered 
equal/range predicates).
+     * Supports OR branch matching. For example, if MV has predicate (ds >= 
'202531' OR ds = '202529')
+     * and query has (ds = '202529'), the query matches one branch of MV's OR 
predicate.
+     * Compensation NOT(ds >= '202531') is generated to exclude the extra MV 
branch.
      */
     public static Map<Expression, ExpressionInfo> 
compensateResidualPredicate(StructInfo queryStructInfo,
             StructInfo viewStructInfo,
             SlotMapping viewToQuerySlotMapping,
-            ComparisonResult comparisonResult) {
-        // TODO Residual predicates compensate, simplify implementation 
currently.
+            ComparisonResult comparisonResult,
+            Set<Expression> extraQueryResiduals) {
         SplitPredicate querySplitPredicate = 
queryStructInfo.getSplitPredicate();
         SplitPredicate viewSplitPredicate = viewStructInfo.getSplitPredicate();
 
-        Set<Expression> viewResidualQueryBasedSet = new HashSet<>();
-        for (Expression viewExpression : 
viewSplitPredicate.getResidualPredicateMap().keySet()) {
-            viewResidualQueryBasedSet.add(
-                    ExpressionUtils.replace(viewExpression, 
viewToQuerySlotMapping.toSlotReferenceMap()));
+        Set<Expression> viewResidualQueryBasedSet = 
mapExpressionsToQueryContext(
+                viewSplitPredicate.getResidualPredicateMap().keySet(), 
viewToQuerySlotMapping);
+        Set<Expression> queryResidualSet = new 
HashSet<>(querySplitPredicate.getResidualPredicateMap().keySet());
+        if (extraQueryResiduals != null) {
+            queryResidualSet.addAll(extraQueryResiduals);
         }
-        viewResidualQueryBasedSet.remove(BooleanLiteral.TRUE);
-
-        Set<Expression> queryResidualSet = 
querySplitPredicate.getResidualPredicateMap().keySet();
-        // remove unnecessary literal BooleanLiteral.TRUE
-        queryResidualSet.remove(BooleanLiteral.TRUE);
-        // query residual predicate can not contain all view residual 
predicate when view have residual predicate,
-        // bail out
-        if (!queryResidualSet.containsAll(viewResidualQueryBasedSet)) {
+
+        Set<Expression> compensations = 
coverResidualSets(viewResidualQueryBasedSet, queryResidualSet);
+        if (compensations == null) {
             return null;
         }
-        queryResidualSet.removeAll(viewResidualQueryBasedSet);
-        Map<Expression, ExpressionInfo> expressionExpressionInfoMap = new 
HashMap<>();
-        for (Expression needCompensate : queryResidualSet) {
-            if (needCompensate.anyMatch(AggregateFunction.class::isInstance)) {
+
+        Map<Expression, ExpressionInfo> compensationMap = new HashMap<>();
+        for (Expression compensation : compensations) {
+            if (compensation.anyMatch(AggregateFunction.class::isInstance)) {
                 return null;
             }
-            expressionExpressionInfoMap.put(needCompensate, 
ExpressionInfo.EMPTY);
+            compensationMap.put(compensation, ExpressionInfo.EMPTY);
+        }
+        return compensationMap;
+    }
+
+    private static Set<Expression> coverResidualSets(Set<Expression> 
viewResidualSet,
+            Set<Expression> queryResidualSet) {
+        Set<Expression> coveredQueryExprs = new HashSet<>();
+        Set<Expression> compensations = new HashSet<>();
+
+        for (Expression viewExpr : viewResidualSet) {
+            Pair<Expression, Set<Expression>> result = 
coverSingleResidual(viewExpr, queryResidualSet);
+            if (result == null) {
+                return null;
+            }
+            coveredQueryExprs.add(result.first);
+            compensations.addAll(result.second);
+        }
+
+        Set<Expression> uncoveredQueryResiduals = 
Sets.difference(queryResidualSet, coveredQueryExprs);
+        compensations.addAll(uncoveredQueryResiduals);
+        return compensations;
+    }
+
+    private static Pair<Expression, Set<Expression>> coverSingleResidual(
+            Expression viewExpr, Set<Expression> queryResidualSet) {
+        Set<Expression> mvBranches = new 
HashSet<>(ExpressionUtils.extractDisjunction(viewExpr));
+
+        for (Expression queryExpr : queryResidualSet) {
+            Set<Expression> queryBranches = new 
HashSet<>(ExpressionUtils.extractDisjunction(queryExpr));
+            Set<Expression> intersection = new HashSet<>(mvBranches);
+            intersection.retainAll(queryBranches);
+
+            if (!intersection.isEmpty()) {
+                return Pair.of(queryExpr, 
buildResidualCompensation(mvBranches, queryBranches, intersection));
+            }
+        }
+        return null;
+    }
+
+    private static Set<Expression> mapExpressionsToQueryContext(
+            Collection<Expression> expressions, SlotMapping 
viewToQuerySlotMapping) {
+        Set<Expression> mapped = new HashSet<>();
+        Map<SlotReference, SlotReference> slotMap = 
viewToQuerySlotMapping.toSlotReferenceMap();
+        for (Expression expr : expressions) {
+            Expression mappedExpr = ExpressionUtils.replace(expr, slotMap);
+            if (mappedExpr != BooleanLiteral.TRUE) {
+                mapped.add(mappedExpr);
+            }
+        }
+        return mapped;
+    }
+
+    private static Set<Expression> buildResidualCompensation(

Review Comment:
   ditto



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