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

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new 4369b358bc1 branch-4.2: [test](mv) Add MV residual compensation 
coverage #64051 (#68389)
4369b358bc1 is described below

commit 4369b358bc1b56cdfae7b84d227291d10166c20d
Author: yujun <[email protected]>
AuthorDate: Wed Sep 23 08:29:47 2026 +0800

    branch-4.2: [test](mv) Add MV residual compensation coverage #64051 (#68389)
    
    cherry-pick: #64051
    
    Co-authored-by: foxtail463 <[email protected]>
    Co-authored-by: yangtao555 <[email protected]>
---
 .../apache/doris/nereids/mv/PredicatesTest.java    | 234 ++++++++++++++++-----
 1 file changed, 187 insertions(+), 47 deletions(-)

diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PredicatesTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PredicatesTest.java
index a324e8e9961..4c70811dcb5 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PredicatesTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PredicatesTest.java
@@ -42,6 +42,8 @@ import java.util.stream.Collectors;
 
 /** Test the method in Predicates*/
 public class PredicatesTest extends SqlTestBase {
+    private static final String SELECT_ALL_TEST_COLUMNS =
+            "select id, score, event_date, event_time, amount, tag from T1 
where ";
 
     @Override
     protected void runBeforeAll() throws Exception {
@@ -51,7 +53,11 @@ public class PredicatesTest extends SqlTestBase {
         createTables(
                 "CREATE TABLE IF NOT EXISTS T1 (\n"
                         + "    id bigint,\n"
-                        + "    score bigint\n"
+                        + "    score bigint,\n"
+                        + "    event_date date,\n"
+                        + "    event_time datetime,\n"
+                        + "    amount decimal(10, 2),\n"
+                        + "    tag varchar(20)\n"
                         + ")\n"
                         + "DUPLICATE KEY(id)\n"
                         + "DISTRIBUTED BY HASH(id, score) BUCKETS 10\n"
@@ -61,7 +67,11 @@ public class PredicatesTest extends SqlTestBase {
                         + ")\n",
                 "CREATE TABLE IF NOT EXISTS T2 (\n"
                         + "    id bigint,\n"
-                        + "    score bigint\n"
+                        + "    score bigint,\n"
+                        + "    event_date date,\n"
+                        + "    event_time datetime,\n"
+                        + "    amount decimal(10, 2),\n"
+                        + "    tag varchar(20)\n"
                         + ")\n"
                         + "DUPLICATE KEY(id)\n"
                         + "DISTRIBUTED BY HASH(id, score) BUCKETS 10\n"
@@ -169,58 +179,146 @@ public class PredicatesTest extends SqlTestBase {
 
     @Test
     public void testResidualCompensateSupportsDnfBranchImplication() {
-        PredicateRewriteContext rewriteContext = buildRewriteContext(
-                "select id, score from T1 where id = 5 or id > 10",
-                "select id, score from T1 where id > 10 or (score = 1 and id = 
5)");
-
-        PredicateCompensation compensationCandidates = 
Predicates.collectCompensationCandidates(
-                rewriteContext.queryStructInfo,
-                rewriteContext.viewStructInfo,
-                rewriteContext.viewToQuerySlotMapping,
-                rewriteContext.comparisonResult,
-                rewriteContext.queryContext);
-        Assertions.assertNotNull(compensationCandidates);
-        Assertions.assertTrue(compensationCandidates.getEquals().isEmpty());
-        Assertions.assertTrue(compensationCandidates.getRanges().isEmpty());
-        Assertions.assertEquals(1, 
compensationCandidates.getResiduals().size());
-        assertPredicateSqlEquals(compensationCandidates.getResiduals(),
-                "OR[(id > 10),AND[(score = 1),(id = 5)]]");
-
-        PredicateCompensation finalPredicateCompensation = 
compensatePredicates(rewriteContext);
-        Assertions.assertNotNull(finalPredicateCompensation);
-        
Assertions.assertTrue(finalPredicateCompensation.getEquals().isEmpty());
-        
Assertions.assertTrue(finalPredicateCompensation.getRanges().isEmpty());
-        Assertions.assertEquals(1, 
finalPredicateCompensation.getResiduals().size());
-        assertPredicateSqlEquals(finalPredicateCompensation.getResiduals(),
+        assertResidualCompensationSucceeds(
+                "id = 5 or id > 10",
+                "id > 10 or (score = 1 and id = 5)",
                 "OR[(id > 10),AND[(score = 1),(id = 5)]]");
     }
 
     @Test
     public void 
testResidualCompensateSupportsStrongerRangeInDnfBranchImplication() {
-        PredicateRewriteContext rewriteContext = buildRewriteContext(
-                "select id, score from T1 where id > 10 or (score = 1 and id = 
5)",
-                "select id, score from T1 where id > 15 or (score = 1 and id = 
5)");
-
-        PredicateCompensation compensationCandidates = 
Predicates.collectCompensationCandidates(
-                rewriteContext.queryStructInfo,
-                rewriteContext.viewStructInfo,
-                rewriteContext.viewToQuerySlotMapping,
-                rewriteContext.comparisonResult,
-                rewriteContext.queryContext);
-        Assertions.assertNotNull(compensationCandidates);
-        Assertions.assertTrue(compensationCandidates.getEquals().isEmpty());
-        Assertions.assertTrue(compensationCandidates.getRanges().isEmpty());
-        Assertions.assertEquals(1, 
compensationCandidates.getResiduals().size());
-        assertPredicateSqlEquals(compensationCandidates.getResiduals(),
+        assertResidualCompensationSucceeds(
+                "id > 10 or (score = 1 and id = 5)",
+                "id > 15 or (score = 1 and id = 5)",
                 "OR[(id > 15),AND[(score = 1),(id = 5)]]");
+    }
 
-        PredicateCompensation finalPredicateCompensation = 
compensatePredicates(rewriteContext);
-        Assertions.assertNotNull(finalPredicateCompensation);
-        
Assertions.assertTrue(finalPredicateCompensation.getEquals().isEmpty());
-        
Assertions.assertTrue(finalPredicateCompensation.getRanges().isEmpty());
-        Assertions.assertEquals(1, 
finalPredicateCompensation.getResiduals().size());
-        assertPredicateSqlEquals(finalPredicateCompensation.getResiduals(),
-                "OR[(id > 15),AND[(score = 1),(id = 5)]]");
+    @Test
+    public void 
testResidualCompensateSupportsComparableLiteralTypesInDnfBranchImplication() {
+        String[][] cases = {
+                {
+                        "event_date >= date '2024-01-01' or score = 1",
+                        "event_date > date '2024-01-01' or score = 1"
+                },
+                {
+                        "event_time <= timestamp '2024-01-02 03:04:05' or 
score = 2",
+                        "event_time < timestamp '2024-01-02 03:04:05' or score 
= 2"
+                },
+                {
+                        "amount >= 10.50 or score = 3",
+                        "amount = 10.50 or score = 3"
+                },
+                {
+                        "tag >= 'm' or score = 4",
+                        "tag > 'm' or score = 4"
+                }
+        };
+        for (String[] testCase : cases) {
+            assertResidualCompensationSucceeds(testCase[0], testCase[1]);
+        }
+    }
+
+    @Test
+    public void 
testResidualCompensateRejectsNonImpliedComparableLiteralTypesInDnfBranchImplication()
 {
+        String[][] cases = {
+                {
+                        "event_date > date '2024-01-01' or score = 1",
+                        "event_date >= date '2024-01-01' or score = 1"
+                },
+                {
+                        "event_time < timestamp '2024-01-02 03:04:05' or score 
= 2",
+                        "event_time <= timestamp '2024-01-02 03:04:05' or 
score = 2"
+                },
+                {
+                        "amount = 10.50 or score = 3",
+                        "amount >= 10.50 or score = 3"
+                },
+                {
+                        "tag > 'm' or score = 4",
+                        "tag >= 'm' or score = 4"
+                }
+        };
+        for (String[] testCase : cases) {
+            assertResidualCompensationFails(testCase[0], testCase[1]);
+        }
+    }
+
+    @Test
+    public void 
testResidualCompensateSupportsBoundaryOperatorImplicationInDnfBranch() {
+        String[][] cases = {
+                {
+                        "id <= 10 or score = 1",
+                        "id < 10 or score = 1"
+                },
+                {
+                        "id >= 10 or score = 1",
+                        "id > 10 or score = 1"
+                },
+                {
+                        "id >= 10 or score = 1",
+                        "id = 10 or score = 1"
+                },
+                {
+                        "id <= 10 or score = 1",
+                        "id = 10 or score = 1"
+                }
+        };
+        for (String[] testCase : cases) {
+            assertResidualCompensationSucceeds(testCase[0], testCase[1]);
+        }
+    }
+
+    @Test
+    public void 
testResidualCompensateRejectsReverseBoundaryOperatorImplicationInDnfBranch() {
+        String[][] cases = {
+                {
+                        "id < 10 or score = 1",
+                        "id <= 10 or score = 1"
+                },
+                {
+                        "id > 10 or score = 1",
+                        "id >= 10 or score = 1"
+                },
+                {
+                        "id = 10 or score = 1",
+                        "id >= 10 or score = 1"
+                },
+                {
+                        "id = 10 or score = 1",
+                        "id <= 10 or score = 1"
+                }
+        };
+        for (String[] testCase : cases) {
+            assertResidualCompensationFails(testCase[0], testCase[1]);
+        }
+    }
+
+    @Test
+    public void testResidualCompensateHandlesNestedDnfBranches() {
+        // This case verifies branch-by-branch coverage for nested DNF 
predicates.
+        // The view predicate is logically:
+        //   id >= 10
+        //   OR (score = 1 AND id IN (5, 6))
+        //   OR (score = 2 AND id <= 3)
+        // Each expanded branch in the covered query predicate is contained by 
one view branch.
+        String viewPredicate = "id >= 10 "
+                + "or (score = 1 and id = 5) "
+                + "or (score = 1 and id = 6) "
+                + "or (score = 2 and id <= 3)";
+        // The covered query predicate is logically:
+        //   (id > 20 AND score IN (3, 4))
+        //   OR (score = 1 AND id IN (5, 6) AND (amount = 8.00 OR tag = 'x'))
+        //   OR (score = 2 AND id <= 3)
+        String coveredQueryPredicate = "((id > 20 and (score = 3 or score = 
4)) "
+                + "or ((score = 1 and (id = 5 or id = 6)) and (amount = 8.00 
or tag = 'x')) "
+                + "or (score = 2 and (id < 3 or id = 3)))";
+        assertResidualCompensationSucceeds(viewPredicate, 
coveredQueryPredicate);
+
+        // The extra branch only satisfies score = 1. Its id = 7 is not in the 
view's id IN (5, 6),
+        // and it does not satisfy id >= 10 or score = 2 AND id <= 3, so the 
residual is unsafe.
+        assertResidualCompensationFails(
+                viewPredicate,
+                coveredQueryPredicate + " or (score = 1 and id = 7)");
     }
 
     @Test
@@ -417,6 +515,48 @@ public class PredicatesTest extends SqlTestBase {
                 rewriteContext.queryContext);
     }
 
+    private void assertResidualCompensationSucceeds(String viewPredicate, 
String queryPredicate,
+            String... expectedResidualPredicates) {
+        PredicateRewriteContext rewriteContext = buildRewriteContext(
+                SELECT_ALL_TEST_COLUMNS + viewPredicate,
+                SELECT_ALL_TEST_COLUMNS + queryPredicate);
+        PredicateCompensation compensationCandidates = 
Predicates.collectCompensationCandidates(
+                rewriteContext.queryStructInfo,
+                rewriteContext.viewStructInfo,
+                rewriteContext.viewToQuerySlotMapping,
+                rewriteContext.comparisonResult,
+                rewriteContext.queryContext);
+        String message = "view predicate: " + viewPredicate + ", query 
predicate: " + queryPredicate;
+        Assertions.assertNotNull(compensationCandidates, message);
+        if (expectedResidualPredicates.length != 0) {
+            assertPredicateSqlEquals(compensationCandidates.getResiduals(), 
expectedResidualPredicates);
+        }
+
+        PredicateCompensation finalPredicateCompensation = 
compensatePredicates(rewriteContext);
+        Assertions.assertNotNull(finalPredicateCompensation, message);
+        if (expectedResidualPredicates.length != 0) {
+            
assertPredicateSqlEquals(finalPredicateCompensation.getResiduals(), 
expectedResidualPredicates);
+        }
+    }
+
+    private void assertResidualCompensationFails(String viewPredicate, String 
queryPredicate) {
+        PredicateRewriteContext rewriteContext = buildRewriteContext(
+                SELECT_ALL_TEST_COLUMNS + viewPredicate,
+                SELECT_ALL_TEST_COLUMNS + queryPredicate);
+        PredicateCompensation compensationCandidates = 
Predicates.collectCompensationCandidates(
+                rewriteContext.queryStructInfo,
+                rewriteContext.viewStructInfo,
+                rewriteContext.viewToQuerySlotMapping,
+                rewriteContext.comparisonResult,
+                rewriteContext.queryContext);
+        String message = "view predicate: " + viewPredicate + ", query 
predicate: " + queryPredicate;
+        Assertions.assertNotNull(compensationCandidates, message);
+        
Assertions.assertFalse(compensationCandidates.getResiduals().isEmpty(), 
message);
+
+        PredicateCompensation finalPredicateCompensation = 
compensatePredicates(rewriteContext);
+        Assertions.assertNull(finalPredicateCompensation, message);
+    }
+
     private static final class PredicateRewriteContext {
         private final StructInfo viewStructInfo;
         private final StructInfo queryStructInfo;


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

Reply via email to