Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand Down Expand Up @@ -49,7 +50,7 @@ private Plan checkChildren(LogicalFilter<? extends Plan> filter) {
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,"
Expand All @@ -65,9 +66,9 @@ private Plan checkChildren(LogicalFilter<? extends Plan> filter) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -70,6 +73,16 @@ void testRejectsCastOnRootVariantMatch() {
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())
Expand All @@ -79,6 +92,48 @@ void testAllowsVariantSubcolumnMatch() {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,6 +48,10 @@ protected void runBeforeAll() throws Exception {
+ " 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;
Expand Down Expand Up @@ -164,26 +171,55 @@ public void testExplodeOuterAccessPaths() throws Exception {
);
}

@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<>();
Expand Down
Loading