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

eldenmoon 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 a04dac6b6fc [fix](match) Allow MATCH on aliased variant subcolumns 
(#63772)
a04dac6b6fc is described below

commit a04dac6b6fccf2732c4ef36959d88eacdeae3845
Author: lihangyu <[email protected]>
AuthorDate: Thu May 28 14:54:03 2026 +0800

    [fix](match) Allow MATCH on aliased variant subcolumns (#63772)
    
    MATCH predicates fail for VARIANT dot subcolumn access
    such as `cast(msg.trace_id as string)`, while the equivalent bracket
    access `msg['trace_id']` works. Dot access can leave an `Alias` around
    the pruned subcolumn slot, and `CheckMatchExpression` rejected the
    aliased slot.
    
    Fix MATCH predicates on VARIANT dot subcolumn access such as
    `msg.trace_id` so they are accepted like equivalent bracket subcolumn
    access.
---
 .../rules/rewrite/CheckMatchExpression.java        |  7 +--
 .../rules/rewrite/CheckMatchExpressionTest.java    | 55 ++++++++++++++++++++
 .../rules/rewrite/VariantPruningLogicTest.java     | 58 ++++++++++++++++++----
 3 files changed, 106 insertions(+), 14 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpression.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpression.java
index feaad409995..8f0ad8706f3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpression.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpression.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.rules.rewrite;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Match;
@@ -49,7 +50,7 @@ public class CheckMatchExpression extends 
OneRewriteRuleFactory {
         for (Expression expr : expressions) {
             if (expr instanceof Match) {
                 Match matchExpression = (Match) expr;
-                SlotReference slotReference = 
getSlotFromSlotOrCastChain(matchExpression.left());
+                SlotReference slotReference = 
getSlotFromSlotCastOrAliasChain(matchExpression.left());
                 if (slotReference == null
                         || !(matchExpression.right() instanceof Literal)) {
                     throw new AnalysisException(String.format("Only support 
match left operand is SlotRef,"
@@ -65,9 +66,9 @@ public class CheckMatchExpression extends 
OneRewriteRuleFactory {
         return filter;
     }
 
-    private SlotReference getSlotFromSlotOrCastChain(Expression expression) {
+    private SlotReference getSlotFromSlotCastOrAliasChain(Expression 
expression) {
         Expression current = expression;
-        while (current instanceof Cast) {
+        while (current instanceof Cast || current instanceof Alias) {
             current = current.child(0);
         }
         return current instanceof SlotReference ? (SlotReference) current : 
null;
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpressionTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpressionTest.java
index fbaa2d97a12..c1991a08aa7 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpressionTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpressionTest.java
@@ -18,10 +18,13 @@
 package org.apache.doris.nereids.rules.rewrite;
 
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Add;
+import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.MatchAny;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
@@ -70,6 +73,16 @@ class CheckMatchExpressionTest {
                 exception.getMessage());
     }
 
+    @Test
+    void testRejectsAliasOnRootVariantMatch() {
+        SlotReference rootVariantSlot = new SlotReference("response", 
VariantType.INSTANCE, true, Arrays.asList());
+        MatchAny match = new MatchAny(new Alias(rootVariantSlot, 
"response.trace_id"), new StringLiteral("doris"));
+
+        AnalysisException exception = 
Assertions.assertThrows(AnalysisException.class, () -> invokeCheck(match));
+        Assertions.assertTrue(exception.getMessage().contains("VARIANT root 
column does not support MATCH"),
+                exception.getMessage());
+    }
+
     @Test
     void testAllowsVariantSubcolumnMatch() {
         SlotReference variantSubcolumnSlot = new SlotReference("response", 
VariantType.INSTANCE, true, Arrays.asList())
@@ -79,6 +92,48 @@ class CheckMatchExpressionTest {
         Assertions.assertDoesNotThrow(() -> invokeCheck(match));
     }
 
+    @Test
+    void testAllowsAliasOnVariantSubcolumnMatch() {
+        SlotReference variantSubcolumnSlot = new SlotReference("response", 
VariantType.INSTANCE, true, Arrays.asList())
+                .withSubPath(Arrays.asList("trace_id"));
+        MatchAny match = new MatchAny(new Alias(variantSubcolumnSlot, 
"response.trace_id"),
+                new StringLiteral("doris"));
+
+        Assertions.assertDoesNotThrow(() -> invokeCheck(match));
+    }
+
+    @Test
+    void testAllowsCastOnAliasVariantSubcolumnMatch() {
+        SlotReference variantSubcolumnSlot = new SlotReference("response", 
VariantType.INSTANCE, true, Arrays.asList())
+                .withSubPath(Arrays.asList("trace_id"));
+        MatchAny match = new MatchAny(new Cast(new Alias(variantSubcolumnSlot, 
"response.trace_id"),
+                StringType.INSTANCE), new StringLiteral("doris"));
+
+        Assertions.assertDoesNotThrow(() -> invokeCheck(match));
+    }
+
+    @Test
+    void testAllowsAliasAndCastChainOnVariantSubcolumnMatch() {
+        SlotReference variantSubcolumnSlot = new SlotReference("response", 
VariantType.INSTANCE, true, Arrays.asList())
+                .withSubPath(Arrays.asList("trace_id"));
+        Expression left = new Cast(new Alias(new Cast(variantSubcolumnSlot, 
StringType.INSTANCE),
+                "response.trace_id"), StringType.INSTANCE);
+        MatchAny match = new MatchAny(left, new StringLiteral("doris"));
+
+        Assertions.assertDoesNotThrow(() -> invokeCheck(match));
+    }
+
+    @Test
+    void testRejectsAliasOnExpressionMatch() {
+        Expression aliasOnExpression = new Alias(new Add(new 
IntegerLiteral(1), new IntegerLiteral(2)),
+                "response.trace_id");
+        MatchAny match = new MatchAny(aliasOnExpression, new 
StringLiteral("doris"));
+
+        AnalysisException exception = 
Assertions.assertThrows(AnalysisException.class, () -> invokeCheck(match));
+        Assertions.assertTrue(exception.getMessage().contains("Only support 
match left operand is SlotRef"),
+                exception.getMessage());
+    }
+
     private void invokeCheck(Expression expression) throws Throwable {
         LogicalOlapScan scan = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
         LogicalFilter<LogicalOlapScan> filter = new 
LogicalFilter<>(ImmutableSet.of(expression), scan);
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/VariantPruningLogicTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/VariantPruningLogicTest.java
index 7a7b4b9ae4a..480c98b76e2 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/VariantPruningLogicTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/VariantPruningLogicTest.java
@@ -18,7 +18,10 @@
 package org.apache.doris.nereids.rules.rewrite;
 
 import org.apache.doris.analysis.ColumnAccessPath;
+import org.apache.doris.analysis.Expr;
+import org.apache.doris.analysis.MatchPredicate;
 import org.apache.doris.analysis.SlotDescriptor;
+import org.apache.doris.analysis.SlotRef;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.NereidsPlanner;
@@ -45,6 +48,10 @@ public class VariantPruningLogicTest extends 
TestWithFeService {
                 + "  id int,\n"
                 + "  v variant\n"
                 + ") properties ('replication_num'='1')");
+        createTable("create table variant_msg_tbl(\n"
+                + "  id int,\n"
+                + "  msg variant\n"
+                + ") properties ('replication_num'='1')");
         
connectContext.getSessionVariable().setDisableNereidsRules(RuleType.PRUNE_EMPTY_PARTITION.name());
         connectContext.getSessionVariable().enableNereidsTimeout = false;
         connectContext.getSessionVariable().enablePruneNestedColumns = true;
@@ -164,26 +171,55 @@ public class VariantPruningLogicTest extends 
TestWithFeService {
         );
     }
 
+    @Test
+    public void testMatchOnDotVariantSubColumnUsesSlotRefInScanPredicate() 
throws Exception {
+        String sql = "select id from variant_msg_tbl "
+                + "where cast(msg.trace_id as string) match_phrase_prefix 
'abc'";
+        List<OlapScanNode> olapScanNodes = collectOlapScanNodes(sql);
+        Assertions.assertEquals(1, olapScanNodes.size());
+
+        List<MatchPredicate> matchPredicates = new ArrayList<>();
+        Expr.collectList(olapScanNodes.get(0).getConjuncts(), 
MatchPredicate.class, matchPredicates);
+        Assertions.assertEquals(1, matchPredicates.size());
+
+        Expr leftWithoutCast = matchPredicates.get(0).getChildWithoutCast(0);
+        Assertions.assertInstanceOf(SlotRef.class, leftWithoutCast, 
matchPredicates.get(0).toString());
+        SlotRef leftSlot = (SlotRef) leftWithoutCast;
+        Assertions.assertEquals(ImmutableList.of("trace_id"), 
leftSlot.getDesc().getSubColLables());
+    }
+
     private Pair<PhysicalPlan, List<SlotDescriptor>> 
collectVariantSlots(String sql) throws Exception {
-        NereidsPlanner planner = (NereidsPlanner) 
executeNereidsSql(sql).planner();
+        NereidsPlanner planner = plan(sql);
         List<SlotDescriptor> variantSlots = new ArrayList<>();
         PhysicalPlan physicalPlan = planner.getPhysicalPlan();
-        for (PlanFragment fragment : planner.getFragments()) {
-            List<OlapScanNode> olapScanNodes =
-                    
fragment.getPlanRoot().collectInCurrentFragment(OlapScanNode.class::isInstance);
-            for (OlapScanNode olapScanNode : olapScanNodes) {
-                List<SlotDescriptor> slots = 
olapScanNode.getTupleDesc().getSlots();
-                for (SlotDescriptor slot : slots) {
-                    Type type = slot.getType();
-                    if (type.isVariantType()) {
-                        variantSlots.add(slot);
-                    }
+        for (OlapScanNode olapScanNode : collectOlapScanNodes(planner)) {
+            List<SlotDescriptor> slots = 
olapScanNode.getTupleDesc().getSlots();
+            for (SlotDescriptor slot : slots) {
+                Type type = slot.getType();
+                if (type.isVariantType()) {
+                    variantSlots.add(slot);
                 }
             }
         }
         return Pair.of(physicalPlan, variantSlots);
     }
 
+    private List<OlapScanNode> collectOlapScanNodes(String sql) throws 
Exception {
+        return collectOlapScanNodes(plan(sql));
+    }
+
+    private List<OlapScanNode> collectOlapScanNodes(NereidsPlanner planner) {
+        List<OlapScanNode> olapScanNodes = new ArrayList<>();
+        for (PlanFragment fragment : planner.getFragments()) {
+            
olapScanNodes.addAll(fragment.getPlanRoot().collectInCurrentFragment(OlapScanNode.class::isInstance));
+        }
+        return olapScanNodes;
+    }
+
+    private NereidsPlanner plan(String sql) throws Exception {
+        return (NereidsPlanner) executeNereidsSql(sql).planner();
+    }
+
     private void assertVariantSubColumnSlots(String sql, List<List<String>> 
expectedSubColPaths) throws Exception {
         Pair<PhysicalPlan, List<SlotDescriptor>> result = 
collectVariantSlots(sql);
         TreeSet<String> actualSubColPaths = new TreeSet<>();


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

Reply via email to