This is an automated email from the ASF dual-hosted git repository.
huajianlan 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 4f60b37402 [feature](Nereids):refactor and add outer join LAsscom.
(#11531)
4f60b37402 is described below
commit 4f60b374020291c76e14177be3d03aa7b88197e4
Author: jakevin <[email protected]>
AuthorDate: Mon Aug 8 20:08:12 2022 +0800
[feature](Nereids):refactor and add outer join LAsscom. (#11531)
refactor and add outer join LAsscom.
Extract the common function to LAsscomHelper.
---
.../rules/exploration/join/JoinCommute.java | 3 +
.../rules/exploration/join/JoinLAsscom.java | 123 ++----------
.../rules/exploration/join/JoinLAsscomHelper.java | 219 +++++++++++++++++++++
.../rules/exploration/join/JoinLAsscomProject.java | 59 ++++++
.../rules/exploration/join/JoinProjectLAsscom.java | 186 -----------------
.../apache/doris/nereids/trees/plans/JoinType.java | 4 +
.../org/apache/doris/nereids/util/JoinUtils.java | 14 +-
.../java/org/apache/doris/nereids/util/Utils.java | 16 +-
...AsscomTest.java => JoinLAsscomProjectTest.java} | 15 +-
.../rules/exploration/join/JoinLAsscomTest.java | 12 +-
10 files changed, 325 insertions(+), 326 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
index 776d722293..19bf4491b9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
@@ -29,6 +29,9 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalProject;
*/
@Developing
public class JoinCommute extends OneExplorationRuleFactory {
+
+ public static final JoinCommute SWAP_OUTER_COMMUTE_BOTTOM_JOIN = new
JoinCommute(true, SwapType.BOTTOM_JOIN);
+
private final SwapType swapType;
private final boolean swapOuter;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
index 8d9bda83af..5e9d173d68 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
@@ -21,23 +21,11 @@ import org.apache.doris.nereids.annotation.Developing;
import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.plans.GroupPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.util.ExpressionUtils;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
/**
- * Rule for change inner join left associative to right.
+ * Rule for change inner join LAsscom (associative and commutive).
*/
@Developing
public class JoinLAsscom extends OneExplorationRuleFactory {
@@ -50,106 +38,19 @@ public class JoinLAsscom extends OneExplorationRuleFactory
{
*/
@Override
public Rule build() {
- return innerLogicalJoin(innerLogicalJoin(groupPlan(), groupPlan()),
groupPlan()).then(topJoin -> {
- if (!check(topJoin)) {
- return null;
- }
-
- LogicalJoin<GroupPlan, GroupPlan> bottomJoin = topJoin.left();
-
- Plan a = bottomJoin.left();
- Plan b = bottomJoin.right();
- Plan c = topJoin.right();
-
- Optional<Expression> optTopJoinOnClause = topJoin.getCondition();
- // inner join, onClause can't be empty().
- Preconditions.checkArgument(optTopJoinOnClause.isPresent(),
- "bottomJoin in inner join, onClause must be present.");
- Expression topJoinOnClause = optTopJoinOnClause.get();
- Optional<Expression> optBottomJoinOnClause =
bottomJoin.getCondition();
- Preconditions.checkArgument(optBottomJoinOnClause.isPresent(),
- "bottomJoin in inner join, onClause must be present.");
- Expression bottomJoinOnClause = optBottomJoinOnClause.get();
-
- List<SlotReference> aOutputSlots = a.getOutput().stream().map(slot
-> (SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> bOutputSlots = b.getOutput().stream().map(slot
-> (SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> cOutputSlots = c.getOutput().stream().map(slot
-> (SlotReference) slot)
- .collect(Collectors.toList());
-
- // Ignore join with some OnClause like:
- // Join C = B + A for above example.
- List<Expression> topJoinOnClauseConjuncts =
ExpressionUtils.extractConjunction(topJoinOnClause);
- for (Expression topJoinOnClauseConjunct :
topJoinOnClauseConjuncts) {
- if
(ExpressionUtils.isIntersecting(topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
- aOutputSlots)
- && ExpressionUtils.isIntersecting(
-
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
- bOutputSlots)
- && ExpressionUtils.isIntersecting(
-
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
- cOutputSlots)
- ) {
+ return logicalJoin(logicalJoin(), groupPlan())
+ .when(JoinLAsscomHelper::check)
+ .when(join -> join.getJoinType().isInnerJoin() ||
join.getJoinType().isLeftOuterJoin()
+ && (join.left().getJoinType().isInnerJoin() ||
join.left().getJoinType().isLeftOuterJoin()))
+ .then(topJoin -> {
+
+ LogicalJoin<GroupPlan, GroupPlan> bottomJoin = topJoin.left();
+ JoinLAsscomHelper helper = JoinLAsscomHelper.of(topJoin,
bottomJoin);
+ if (!helper.initJoinOnCondition()) {
return null;
}
- }
- List<Expression> bottomJoinOnClauseConjuncts =
ExpressionUtils.extractConjunction(bottomJoinOnClause);
-
- List<Expression> allOnCondition = Lists.newArrayList();
- allOnCondition.addAll(topJoinOnClauseConjuncts);
- allOnCondition.addAll(bottomJoinOnClauseConjuncts);
-
- List<SlotReference> newBottomJoinSlots = Lists.newArrayList();
- newBottomJoinSlots.addAll(aOutputSlots);
- newBottomJoinSlots.addAll(cOutputSlots);
-
- List<Expression> newBottomJoinOnCondition = Lists.newArrayList();
- List<Expression> newTopJoinOnCondition = Lists.newArrayList();
- for (Expression onCondition : allOnCondition) {
- List<SlotReference> slots =
onCondition.collect(SlotReference.class::isInstance);
- if (new HashSet<>(newBottomJoinSlots).containsAll(slots)) {
- newBottomJoinOnCondition.add(onCondition);
- } else {
- newTopJoinOnCondition.add(onCondition);
- }
- }
-
- // newBottomJoinOnCondition/newTopJoinOnCondition is empty. They
are cross join.
- // Example:
- // A: col1, col2. B: col2, col3. C: col3, col4
- // (A & B on A.col2=B.col2) & C on B.col3=C.col3.
- // If (A & B) & C -> (A & C) & B.
- // (A & C) will be cross join (newBottomJoinOnCondition is empty)
- if (newBottomJoinOnCondition.isEmpty() ||
newTopJoinOnCondition.isEmpty()) {
- return null;
- }
-
- // new bottom join (a, c)
- LogicalJoin newBottomJoin = new LogicalJoin(
- bottomJoin.getJoinType(),
- Optional.of(ExpressionUtils.and(newBottomJoinOnCondition)),
- a, c);
-
- // TODO: add column map (use project)
- // SlotReference bind() may have solved this problem.
- // source: | A | B | C |
- // target: | A | C | B |
-
- // new top join: b
- LogicalJoin newTopJoin = new LogicalJoin(
- topJoin.getJoinType(),
- Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
- newBottomJoin, b);
-
- return newTopJoin;
- }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
- }
- private boolean check(LogicalJoin topJoin) {
- if (topJoin.getJoinReorderContext().hasCommute()) {
- return false;
- }
- return true;
+ return helper.newTopJoin();
+ }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomHelper.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomHelper.java
new file mode 100644
index 0000000000..0a67d42bbb
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomHelper.java
@@ -0,0 +1,219 @@
+// 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.join;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Common function for JoinLAsscom
+ */
+public class JoinLAsscomHelper {
+ /*
+ * topJoin newTopJoin
+ * / \ / \
+ * bottomJoin C --> newBottomJoin B
+ * / \ / \
+ * A B A C
+ */
+ private final LogicalJoin topJoin;
+ private final LogicalJoin<GroupPlan, GroupPlan> bottomJoin;
+ private final Plan a;
+ private final Plan b;
+ private final Plan c;
+
+ private final Expression topJoinOnClause;
+ private final Expression bottomJoinOnClause;
+
+ private final List<SlotReference> aOutputSlots;
+ private final List<SlotReference> bOutputSlots;
+ private final List<SlotReference> cOutputSlots;
+
+ private final List<Expression> newBottomJoinOnCondition =
Lists.newArrayList();
+ private final List<Expression> newTopJoinOnCondition =
Lists.newArrayList();
+
+ /**
+ * Init plan and output.
+ */
+ public JoinLAsscomHelper(LogicalJoin<? extends Plan, GroupPlan> topJoin,
+ LogicalJoin<GroupPlan, GroupPlan> bottomJoin) {
+ this.topJoin = topJoin;
+ this.bottomJoin = bottomJoin;
+
+ a = bottomJoin.left();
+ b = bottomJoin.right();
+ c = topJoin.right();
+
+ Preconditions.checkArgument(topJoin.getCondition().isPresent(),
"topJoin onClause must be present.");
+ topJoinOnClause = topJoin.getCondition().get();
+ Preconditions.checkArgument(bottomJoin.getCondition().isPresent(),
"bottomJoin onClause must be present.");
+ bottomJoinOnClause = bottomJoin.getCondition().get();
+
+ aOutputSlots = Utils.getOutputSlotReference(a);
+ bOutputSlots = Utils.getOutputSlotReference(b);
+ cOutputSlots = Utils.getOutputSlotReference(c);
+ }
+
+ public static JoinLAsscomHelper of(LogicalJoin<? extends Plan, GroupPlan>
topJoin,
+ LogicalJoin<GroupPlan, GroupPlan> bottomJoin) {
+ return new JoinLAsscomHelper(topJoin, bottomJoin);
+ }
+
+ /**
+ * Get the onCondition of newTopJoin and newBottomJoin.
+ */
+ public boolean initJoinOnCondition() {
+ List<Expression> topJoinOnClauseConjuncts =
ExpressionUtils.extractConjunction(topJoinOnClause);
+ for (Expression topJoinOnClauseConjunct : topJoinOnClauseConjuncts) {
+ // Ignore join with some OnClause like:
+ // Join C = B + A for above example.
+ List<SlotReference> topJoinUsedSlot =
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance);
+ if (ExpressionUtils.isIntersecting(topJoinUsedSlot, aOutputSlots)
+ && ExpressionUtils.isIntersecting(topJoinUsedSlot,
bOutputSlots)
+ && ExpressionUtils.isIntersecting(topJoinUsedSlot,
cOutputSlots)
+ ) {
+ return false;
+ }
+ }
+
+ List<Expression> allOnCondition = Lists.newArrayList();
+ allOnCondition.addAll(topJoinOnClauseConjuncts);
+
allOnCondition.addAll(ExpressionUtils.extractConjunction(bottomJoinOnClause));
+
+ HashSet<SlotReference> newBottomJoinSlots = new
HashSet<>(aOutputSlots);
+ newBottomJoinSlots.addAll(cOutputSlots);
+
+ for (Expression onCondition : allOnCondition) {
+ List<SlotReference> slots =
onCondition.collect(SlotReference.class::isInstance);
+ if (newBottomJoinSlots.containsAll(slots)) {
+ newBottomJoinOnCondition.add(onCondition);
+ } else {
+ newTopJoinOnCondition.add(onCondition);
+ }
+ }
+
+ // newBottomJoinOnCondition/newTopJoinOnCondition is empty. They are
cross join.
+ // Example:
+ // A: col1, col2. B: col2, col3. C: col3, col4
+ // (A & B on A.col2=B.col2) & C on B.col3=C.col3.
+ // (A & B) & C -> (A & C) & B.
+ // (A & C) will be cross join (newBottomJoinOnCondition is empty)
+ if (newBottomJoinOnCondition.isEmpty() ||
newTopJoinOnCondition.isEmpty()) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Get projectExpr of left and right.
+ * Just for project-inside.
+ */
+ private Pair<List<NamedExpression>, List<NamedExpression>>
getProjectExprs() {
+ Preconditions.checkArgument(topJoin.left() instanceof LogicalProject);
+ LogicalProject project = (LogicalProject) topJoin.left();
+
+ List<NamedExpression> projectExprs = project.getProjects();
+ List<NamedExpression> newRightProjectExprs = Lists.newArrayList();
+ List<NamedExpression> newLeftProjectExpr = Lists.newArrayList();
+
+ HashSet<SlotReference> bOutputSlotsSet = new HashSet<>(bOutputSlots);
+ for (NamedExpression projectExpr : projectExprs) {
+ List<SlotReference> usedSlotRefs =
projectExpr.collect(SlotReference.class::isInstance);
+ if (bOutputSlotsSet.containsAll(usedSlotRefs)) {
+ newRightProjectExprs.add(projectExpr);
+ } else {
+ newLeftProjectExpr.add(projectExpr);
+ }
+ }
+
+ return new Pair<>(newLeftProjectExpr, newRightProjectExprs);
+ }
+
+
+ private LogicalJoin<GroupPlan, GroupPlan> newBottomJoin() {
+ return new LogicalJoin(
+ bottomJoin.getJoinType(),
+ Optional.of(ExpressionUtils.and(newBottomJoinOnCondition)),
+ a, c);
+ }
+
+ /**
+ * Create topJoin for project-inside.
+ */
+ public LogicalJoin newProjectTopJoin() {
+ Plan left;
+ Plan right;
+
+ List<NamedExpression> newLeftProjectExpr = getProjectExprs().first;
+ List<NamedExpression> newRightProjectExprs = getProjectExprs().second;
+ if (!newLeftProjectExpr.isEmpty()) {
+ left = new LogicalProject<>(newLeftProjectExpr, newBottomJoin());
+ } else {
+ left = newBottomJoin();
+ }
+ if (!newRightProjectExprs.isEmpty()) {
+ right = new LogicalProject<>(newRightProjectExprs, b);
+ } else {
+ right = b;
+ }
+
+ return new LogicalJoin<>(
+ topJoin.getJoinType(),
+ Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
+ left, right);
+ }
+
+ /**
+ * Create topJoin for no-project-inside.
+ */
+ public LogicalJoin newTopJoin() {
+ // TODO: add column map (use project)
+ // SlotReference bind() may have solved this problem.
+ // source: | A | B | C |
+ // target: | A | C | B |
+
+ return new LogicalJoin(
+ topJoin.getJoinType(),
+ Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
+ newBottomJoin(), b);
+ }
+
+ public static boolean check(LogicalJoin topJoin) {
+ if (topJoin.getJoinReorderContext().hasCommute()) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProject.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProject.java
new file mode 100644
index 0000000000..e6d99644af
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProject.java
@@ -0,0 +1,59 @@
+// 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.join;
+
+import org.apache.doris.nereids.annotation.Developing;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+
+/**
+ * Rule for change inner join left associative to right.
+ */
+@Developing
+public class JoinLAsscomProject extends OneExplorationRuleFactory {
+ /*
+ * topJoin newTopJoin
+ * / \ / \
+ * project C newLeftProject newRightProject
+ * / ──► / \
+ * bottomJoin newBottomJoin B
+ * / \ / \
+ * A B A C
+ */
+ @Override
+ public Rule build() {
+ return logicalJoin(logicalProject(logicalJoin()), groupPlan())
+ .when(JoinLAsscomHelper::check)
+ .when(join -> join.getJoinType().isInnerJoin() ||
join.getJoinType().isLeftOuterJoin()
+ && (join.left().child().getJoinType().isInnerJoin() ||
join.left().child().getJoinType()
+ .isLeftOuterJoin()))
+ .then(topJoin -> {
+ LogicalJoin<GroupPlan, GroupPlan> bottomJoin =
topJoin.left().child();
+
+ JoinLAsscomHelper helper = JoinLAsscomHelper.of(topJoin,
bottomJoin);
+ if (!helper.initJoinOnCondition()) {
+ return null;
+ }
+
+ return helper.newProjectTopJoin();
+ }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscom.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscom.java
deleted file mode 100644
index afe7ac74b8..0000000000
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscom.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// 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.join;
-
-import org.apache.doris.nereids.annotation.Developing;
-import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.rules.RuleType;
-import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.NamedExpression;
-import org.apache.doris.nereids.trees.expressions.SlotReference;
-import org.apache.doris.nereids.trees.plans.GroupPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
-import org.apache.doris.nereids.util.ExpressionUtils;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-/**
- * Rule for change inner join left associative to right.
- */
-@Developing
-public class JoinProjectLAsscom extends OneExplorationRuleFactory {
- /*
- * topJoin newTopJoin
- * / \ / \
- * project C newLeftProject newRightProject
- * / ──► / \
- * bottomJoin newBottomJoin B
- * / \ / \
- * A B A C
- */
- @Override
- public Rule build() {
- return innerLogicalJoin(logicalProject(innerLogicalJoin(groupPlan(),
groupPlan())), groupPlan())
- .when(this::check)
- .then(topJoin -> {
- if (!check(topJoin)) {
- return null;
- }
-
- LogicalProject<LogicalJoin<GroupPlan, GroupPlan>> project =
topJoin.left();
- LogicalJoin<GroupPlan, GroupPlan> bottomJoin = project.child();
-
- Plan a = bottomJoin.left();
- Plan b = bottomJoin.right();
- Plan c = topJoin.right();
-
- Optional<Expression> optTopJoinOnClause =
topJoin.getCondition();
- // inner join, onClause can't be empty().
- Preconditions.checkArgument(optTopJoinOnClause.isPresent(),
- "bottomJoin in inner join, onClause must be present.");
- Expression topJoinOnClause = optTopJoinOnClause.get();
- Optional<Expression> optBottomJoinOnClause =
bottomJoin.getCondition();
- Preconditions.checkArgument(optBottomJoinOnClause.isPresent(),
- "bottomJoin in inner join, onClause must be present.");
- Expression bottomJoinOnClause = optBottomJoinOnClause.get();
-
- List<SlotReference> aOutputSlots =
a.getOutput().stream().map(slot -> (SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> bOutputSlots =
b.getOutput().stream().map(slot -> (SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> cOutputSlots =
c.getOutput().stream().map(slot -> (SlotReference) slot)
- .collect(Collectors.toList());
-
- // Ignore join with some OnClause like:
- // Join C = B + A for above example.
- List<Expression> topJoinOnClauseConjuncts =
ExpressionUtils.extractConjunction(topJoinOnClause);
- for (Expression topJoinOnClauseConjunct :
topJoinOnClauseConjuncts) {
- if (ExpressionUtils.isIntersecting(
-
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance), aOutputSlots)
- && ExpressionUtils.isIntersecting(
-
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
- bOutputSlots)
- && ExpressionUtils.isIntersecting(
-
topJoinOnClauseConjunct.collect(SlotReference.class::isInstance),
- cOutputSlots)
- ) {
- return null;
- }
- }
- List<Expression> bottomJoinOnClauseConjuncts =
ExpressionUtils.extractConjunction(
- bottomJoinOnClause);
-
- List<Expression> allOnCondition = Lists.newArrayList();
- allOnCondition.addAll(topJoinOnClauseConjuncts);
- allOnCondition.addAll(bottomJoinOnClauseConjuncts);
-
- List<SlotReference> newBottomJoinSlots = Lists.newArrayList();
- newBottomJoinSlots.addAll(aOutputSlots);
- newBottomJoinSlots.addAll(cOutputSlots);
-
- List<Expression> newBottomJoinOnCondition =
Lists.newArrayList();
- List<Expression> newTopJoinOnCondition = Lists.newArrayList();
- for (Expression onCondition : allOnCondition) {
- List<SlotReference> slots =
onCondition.collect(SlotReference.class::isInstance);
- if (new HashSet<>(newBottomJoinSlots).containsAll(slots)) {
- newBottomJoinOnCondition.add(onCondition);
- } else {
- newTopJoinOnCondition.add(onCondition);
- }
- }
-
- // newBottomJoinOnCondition/newTopJoinOnCondition is empty.
They are cross join.
- // Example:
- // A: col1, col2. B: col2, col3. C: col3, col4
- // (A & B on A.col2=B.col2) & C on B.col3=C.col3.
- // If (A & B) & C -> (A & C) & B.
- // (A & C) will be cross join (newBottomJoinOnCondition is
empty)
- if (newBottomJoinOnCondition.isEmpty() ||
newTopJoinOnCondition.isEmpty()) {
- return null;
- }
-
- Plan newBottomJoin = new LogicalJoin(
- bottomJoin.getJoinType(),
-
Optional.of(ExpressionUtils.and(newBottomJoinOnCondition)),
- a, c);
-
- // Handle project.
- List<NamedExpression> projectExprs = project.getProjects();
- List<NamedExpression> newRightProjectExprs =
Lists.newArrayList();
- List<NamedExpression> newLeftProjectExpr =
Lists.newArrayList();
- for (NamedExpression projectExpr : projectExprs) {
- List<SlotReference> usedSlotRefs =
projectExpr.collect(SlotReference.class::isInstance);
- if (new HashSet<>(bOutputSlots).containsAll(usedSlotRefs))
{
- newRightProjectExprs.add(projectExpr);
- } else {
- newLeftProjectExpr.add(projectExpr);
- }
- }
-
- // project include b, add project for right.
- if (newRightProjectExprs.size() != 0) {
- LogicalProject newRightProject = new
LogicalProject<>(newRightProjectExprs, b);
-
- if (newLeftProjectExpr.size() != 0) {
- // project include bottom join, add project for left
bottom join.
- LogicalProject newLeftProject = new
LogicalProject<>(newLeftProjectExpr, newBottomJoin);
- return new LogicalJoin(
- topJoin.getJoinType(),
-
Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
- newLeftProject, newRightProject);
- }
- return new LogicalJoin(
- topJoin.getJoinType(),
-
Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
- newBottomJoin, newRightProject);
- }
-
- return new LogicalJoin(
- topJoin.getJoinType(),
-
Optional.of(ExpressionUtils.and(newTopJoinOnCondition)),
- newBottomJoin, b);
- }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
- }
-
- private boolean check(LogicalJoin topJoin) {
- if (topJoin.getJoinReorderContext().hasCommute()) {
- return false;
- }
- return true;
- }
-
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
index 77b7c447dc..9e7e933d84 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
@@ -100,6 +100,10 @@ public enum JoinType {
return this == FULL_OUTER_JOIN;
}
+ public final boolean isLeftOuterJoin() {
+ return this == LEFT_OUTER_JOIN;
+ }
+
public final boolean isSemiOrAntiJoin() {
return this == LEFT_SEMI_JOIN || this == RIGHT_SEMI_JOIN || this ==
LEFT_ANTI_JOIN || this == RIGHT_ANTI_JOIN;
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/JoinUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/JoinUtils.java
index 711e954528..5c66dc85a2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/JoinUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/JoinUtils.java
@@ -29,7 +29,6 @@ import com.google.common.collect.Lists;
import java.util.HashSet;
import java.util.List;
-import java.util.stream.Collectors;
/**
* Utils for join
@@ -53,10 +52,8 @@ public class JoinUtils {
return eqConjuncts;
}
- List<SlotReference> leftSlots =
join.left().getOutput().stream().map(slot -> (SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> rightSlots =
join.right().getOutput().stream().map(slot -> (SlotReference) slot)
- .collect(Collectors.toList());
+ List<SlotReference> leftSlots =
Utils.getOutputSlotReference(join.left());
+ List<SlotReference> rightSlots =
Utils.getOutputSlotReference(join.right());
Expression onCondition = join.getCondition().get();
List<Expression> conjunctList =
ExpressionUtils.extractConjunction(onCondition);
@@ -93,13 +90,10 @@ public class JoinUtils {
Pair<List<SlotReference>, List<SlotReference>> childSlots =
new Pair<>(Lists.newArrayList(), Lists.newArrayList());
- List<SlotReference> leftSlots =
join.left().getOutput().stream().map(slot -> (SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> rightSlots =
join.right().getOutput().stream().map(slot -> (SlotReference) slot)
- .collect(Collectors.toList());
+ List<SlotReference> leftSlots =
Utils.getOutputSlotReference(join.left());
+ List<SlotReference> rightSlots =
Utils.getOutputSlotReference(join.right());
List<EqualTo> equalToList = getEqualTo(join);
-
for (EqualTo equalTo : equalToList) {
List<SlotReference> leftOnSlots =
equalTo.left().collect(SlotReference.class::isInstance);
List<SlotReference> rightOnSlots =
equalTo.right().collect(SlotReference.class::isInstance);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
index a5bc161570..cba61cac71 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
@@ -17,11 +17,15 @@
package org.apache.doris.nereids.util;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.Plan;
+
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang3.StringUtils;
import java.util.HashSet;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Utils for Nereids.
@@ -56,11 +60,10 @@ public class Utils {
/**
* Helper function to eliminate unnecessary checked exception caught
requirement from the main logic of translator.
- *
*/
@SuppressWarnings("unchecked")
public static <R> R execWithReturnVal(Supplier<R> f) {
- final Object[] ans = new Object[]{null};
+ final Object[] ans = new Object[] {null};
try {
ans[0] = f.get();
} catch (Exception e) {
@@ -107,4 +110,13 @@ public class Utils {
}
return new HashSet<>(one).containsAll(other) && new
HashSet<>(other).containsAll(one);
}
+
+ /**
+ * Get SlotReference from output of plam.
+ * Warning, plan must have bound, because exists Slot Cast to
SlotReference.
+ */
+ public static List<SlotReference> getOutputSlotReference(Plan plan) {
+ return plan.getOutput().stream().map(SlotReference.class::cast)
+ .collect(Collectors.toList());
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscomTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProjectTest.java
similarity index 90%
rename from
fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscomTest.java
rename to
fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProjectTest.java
index 95a106c15d..eda887fe28 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinProjectLAsscomTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomProjectTest.java
@@ -31,6 +31,7 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.util.PlanConstructor;
+import org.apache.doris.nereids.util.Utils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -41,9 +42,8 @@ import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
-import java.util.stream.Collectors;
-public class JoinProjectLAsscomTest {
+public class JoinLAsscomProjectTest {
private static final List<LogicalOlapScan> scans = Lists.newArrayList();
private static final List<List<SlotReference>> outputs =
Lists.newArrayList();
@@ -58,12 +58,9 @@ public class JoinProjectLAsscomTest {
scans.add(scan2);
scans.add(scan3);
- List<SlotReference> t1Output = scan1.getOutput().stream().map(slot ->
(SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> t2Output = scan2.getOutput().stream().map(slot ->
(SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> t3Output = scan3.getOutput().stream().map(slot ->
(SlotReference) slot)
- .collect(Collectors.toList());
+ List<SlotReference> t1Output = Utils.getOutputSlotReference(scan1);
+ List<SlotReference> t2Output = Utils.getOutputSlotReference(scan2);
+ List<SlotReference> t3Output = Utils.getOutputSlotReference(scan3);
outputs.add(t1Output);
outputs.add(t2Output);
@@ -97,7 +94,7 @@ public class JoinProjectLAsscomTest {
LogicalJoin<LogicalProject<LogicalJoin<LogicalOlapScan,
LogicalOlapScan>>, LogicalOlapScan> topJoin
= new LogicalJoin<>(JoinType.INNER_JOIN,
Optional.of(topJoinOnCondition), project, scans.get(2));
- Rule rule = new JoinProjectLAsscom().build();
+ Rule rule = new JoinLAsscomProject().build();
List<Plan> transform = rule.transform(topJoin, plannerContext);
Assertions.assertEquals(1, transform.size());
Assertions.assertTrue(transform.get(0) instanceof LogicalJoin);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomTest.java
index 11e7bbbbf1..6d7984db5d 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscomTest.java
@@ -28,6 +28,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
import org.apache.doris.nereids.util.PlanConstructor;
+import org.apache.doris.nereids.util.Utils;
import com.google.common.collect.Lists;
import mockit.Mocked;
@@ -37,7 +38,6 @@ import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
-import java.util.stream.Collectors;
public class JoinLAsscomTest {
@@ -49,17 +49,13 @@ public class JoinLAsscomTest {
LogicalOlapScan scan1 =
PlanConstructor.newLogicalOlapScanWithTable("t1");
LogicalOlapScan scan2 =
PlanConstructor.newLogicalOlapScanWithTable("t2");
LogicalOlapScan scan3 =
PlanConstructor.newLogicalOlapScanWithTable("t3");
-
scans.add(scan1);
scans.add(scan2);
scans.add(scan3);
- List<SlotReference> t1Output = scan1.getOutput().stream().map(slot ->
(SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> t2Output = scan2.getOutput().stream().map(slot ->
(SlotReference) slot)
- .collect(Collectors.toList());
- List<SlotReference> t3Output = scan3.getOutput().stream().map(slot ->
(SlotReference) slot)
- .collect(Collectors.toList());
+ List<SlotReference> t1Output = Utils.getOutputSlotReference(scan1);
+ List<SlotReference> t2Output = Utils.getOutputSlotReference(scan2);
+ List<SlotReference> t3Output = Utils.getOutputSlotReference(scan3);
outputs.add(t1Output);
outputs.add(t2Output);
outputs.add(t3Output);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]