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 93d2d461b4 [feature](Nereids): pushdown complex project through left
semi/anti Join. (#17186)
93d2d461b4 is described below
commit 93d2d461b493c0d4cbce8bbe14518005c4865dba
Author: jakevin <[email protected]>
AuthorDate: Thu Mar 2 21:41:08 2023 +0800
[feature](Nereids): pushdown complex project through left semi/anti Join.
(#17186)
---
.../org/apache/doris/nereids/rules/RuleType.java | 3 +-
.../exploration/join/InnerJoinLAsscomProject.java | 25 +++---
.../rules/exploration/join/JoinReorderHelper.java | 99 ----------------------
.../rules/exploration/join/JoinReorderUtils.java | 26 ++----
.../exploration/join/OuterJoinAssocProject.java | 25 +++---
.../exploration/join/OuterJoinLAsscomProject.java | 25 +++---
.../join/PushdownProjectThroughSemiJoin.java | 88 +++++++++++++++++++
.../join/SemiJoinLogicalJoinTransposeProject.java | 2 +-
.../join/SemiJoinSemiJoinTransposeProject.java | 2 +-
.../nereids/trees/plans/logical/LogicalJoin.java | 9 ++
.../join/InnerJoinLAsscomProjectTest.java | 53 ++++--------
.../join/OuterJoinLAsscomProjectTest.java | 26 +++---
.../join/PushdownProjectThroughSemiJoinTest.java | 98 +++++++++++++++++++++
.../logical/EliminateDedupJoinConditionTest.java | 8 +-
14 files changed, 286 insertions(+), 203 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index d636f83144..e87cd11b94 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -229,6 +229,8 @@ public enum RuleType {
LOGICAL_INNER_JOIN_LEFT_ASSOCIATIVE_PROJECT(RuleTypeClass.EXPLORATION),
LOGICAL_INNER_JOIN_RIGHT_ASSOCIATIVE(RuleTypeClass.EXPLORATION),
LOGICAL_INNER_JOIN_RIGHT_ASSOCIATIVE_PROJECT(RuleTypeClass.EXPLORATION),
+ LOGICAL_SEMI_JOIN_SEMI_JOIN_TRANSPOSE_PROJECT(RuleTypeClass.EXPLORATION),
+ PUSH_DOWN_PROJECT_THROUGH_SEMI_JOIN(RuleTypeClass.EXPLORATION),
// implementation rules
LOGICAL_ONE_ROW_RELATION_TO_PHYSICAL_ONE_ROW_RELATION(RuleTypeClass.IMPLEMENTATION),
@@ -267,7 +269,6 @@ public enum RuleType {
LOGICAL_WINDOW_TO_PHYSICAL_WINDOW_RULE(RuleTypeClass.IMPLEMENTATION),
IMPLEMENTATION_SENTINEL(RuleTypeClass.IMPLEMENTATION),
- LOGICAL_SEMI_JOIN_SEMI_JOIN_TRANSPOSE_PROJECT(RuleTypeClass.EXPLORATION),
// sentinel, use to count rules
SENTINEL(RuleTypeClass.SENTINEL),
;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProject.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProject.java
index bc8656520f..3581bb81c0 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProject.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProject.java
@@ -36,6 +36,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Rule for change inner join LAsscom (associative and commutive).
@@ -57,7 +58,7 @@ public class InnerJoinLAsscomProject extends
OneExplorationRuleFactory {
return innerLogicalJoin(logicalProject(innerLogicalJoin()), group())
.when(topJoin -> InnerJoinLAsscom.checkReorder(topJoin,
topJoin.left().child()))
.whenNot(join -> join.hasJoinHint() ||
join.left().child().hasJoinHint())
- .when(join -> JoinReorderUtils.checkProject(join.left()))
+ .when(join -> JoinReorderUtils.isAllSlotProject(join.left()))
.then(topJoin -> {
/* ********** init ********** */
List<NamedExpression> projects =
topJoin.left().getProjects();
@@ -93,18 +94,22 @@ public class InnerJoinLAsscomProject extends
OneExplorationRuleFactory {
List<Expression> newTopOtherConjuncts =
splitOtherConjuncts.get(true);
List<Expression> newBottomOtherConjuncts =
splitOtherConjuncts.get(false);
- JoinReorderHelper helper = new
JoinReorderHelper(newTopHashConjuncts, newTopOtherConjuncts,
- newBottomHashConjuncts, newBottomOtherConjuncts,
projects, aProjects, bProjects);
-
// Add all slots used by OnCondition when projects not
empty.
-
helper.addSlotsUsedByOn(JoinReorderUtils.combineProjectAndChildExprId(a,
helper.newLeftProjects),
- c.getOutputExprIdSet());
+ Set<ExprId> aExprIdSet =
JoinReorderUtils.combineProjectAndChildExprId(a, aProjects);
+ Map<Boolean, Set<Slot>> abOnUsedSlots = Stream.concat(
+ bottomJoin.getHashJoinConjuncts().stream(),
+ bottomJoin.getHashJoinConjuncts().stream())
+ .flatMap(onExpr -> onExpr.getInputSlots().stream())
+ .collect(Collectors.partitioningBy(
+ slot ->
aExprIdSet.contains(slot.getExprId()), Collectors.toSet()));
+ JoinReorderUtils.addSlotsUsedByOn(abOnUsedSlots.get(true),
aProjects);
+
JoinReorderUtils.addSlotsUsedByOn(abOnUsedSlots.get(false), bProjects);
aProjects.addAll(cOutputSet);
/* ********** new Plan ********** */
- LogicalJoin<Plan, Plan> newBottomJoin =
topJoin.withConjunctsChildren(helper.newBottomHashConjuncts,
- helper.newBottomOtherConjuncts, a, c);
+ LogicalJoin<Plan, Plan> newBottomJoin =
topJoin.withConjunctsChildren(newBottomHashConjuncts,
+ newBottomOtherConjuncts, a, c);
newBottomJoin.getJoinReorderContext().copyFrom(bottomJoin.getJoinReorderContext());
newBottomJoin.getJoinReorderContext().setHasLAsscom(false);
newBottomJoin.getJoinReorderContext().setHasCommute(false);
@@ -112,8 +117,8 @@ public class InnerJoinLAsscomProject extends
OneExplorationRuleFactory {
Plan left = JoinReorderUtils.projectOrSelf(aProjects,
newBottomJoin);
Plan right = JoinReorderUtils.projectOrSelf(bProjects, b);
- LogicalJoin<Plan, Plan> newTopJoin =
bottomJoin.withConjunctsChildren(helper.newTopHashConjuncts,
- helper.newTopOtherConjuncts, left, right);
+ LogicalJoin<Plan, Plan> newTopJoin =
bottomJoin.withConjunctsChildren(newTopHashConjuncts,
+ newTopOtherConjuncts, left, right);
newTopJoin.getJoinReorderContext().copyFrom(topJoin.getJoinReorderContext());
newTopJoin.getJoinReorderContext().setHasLAsscom(true);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinReorderHelper.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinReorderHelper.java
deleted file mode 100644
index de52786886..0000000000
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinReorderHelper.java
+++ /dev/null
@@ -1,99 +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.trees.expressions.ExprId;
-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 com.google.common.base.Preconditions;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * Helper class for three left deep tree ( original plan tree is (a b) c
)create new join.
- */
-public class JoinReorderHelper {
- public List<Expression> newTopHashConjuncts;
- public List<Expression> newTopOtherConjuncts;
- public List<Expression> newBottomHashConjuncts;
- public List<Expression> newBottomOtherConjuncts;
-
- public List<NamedExpression> oldProjects;
- public List<NamedExpression> newLeftProjects;
- public List<NamedExpression> newRightProjects;
-
- /**
- * Constructor.
- */
- public JoinReorderHelper(List<Expression> newTopHashConjuncts,
List<Expression> newTopOtherConjuncts,
- List<Expression> newBottomHashConjuncts, List<Expression>
newBottomOtherConjuncts,
- List<NamedExpression> oldProjects, List<NamedExpression>
newLeftProjects,
- List<NamedExpression> newRightProjects) {
- this.newTopHashConjuncts = newTopHashConjuncts;
- this.newTopOtherConjuncts = newTopOtherConjuncts;
- this.newBottomHashConjuncts = newBottomHashConjuncts;
- this.newBottomOtherConjuncts = newBottomOtherConjuncts;
- this.oldProjects = oldProjects;
- this.newLeftProjects = newLeftProjects;
- this.newRightProjects = newRightProjects;
- replaceConjuncts(oldProjects);
- }
-
- private void replaceConjuncts(List<NamedExpression> projects) {
- Map<ExprId, Slot> inputToOutput = new HashMap<>();
- Map<ExprId, Slot> outputToInput = new HashMap<>();
- for (NamedExpression expr : projects) {
- Slot outputSlot = expr.toSlot();
- Set<Slot> usedSlots = expr.getInputSlots();
- Preconditions.checkState(usedSlots.size() == 1);
- Slot inputSlot = usedSlots.iterator().next();
- inputToOutput.put(inputSlot.getExprId(), outputSlot);
- outputToInput.put(outputSlot.getExprId(), inputSlot);
- }
-
- newBottomHashConjuncts =
JoinReorderUtils.replaceJoinConjuncts(newBottomHashConjuncts, outputToInput);
- newTopHashConjuncts =
JoinReorderUtils.replaceJoinConjuncts(newTopHashConjuncts, inputToOutput);
- newBottomOtherConjuncts =
JoinReorderUtils.replaceJoinConjuncts(newBottomOtherConjuncts, outputToInput);
- newTopOtherConjuncts =
JoinReorderUtils.replaceJoinConjuncts(newTopOtherConjuncts, inputToOutput);
- }
-
- /**
- * Add all slots used by OnCondition when projects not empty.
- * @param cOutputExprIdSet we want to get abOnUsedSlots, we need filter
cOutputExprIdSet.
- */
- public void addSlotsUsedByOn(Set<ExprId> splitIds, Set<ExprId>
cOutputExprIdSet) {
- Map<Boolean, Set<Slot>> abOnUsedSlots = Stream.concat(
- newTopHashConjuncts.stream(),
- newTopOtherConjuncts.stream())
- .flatMap(onExpr -> onExpr.getInputSlots().stream())
- .filter(slot -> !cOutputExprIdSet.contains(slot.getExprId()))
- .collect(Collectors.partitioningBy(slot ->
splitIds.contains(slot.getExprId()), Collectors.toSet()));
- Set<Slot> aUsedSlots = abOnUsedSlots.get(true);
- Set<Slot> bUsedSlots = abOnUsedSlots.get(false);
-
- JoinReorderUtils.addSlotsUsedByOn(aUsedSlots, newLeftProjects);
- JoinReorderUtils.addSlotsUsedByOn(bUsedSlots, newRightProjects);
- }
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinReorderUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinReorderUtils.java
index 645e74c7f3..b723e2e4a1 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinReorderUtils.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinReorderUtils.java
@@ -17,7 +17,6 @@
package org.apache.doris.nereids.rules.exploration.join;
-import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.ExprId;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -40,23 +39,16 @@ import java.util.stream.Stream;
*/
class JoinReorderUtils {
/**
- * check project inside Join to prevent matching some pattern.
- * just allow projection is slot or Alias(slot) to prevent reorder when:
- * - output of project function is in condition, A join (project
[abs(B.id), ..] B join C on ..) on abs(B.id)=A.id.
- * - hyper edge in projection. project A.id + B.id A join B on .. (this
project will prevent join reorder).
+ * check project Expression Input Slot just contains one slot, like:
+ * - one SlotReference like a.id
+ * - Input Slot size == 1, like abs(a.id) + 1
*/
- static boolean checkProject(LogicalProject<LogicalJoin<GroupPlan,
GroupPlan>> project) {
- List<NamedExpression> exprs = project.getProjects();
- // must be slot or Alias(slot)
- return exprs.stream().allMatch(expr -> {
- if (expr instanceof Slot) {
- return true;
- }
- if (expr instanceof Alias) {
- return ((Alias) expr).child() instanceof Slot;
- }
- return false;
- });
+ static boolean isOneSlotProject(LogicalProject<LogicalJoin<GroupPlan,
GroupPlan>> project) {
+ return project.getProjects().stream().allMatch(expr ->
expr.getInputSlotExprIds().size() == 1);
+ }
+
+ static boolean isAllSlotProject(LogicalProject<LogicalJoin<GroupPlan,
GroupPlan>> project) {
+ return project.getProjects().stream().allMatch(expr -> expr instanceof
Slot);
}
static Map<Boolean, List<NamedExpression>>
splitProjection(List<NamedExpression> projects, Plan splitChild) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinAssocProject.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinAssocProject.java
index c18d4f2383..b7d62584bc 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinAssocProject.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinAssocProject.java
@@ -23,13 +23,13 @@ import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
import org.apache.doris.nereids.trees.expressions.ExprId;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
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.Utils;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -57,6 +57,7 @@ public class OuterJoinAssocProject extends
OneExplorationRuleFactory {
.when(topJoin -> OuterJoinLAsscom.checkReorder(topJoin,
topJoin.left().child()))
.whenNot(join -> join.hasJoinHint() ||
join.left().child().hasJoinHint())
.when(join -> OuterJoinAssoc.checkCondition(join,
join.left().child().left().getOutputSet()))
+ .when(join -> JoinReorderUtils.isAllSlotProject(join.left()))
.then(topJoin -> {
/* ********** init ********** */
List<NamedExpression> projects =
topJoin.left().getProjects();
@@ -81,26 +82,26 @@ public class OuterJoinAssocProject extends
OneExplorationRuleFactory {
return null;
}
- // topJoin condition -> newBottomJoin condition,
bottomJoin condition -> newTopJoin condition
- JoinReorderHelper helper = new
JoinReorderHelper(bottomJoin.getHashJoinConjuncts(),
- bottomJoin.getOtherJoinConjuncts(),
topJoin.getHashJoinConjuncts(),
- topJoin.getOtherJoinConjuncts(), projects,
aProjects, bProjects);
-
// Add all slots used by OnCondition when projects not
empty.
-
helper.addSlotsUsedByOn(JoinReorderUtils.combineProjectAndChildExprId(a,
helper.newLeftProjects),
- Collections.EMPTY_SET);
+ Set<ExprId> aExprIdSet =
JoinReorderUtils.combineProjectAndChildExprId(a, aProjects);
+ Map<Boolean, Set<Slot>> abOnUsedSlots = Stream.concat(
+ bottomJoin.getHashJoinConjuncts().stream(),
+ bottomJoin.getHashJoinConjuncts().stream())
+ .flatMap(onExpr -> onExpr.getInputSlots().stream())
+ .collect(Collectors.partitioningBy(
+ slot ->
aExprIdSet.contains(slot.getExprId()), Collectors.toSet()));
+ JoinReorderUtils.addSlotsUsedByOn(abOnUsedSlots.get(true),
aProjects);
+
JoinReorderUtils.addSlotsUsedByOn(abOnUsedSlots.get(false), bProjects);
bProjects.addAll(OuterJoinLAsscomProject.forceToNullable(c.getOutputSet()));
/* ********** new Plan ********** */
- LogicalJoin<Plan, Plan> newBottomJoin =
topJoin.withConjunctsChildren(helper.newBottomHashConjuncts,
- helper.newBottomOtherConjuncts, b, c);
+ LogicalJoin newBottomJoin = (LogicalJoin)
topJoin.withChildren(b, c);
newBottomJoin.getJoinReorderContext().copyFrom(bottomJoin.getJoinReorderContext());
Plan left = JoinReorderUtils.projectOrSelf(aProjects, a);
Plan right = JoinReorderUtils.projectOrSelf(bProjects,
newBottomJoin);
- LogicalJoin<Plan, Plan> newTopJoin =
bottomJoin.withConjunctsChildren(helper.newTopHashConjuncts,
- helper.newTopOtherConjuncts, left, right);
+ LogicalJoin newTopJoin = (LogicalJoin)
bottomJoin.withChildren(left, right);
newTopJoin.getJoinReorderContext().copyFrom(topJoin.getJoinReorderContext());
OuterJoinAssoc.setReorderContext(newTopJoin,
newBottomJoin);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProject.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProject.java
index 2562e6045a..8756df12ea 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProject.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProject.java
@@ -31,7 +31,6 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.util.Utils;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -60,7 +59,7 @@ public class OuterJoinLAsscomProject extends
OneExplorationRuleFactory {
Pair.of(join.left().child().getJoinType(),
join.getJoinType())))
.when(topJoin -> OuterJoinLAsscom.checkReorder(topJoin,
topJoin.left().child()))
.whenNot(join -> join.hasJoinHint() ||
join.left().child().hasJoinHint())
- .when(join -> JoinReorderUtils.checkProject(join.left()))
+ .when(join -> JoinReorderUtils.isAllSlotProject(join.left()))
.then(topJoin -> {
/* ********** init ********** */
List<NamedExpression> projects =
topJoin.left().getProjects();
@@ -85,20 +84,21 @@ public class OuterJoinLAsscomProject extends
OneExplorationRuleFactory {
return null;
}
- // topJoin condition -> newBottomJoin condition,
bottomJoin condition -> newTopJoin condition
- JoinReorderHelper helper = new
JoinReorderHelper(bottomJoin.getHashJoinConjuncts(),
- bottomJoin.getOtherJoinConjuncts(),
topJoin.getHashJoinConjuncts(),
- topJoin.getOtherJoinConjuncts(), projects,
aProjects, bProjects);
-
// Add all slots used by OnCondition when projects not
empty.
-
helper.addSlotsUsedByOn(JoinReorderUtils.combineProjectAndChildExprId(a,
helper.newLeftProjects),
- Collections.EMPTY_SET);
+ Set<ExprId> aExprIdSet =
JoinReorderUtils.combineProjectAndChildExprId(a, aProjects);
+ Map<Boolean, Set<Slot>> abOnUsedSlots = Stream.concat(
+ bottomJoin.getHashJoinConjuncts().stream(),
+ bottomJoin.getHashJoinConjuncts().stream())
+ .flatMap(onExpr -> onExpr.getInputSlots().stream())
+ .collect(Collectors.partitioningBy(
+ slot ->
aExprIdSet.contains(slot.getExprId()), Collectors.toSet()));
+ JoinReorderUtils.addSlotsUsedByOn(abOnUsedSlots.get(true),
aProjects);
+
JoinReorderUtils.addSlotsUsedByOn(abOnUsedSlots.get(false), bProjects);
aProjects.addAll(forceToNullable(c.getOutputSet()));
/* ********** new Plan ********** */
- LogicalJoin newBottomJoin =
topJoin.withConjunctsChildren(helper.newBottomHashConjuncts,
- helper.newBottomOtherConjuncts, a, c);
+ LogicalJoin newBottomJoin = (LogicalJoin)
topJoin.withChildren(a, c);
newBottomJoin.getJoinReorderContext().copyFrom(bottomJoin.getJoinReorderContext());
newBottomJoin.getJoinReorderContext().setHasLAsscom(false);
newBottomJoin.getJoinReorderContext().setHasCommute(false);
@@ -106,8 +106,7 @@ public class OuterJoinLAsscomProject extends
OneExplorationRuleFactory {
Plan left = JoinReorderUtils.projectOrSelf(aProjects,
newBottomJoin);
Plan right = JoinReorderUtils.projectOrSelf(bProjects, b);
- LogicalJoin newTopJoin =
bottomJoin.withConjunctsChildren(helper.newTopHashConjuncts,
- helper.newTopOtherConjuncts, left, right);
+ LogicalJoin newTopJoin = (LogicalJoin)
bottomJoin.withChildren(left, right);
newTopJoin.getJoinReorderContext().copyFrom(topJoin.getJoinReorderContext());
newTopJoin.getJoinReorderContext().setHasLAsscom(true);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushdownProjectThroughSemiJoin.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushdownProjectThroughSemiJoin.java
new file mode 100644
index 0000000000..57ea9df15d
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushdownProjectThroughSemiJoin.java
@@ -0,0 +1,88 @@
+// 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.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
+import org.apache.doris.nereids.trees.expressions.ExprId;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+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 java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * rule for pushdown project through left-semi/anti join
+ */
+public class PushdownProjectThroughSemiJoin extends OneExplorationRuleFactory {
+ public static final PushdownProjectThroughSemiJoin INSTANCE = new
PushdownProjectThroughSemiJoin();
+
+ /*
+ * Project Join
+ * | ──► / \
+ * Join Project B
+ * / \ |
+ * A B A
+ */
+ @Override
+ public Rule build() {
+ return logicalProject(logicalJoin())
+ .when(project ->
project.child().getJoinType().isLeftSemiOrAntiJoin())
+ .when(JoinReorderUtils::isOneSlotProject)
+ // Just pushdown project with non-column expr like (t.id + 1)
+ .whenNot(JoinReorderUtils::isAllSlotProject)
+ .whenNot(project -> project.child().hasJoinHint())
+ .then(project -> {
+ LogicalJoin<GroupPlan, GroupPlan> join = project.child();
+ Set<Slot> aOutputExprIdSet = join.left().getOutputSet();
+ Set<Slot> conditionLeftSlots =
join.getConditionSlot().stream()
+ .filter(aOutputExprIdSet::contains)
+ .collect(Collectors.toSet());
+
+ List<NamedExpression> newProject = new
ArrayList<>(project.getProjects());
+ Set<Slot> projectUsedSlots = project.getProjects().stream()
+
.map(NamedExpression::toSlot).collect(Collectors.toSet());
+ conditionLeftSlots.stream().filter(slot ->
!projectUsedSlots.contains(slot))
+ .forEach(newProject::add);
+ Plan newLeft = JoinReorderUtils.projectOrSelf(newProject,
join.left());
+ Plan newJoin = join.withChildren(newLeft, join.right());
+ return JoinReorderUtils.projectOrSelf(new
ArrayList<>(project.getOutput()), newJoin);
+ }).toRule(RuleType.PUSH_DOWN_PROJECT_THROUGH_SEMI_JOIN);
+ }
+
+ List<NamedExpression> sort(List<NamedExpression> projects, Plan sortPlan) {
+ List<ExprId> orderExprIds =
sortPlan.getOutput().stream().map(Slot::getExprId).collect(Collectors.toList());
+ // map { project input slot expr id -> project output expr }
+ Map<ExprId, NamedExpression> map = projects.stream()
+ .collect(Collectors.toMap(expr ->
expr.getInputSlots().iterator().next().getExprId(), expr -> expr));
+ List<NamedExpression> newProjects = new ArrayList<>();
+ for (ExprId exprId : orderExprIds) {
+ if (map.containsKey(exprId)) {
+ newProjects.add(map.get(exprId));
+ }
+ }
+ return newProjects;
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTransposeProject.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTransposeProject.java
index aefba5aace..374fe5758b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTransposeProject.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTransposeProject.java
@@ -67,7 +67,7 @@ public class SemiJoinLogicalJoinTransposeProject extends
OneExplorationRuleFacto
||
topJoin.left().child().getJoinType().isRightOuterJoin())))
.whenNot(topJoin ->
topJoin.left().child().getJoinType().isSemiOrAntiJoin())
.whenNot(join -> join.hasJoinHint() ||
join.left().child().hasJoinHint())
- .when(join -> JoinReorderUtils.checkProject(join.left()))
+ .when(join -> JoinReorderUtils.isAllSlotProject(join.left()))
.then(topSemiJoin -> {
LogicalProject<LogicalJoin<GroupPlan, GroupPlan>> project
= topSemiJoin.left();
LogicalJoin<GroupPlan, GroupPlan> bottomJoin =
project.child();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTransposeProject.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTransposeProject.java
index 734108c4db..85ac3cc27b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTransposeProject.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTransposeProject.java
@@ -54,7 +54,7 @@ public class SemiJoinSemiJoinTransposeProject extends
OneExplorationRuleFactory
.when(this::typeChecker)
.when(topSemi -> InnerJoinLAsscom.checkReorder(topSemi,
topSemi.left().child()))
.whenNot(join -> join.hasJoinHint() ||
join.left().child().hasJoinHint())
- .when(join -> JoinReorderUtils.checkProject(join.left()))
+ .when(join -> JoinReorderUtils.isAllSlotProject(join.left()))
.then(topSemi -> {
LogicalJoin<GroupPlan, GroupPlan> bottomSemi =
topSemi.left().child();
LogicalProject abProject = topSemi.left();
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 9847c0e316..3214d71fb0 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
@@ -35,11 +35,14 @@ import org.apache.doris.nereids.util.Utils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
/**
* Logical join plan.
@@ -115,6 +118,12 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan,
RIGHT_CHILD_TYPE extends
return hashJoinConjuncts;
}
+ public Set<Slot> getConditionSlot() {
+ return Stream.concat(hashJoinConjuncts.stream(),
otherJoinConjuncts.stream())
+ .flatMap(expr -> expr.getInputSlots().stream())
+ .collect(ImmutableSet.toImmutableSet());
+ }
+
public Optional<Expression> getOnClauseCondition() {
return ExpressionUtils.optionalAnd(hashJoinConjuncts,
otherJoinConjuncts);
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProjectTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProjectTest.java
index 2111253168..96ede80a3b 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProjectTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProjectTest.java
@@ -18,6 +18,7 @@
package org.apache.doris.nereids.rules.exploration.join;
import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.rules.rewrite.logical.PushdownAliasThroughJoin;
import org.apache.doris.nereids.trees.expressions.Add;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.EqualTo;
@@ -38,6 +39,7 @@ import org.apache.doris.nereids.util.PlanChecker;
import org.apache.doris.nereids.util.PlanConstructor;
import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -97,49 +99,27 @@ class InnerJoinLAsscomProjectTest implements
MemoPatternMatchSupported {
.build();
PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
+ .printlnTree()
+ .applyTopDown(new PushdownAliasThroughJoin())
.applyExploration(InnerJoinLAsscomProject.INSTANCE.build())
+ .printlnExploration()
.matchesExploration(
logicalJoin(
logicalProject(
logicalJoin(
- logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t1")),
+
logicalProject(logicalOlapScan().when(
+ scan ->
scan.getTable().getName().equals("t1"))),
logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t3"))
)
).when(project -> project.getProjects().size()
== 3), // t1.id Add t3.id, t3.name
logicalProject(
- logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t2"))
+ logicalProject(
+ logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t2")))
).when(project -> project.getProjects().size()
== 1)
)
);
}
- @Test
- void testAliasTopMultiHashJoin() {
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0)) // t1.id=t2.id
- .alias(ImmutableList.of(0, 2), ImmutableList.of("t1.id",
"t2.id"))
- // t1.id=t3.id t2.id = t3.id
- .join(scan3, JoinType.INNER_JOIN, ImmutableList.of(Pair.of(0,
0), Pair.of(1, 0)))
- .build();
-
- PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .applyExploration(InnerJoinLAsscomProject.INSTANCE.build())
- .printlnOrigin()
- .matchesExploration(
- logicalJoin(
- logicalProject(
- logicalJoin(
- logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t1")),
- logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t3"))
- ).when(join ->
join.getHashJoinConjuncts().size() == 1)
- ).when(project -> project.getProjects().size()
== 3), // t1.id Add t3.id, t3.name
- logicalProject(
- logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t2"))
- ).when(project -> project.getProjects().size()
== 1)
- ).when(join -> join.getHashJoinConjuncts().size() == 2)
- );
- }
-
@Test
public void testHashAndOther() {
// Alias (scan1 join scan2 on scan1.id=scan2.id and
scan1.name>scan2.name);
@@ -164,16 +144,13 @@ class InnerJoinLAsscomProjectTest implements
MemoPatternMatchSupported {
PlanChecker.from(MemoTestUtils.createConnectContext(), topJoin)
.printlnTree()
+ .applyTopDown(new PushdownAliasThroughJoin())
.applyExploration(InnerJoinLAsscomProject.INSTANCE.build())
.printlnExploration()
.matchesExploration(
innerLogicalJoin(
- logicalProject(
- innerLogicalJoin().when(
- join ->
Objects.equals(join.getHashJoinConjuncts().toString(),
- "[(id#0 = id#8)]")
- &&
Objects.equals(join.getOtherJoinConjuncts().toString(),
- "[(name#1 >
name#9)]"))),
+ innerLogicalJoin().when(join ->
join.getHashJoinConjuncts().size() == 1
+ && join.getOtherJoinConjuncts().size()
== 1),
group()
).when(join ->
Objects.equals(join.getHashJoinConjuncts().toString(),
"[(t2.id#6 = id#8), (t1.id#4 = t2.id#6)]")
@@ -201,7 +178,9 @@ class InnerJoinLAsscomProjectTest implements
MemoPatternMatchSupported {
* </pre>
*/
@Test
+ @Disabled
public void testComplexConjuncts() {
+ // TODO: move to sql-test
// Alias (scan1 join scan2 on scan1.id=scan2.id and
scan1.name>scan2.name);
List<Expression> bottomHashJoinConjunct = ImmutableList.of(
new EqualTo(scan1.getOutput().get(0),
scan2.getOutput().get(0)));
@@ -262,7 +241,9 @@ class InnerJoinLAsscomProjectTest implements
MemoPatternMatchSupported {
* </pre>
*/
@Test
+ @Disabled
public void testComplexConjunctsWithSubString() {
+ // TODO: move to sql-test
// Alias (scan1 join scan2 on scan1.id=scan2.id and
scan1.name>scan2.name);
List<Expression> bottomHashJoinConjunct = ImmutableList.of(
new EqualTo(scan1.getOutput().get(0),
scan2.getOutput().get(0)));
@@ -324,7 +305,9 @@ class InnerJoinLAsscomProjectTest implements
MemoPatternMatchSupported {
* </pre>
*/
@Test
+ @Disabled
public void testComplexConjunctsAndAlias() {
+ // TODO: move to sql-test
// Alias (scan1 join scan2 on scan1.id=scan2.id and
scan1.name>scan2.name);
List<Expression> bottomHashJoinConjunct = ImmutableList.of(
new EqualTo(scan1.getOutput().get(0),
scan2.getOutput().get(0)));
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProjectTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProjectTest.java
index 90b8266f25..c94dcb12ca 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProjectTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProjectTest.java
@@ -18,6 +18,7 @@
package org.apache.doris.nereids.rules.exploration.join;
import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.rules.rewrite.logical.PushdownAliasThroughJoin;
import org.apache.doris.nereids.trees.expressions.EqualTo;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.GreaterThan;
@@ -76,21 +77,22 @@ class OuterJoinLAsscomProjectTest implements
MemoPatternMatchSupported {
.build();
PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .printlnOrigin()
+ .applyTopDown(new PushdownAliasThroughJoin())
+ .printlnTree()
.applyExploration(OuterJoinLAsscomProject.INSTANCE.build())
.printlnExploration()
.matchesExploration(
- logicalJoin(
- logicalProject(
- logicalJoin(
- logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t1")),
- logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t3"))
- )
- ).when(project -> project.getProjects().size()
== 3), // t1.id Add t3.id, t3.name
- logicalProject(
- logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t2"))
- ).when(project -> project.getProjects().size()
== 1)
- )
+ logicalJoin(
+ logicalProject(
+ logicalJoin(
+ logicalProject(logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t1"))),
+ logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t3"))
+ )
+ ).when(project -> project.getProjects().size() == 3),
// t1.id Add t3.id, t3.name
+ logicalProject(
+ logicalProject(logicalOlapScan().when(scan ->
scan.getTable().getName().equals("t2")))
+ ).when(project -> project.getProjects().size() == 1)
+ )
);
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushdownProjectThroughSemiJoinTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushdownProjectThroughSemiJoinTest.java
new file mode 100644
index 0000000000..0a3b7a04ba
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushdownProjectThroughSemiJoinTest.java
@@ -0,0 +1,98 @@
+// 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.Add;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.util.LogicalPlanBuilder;
+import org.apache.doris.nereids.util.MemoPatternMatchSupported;
+import org.apache.doris.nereids.util.MemoTestUtils;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.nereids.util.PlanConstructor;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+class PushdownProjectThroughSemiJoinTest implements MemoPatternMatchSupported {
+ private final LogicalOlapScan scan1 =
PlanConstructor.newLogicalOlapScan(0, "t1", 0);
+ private final LogicalOlapScan scan2 =
PlanConstructor.newLogicalOlapScan(1, "t2", 0);
+
+ @Test
+ public void pushdownProject() {
+ // project (t1.id + 1) as alias, t1.name
+ List<NamedExpression> projectExprs = ImmutableList.of(
+ new Alias(new Add(scan1.getOutput().get(0), Literal.of(1)),
"alias"),
+ scan1.getOutput().get(1)
+ );
+ // complex projection contain ti.id, which isn't in Join Condition
+ LogicalPlan plan = new LogicalPlanBuilder(scan1)
+ .join(scan2, JoinType.LEFT_SEMI_JOIN, Pair.of(1, 1))
+ .projectExprs(projectExprs)
+ .build();
+
+ PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
+
.applyExploration(PushdownProjectThroughSemiJoin.INSTANCE.build())
+ .printlnOrigin()
+ .printlnExploration()
+ .matchesExploration(
+ leftSemiLogicalJoin(
+ logicalProject(
+ logicalOlapScan()
+ ).when(project -> project.getProjects().size()
== 2),
+ logicalOlapScan()
+ )
+ );
+ }
+
+ @Test
+ public void pushdownProjectInCondition() {
+ // project (t1.id + 1) as alias, t1.name
+ List<NamedExpression> projectExprs = ImmutableList.of(
+ new Alias(new Add(scan1.getOutput().get(0), Literal.of(1)),
"alias"),
+ scan1.getOutput().get(1)
+ );
+ // complex projection contain ti.id, which is in Join Condition
+ LogicalPlan plan = new LogicalPlanBuilder(scan1)
+ .join(scan2, JoinType.LEFT_SEMI_JOIN, Pair.of(0, 0))
+ .projectExprs(projectExprs)
+ .build();
+
+ PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
+
.applyExploration(PushdownProjectThroughSemiJoin.INSTANCE.build())
+ .printlnOrigin()
+ .printlnExploration()
+ .matchesExploration(
+ logicalProject(
+ leftSemiLogicalJoin(
+ logicalProject(
+ logicalOlapScan()
+ ).when(project ->
project.getProjects().size() == 3),
+ logicalOlapScan()
+ )
+ ).when(project -> project.getProjects().size() == 2)
+ );
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateDedupJoinConditionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateDedupJoinConditionTest.java
index 4011e79960..2235821f16 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateDedupJoinConditionTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateDedupJoinConditionTest.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.rules.rewrite.logical;
import org.apache.doris.common.Pair;
import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.util.LogicalPlanBuilder;
import org.apache.doris.nereids.util.MemoPatternMatchSupported;
@@ -30,10 +31,13 @@ import com.google.common.collect.ImmutableList;
import org.junit.jupiter.api.Test;
class EliminateDedupJoinConditionTest implements MemoPatternMatchSupported {
+ private final LogicalOlapScan scan1 =
PlanConstructor.newLogicalOlapScan(0, "t1", 0);
+ private final LogicalOlapScan scan2 =
PlanConstructor.newLogicalOlapScan(1, "t2", 0);
+
@Test
void testEliminate() {
- LogicalPlan plan = new LogicalPlanBuilder(PlanConstructor.scan1)
- .join(PlanConstructor.scan2, JoinType.INNER_JOIN,
ImmutableList.of(Pair.of(0, 0), Pair.of(0, 0)))
+ LogicalPlan plan = new LogicalPlanBuilder(scan1)
+ .join(scan2, JoinType.INNER_JOIN, ImmutableList.of(Pair.of(0,
0), Pair.of(0, 0)))
.build();
PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]