This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
commit a237f7ec6e64ebc60def0c282fd6e3f685820a73 Author: 谢健 <jianx...@gmail.com> AuthorDate: Thu Apr 25 16:31:38 2024 +0800 [feature](Nereids): add equal set in functional dependencies (#33642) --- .../nereids/properties/FunctionalDependencies.java | 42 +++- .../trees/plans/BlockFuncDepsPropagation.java | 5 + .../nereids/trees/plans/PropagateFuncDeps.java | 5 + .../trees/plans/logical/LogicalAggregate.java | 5 + .../trees/plans/logical/LogicalAssertNumRows.java | 6 + .../plans/logical/LogicalCatalogRelation.java | 5 + .../plans/logical/LogicalDeferMaterializeTopN.java | 5 + .../nereids/trees/plans/logical/LogicalExcept.java | 15 ++ .../nereids/trees/plans/logical/LogicalFilter.java | 10 + .../trees/plans/logical/LogicalGenerate.java | 6 + .../nereids/trees/plans/logical/LogicalHaving.java | 10 + .../trees/plans/logical/LogicalIntersect.java | 9 + .../nereids/trees/plans/logical/LogicalJoin.java | 16 ++ .../nereids/trees/plans/logical/LogicalLimit.java | 5 + .../trees/plans/logical/LogicalOneRowRelation.java | 16 ++ .../nereids/trees/plans/logical/LogicalPlan.java | 3 + .../trees/plans/logical/LogicalProject.java | 21 ++ .../nereids/trees/plans/logical/LogicalRepeat.java | 5 + .../trees/plans/logical/LogicalSqlCache.java | 21 +- .../trees/plans/logical/LogicalSubQueryAlias.java | 11 + .../nereids/trees/plans/logical/LogicalTopN.java | 5 + .../nereids/trees/plans/logical/LogicalUnion.java | 61 ++++++ .../nereids/trees/plans/logical/LogicalView.java | 6 + .../nereids/trees/plans/logical/LogicalWindow.java | 6 + .../apache/doris/nereids/util/ExpressionUtils.java | 8 + .../doris/nereids/util/ImmutableEqualSet.java | 69 +++++-- .../doris/nereids/properties/EqualSetTest.java | 230 +++++++++++++++++++++ .../suites/nereids_syntax_p0/join_order.groovy | 8 +- 28 files changed, 575 insertions(+), 39 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java index 2b0c1f5c914..a516bf9ae1c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java @@ -18,11 +18,13 @@ package org.apache.doris.nereids.properties; import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.util.ImmutableEqualSet; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -36,15 +38,20 @@ public class FunctionalDependencies { public static final FunctionalDependencies EMPTY_FUNC_DEPS = new FunctionalDependencies(new NestedSet().toImmutable(), - new NestedSet().toImmutable(), new ImmutableSet.Builder<FdItem>().build()); + new NestedSet().toImmutable(), new ImmutableSet.Builder<FdItem>().build(), + ImmutableEqualSet.empty()); private final NestedSet uniqueSet; private final NestedSet uniformSet; private final ImmutableSet<FdItem> fdItems; - private FunctionalDependencies(NestedSet uniqueSet, NestedSet uniformSet, ImmutableSet<FdItem> fdItems) { + private final ImmutableEqualSet<Slot> equalSet; + + private FunctionalDependencies( + NestedSet uniqueSet, NestedSet uniformSet, ImmutableSet<FdItem> fdItems, ImmutableEqualSet<Slot> equalSet) { this.uniqueSet = uniqueSet; this.uniformSet = uniformSet; this.fdItems = fdItems; + this.equalSet = equalSet; } public boolean isEmpty() { @@ -87,13 +94,26 @@ public class FunctionalDependencies { return slotSet.stream().noneMatch(Slot::nullable) && isUniform(slotSet); } + public boolean isNullSafeEqual(Slot l, Slot r) { + return equalSet.isEqual(l, r); + } + + public boolean isEqualAndNotNotNull(Slot l, Slot r) { + return equalSet.isEqual(l, r) && !l.nullable() && !r.nullable(); + } + + public List<Set<Slot>> calAllEqualSet() { + return equalSet.calEqualSetList(); + } + public ImmutableSet<FdItem> getFdItems() { return fdItems; } @Override public String toString() { - return String.format("FuncDeps[uniform:%s, unique:%s, fdItems:%s]", uniformSet, uniqueSet, fdItems); + return String.format("FuncDeps[uniform:%s, unique:%s, fdItems:%s, equalSet:%s]", + uniformSet, uniqueSet, fdItems, equalSet); } /** @@ -103,17 +123,21 @@ public class FunctionalDependencies { private final NestedSet uniqueSet; private final NestedSet uniformSet; private ImmutableSet<FdItem> fdItems; + private final ImmutableEqualSet.Builder<Slot> equalSetBuilder; public Builder() { uniqueSet = new NestedSet(); uniformSet = new NestedSet(); fdItems = new ImmutableSet.Builder<FdItem>().build(); + equalSetBuilder = new ImmutableEqualSet.Builder<>(); } public Builder(FunctionalDependencies other) { this.uniformSet = new NestedSet(other.uniformSet); this.uniqueSet = new NestedSet(other.uniqueSet); this.fdItems = ImmutableSet.copyOf(other.fdItems); + equalSetBuilder = new ImmutableEqualSet.Builder<>(other.equalSet); + } public void addUniformSlot(Slot slot) { @@ -147,11 +171,20 @@ public class FunctionalDependencies { public void addFunctionalDependencies(FunctionalDependencies fd) { uniformSet.add(fd.uniformSet); uniqueSet.add(fd.uniqueSet); + equalSetBuilder.addEqualSet(fd.equalSet); + } + + public void addEqualPair(Slot l, Slot r) { + equalSetBuilder.addEqualPair(l, r); + } + + public void addEqualSet(FunctionalDependencies functionalDependencies) { + equalSetBuilder.addEqualSet(functionalDependencies.equalSet); } public FunctionalDependencies build() { return new FunctionalDependencies(uniqueSet.toImmutable(), uniformSet.toImmutable(), - ImmutableSet.copyOf(fdItems)); + ImmutableSet.copyOf(fdItems), equalSetBuilder.build()); } public void pruneSlots(Set<Slot> outputSlots) { @@ -162,6 +195,7 @@ public class FunctionalDependencies { public void replace(Map<Slot, Slot> replaceMap) { uniformSet.replace(replaceMap); uniqueSet.replace(replaceMap); + equalSetBuilder.replace(replaceMap); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java index a679ad0f7d2..68464364371 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java @@ -46,4 +46,9 @@ public interface BlockFuncDepsPropagation extends LogicalPlan { default void computeUniform(FunctionalDependencies.Builder fdBuilder) { // don't generate } + + @Override + default void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + // don't generate + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java index f37059049ae..3d3d3cc8271 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java @@ -66,4 +66,9 @@ public interface PropagateFuncDeps extends LogicalPlan { default void computeUniform(FunctionalDependencies.Builder fdBuilder) { fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies()); } + + @Override + default void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java index 73ddde6cef5..031ea8a46b5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java @@ -397,4 +397,9 @@ public class LogicalAggregate<CHILD_TYPE extends Plan> return builder.build(); } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java index cad8d6e14d6..a3f540d1847 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java @@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.plans.logical; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.FdItem; +import org.apache.doris.nereids.properties.FunctionalDependencies; import org.apache.doris.nereids.properties.FunctionalDependencies.Builder; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement; @@ -145,4 +146,9 @@ public class LogicalAssertNumRows<CHILD_TYPE extends Plan> extends LogicalUnary< getOutput().forEach(fdBuilder::addUniformSlot); } } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java index 433feb741ba..277695be079 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java @@ -215,4 +215,9 @@ public abstract class LogicalCatalogRelation extends LogicalRelation implements } return slotSet.build(); } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + // don't generate any equal pair + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java index 06c03f17f04..cf894fbce3a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java @@ -209,4 +209,9 @@ public class LogicalDeferMaterializeTopN<CHILD_TYPE extends Plan> extends Logica "columnIdSlot", columnIdSlot ); } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java index d0223954411..5cfec0a4ed7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java @@ -21,6 +21,7 @@ import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.ExprFdItem; import org.apache.doris.nereids.properties.FdFactory; import org.apache.doris.nereids.properties.FdItem; +import org.apache.doris.nereids.properties.FunctionalDependencies; import org.apache.doris.nereids.properties.FunctionalDependencies.Builder; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.NamedExpression; @@ -148,6 +149,20 @@ public class LogicalExcept extends LogicalSetOperation { fdBuilder.replace(replaceMap); } + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies()); + Map<Slot, Slot> replaceMap = new HashMap<>(); + List<Slot> output = getOutput(); + List<? extends Slot> originalOutputs = regularChildrenOutputs.isEmpty() + ? child(0).getOutput() + : regularChildrenOutputs.get(0); + for (int i = 0; i < output.size(); i++) { + replaceMap.put(originalOutputs.get(i), output.get(i)); + } + fdBuilder.replace(replaceMap); + } + @Override public void computeUniform(Builder fdBuilder) { fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java index 69d378fd6a5..45b96bda451 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java @@ -17,6 +17,7 @@ package org.apache.doris.nereids.trees.plans.logical; +import org.apache.doris.common.Pair; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.FdItem; import org.apache.doris.nereids.properties.FunctionalDependencies.Builder; @@ -166,4 +167,13 @@ public class LogicalFilter<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T getConjuncts().forEach(e -> fdBuilder.addUniformSlot(ExpressionUtils.extractUniformSlot(e))); fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies()); } + + @Override + public void computeEqualSet(Builder fdBuilder) { + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + for (Expression expression : getConjuncts()) { + Optional<Pair<Slot, Slot>> equalSlot = ExpressionUtils.extractEqualSlot(expression); + equalSlot.ifPresent(slotSlotPair -> fdBuilder.addEqualPair(slotSlotPair.first, slotSlotPair.second)); + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java index a54a7514dbc..c0195e11fed 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java @@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.plans.logical; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.FdItem; +import org.apache.doris.nereids.properties.FunctionalDependencies; import org.apache.doris.nereids.properties.FunctionalDependencies.Builder; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; @@ -169,4 +170,9 @@ public class LogicalGenerate<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD public void computeUniform(Builder fdBuilder) { fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies()); } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java index da526c33af9..41ee1b14712 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java @@ -17,6 +17,7 @@ package org.apache.doris.nereids.trees.plans.logical; +import org.apache.doris.common.Pair; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.FdItem; import org.apache.doris.nereids.properties.FunctionalDependencies.Builder; @@ -138,6 +139,15 @@ public class LogicalHaving<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T return builder.build(); } + @Override + public void computeEqualSet(Builder fdBuilder) { + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + for (Expression expression : getConjuncts()) { + Optional<Pair<Slot, Slot>> equalSlot = ExpressionUtils.extractEqualSlot(expression); + equalSlot.ifPresent(slotSlotPair -> fdBuilder.addEqualPair(slotSlotPair.first, slotSlotPair.second)); + } + } + @Override public String toString() { return Utils.toSqlString("LogicalHaving", "predicates", getPredicate()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java index e9e4889a8e5..3d3ccd9ad8e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java @@ -139,6 +139,15 @@ public class LogicalIntersect extends LogicalSetOperation { } } + @Override + public void computeEqualSet(Builder fdBuilder) { + for (Plan child : children) { + fdBuilder.addEqualSet( + child.getLogicalProperties().getFunctionalDependencies()); + replaceSlotInFuncDeps(fdBuilder, child.getOutput(), getOutput()); + } + } + @Override public ImmutableSet<FdItem> computeFdItems() { Set<NamedExpression> output = ImmutableSet.copyOf(getOutput()); 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 ea92aeca22d..9ad0b5ab23f 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 @@ -696,4 +696,20 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends fdBuilder.addUniformSlot(left().getLogicalProperties().getFunctionalDependencies()); } } + + @Override + public void computeEqualSet(Builder fdBuilder) { + if (!joinType.isLeftSemiOrAntiJoin()) { + fdBuilder.addEqualSet(right().getLogicalProperties().getFunctionalDependencies()); + } + if (!joinType.isRightSemiOrAntiJoin()) { + fdBuilder.addEqualSet(left().getLogicalProperties().getFunctionalDependencies()); + } + if (joinType.isInnerJoin()) { + for (Expression expression : getHashJoinConjuncts()) { + Optional<Pair<Slot, Slot>> equalSlot = ExpressionUtils.extractEqualSlot(expression); + equalSlot.ifPresent(slotSlotPair -> fdBuilder.addEqualPair(slotSlotPair.first, slotSlotPair.second)); + } + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java index 02558fe2ed2..0fdf3212d88 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java @@ -181,4 +181,9 @@ public class LogicalLimit<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TY } return fdItems; } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java index 78a1e9a3a5e..b6e5af2c591 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java @@ -23,6 +23,7 @@ import org.apache.doris.nereids.properties.FdFactory; import org.apache.doris.nereids.properties.FdItem; import org.apache.doris.nereids.properties.FunctionalDependencies; import org.apache.doris.nereids.properties.LogicalProperties; +import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Slot; @@ -37,7 +38,9 @@ import org.apache.doris.nereids.util.Utils; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -157,4 +160,17 @@ public class LogicalOneRowRelation extends LogicalRelation implements OneRowRela return builder.build(); } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + Map<Expression, NamedExpression> aliasMap = new HashMap<>(); + for (NamedExpression namedExpr : getOutputs()) { + if (namedExpr instanceof Alias) { + if (aliasMap.containsKey(namedExpr.child(0))) { + fdBuilder.addEqualPair(namedExpr.toSlot(), aliasMap.get(namedExpr.child(0)).toSlot()); + } + aliasMap.put(namedExpr.child(0), namedExpr); + } + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java index c0fb6ae7365..8c3be3b0fa4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java @@ -63,6 +63,7 @@ public interface LogicalPlan extends Plan { FunctionalDependencies.Builder fdBuilder = new FunctionalDependencies.Builder(); computeUniform(fdBuilder); computeUnique(fdBuilder); + computeEqualSet(fdBuilder); ImmutableSet<FdItem> fdItems = computeFdItems(); fdBuilder.addFdItems(fdItems); return fdBuilder.build(); @@ -73,4 +74,6 @@ public interface LogicalPlan extends Plan { void computeUnique(FunctionalDependencies.Builder fdBuilder); void computeUniform(FunctionalDependencies.Builder fdBuilder); + + void computeEqualSet(FunctionalDependencies.Builder fdBuilder); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java index ebecee0d3ae..de23bc6f5b0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java @@ -23,6 +23,7 @@ import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.FdItem; import org.apache.doris.nereids.properties.FunctionalDependencies; import org.apache.doris.nereids.properties.LogicalProperties; +import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.BoundStar; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; @@ -41,7 +42,9 @@ import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableSet; import org.json.JSONObject; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -282,4 +285,22 @@ public class LogicalProject<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_ } fdBuilder.pruneSlots(getOutputSet()); } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + Map<Expression, NamedExpression> aliasMap = new HashMap<>(); + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + for (NamedExpression expr : getProjects()) { + if (expr instanceof Alias) { + if (aliasMap.containsKey(expr.child(0))) { + fdBuilder.addEqualPair(expr.toSlot(), aliasMap.get(expr.child(0)).toSlot()); + } + aliasMap.put(expr.child(0), expr); + if (expr.child(0).isSlot()) { + fdBuilder.addEqualPair(expr.toSlot(), (Slot) expr.child(0)); + } + } + } + fdBuilder.pruneSlots(getOutputSet()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java index 95ae92b6866..9c24fab3352 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java @@ -204,4 +204,9 @@ public class LogicalRepeat<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T return builder.build(); } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSqlCache.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSqlCache.java index 663044d569f..26c3006d5e5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSqlCache.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSqlCache.java @@ -20,11 +20,10 @@ package org.apache.doris.nereids.trees.plans.logical; import org.apache.doris.analysis.Expr; import org.apache.doris.common.util.DebugUtil; import org.apache.doris.nereids.memo.GroupExpression; -import org.apache.doris.nereids.properties.FdItem; -import org.apache.doris.nereids.properties.FunctionalDependencies.Builder; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.plans.BlockFuncDepsPropagation; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.TreeStringPlan; @@ -36,14 +35,13 @@ import org.apache.doris.qe.ResultSet; import org.apache.doris.thrift.TUniqueId; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import java.util.List; import java.util.Objects; import java.util.Optional; /** LogicalSqlCache */ -public class LogicalSqlCache extends LogicalLeaf implements SqlCache, TreeStringPlan { +public class LogicalSqlCache extends LogicalLeaf implements SqlCache, TreeStringPlan, BlockFuncDepsPropagation { private final TUniqueId queryId; private final List<String> columnLabels; private final List<Expr> resultExprs; @@ -137,19 +135,4 @@ public class LogicalSqlCache extends LogicalLeaf implements SqlCache, TreeString public String getChildrenTreeString() { return planBody; } - - @Override - public ImmutableSet<FdItem> computeFdItems() { - return ImmutableSet.of(); - } - - @Override - public void computeUnique(Builder fdBuilder) { - - } - - @Override - public void computeUniform(Builder fdBuilder) { - - } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java index ea9fd143c94..5d23561b1a9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java @@ -191,6 +191,17 @@ public class LogicalSubQueryAlias<CHILD_TYPE extends Plan> extends LogicalUnary< return ImmutableSet.of(); } + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies()); + Map<Slot, Slot> replaceMap = new HashMap<>(); + List<Slot> outputs = getOutput(); + for (int i = 0; i < outputs.size(); i++) { + replaceMap.put(child(0).getOutput().get(i), outputs.get(i)); + } + fdBuilder.replace(replaceMap); + } + public void setRelationId(RelationId relationId) { this.relationId = relationId; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java index 791ded58cce..dd1c171ca2c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java @@ -187,6 +187,11 @@ public class LogicalTopN<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYP } } + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies()); + } + @Override public ImmutableSet<FdItem> computeFdItems() { ImmutableSet<FdItem> fdItems = child(0).getLogicalProperties().getFunctionalDependencies().getFdItems(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java index 57d944bc2b9..8c5bbfcc6a6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java @@ -25,6 +25,7 @@ import org.apache.doris.nereids.properties.FunctionalDependencies; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; +import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.PlanType; @@ -36,7 +37,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -196,6 +202,61 @@ public class LogicalUnion extends LogicalSetOperation implements Union, OutputPr // don't propagate uniform slots } + private List<Set<Integer>> mapSlotToIndex(Plan plan, List<Set<Slot>> equalSlotsList) { + Map<Slot, Integer> slotToIndex = new HashMap<>(); + for (int i = 0; i < plan.getOutput().size(); i++) { + slotToIndex.put(plan.getOutput().get(i), i); + } + List<Set<Integer>> equalSlotIndicesList = new ArrayList<>(); + for (Set<Slot> equalSlots : equalSlotsList) { + Set<Integer> equalSlotIndices = new HashSet<>(); + for (Slot slot : equalSlots) { + if (slotToIndex.containsKey(slot)) { + equalSlotIndices.add(slotToIndex.get(slot)); + } + } + if (equalSlotIndices.size() > 1) { + equalSlotIndicesList.add(equalSlotIndices); + } + } + return equalSlotIndicesList; + } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + List<Set<Integer>> unionEqualSlotIndicesList = new ArrayList<>(); + + for (Plan child : children) { + List<Set<Slot>> childEqualSlotsList = + child.getLogicalProperties().getFunctionalDependencies().calAllEqualSet(); + List<Set<Integer>> childEqualSlotsIndicesList = mapSlotToIndex(child, childEqualSlotsList); + if (unionEqualSlotIndicesList.isEmpty()) { + unionEqualSlotIndicesList = childEqualSlotsIndicesList; + } else { + // Only all child of union has the equal pair, we keep the equal pair. + // It means we should calculate the intersection of all child + for (Set<Integer> childEqualSlotIndices : childEqualSlotsIndicesList) { + for (Set<Integer> unionEqualSlotIndices : unionEqualSlotIndicesList) { + if (Collections.disjoint(childEqualSlotIndices, unionEqualSlotIndices)) { + unionEqualSlotIndices.retainAll(childEqualSlotIndices); + } + } + } + } + + List<Slot> ouputList = getOutput(); + for (Set<Integer> equalSlotIndices : unionEqualSlotIndicesList) { + if (equalSlotIndices.size() <= 1) { + continue; + } + int first = equalSlotIndices.iterator().next(); + for (int idx : equalSlotIndices) { + fdBuilder.addEqualPair(ouputList.get(first), ouputList.get(idx)); + } + } + } + } + @Override public ImmutableSet<FdItem> computeFdItems() { Set<NamedExpression> output = ImmutableSet.copyOf(getOutput()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java index 200ccc2ffcb..371d3cae43f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java @@ -21,6 +21,7 @@ import org.apache.doris.catalog.View; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.FdItem; +import org.apache.doris.nereids.properties.FunctionalDependencies; import org.apache.doris.nereids.properties.FunctionalDependencies.Builder; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; @@ -146,4 +147,9 @@ public class LogicalView<BODY extends Plan> extends LogicalUnary<BODY> { public void computeUniform(Builder fdBuilder) { fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies()); } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java index b03a3365402..56a306b6005 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java @@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.plans.logical; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.FdItem; +import org.apache.doris.nereids.properties.FunctionalDependencies; import org.apache.doris.nereids.properties.FunctionalDependencies.Builder; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; @@ -324,4 +325,9 @@ public class LogicalWindow<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T } } } + + @Override + public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) { + fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies()); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java index 51eef31de2f..a6a4d999a92 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java @@ -20,6 +20,7 @@ package org.apache.doris.nereids.util; import org.apache.doris.catalog.TableIf.TableType; import org.apache.doris.common.MaterializedViewException; import org.apache.doris.common.NereidsException; +import org.apache.doris.common.Pair; import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; import org.apache.doris.nereids.rules.expression.rules.FoldConstantRule; @@ -130,6 +131,13 @@ public class ExpressionUtils { } } + public static Optional<Pair<Slot, Slot>> extractEqualSlot(Expression expr) { + if (expr instanceof EqualTo && expr.child(0).isSlot() && expr.child(1).isSlot()) { + return Optional.of(Pair.of((Slot) expr.child(0), (Slot) expr.child(1))); + } + return Optional.empty(); + } + public static Optional<Expression> optionalAnd(List<Expression> expressions) { if (expressions.isEmpty()) { return Optional.empty(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ImmutableEqualSet.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ImmutableEqualSet.java index f5f3dd75b51..35bc78ac117 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ImmutableEqualSet.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ImmutableEqualSet.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; /** @@ -44,34 +45,66 @@ public class ImmutableEqualSet<T> { * Builder for ImmutableEqualSet. */ public static class Builder<T> { - private final Map<T, T> parent = new LinkedHashMap<>(); - private final Map<T, Integer> size = new LinkedHashMap<>(); + private Map<T, T> parent; + + Builder(Map<T, T> parent) { + this.parent = parent; + } + + public Builder() { + this(new LinkedHashMap<>()); + } + + public Builder(ImmutableEqualSet<T> equalSet) { + this(new LinkedHashMap<>(equalSet.root)); + } + + /** + * replace all key value according replace map + */ + public void replace(Map<T, T> replaceMap) { + Map<T, T> newMap = new LinkedHashMap<>(); + for (Entry<T, T> entry : parent.entrySet()) { + newMap.put(replaceMap.getOrDefault(entry.getKey(), entry.getKey()), + replaceMap.getOrDefault(entry.getValue(), entry.getValue())); + } + parent = newMap; + } /** * Add a equal pair */ public void addEqualPair(T a, T b) { + if (!parent.containsKey(a)) { + parent.put(a, a); + } + if (!parent.containsKey(b)) { + parent.put(b, b); + } T root1 = findRoot(a); T root2 = findRoot(b); if (root1 != root2) { - parent.put(b, root1); - findRoot(b); + parent.put(root1, root2); } } - private T findRoot(T a) { - parent.putIfAbsent(a, a); // Ensure that the element is added - size.putIfAbsent(a, 1); // Initialize size to 1 + public void addEqualSet(ImmutableEqualSet<T> equalSet) { + this.parent.putAll(equalSet.root); + } - if (!parent.get(a).equals(a)) { - parent.put(a, findRoot(parent.get(a))); // Path compression + private T findRoot(T a) { + if (a.equals(parent.get(a))) { + return parent.get(a); } - return parent.get(a); + return findRoot(parent.get(a)); } public ImmutableEqualSet<T> build() { - parent.keySet().forEach(this::findRoot); - return new ImmutableEqualSet<>(parent); + ImmutableMap.Builder<T, T> foldMapBuilder = new ImmutableMap.Builder<>(); + for (T k : parent.keySet()) { + foldMapBuilder.put(k, findRoot(k)); + } + return new ImmutableEqualSet<>(foldMapBuilder.build()); } } @@ -103,4 +136,16 @@ public class ImmutableEqualSet<T> { public Set<T> getAllItemSet() { return ImmutableSet.copyOf(root.keySet()); } + + public boolean isEqual(T l, T r) { + if (!root.containsKey(l) || !root.containsKey(r)) { + return false; + } + return root.get(l) == root.get(r); + } + + @Override + public String toString() { + return root.toString(); + } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/EqualSetTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/EqualSetTest.java new file mode 100644 index 00000000000..fec795ccbe2 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/EqualSetTest.java @@ -0,0 +1,230 @@ +// 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.properties; + +import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.util.PlanChecker; +import org.apache.doris.utframe.TestWithFeService; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class EqualSetTest extends TestWithFeService { + Slot slot1 = new SlotReference("1", IntegerType.INSTANCE, false); + Slot slot2 = new SlotReference("2", IntegerType.INSTANCE, false); + Slot slot3 = new SlotReference("1", IntegerType.INSTANCE, false); + Slot slot4 = new SlotReference("1", IntegerType.INSTANCE, false); + + @Override + protected void runBeforeAll() throws Exception { + createDatabase("test"); + createTable("create table test.agg (\n" + + "id int not null,\n" + + "id2 int replace not null,\n" + + "name varchar(128) replace not null )\n" + + "AGGREGATE KEY(id)\n" + + "distributed by hash(id) buckets 10\n" + + "properties('replication_num' = '1');"); + createTable("create table test.uni (\n" + + "id int not null,\n" + + "id2 int not null,\n" + + "name varchar(128) not null)\n" + + "UNIQUE KEY(id)\n" + + "distributed by hash(id) buckets 10\n" + + "properties('replication_num' = '1');"); + connectContext.setDatabase("test"); + } + + @Test + void testAgg() { + Plan plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg where id2 = id group by id, id2") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + } + + @Test + void testTopNLimit() { + Plan plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg where id2 = id limit 1") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg where id2 = id limit 1 order by id") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + } + + @Test + void testSetOp() { + Plan plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg where id2 = id intersect select id, id2 from agg") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg where id2 = id except select id, id2 from agg") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg where id2 = id union all select id, id2 from agg") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isEmpty()); + plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg where id2 = id union all select id, id2 from agg where id2 = id") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg union all select id, id2 from agg where id2 = id") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isEmpty()); + } + + @Test + void testFilterHaving() { + Plan plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg where id2 = id") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg group by id, id2 having id = id2") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + } + + @Test + void testGenerate() { + Plan plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from agg lateral view explode([1,2,3]) tmp1 as e1 where id = id2") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + } + + @Test + void testJoin() { + // inner join + Plan plan = PlanChecker.from(connectContext) + .analyze("select uni.id, agg.id from agg join uni " + + "where agg.id = uni.id") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + + // foj + plan = PlanChecker.from(connectContext) + .analyze("select t1.id, t2.id, t3.id from agg as t1 join uni as t2 " + + " on t1.id = t2.id full outer join uni as t3 on t1.id2 = t2.id2") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies() + .isEqualAndNotNotNull(plan.getOutput().get(0), plan.getOutput().get(1))); + Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(2))); + + // loj + plan = PlanChecker.from(connectContext) + .analyze("select t1.id, t2.id, t3.id from agg as t1 join uni as t2 " + + " on t1.id = t2.id left outer join uni as t3 on t1.id2 = t2.id2") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isEqualAndNotNotNull(plan.getOutput().get(0), plan.getOutput().get(1))); + Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(2))); + + // roj + plan = PlanChecker.from(connectContext) + .analyze("select t1.id, t2.id, t3.id from agg as t1 join uni as t2 " + + " on t1.id = t2.id right outer join uni as t3 on t1.id2 = t2.id2") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies() + .isEqualAndNotNotNull(plan.getOutput().get(0), plan.getOutput().get(1))); + Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies() + .isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(2))); + } + + @Test + void testOneRowRelation() { + Plan plan = PlanChecker.from(connectContext) + .analyze("select 1, 1") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties() + .getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + } + + @Test + void testProject() { + Plan plan = PlanChecker.from(connectContext) + .analyze("select id as o1, id as o2, id2 as o4, 1 as c1, 1 as c2 from uni where id = id2") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties() + .getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + Assertions.assertTrue(plan.getLogicalProperties() + .getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(2))); + Assertions.assertTrue(plan.getLogicalProperties() + .getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(1), plan.getOutput().get(2))); + Assertions.assertTrue(plan.getLogicalProperties() + .getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(3), plan.getOutput().get(4))); + } + + @Test + void testSubQuery() { + Plan plan = PlanChecker.from(connectContext) + .analyze("select id, id2 from (select id, id2 from agg where id = id2) t") + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties() + .getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + } + + @Test + void testWindow() { + // partition by uniform + Plan plan = PlanChecker.from(connectContext) + .analyze("select id, id2, row_number() over(partition by id) from agg where id = id2") + .rewrite() + .getPlan(); + Assertions.assertTrue(plan.getLogicalProperties() + .getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), plan.getOutput().get(1))); + } + +} diff --git a/regression-test/suites/nereids_syntax_p0/join_order.groovy b/regression-test/suites/nereids_syntax_p0/join_order.groovy index 64056e0202b..461c2bb4485 100644 --- a/regression-test/suites/nereids_syntax_p0/join_order.groovy +++ b/regression-test/suites/nereids_syntax_p0/join_order.groovy @@ -43,7 +43,7 @@ suite("join_order") { """ sql """ drop table if exists outerjoin_C_order;""" sql """ - create table outerjoin_C_order ( c int not null ) + create table outerjoin_C_order ( c int not null, c2 int not null ) ENGINE=OLAP DISTRIBUTED BY HASH(c) BUCKETS 1 PROPERTIES ( @@ -77,7 +77,7 @@ suite("join_order") { sql """insert into outerjoin_A_order values( 1,2 );""" sql """insert into outerjoin_B_order values( 1 );""" - sql """insert into outerjoin_C_order values( 1 );""" + sql """insert into outerjoin_C_order values( 1,2 );""" sql """insert into outerjoin_D_order values( 1,2,3 );""" sql """insert into outerjoin_E_order values( 1,2 );""" @@ -96,13 +96,13 @@ suite("join_order") { sql 'set disable_join_reorder=true;' explain { - sql("select * from outerjoin_A_order, outerjoin_B_order, outerjoin_C_order where outerjoin_A_order.a1 = outerjoin_C_order.c and outerjoin_B_order.b = outerjoin_C_order.c;") + sql("select * from outerjoin_A_order, outerjoin_B_order, outerjoin_C_order where outerjoin_A_order.a1 = outerjoin_C_order.c and outerjoin_B_order.b = outerjoin_C_order.c2;") contains "CROSS JOIN" } sql 'set disable_join_reorder=false;' explain { - sql("select * from outerjoin_A_order, outerjoin_B_order, outerjoin_C_order where outerjoin_A_order.a1 = outerjoin_C_order.c and outerjoin_B_order.b = outerjoin_C_order.c;") + sql("select * from outerjoin_A_order, outerjoin_B_order, outerjoin_C_order where outerjoin_A_order.a1 = outerjoin_C_order.c and outerjoin_B_order.b = outerjoin_C_order.c2;") notContains "CROSS JOIN" } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org