This is an automated email from the ASF dual-hosted git repository. jakevin 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 58e7ad82b50 [feature](Nereids): support infer join when comapring mv (#28988) 58e7ad82b50 is described below commit 58e7ad82b504897882b90d21cbe0aeef94186c7f Author: 谢健 <jianx...@gmail.com> AuthorDate: Wed Dec 27 10:40:44 2023 +0800 [feature](Nereids): support infer join when comapring mv (#28988) --- .../jobs/joinorder/hypergraph/HyperGraph.java | 171 +----------- .../jobs/joinorder/hypergraph/edge/Edge.java | 40 ++- .../jobs/joinorder/hypergraph/edge/JoinEdge.java | 12 + .../rules/exploration/mv/ComparisonResult.java | 41 ++- .../rules/exploration/mv/HyperGraphComparator.java | 299 +++++++++++++++++++++ .../nereids/rules/exploration/mv/StructInfo.java | 3 +- .../nereids/trees/plans/logical/LogicalJoin.java | 5 + .../joinorder/hypergraph/CompareOuterJoinTest.java | 25 +- ...ompareOuterJoinTest.java => InferJoinTest.java} | 105 +++----- .../joinorder/hypergraph/PullupExpressionTest.java | 9 +- .../rules/exploration/mv/BuildStructInfoTest.java | 4 +- 11 files changed, 444 insertions(+), 270 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java index 7bd33c64b3c..f094efe7180 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java @@ -27,8 +27,6 @@ import org.apache.doris.nereids.jobs.joinorder.hypergraph.node.DPhyperNode; import org.apache.doris.nereids.jobs.joinorder.hypergraph.node.StructInfoNode; import org.apache.doris.nereids.memo.Group; import org.apache.doris.nereids.memo.GroupExpression; -import org.apache.doris.nereids.rules.exploration.mv.ComparisonResult; -import org.apache.doris.nereids.rules.exploration.mv.LogicalCompatibilityContext; import org.apache.doris.nereids.rules.rewrite.PushDownFilterThroughJoin; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.Expression; @@ -44,20 +42,14 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalProject; import org.apache.doris.nereids.util.PlanUtils; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -272,11 +264,11 @@ public class HyperGraph { filterEdges.forEach(e -> { if (LongBitmap.isSubset(e.getReferenceNodes(), leftSubNodes) && !PushDownFilterThroughJoin.COULD_PUSH_THROUGH_LEFT.contains(joinEdge.getJoinType())) { - e.addRejectEdge(joinEdge); + e.addLeftRejectEdge(joinEdge); } if (LongBitmap.isSubset(e.getReferenceNodes(), rightSubNodes) && !PushDownFilterThroughJoin.COULD_PUSH_THROUGH_RIGHT.contains(joinEdge.getJoinType())) { - e.addRejectEdge(joinEdge); + e.addRightRejectEdge(joinEdge); } }); } @@ -293,11 +285,11 @@ public class HyperGraph { JoinEdge childA = joinEdges.get(i); if (!JoinType.isAssoc(childA.getJoinType(), edgeB.getJoinType())) { leftRequired = LongBitmap.newBitmapUnion(leftRequired, childA.getLeftSubNodes(joinEdges)); - childA.addRejectEdge(edgeB); + childA.addLeftRejectEdge(edgeB); } if (!JoinType.isLAssoc(childA.getJoinType(), edgeB.getJoinType())) { leftRequired = LongBitmap.newBitmapUnion(leftRequired, childA.getRightSubNodes(joinEdges)); - childA.addRejectEdge(edgeB); + childA.addLeftRejectEdge(edgeB); } } @@ -305,11 +297,11 @@ public class HyperGraph { JoinEdge childA = joinEdges.get(i); if (!JoinType.isAssoc(edgeB.getJoinType(), childA.getJoinType())) { rightRequired = LongBitmap.newBitmapUnion(rightRequired, childA.getRightSubNodes(joinEdges)); - childA.addRejectEdge(edgeB); + childA.addRightRejectEdge(edgeB); } if (!JoinType.isRAssoc(edgeB.getJoinType(), childA.getJoinType())) { rightRequired = LongBitmap.newBitmapUnion(rightRequired, childA.getLeftSubNodes(joinEdges)); - childA.addRejectEdge(edgeB); + childA.addRightRejectEdge(edgeB); } } edgeB.setLeftExtendedNodes(leftRequired); @@ -597,157 +589,6 @@ public class HyperGraph { return joinEdges.size() + filterEdges.size(); } - /** - * compare hypergraph - * - * @param viewHG the compared hyper graph - * @return Comparison result - */ - public ComparisonResult isLogicCompatible(HyperGraph viewHG, LogicalCompatibilityContext ctx) { - // 1 try to construct a map which can be mapped from edge to edge - Map<Edge, Edge> queryToView = constructMapWithNode(viewHG, ctx.getQueryToViewNodeIDMapping()); - - // 2. compare them by expression and extract residual expr - ComparisonResult.Builder builder = new ComparisonResult.Builder(); - ComparisonResult edgeCompareRes = compareEdgesWithExpr(queryToView, ctx.getQueryToViewEdgeExpressionMapping()); - if (edgeCompareRes.isInvalid()) { - return ComparisonResult.INVALID; - } - builder.addComparisonResult(edgeCompareRes); - - // 3. pull join edge of view is no sense, so reject them - if (!queryToView.values().containsAll(viewHG.joinEdges)) { - return ComparisonResult.INVALID; - } - - // 4. process residual edges - List<Expression> residualQueryJoin = - processOrphanEdges(Sets.difference(Sets.newHashSet(joinEdges), queryToView.keySet())); - if (residualQueryJoin == null) { - return ComparisonResult.INVALID; - } - builder.addQueryExpressions(residualQueryJoin); - - List<Expression> residualQueryFilter = - processOrphanEdges(Sets.difference(Sets.newHashSet(filterEdges), queryToView.keySet())); - if (residualQueryFilter == null) { - return ComparisonResult.INVALID; - } - builder.addQueryExpressions(residualQueryFilter); - - List<Expression> residualViewFilter = - processOrphanEdges( - Sets.difference(Sets.newHashSet(viewHG.filterEdges), Sets.newHashSet(queryToView.values()))); - if (residualViewFilter == null) { - return ComparisonResult.INVALID; - } - builder.addViewExpressions(residualViewFilter); - - return builder.build(); - } - - private List<Expression> processOrphanEdges(Set<Edge> edges) { - List<Expression> expressions = new ArrayList<>(); - for (Edge edge : edges) { - if (!edge.canPullUp()) { - return null; - } - expressions.addAll(edge.getExpressions()); - } - return expressions; - } - - private Map<Edge, Edge> constructMapWithNode(HyperGraph viewHG, Map<Integer, Integer> nodeMap) { - // TODO use hash map to reduce loop - Map<Edge, Edge> joinEdgeMap = joinEdges.stream().map(qe -> { - Optional<JoinEdge> viewEdge = viewHG.joinEdges.stream() - .filter(ve -> compareEdgeWithNode(qe, ve, nodeMap)).findFirst(); - return Pair.of(qe, viewEdge); - }).filter(e -> e.second.isPresent()).collect(ImmutableMap.toImmutableMap(p -> p.first, p -> p.second.get())); - Map<Edge, Edge> filterEdgeMap = filterEdges.stream().map(qe -> { - Optional<FilterEdge> viewEdge = viewHG.filterEdges.stream() - .filter(ve -> compareEdgeWithNode(qe, ve, nodeMap)).findFirst(); - return Pair.of(qe, viewEdge); - }).filter(e -> e.second.isPresent()).collect(ImmutableMap.toImmutableMap(p -> p.first, p -> p.second.get())); - return ImmutableMap.<Edge, Edge>builder().putAll(joinEdgeMap).putAll(filterEdgeMap).build(); - } - - private boolean compareEdgeWithNode(Edge t, Edge o, Map<Integer, Integer> nodeMap) { - if (t instanceof FilterEdge && o instanceof FilterEdge) { - return compareEdgeWithFilter((FilterEdge) t, (FilterEdge) o, nodeMap); - } else if (t instanceof JoinEdge && o instanceof JoinEdge) { - return compareJoinEdge((JoinEdge) t, (JoinEdge) o, nodeMap); - } - return false; - } - - private boolean compareEdgeWithFilter(FilterEdge t, FilterEdge o, Map<Integer, Integer> nodeMap) { - long tChild = t.getReferenceNodes(); - long oChild = o.getReferenceNodes(); - return compareNodeMap(tChild, oChild, nodeMap); - } - - private boolean compareJoinEdge(JoinEdge t, JoinEdge o, Map<Integer, Integer> nodeMap) { - long tLeft = t.getLeftExtendedNodes(); - long tRight = t.getRightExtendedNodes(); - long oLeft = o.getLeftExtendedNodes(); - long oRight = o.getRightExtendedNodes(); - if (!t.getJoinType().equals(o.getJoinType()) && !t.getJoinType().swap().equals(o.getJoinType())) { - return false; - } - boolean matched = false; - if (t.getJoinType().swap().equals(o.getJoinType())) { - matched |= compareNodeMap(tRight, oLeft, nodeMap) && compareNodeMap(tLeft, oRight, nodeMap); - } - matched |= compareNodeMap(tLeft, oLeft, nodeMap) && compareNodeMap(tRight, oRight, nodeMap); - return matched; - } - - private boolean compareNodeMap(long bitmap1, long bitmap2, Map<Integer, Integer> nodeIDMap) { - long newBitmap1 = LongBitmap.newBitmap(); - for (int i : LongBitmap.getIterator(bitmap1)) { - int mappedI = nodeIDMap.getOrDefault(i, 0); - newBitmap1 = LongBitmap.set(newBitmap1, mappedI); - } - return bitmap2 == newBitmap1; - } - - private ComparisonResult compareEdgesWithExpr(Map<Edge, Edge> queryToViewedgeMap, - Map<Expression, Expression> queryToView) { - ComparisonResult.Builder builder = new ComparisonResult.Builder(); - for (Entry<Edge, Edge> e : queryToViewedgeMap.entrySet()) { - ComparisonResult res = compareEdgeWithExpr(e.getKey(), e.getValue(), queryToView); - if (res.isInvalid()) { - return ComparisonResult.INVALID; - } - builder.addComparisonResult(res); - } - return builder.build(); - } - - private ComparisonResult compareEdgeWithExpr(Edge query, Edge view, Map<Expression, Expression> queryToView) { - Set<? extends Expression> queryExprSet = query.getExpressionSet(); - Set<? extends Expression> viewExprSet = view.getExpressionSet(); - - Set<Expression> equalViewExpr = new HashSet<>(); - List<Expression> residualQueryExpr = new ArrayList<>(); - for (Expression queryExpr : queryExprSet) { - if (queryToView.containsKey(queryExpr) && viewExprSet.contains(queryToView.get(queryExpr))) { - equalViewExpr.add(queryToView.get(queryExpr)); - } else { - residualQueryExpr.add(queryExpr); - } - } - List<Expression> residualViewExpr = ImmutableList.copyOf(Sets.difference(viewExprSet, equalViewExpr)); - if (!residualViewExpr.isEmpty() && !view.canPullUp()) { - return ComparisonResult.INVALID; - } - if (!residualQueryExpr.isEmpty() && !query.canPullUp()) { - return ComparisonResult.INVALID; - } - return new ComparisonResult(residualQueryExpr, residualViewExpr); - } - /** * For the given hyperGraph, make a textual representation in the form * of a dotty graph. You can save this to a file and then use Graphviz diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/Edge.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/Edge.java index c47300922b5..9bdb9ac272f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/Edge.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/Edge.java @@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.expressions.Slot; import com.google.common.collect.ImmutableSet; import java.util.BitSet; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -53,7 +54,8 @@ public abstract class Edge { // record all sub nodes behind in this operator. It's T function in paper private final long subTreeNodes; - private long rejectNodes = 0; + private final Set<JoinEdge> leftRejectEdges; + private final Set<JoinEdge> rightRejectEdges; /** * Create simple edge. @@ -69,14 +71,36 @@ public abstract class Edge { this.leftExtendedNodes = leftRequiredNodes; this.rightExtendedNodes = rightRequiredNodes; this.subTreeNodes = subTreeNodes; + this.leftRejectEdges = new HashSet<>(); + this.rightRejectEdges = new HashSet<>(); } public boolean isSimple() { return LongBitmap.getCardinality(leftExtendedNodes) == 1 && LongBitmap.getCardinality(rightExtendedNodes) == 1; } - public void addRejectEdge(Edge edge) { - rejectNodes = LongBitmap.newBitmapUnion(edge.getReferenceNodes(), rejectNodes); + public void addLeftRejectEdge(JoinEdge edge) { + leftRejectEdges.add(edge); + } + + public void addRightRejectEdge(JoinEdge edge) { + rightRejectEdges.add(edge); + } + + public void addLeftRejectEdges(Set<JoinEdge> edge) { + leftRejectEdges.addAll(edge); + } + + public void addRightRejectEdges(Set<JoinEdge> edge) { + rightRejectEdges.addAll(edge); + } + + public Set<JoinEdge> getLeftRejectEdge() { + return ImmutableSet.copyOf(leftRejectEdges); + } + + public Set<JoinEdge> getRightRejectEdge() { + return ImmutableSet.copyOf(rightRejectEdges); } public void addLeftExtendNode(long left) { @@ -183,16 +207,6 @@ public abstract class Edge { return ImmutableSet.copyOf(getExpressions()); } - public boolean canPullUp() { - // Only inner join and filter with none rejectNodes can be pull up - return rejectNodes == 0 - && !(this instanceof JoinEdge && !((JoinEdge) this).getJoinType().isInnerJoin()); - } - - public long getRejectNodes() { - return rejectNodes; - } - public Expression getExpression(int i) { return getExpressions().get(i); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/JoinEdge.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/JoinEdge.java index 81e80bee85f..635bf91f364 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/JoinEdge.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/JoinEdge.java @@ -45,6 +45,18 @@ public class JoinEdge extends Edge { this.join = join; } + /** + * swap the edge + */ + public JoinEdge swap() { + JoinEdge swapEdge = new + JoinEdge(join.swap(), getIndex(), getRightChildEdges(), + getLeftChildEdges(), getSubTreeNodes(), getRightRequiredNodes(), getLeftRequiredNodes()); + swapEdge.addLeftRejectEdges(getLeftRejectEdge()); + swapEdge.addRightRejectEdges(getRightRejectEdge()); + return swapEdge; + } + public JoinType getJoinType() { return join.getJoinType(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/ComparisonResult.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/ComparisonResult.java index cc8284f33e6..8836745465e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/ComparisonResult.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/ComparisonResult.java @@ -18,29 +18,36 @@ package org.apache.doris.nereids.rules.exploration.mv; import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Slot; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import java.util.Collection; import java.util.List; +import java.util.Set; /** * comparison result of view and query */ public class ComparisonResult { - public static final ComparisonResult INVALID = new ComparisonResult(ImmutableList.of(), ImmutableList.of(), false); - public static final ComparisonResult EMPTY = new ComparisonResult(ImmutableList.of(), ImmutableList.of(), true); + public static final ComparisonResult INVALID = + new ComparisonResult(ImmutableList.of(), ImmutableList.of(), ImmutableSet.of(), false); private final boolean valid; private final List<Expression> viewExpressions; private final List<Expression> queryExpressions; + private final Set<Set<Slot>> viewNoNullableSlot; - public ComparisonResult(List<Expression> queryExpressions, List<Expression> viewExpressions) { - this(queryExpressions, viewExpressions, true); + public ComparisonResult(List<Expression> queryExpressions, List<Expression> viewExpressions, + Set<Set<Slot>> viewNoNullableSlot) { + this(queryExpressions, viewExpressions, viewNoNullableSlot, true); } - ComparisonResult(List<Expression> queryExpressions, List<Expression> viewExpressions, boolean valid) { + ComparisonResult(List<Expression> queryExpressions, List<Expression> viewExpressions, + Set<Set<Slot>> viewNoNullableSlot, boolean valid) { this.viewExpressions = ImmutableList.copyOf(viewExpressions); this.queryExpressions = ImmutableList.copyOf(queryExpressions); + this.viewNoNullableSlot = ImmutableSet.copyOf(viewNoNullableSlot); this.valid = valid; } @@ -52,6 +59,10 @@ public class ComparisonResult { return queryExpressions; } + public Set<Set<Slot>> getViewNoNullableSlot() { + return viewNoNullableSlot; + } + public boolean isInvalid() { return !valid; } @@ -62,6 +73,7 @@ public class ComparisonResult { public static class Builder { ImmutableList.Builder<Expression> queryBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder<Expression> viewBuilder = new ImmutableList.Builder<>(); + ImmutableSet.Builder<Set<Slot>> viewNoNullableSlotBuilder = new ImmutableSet.Builder<>(); boolean valid = true; /** @@ -77,18 +89,31 @@ public class ComparisonResult { return this; } - public Builder addQueryExpressions(Collection<Expression> expressions) { + public Builder addQueryExpressions(Collection<? extends Expression> expressions) { queryBuilder.addAll(expressions); return this; } - public Builder addViewExpressions(Collection<Expression> expressions) { + public Builder addViewExpressions(Collection<? extends Expression> expressions) { viewBuilder.addAll(expressions); return this; } + public Builder addViewNoNullableSlot(Set<Slot> viewNoNullableSlot) { + viewNoNullableSlotBuilder.add(ImmutableSet.copyOf(viewNoNullableSlot)); + return this; + } + + public boolean isInvalid() { + return !valid; + } + public ComparisonResult build() { - return new ComparisonResult(queryBuilder.build(), viewBuilder.build(), valid); + if (isInvalid()) { + return ComparisonResult.INVALID; + } + return new ComparisonResult(queryBuilder.build(), viewBuilder.build(), + viewNoNullableSlotBuilder.build(), valid); } } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphComparator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphComparator.java new file mode 100644 index 00000000000..a869fe729a9 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphComparator.java @@ -0,0 +1,299 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.rules.exploration.mv; + +import org.apache.doris.common.Pair; +import org.apache.doris.nereids.jobs.joinorder.hypergraph.HyperGraph; +import org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.LongBitmap; +import org.apache.doris.nereids.jobs.joinorder.hypergraph.edge.Edge; +import org.apache.doris.nereids.jobs.joinorder.hypergraph.edge.FilterEdge; +import org.apache.doris.nereids.jobs.joinorder.hypergraph.edge.JoinEdge; +import org.apache.doris.nereids.rules.rewrite.PushDownFilterThroughJoin; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.plans.JoinType; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; + +/** + * HyperGraphComparator + */ +public class HyperGraphComparator { + // This second join can be inferred to the first join by map value, + // The map value means the child's should be no-nullable + static Map<Pair<JoinType, JoinType>, Pair<Boolean, Boolean>> canInferredJoinTypeMap = ImmutableMap + .<Pair<JoinType, JoinType>, Pair<Boolean, Boolean>>builder() + .put(Pair.of(JoinType.LEFT_SEMI_JOIN, JoinType.INNER_JOIN), Pair.of(false, false)) + .put(Pair.of(JoinType.RIGHT_SEMI_JOIN, JoinType.INNER_JOIN), Pair.of(false, false)) + .put(Pair.of(JoinType.INNER_JOIN, JoinType.LEFT_OUTER_JOIN), Pair.of(false, true)) + .put(Pair.of(JoinType.INNER_JOIN, JoinType.RIGHT_OUTER_JOIN), Pair.of(true, false)) + .put(Pair.of(JoinType.INNER_JOIN, JoinType.FULL_OUTER_JOIN), Pair.of(true, true)) + .put(Pair.of(JoinType.LEFT_OUTER_JOIN, JoinType.FULL_OUTER_JOIN), Pair.of(true, false)) + .put(Pair.of(JoinType.RIGHT_OUTER_JOIN, JoinType.FULL_OUTER_JOIN), Pair.of(false, true)) + .build(); + + // record inferred edges when comparing mv + private final HyperGraph queryHyperGraph; + private final HyperGraph viewHyperGraph; + private final Map<JoinEdge, Pair<JoinType, Set<Slot>>> inferredViewEdgeMap = new HashMap<>(); + private final Map<Edge, List<? extends Expression>> pullUpQueryExprWithEdge = new HashMap<>(); + private final Map<Edge, List<? extends Expression>> pullUpViewExprWithEdge = new HashMap<>(); + private final LogicalCompatibilityContext logicalCompatibilityContext; + + HyperGraphComparator(HyperGraph queryHyperGraph, HyperGraph viewHyperGraph, + LogicalCompatibilityContext logicalCompatibilityContext) { + this.queryHyperGraph = queryHyperGraph; + this.viewHyperGraph = viewHyperGraph; + this.logicalCompatibilityContext = logicalCompatibilityContext; + } + + /** + * compare hypergraph + * + * @param viewHG the compared hyper graph + * @return Comparison result + */ + public static ComparisonResult isLogicCompatible(HyperGraph queryHG, HyperGraph viewHG, + LogicalCompatibilityContext ctx) { + return new HyperGraphComparator(queryHG, viewHG, ctx).isLogicCompatible(); + } + + private ComparisonResult isLogicCompatible() { + // 1 try to construct a map which can be mapped from edge to edge + Map<Edge, Edge> queryToView = constructMapWithNode(); + + // 2. compare them by expression and extract residual expr + queryToView.forEach(this::compareEdgeWithExpr); + + // 3. process residual edges + Sets.difference(getQueryJoinEdgeSet(), queryToView.keySet()) + .forEach(e -> pullUpQueryExprWithEdge.put(e, e.getExpressions())); + Sets.difference(getQueryFilterEdgeSet(), queryToView.keySet()) + .forEach(e -> pullUpQueryExprWithEdge.put(e, e.getExpressions())); + Sets.difference(getViewJoinEdgeSet(), Sets.newHashSet(queryToView.values())) + .forEach(e -> pullUpViewExprWithEdge.put(e, e.getExpressions())); + Sets.difference(getViewFilterEdgeSet(), Sets.newHashSet(queryToView.values())) + .forEach(e -> pullUpViewExprWithEdge.put(e, e.getExpressions())); + + return buildComparisonRes(); + } + + private ComparisonResult buildComparisonRes() { + ComparisonResult.Builder builder = new ComparisonResult.Builder(); + for (Entry<Edge, List<? extends Expression>> e : pullUpQueryExprWithEdge.entrySet()) { + if (!e.getValue().isEmpty() && !canPullUp(e.getKey())) { + return ComparisonResult.INVALID; + } + builder.addQueryExpressions(e.getValue()); + } + for (Entry<Edge, List<? extends Expression>> e : pullUpViewExprWithEdge.entrySet()) { + if (!e.getValue().isEmpty() && !canPullUp(e.getKey())) { + return ComparisonResult.INVALID; + } + builder.addViewExpressions(e.getValue()); + } + for (Pair<JoinType, Set<Slot>> inferredCond : inferredViewEdgeMap.values()) { + builder.addViewNoNullableSlot(inferredCond.second); + } + return builder.build(); + } + + private boolean canPullUp(Edge edge) { + // Only inner join and filter with none rejectNodes can be pull up + if (edge instanceof JoinEdge && !((JoinEdge) edge).getJoinType().isInnerJoin()) { + return false; + } + boolean pullFromLeft = edge.getLeftRejectEdge().stream() + .map(e -> inferredViewEdgeMap.getOrDefault(e, Pair.of(e.getJoinType(), null))) + .allMatch(e -> canPullFromLeft(edge, e.first)); + boolean pullFromRight = edge.getRightRejectEdge().stream() + .map(e -> inferredViewEdgeMap.getOrDefault(e, Pair.of(e.getJoinType(), null))) + .allMatch(e -> canPullFromRight(edge, e.first)); + return pullFromLeft && pullFromRight; + } + + private boolean canPullFromLeft(Edge bottomEdge, JoinType topJoinType) { + if (bottomEdge instanceof FilterEdge) { + return PushDownFilterThroughJoin.COULD_PUSH_THROUGH_LEFT.contains(topJoinType); + } else if (bottomEdge instanceof JoinEdge) { + return JoinType.isAssoc(((JoinEdge) bottomEdge).getJoinType(), topJoinType) + || JoinType.isLAssoc(((JoinEdge) bottomEdge).getJoinType(), topJoinType); + } + return false; + } + + private boolean canPullFromRight(Edge bottomEdge, JoinType topJoinType) { + if (bottomEdge instanceof FilterEdge) { + return PushDownFilterThroughJoin.COULD_PUSH_THROUGH_RIGHT.contains(topJoinType); + } else if (bottomEdge instanceof JoinEdge) { + return JoinType.isAssoc(topJoinType, ((JoinEdge) bottomEdge).getJoinType()) + || JoinType.isRAssoc(topJoinType, ((JoinEdge) bottomEdge).getJoinType()); + } + return false; + } + + private List<JoinEdge> getQueryJoinEdges() { + return queryHyperGraph.getJoinEdges(); + } + + private Set<JoinEdge> getQueryJoinEdgeSet() { + return ImmutableSet.copyOf(queryHyperGraph.getJoinEdges()); + } + + private List<FilterEdge> getQueryFilterEdges() { + return queryHyperGraph.getFilterEdges(); + } + + private Set<FilterEdge> getQueryFilterEdgeSet() { + return ImmutableSet.copyOf(queryHyperGraph.getFilterEdges()); + } + + private Set<FilterEdge> getViewFilterEdgeSet() { + return ImmutableSet.copyOf(viewHyperGraph.getFilterEdges()); + } + + private Set<JoinEdge> getViewJoinEdgeSet() { + return ImmutableSet.copyOf(viewHyperGraph.getJoinEdges()); + } + + private List<JoinEdge> getViewJoinEdges() { + return viewHyperGraph.getJoinEdges(); + } + + private List<FilterEdge> getViewFilterEdges() { + return viewHyperGraph.getFilterEdges(); + } + + private Map<Expression, Expression> getQueryToViewExprMap() { + return logicalCompatibilityContext.getQueryToViewEdgeExpressionMapping(); + } + + private Map<Integer, Integer> getQueryToViewNodeIdMap() { + return logicalCompatibilityContext.getQueryToViewNodeIDMapping(); + } + + private Map<Edge, Edge> constructMapWithNode() { + // TODO use hash map to reduce loop + Map<Edge, Edge> joinEdgeMap = getQueryJoinEdges().stream().map(qe -> { + Optional<JoinEdge> viewEdge = getViewJoinEdges().stream() + .filter(ve -> compareEdgeWithNode(qe, ve)).findFirst(); + return Pair.of(qe, viewEdge); + }).filter(e -> e.second.isPresent()).collect(ImmutableMap.toImmutableMap(p -> p.first, p -> p.second.get())); + Map<Edge, Edge> filterEdgeMap = getQueryFilterEdges().stream().map(qe -> { + Optional<FilterEdge> viewEdge = getViewFilterEdges().stream() + .filter(ve -> compareEdgeWithNode(qe, ve)).findFirst(); + return Pair.of(qe, viewEdge); + }).filter(e -> e.second.isPresent()).collect(ImmutableMap.toImmutableMap(p -> p.first, p -> p.second.get())); + return ImmutableMap.<Edge, Edge>builder().putAll(joinEdgeMap).putAll(filterEdgeMap).build(); + } + + private boolean compareEdgeWithNode(Edge query, Edge view) { + if (query instanceof FilterEdge && view instanceof FilterEdge) { + return compareEdgeWithFilter((FilterEdge) query, (FilterEdge) view); + } else if (query instanceof JoinEdge && view instanceof JoinEdge) { + return compareJoinEdge((JoinEdge) query, (JoinEdge) view); + } + return false; + } + + private boolean compareEdgeWithFilter(FilterEdge query, FilterEdge view) { + long qChild = query.getReferenceNodes(); + long vChild = view.getReferenceNodes(); + return rewriteQueryNodeMap(qChild) == vChild; + } + + private boolean compareJoinEdge(JoinEdge query, JoinEdge view) { + if (query.getJoinType().equals(view.getJoinType()) + || canInferredJoinTypeMap.containsKey(Pair.of(query.getJoinType(), view.getJoinType()))) { + if (tryInferEdge(query, view)) { + return true; + } + + } + + if (query.getJoinType().swap().equals(view.getJoinType()) + || canInferredJoinTypeMap.containsKey(Pair.of(query.getJoinType().swap(), view.getJoinType()))) { + if (tryInferEdge(query.swap(), view)) { + return true; + } + } + + return false; + } + + private boolean tryInferEdge(JoinEdge query, JoinEdge view) { + if (rewriteQueryNodeMap(query.getLeftExtendedNodes()) != view.getLeftExtendedNodes() + || rewriteQueryNodeMap(query.getRightExtendedNodes()) != view.getRightExtendedNodes()) { + return false; + } + if (!query.getJoinType().equals(view.getJoinType())) { + Pair<Boolean, Boolean> noNullableChild = canInferredJoinTypeMap.getOrDefault( + Pair.of(query.getJoinType(), view.getJoinType()), null); + if (noNullableChild == null) { + return false; + } + Set<Slot> noNullableSlot = Sets.union( + noNullableChild.first ? view.getJoin().left().getOutputSet() : ImmutableSet.of(), + noNullableChild.second ? view.getJoin().right().getOutputSet() : ImmutableSet.of() + ); + inferredViewEdgeMap.put(view, Pair.of(query.getJoinType(), noNullableSlot)); + } + return true; + } + + private long rewriteQueryNodeMap(long bitmap) { + long newBitmap = LongBitmap.newBitmap(); + for (int i : LongBitmap.getIterator(bitmap)) { + int newIdx = getQueryToViewNodeIdMap().getOrDefault(i, 0); + newBitmap = LongBitmap.set(newBitmap, newIdx); + } + return newBitmap; + } + + private void compareEdgeWithExpr(Edge query, Edge view) { + Set<? extends Expression> queryExprSet = query.getExpressionSet(); + Set<? extends Expression> viewExprSet = view.getExpressionSet(); + + Set<Expression> exprMappedOfView = new HashSet<>(); + List<Expression> residualQueryExpr = new ArrayList<>(); + for (Expression queryExpr : queryExprSet) { + if (getQueryToViewExprMap().containsKey(queryExpr) && viewExprSet.contains( + getQueryToViewExprMap().get(queryExpr))) { + exprMappedOfView.add(getQueryToViewExprMap().get(queryExpr)); + } else { + residualQueryExpr.add(queryExpr); + } + } + List<Expression> residualViewExpr = ImmutableList.copyOf(Sets.difference(viewExprSet, exprMappedOfView)); + pullUpQueryExprWithEdge.put(query, residualQueryExpr); + pullUpViewExprWithEdge.put(query, residualViewExpr); + } + +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java index 20da9ee12fd..2688882be97 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java @@ -265,7 +265,8 @@ public class StructInfo { */ public static ComparisonResult isGraphLogicalEquals(StructInfo queryStructInfo, StructInfo viewStructInfo, LogicalCompatibilityContext compatibilityContext) { - return queryStructInfo.hyperGraph.isLogicCompatible(viewStructInfo.hyperGraph, compatibilityContext); + return HyperGraphComparator + .isLogicCompatible(queryStructInfo.hyperGraph, viewStructInfo.hyperGraph, compatibilityContext); } private static class RelationCollector extends DefaultPlanVisitor<Void, List<CatalogRelation>> { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java index 773dbce5fc2..c729aec752f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java @@ -181,6 +181,11 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends this.markJoinSlotReference = markJoinSlotReference; } + public LogicalJoin<? extends Plan, ? extends Plan> swap() { + return withTypeChildren(getJoinType().swap(), + right(), left()); + } + public List<Expression> getOtherJoinConjuncts() { return otherJoinConjuncts; } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java index cfc88b2aa3c..105a5d450c3 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java @@ -20,6 +20,8 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph; import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.rules.RuleSet; import org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule; +import org.apache.doris.nereids.rules.exploration.mv.ComparisonResult; +import org.apache.doris.nereids.rules.exploration.mv.HyperGraphComparator; import org.apache.doris.nereids.rules.exploration.mv.LogicalCompatibilityContext; import org.apache.doris.nereids.rules.exploration.mv.StructInfo; import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping; @@ -62,7 +64,8 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - Assertions.assertFalse(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); + Assertions.assertFalse( + HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)).isInvalid()); } @Test @@ -79,7 +82,8 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - Assertions.assertFalse(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); + Assertions.assertFalse( + HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)).isInvalid()); } @Test @@ -104,9 +108,9 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List<Expression> exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); - Assertions.assertEquals(1, exprList.size()); - Assertions.assertEquals("(id = 0)", exprList.get(0).toSql()); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); + Assertions.assertEquals(1, res.getQueryExpressions().size()); + Assertions.assertEquals("(id = 0)", res.getQueryExpressions().get(0).toSql()); } @Disabled @@ -132,7 +136,7 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List<Expression> exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); + List<Expression> exprList = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)).getQueryExpressions(); Assertions.assertEquals(0, exprList.size()); } @@ -159,9 +163,9 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List<Expression> exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); - Assertions.assertEquals(1, exprList.size()); - Assertions.assertEquals("(id = 0)", exprList.get(0).toSql()); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); + Assertions.assertEquals(1, res.getQueryExpressions().size()); + Assertions.assertEquals("(id = 0)", res.getQueryExpressions().get(0).toSql()); } @Test @@ -187,7 +191,8 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - Assertions.assertTrue(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); + Assertions.assertTrue(res.isInvalid()); } LogicalCompatibilityContext constructContext(Plan p1, Plan p2) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferJoinTest.java similarity index 64% copy from fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java copy to fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferJoinTest.java index cfc88b2aa3c..33100c1180b 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferJoinTest.java @@ -20,70 +20,26 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph; import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.rules.RuleSet; import org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule; +import org.apache.doris.nereids.rules.exploration.mv.ComparisonResult; +import org.apache.doris.nereids.rules.exploration.mv.HyperGraphComparator; import org.apache.doris.nereids.rules.exploration.mv.LogicalCompatibilityContext; import org.apache.doris.nereids.rules.exploration.mv.StructInfo; import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping; import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping; import org.apache.doris.nereids.sqltest.SqlTestBase; -import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait; import org.apache.doris.nereids.trees.plans.Plan; -import org.apache.doris.nereids.util.HyperGraphBuilder; import org.apache.doris.nereids.util.PlanChecker; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import java.util.List; +import java.util.stream.Collectors; -class CompareOuterJoinTest extends SqlTestBase { +class InferJoinTest extends SqlTestBase { @Test - void testStarGraphWithInnerJoin() { - // t2 - // | - //t3-- t1 -- t4 - // | - // t5 - CascadesContext c1 = createCascadesContext( - "select * from T1, T2, T3, T4 where " - + "T1.id = T2.id " - + "and T1.id = T3.id " - + "and T1.id = T4.id ", - connectContext - ); - Plan p1 = PlanChecker.from(c1) - .analyze() - .rewrite() - .getPlan().child(0); - Plan p2 = PlanChecker.from(c1) - .analyze() - .rewrite() - .applyExploration(RuleSet.BUSHY_TREE_JOIN_REORDER) - .getAllPlan().get(0).child(0); - HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); - HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - Assertions.assertFalse(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); - } - - @Test - void testRandomQuery() { - Plan p1 = new HyperGraphBuilder().randomBuildPlanWith(3, 3); - p1 = PlanChecker.from(connectContext, p1) - .analyze() - .rewrite() - .getPlan(); - Plan p2 = PlanChecker.from(connectContext, p1) - .analyze() - .rewrite() - .applyExploration(RuleSet.BUSHY_TREE_JOIN_REORDER) - .getAllPlan().get(0); - HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); - HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - Assertions.assertFalse(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); - } - - @Test - void testInnerJoinWithFilter() { + void testInnerInferLeft() { connectContext.getSessionVariable().setDisableNereidsRules("INFER_PREDICATES"); CascadesContext c1 = createCascadesContext( "select * from T1 inner join T2 on T1.id = T2.id where T1.id = 0", @@ -94,7 +50,7 @@ class CompareOuterJoinTest extends SqlTestBase { .rewrite() .getPlan().child(0); CascadesContext c2 = createCascadesContext( - "select * from T1 inner join T2 on T1.id = T2.id", + "select * from T1 left join T2 on T1.id = T2.id where T1.id = 0", connectContext ); Plan p2 = PlanChecker.from(c2) @@ -104,14 +60,15 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List<Expression> exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); - Assertions.assertEquals(1, exprList.size()); - Assertions.assertEquals("(id = 0)", exprList.get(0).toSql()); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); + Assertions.assertFalse(res.isInvalid()); + Assertions.assertEquals(1, res.getViewNoNullableSlot().size()); + Assertions.assertEquals("[id, score]", + res.getViewNoNullableSlot().iterator().next().stream().map(ExpressionTrait::toSql).collect(Collectors.toList()).toString()); } - @Disabled @Test - void testInnerJoinWithFilter2() { + void testInnerInferLeftWithFilter() { connectContext.getSessionVariable().setDisableNereidsRules("INFER_PREDICATES"); CascadesContext c1 = createCascadesContext( "select * from T1 inner join T2 on T1.id = T2.id where T1.id = 0", @@ -122,7 +79,7 @@ class CompareOuterJoinTest extends SqlTestBase { .rewrite() .getPlan().child(0); CascadesContext c2 = createCascadesContext( - "select * from T1 inner join T2 on T1.id = T2.id where T1.id = 0", + "select * from T1 left join (select * from T2 where id = 0) T2 on T1.id = T2.id", connectContext ); Plan p2 = PlanChecker.from(c2) @@ -132,24 +89,34 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List<Expression> exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); - Assertions.assertEquals(0, exprList.size()); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); + Assertions.assertFalse(res.isInvalid()); + Assertions.assertEquals(1, res.getViewNoNullableSlot().size()); + Assertions.assertEquals("[id, score]", + res.getViewNoNullableSlot().iterator().next().stream().map(ExpressionTrait::toSql).collect(Collectors.toList()).toString()); + Assertions.assertEquals("(id = 0)", res.getViewExpressions().get(0).toSql()); + Assertions.assertEquals("(id = 0)", res.getQueryExpressions().get(0).toSql()); } + // should consider rebuilding the hypergraph + @Disabled @Test - void testLeftOuterJoinWithLeftFilter() { + void testInnerInferLeftWithJoinCond() { connectContext.getSessionVariable().setDisableNereidsRules("INFER_PREDICATES"); CascadesContext c1 = createCascadesContext( - "select * from ( select * from T1 where T1.id = 0) T1 left outer join T2 on T1.id = T2.id", + "select * from T1 inner join " + + "(select T2.id from T2 inner join T3 on T2.id = T3.id) T2 " + + "on T1.id = T2.id", connectContext ); - connectContext.getSessionVariable().setDisableNereidsRules("INFER_PREDICATES"); Plan p1 = PlanChecker.from(c1) .analyze() .rewrite() .getPlan().child(0); CascadesContext c2 = createCascadesContext( - "select * from T1 left outer join T2 on T1.id = T2.id", + "select * from T1 left join " + + "(select T2.id from T2 inner join T3 on T2.id = T3.id and T2.score = T3.score) T2 " + + "on T1.id = T2.id", connectContext ); Plan p2 = PlanChecker.from(c2) @@ -159,9 +126,12 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List<Expression> exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); - Assertions.assertEquals(1, exprList.size()); - Assertions.assertEquals("(id = 0)", exprList.get(0).toSql()); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); + Assertions.assertFalse(res.isInvalid()); + Assertions.assertEquals(1, res.getViewNoNullableSlot().size()); + Assertions.assertEquals("[id, score]", + res.getViewNoNullableSlot().iterator().next().stream().map(ExpressionTrait::toSql).collect(Collectors.toList()).toString()); + Assertions.assertEquals("score = score", res.getQueryExpressions().get(0).toSql()); } @Test @@ -187,7 +157,8 @@ class CompareOuterJoinTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - Assertions.assertTrue(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); + Assertions.assertTrue(res.isInvalid()); } LogicalCompatibilityContext constructContext(Plan p1, Plan p2) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java index d4818a844e8..4da4905f8aa 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java @@ -21,6 +21,7 @@ import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.rules.RuleSet; import org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule; import org.apache.doris.nereids.rules.exploration.mv.ComparisonResult; +import org.apache.doris.nereids.rules.exploration.mv.HyperGraphComparator; import org.apache.doris.nereids.rules.exploration.mv.LogicalCompatibilityContext; import org.apache.doris.nereids.rules.exploration.mv.StructInfo; import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping; @@ -54,7 +55,7 @@ class PullupExpressionTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - ComparisonResult res = h1.isLogicCompatible(h2, constructContext(p1, p2)); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); Assertions.assertEquals(2, res.getQueryExpressions().size()); Assertions.assertEquals("(id = 1)", res.getQueryExpressions().get(0).toSql()); Assertions.assertEquals("(id = 1)", res.getQueryExpressions().get(1).toSql()); @@ -81,7 +82,7 @@ class PullupExpressionTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - ComparisonResult res = h1.isLogicCompatible(h2, constructContext(p1, p2)); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); Assertions.assertEquals(1, res.getQueryExpressions().size()); Assertions.assertEquals("(score = score)", res.getQueryExpressions().get(0).toSql()); } @@ -107,7 +108,7 @@ class PullupExpressionTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - ComparisonResult res = h1.isLogicCompatible(h2, constructContext(p1, p2)); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); Assertions.assertEquals(2, res.getViewExpressions().size()); Assertions.assertEquals("(id = 1)", res.getViewExpressions().get(0).toSql()); Assertions.assertEquals("(id = 1)", res.getViewExpressions().get(1).toSql()); @@ -134,7 +135,7 @@ class PullupExpressionTest extends SqlTestBase { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - ComparisonResult res = h1.isLogicCompatible(h2, constructContext(p1, p2)); + ComparisonResult res = HyperGraphComparator.isLogicCompatible(h1, h2, constructContext(p1, p2)); Assertions.assertEquals(1, res.getViewExpressions().size()); Assertions.assertEquals("(score = score)", res.getViewExpressions().get(0).toSql()); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/BuildStructInfoTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/BuildStructInfoTest.java index f68365f6708..bf5edfa45ec 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/BuildStructInfoTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/BuildStructInfoTest.java @@ -79,7 +79,8 @@ class BuildStructInfoTest extends SqlTestBase { .when(j -> { HyperGraph structInfo = HyperGraph.toStructInfo(j).get(0); Assertions.assertTrue(structInfo.getJoinEdge(0).getJoinType().isLeftOuterJoin()); - Assertions.assertEquals(3L, structInfo.getFilterEdge(0).getRejectNodes()); + Assertions.assertEquals(0, structInfo.getFilterEdge(0).getLeftRejectEdge().size()); + Assertions.assertEquals(1, structInfo.getFilterEdge(0).getRightRejectEdge().size()); return true; })); @@ -92,7 +93,6 @@ class BuildStructInfoTest extends SqlTestBase { .when(j -> { HyperGraph structInfo = HyperGraph.toStructInfo(j).get(0); Assertions.assertTrue(structInfo.getJoinEdge(0).getJoinType().isLeftOuterJoin()); - Assertions.assertEquals(0, structInfo.getFilterEdge(0).getRejectNodes()); return true; })); } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org