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]