[feature](Nereids): InnerJoinLeftAssociate, InnerJoinRightAssociate and JoinExchange. (#14051)
This commit is contained in:
@ -140,7 +140,6 @@ public enum RuleType {
|
||||
// exploration rules
|
||||
TEST_EXPLORATION(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_JOIN_COMMUTATE(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_LEFT_JOIN_ASSOCIATIVE(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_INNER_JOIN_LASSCOM(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_INNER_JOIN_LASSCOM_PROJECT(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_OUTER_JOIN_LASSCOM(RuleTypeClass.EXPLORATION),
|
||||
@ -148,6 +147,14 @@ public enum RuleType {
|
||||
LOGICAL_SEMI_JOIN_LOGICAL_JOIN_TRANSPOSE(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_SEMI_JOIN_LOGICAL_JOIN_TRANSPOSE_PROJECT(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_SEMI_JOIN_SEMI_JOIN_TRANPOSE(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_JOIN_EXCHANGE(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_JOIN_EXCHANGE_LEFT_PROJECT(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_JOIN_EXCHANGE_RIGHT_PROJECT(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_JOIN_EXCHANGE_BOTH_PROJECT(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_INNER_JOIN_LEFT_ASSOCIATIVE(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_INNER_JOIN_LEFT_ASSOCIATIVE_PROJECT(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_INNER_JOIN_RIGHT_ASSOCIATIVE(RuleTypeClass.EXPLORATION),
|
||||
LOGICAL_INNER_JOIN_RIGHT_ASSOCIATIVE_PROJECT(RuleTypeClass.EXPLORATION),
|
||||
|
||||
// implementation rules
|
||||
LOGICAL_ONE_ROW_RELATION_TO_PHYSICAL_ONE_ROW_RELATION(RuleTypeClass.IMPLEMENTATION),
|
||||
@ -183,8 +190,8 @@ public enum RuleType {
|
||||
return ruleTypeClass;
|
||||
}
|
||||
|
||||
public <INPUT_TYPE extends Plan, OUTPUT_TYPE extends Plan>
|
||||
Rule build(PatternMatcher<INPUT_TYPE, OUTPUT_TYPE> patternMatcher) {
|
||||
public <INPUT_TYPE extends Plan, OUTPUT_TYPE extends Plan> Rule build(
|
||||
PatternMatcher<INPUT_TYPE, OUTPUT_TYPE> patternMatcher) {
|
||||
return patternMatcher.toRule(this);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,121 @@
|
||||
// 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.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.Slot;
|
||||
import org.apache.doris.nereids.trees.plans.GroupPlan;
|
||||
import org.apache.doris.nereids.trees.plans.JoinType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.util.JoinUtils;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Rule for inner join LeftAssociate.
|
||||
*/
|
||||
public class InnerJoinLeftAssociate extends OneExplorationRuleFactory {
|
||||
/*
|
||||
* topJoin newTopJoin
|
||||
* / \ / \
|
||||
* A bottomJoin -> newBottomJoin C
|
||||
* / \ / \
|
||||
* B C A B
|
||||
*/
|
||||
public static final InnerJoinLeftAssociate INSTANCE = new InnerJoinLeftAssociate();
|
||||
|
||||
@Override
|
||||
public Rule build() {
|
||||
return innerLogicalJoin(group(), innerLogicalJoin())
|
||||
.when(InnerJoinLeftAssociate::checkReorder)
|
||||
.then(topJoin -> {
|
||||
LogicalJoin<GroupPlan, GroupPlan> bottomJoin = topJoin.right();
|
||||
GroupPlan a = topJoin.left();
|
||||
GroupPlan b = bottomJoin.left();
|
||||
GroupPlan c = bottomJoin.right();
|
||||
|
||||
// Split condition
|
||||
Set<Slot> abOutputSet = JoinUtils.getJoinOutputSet(a, b);
|
||||
Map<Boolean, List<Expression>> hashConjunctsSplit = Stream.concat(
|
||||
topJoin.getHashJoinConjuncts().stream(),
|
||||
bottomJoin.getHashJoinConjuncts().stream())
|
||||
.collect(Collectors.partitioningBy(condition -> {
|
||||
Set<Slot> usedSlot = condition.getInputSlots();
|
||||
return abOutputSet.containsAll(usedSlot);
|
||||
}));
|
||||
|
||||
Map<Boolean, List<Expression>> otherConjunctsSplit = Stream.concat(
|
||||
topJoin.getOtherJoinConjuncts().stream(),
|
||||
bottomJoin.getOtherJoinConjuncts().stream())
|
||||
.collect(Collectors.partitioningBy(condition -> {
|
||||
Set<Slot> usedSlot = condition.getInputSlots();
|
||||
return abOutputSet.containsAll(usedSlot);
|
||||
}));
|
||||
|
||||
List<Expression> newBottomHashJoinConjuncts = hashConjunctsSplit.get(true);
|
||||
List<Expression> newTopHashJoinConjuncts = hashConjunctsSplit.get(false);
|
||||
Preconditions.checkArgument(newTopHashJoinConjuncts.size() > 0);
|
||||
if (newBottomHashJoinConjuncts.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Expression> newBottomOtherJoinConjuncts = otherConjunctsSplit.get(true);
|
||||
List<Expression> newTopOtherJoinConjuncts = otherConjunctsSplit.get(false);
|
||||
|
||||
// new join.
|
||||
LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new LogicalJoin<>(JoinType.INNER_JOIN,
|
||||
newBottomHashJoinConjuncts, newBottomOtherJoinConjuncts,
|
||||
a, b, bottomJoin.getJoinReorderContext());
|
||||
newBottomJoin.getJoinReorderContext().setHasCommute(false);
|
||||
newBottomJoin.getJoinReorderContext().setHasRightAssociate(false);
|
||||
newBottomJoin.getJoinReorderContext().setHasLeftAssociate(false);
|
||||
newBottomJoin.getJoinReorderContext().setHasExchange(false);
|
||||
|
||||
LogicalJoin<LogicalJoin<GroupPlan, GroupPlan>, GroupPlan> newTopJoin = new LogicalJoin<>(
|
||||
JoinType.INNER_JOIN, newTopHashJoinConjuncts, newTopOtherJoinConjuncts,
|
||||
newBottomJoin, c, topJoin.getJoinReorderContext());
|
||||
newTopJoin.getJoinReorderContext().setHasLeftAssociate(true);
|
||||
newTopJoin.getJoinReorderContext().setHasCommute(false);
|
||||
|
||||
return newTopJoin;
|
||||
}).toRule(RuleType.LOGICAL_INNER_JOIN_LEFT_ASSOCIATIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check JoinReorderContext.
|
||||
*/
|
||||
public static boolean checkReorder(LogicalJoin<GroupPlan, ? extends Plan> topJoin) {
|
||||
if (topJoin.getJoinReorderContext().hasCommute()
|
||||
|| topJoin.getJoinReorderContext().hasLeftAssociate()
|
||||
|| topJoin.getJoinReorderContext().hasRightAssociate()
|
||||
|| topJoin.getJoinReorderContext().hasExchange()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
// 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.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.Slot;
|
||||
import org.apache.doris.nereids.trees.plans.GroupPlan;
|
||||
import org.apache.doris.nereids.trees.plans.JoinType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.util.JoinUtils;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Rule for inner join RightAssociate.
|
||||
*/
|
||||
public class InnerJoinRightAssociate extends OneExplorationRuleFactory {
|
||||
// topJoin newTopJoin
|
||||
// / \ / \
|
||||
// bottomJoin C -> A newBottomJoin
|
||||
// / \ / \
|
||||
// A B B C
|
||||
|
||||
public static final InnerJoinRightAssociate INSTANCE = new InnerJoinRightAssociate();
|
||||
|
||||
@Override
|
||||
public Rule build() {
|
||||
return innerLogicalJoin(innerLogicalJoin(), group())
|
||||
.when(InnerJoinRightAssociate::checkReorder)
|
||||
.then(topJoin -> {
|
||||
LogicalJoin<GroupPlan, GroupPlan> bottomJoin = topJoin.left();
|
||||
GroupPlan a = bottomJoin.left();
|
||||
GroupPlan b = bottomJoin.right();
|
||||
GroupPlan c = topJoin.right();
|
||||
|
||||
// Split condition
|
||||
Set<Slot> bcOutputSet = JoinUtils.getJoinOutputSet(b, c);
|
||||
Map<Boolean, List<Expression>> hashConjunctsSplit = Stream.concat(
|
||||
topJoin.getHashJoinConjuncts().stream(),
|
||||
bottomJoin.getHashJoinConjuncts().stream())
|
||||
.collect(Collectors.partitioningBy(condition -> {
|
||||
Set<Slot> usedSlot = condition.getInputSlots();
|
||||
return bcOutputSet.containsAll(usedSlot);
|
||||
}));
|
||||
|
||||
Map<Boolean, List<Expression>> otherConjunctsSplit = Stream.concat(
|
||||
topJoin.getOtherJoinConjuncts().stream(),
|
||||
bottomJoin.getOtherJoinConjuncts().stream())
|
||||
.collect(Collectors.partitioningBy(condition -> {
|
||||
Set<Slot> usedSlot = condition.getInputSlots();
|
||||
return bcOutputSet.containsAll(usedSlot);
|
||||
}));
|
||||
|
||||
List<Expression> newBottomHashJoinConjuncts = hashConjunctsSplit.get(true);
|
||||
List<Expression> newTopHashJoinConjuncts = hashConjunctsSplit.get(false);
|
||||
Preconditions.checkArgument(newTopHashJoinConjuncts.size() > 0);
|
||||
if (newBottomHashJoinConjuncts.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Expression> newBottomOtherJoinConjuncts = otherConjunctsSplit.get(true);
|
||||
List<Expression> newTopOtherJoinConjuncts = otherConjunctsSplit.get(false);
|
||||
|
||||
LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new LogicalJoin<>(JoinType.INNER_JOIN,
|
||||
newBottomHashJoinConjuncts, newBottomOtherJoinConjuncts,
|
||||
b, c, bottomJoin.getJoinReorderContext());
|
||||
newBottomJoin.getJoinReorderContext().setHasCommute(false);
|
||||
newBottomJoin.getJoinReorderContext().setHasRightAssociate(false);
|
||||
newBottomJoin.getJoinReorderContext().setHasLeftAssociate(false);
|
||||
newBottomJoin.getJoinReorderContext().setHasExchange(false);
|
||||
|
||||
LogicalJoin<GroupPlan, LogicalJoin<GroupPlan, GroupPlan>> newTopJoin = new LogicalJoin<>(
|
||||
JoinType.INNER_JOIN, newTopHashJoinConjuncts, newTopOtherJoinConjuncts,
|
||||
a, newBottomJoin, topJoin.getJoinReorderContext());
|
||||
newTopJoin.getJoinReorderContext().setHasRightAssociate(true);
|
||||
newTopJoin.getJoinReorderContext().setHasCommute(false);
|
||||
|
||||
return newTopJoin;
|
||||
}).toRule(RuleType.LOGICAL_INNER_JOIN_RIGHT_ASSOCIATIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check JoinReorderContext.
|
||||
*/
|
||||
public static boolean checkReorder(LogicalJoin<? extends Plan, GroupPlan> topJoin) {
|
||||
return !topJoin.getJoinReorderContext().hasCommute()
|
||||
&& !topJoin.getJoinReorderContext().hasRightAssociate()
|
||||
&& !topJoin.getJoinReorderContext().hasRightAssociate()
|
||||
&& !topJoin.getJoinReorderContext().hasExchange();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,139 @@
|
||||
// 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.Slot;
|
||||
import org.apache.doris.nereids.trees.plans.GroupPlan;
|
||||
import org.apache.doris.nereids.trees.plans.JoinType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.util.JoinUtils;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* rule factory for exchange without inside-project.
|
||||
*/
|
||||
@Developing
|
||||
public class JoinExchange extends OneExplorationRuleFactory {
|
||||
public static final JoinExchange INSTANCE = new JoinExchange();
|
||||
|
||||
/*
|
||||
* topJoin newTopJoin
|
||||
* / \ / \
|
||||
* leftJoin rightJoin --> newLeftJoin newRightJoin
|
||||
* / \ / \ / \ / \
|
||||
* A B C D A C B D
|
||||
*/
|
||||
@Override
|
||||
public Rule build() {
|
||||
return innerLogicalJoin(innerLogicalJoin(), innerLogicalJoin())
|
||||
.when(JoinExchange::checkReorder)
|
||||
.then(topJoin -> {
|
||||
LogicalJoin<GroupPlan, GroupPlan> leftJoin = topJoin.left();
|
||||
LogicalJoin<GroupPlan, GroupPlan> rightJoin = topJoin.right();
|
||||
GroupPlan a = leftJoin.left();
|
||||
GroupPlan b = leftJoin.right();
|
||||
GroupPlan c = rightJoin.left();
|
||||
GroupPlan d = rightJoin.right();
|
||||
|
||||
Set<Slot> acOutputSet = JoinUtils.getJoinOutputSet(a, c);
|
||||
Set<Slot> bdOutputSet = JoinUtils.getJoinOutputSet(b, d);
|
||||
|
||||
List<Expression> newLeftJoinHashJoinConjuncts = Lists.newArrayList();
|
||||
List<Expression> newRightJoinHashJoinConjuncts = Lists.newArrayList();
|
||||
List<Expression> newTopJoinHashJoinConjuncts = new ArrayList<>(leftJoin.getHashJoinConjuncts());
|
||||
newTopJoinHashJoinConjuncts.addAll(rightJoin.getHashJoinConjuncts());
|
||||
splitTopConditon(topJoin.getHashJoinConjuncts(), acOutputSet, bdOutputSet,
|
||||
newLeftJoinHashJoinConjuncts, newRightJoinHashJoinConjuncts, newTopJoinHashJoinConjuncts);
|
||||
|
||||
List<Expression> newLeftJoinOtherJoinConjuncts = Lists.newArrayList();
|
||||
List<Expression> newRightJoinOtherJoinConjuncts = Lists.newArrayList();
|
||||
List<Expression> newTopJoinOtherJoinConjuncts = new ArrayList<>(leftJoin.getOtherJoinConjuncts());
|
||||
newTopJoinOtherJoinConjuncts.addAll(rightJoin.getOtherJoinConjuncts());
|
||||
splitTopConditon(topJoin.getOtherJoinConjuncts(), acOutputSet, bdOutputSet,
|
||||
newLeftJoinOtherJoinConjuncts, newRightJoinOtherJoinConjuncts,
|
||||
newTopJoinOtherJoinConjuncts);
|
||||
|
||||
if (newLeftJoinHashJoinConjuncts.size() == 0 || newRightJoinHashJoinConjuncts.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
LogicalJoin<GroupPlan, GroupPlan> newLeftJoin = new LogicalJoin<>(JoinType.INNER_JOIN,
|
||||
newLeftJoinHashJoinConjuncts, newLeftJoinOtherJoinConjuncts,
|
||||
a, c, leftJoin.getJoinReorderContext());
|
||||
newLeftJoin.getJoinReorderContext().setHasCommute(false);
|
||||
newLeftJoin.getJoinReorderContext().setHasLeftAssociate(false);
|
||||
newLeftJoin.getJoinReorderContext().setHasRightAssociate(false);
|
||||
newLeftJoin.getJoinReorderContext().setHasExchange(false);
|
||||
|
||||
LogicalJoin<GroupPlan, GroupPlan> newRightJoin = new LogicalJoin<>(JoinType.INNER_JOIN,
|
||||
newRightJoinHashJoinConjuncts, newRightJoinOtherJoinConjuncts,
|
||||
b, d, rightJoin.getJoinReorderContext());
|
||||
newRightJoin.getJoinReorderContext().setHasCommute(false);
|
||||
newRightJoin.getJoinReorderContext().setHasLeftAssociate(false);
|
||||
newRightJoin.getJoinReorderContext().setHasRightAssociate(false);
|
||||
newRightJoin.getJoinReorderContext().setHasExchange(false);
|
||||
|
||||
LogicalJoin<LogicalJoin<GroupPlan, GroupPlan>, LogicalJoin<GroupPlan, GroupPlan>>
|
||||
newTopJoin = new LogicalJoin<>(JoinType.INNER_JOIN,
|
||||
newTopJoinHashJoinConjuncts, newTopJoinOtherJoinConjuncts,
|
||||
newLeftJoin, newRightJoin, topJoin.getJoinReorderContext());
|
||||
newTopJoin.getJoinReorderContext().setHasExchange(true);
|
||||
|
||||
return newTopJoin;
|
||||
}).toRule(RuleType.LOGICAL_JOIN_EXCHANGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* check reorder masks.
|
||||
*/
|
||||
public static boolean checkReorder(LogicalJoin<? extends Plan, ? extends Plan> topJoin) {
|
||||
if (topJoin.getJoinReorderContext().hasCommute()
|
||||
|| topJoin.getJoinReorderContext().hasLeftAssociate()
|
||||
|| topJoin.getJoinReorderContext().hasRightAssociate()
|
||||
|| topJoin.getJoinReorderContext().hasExchange()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void splitTopConditon(List<Expression> topCondition, Set<Slot> acOutputSet, Set<Slot> bdOutputSet,
|
||||
List<Expression> newLeftCondition, List<Expression> newRightCondition,
|
||||
List<Expression> remainTopCondition) {
|
||||
for (Expression hashJoinConjunct : topCondition) {
|
||||
Set<Slot> inputSlots = hashJoinConjunct.getInputSlots();
|
||||
if (acOutputSet.containsAll(inputSlots)) {
|
||||
newLeftCondition.add(hashJoinConjunct);
|
||||
} else if (bdOutputSet.containsAll(inputSlots)) {
|
||||
newRightCondition.add(hashJoinConjunct);
|
||||
} else {
|
||||
remainTopCondition.add(hashJoinConjunct);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -303,13 +303,6 @@ public class ReorderJoin extends OneRewriteRuleFactory {
|
||||
&& !changeChildren;
|
||||
}
|
||||
|
||||
private Set<Slot> getJoinOutput(Plan left, Plan right) {
|
||||
HashSet<Slot> joinOutput = new HashSet<>();
|
||||
joinOutput.addAll(left.getOutput());
|
||||
joinOutput.addAll(right.getOutput());
|
||||
return joinOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find hash condition from joinFilter
|
||||
* Get InnerJoin from left, right from [candidates].
|
||||
@ -328,7 +321,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
|
||||
Plan candidate = candidates.get(i);
|
||||
Set<Slot> rightOutputSet = candidate.getOutputSet();
|
||||
|
||||
Set<Slot> joinOutput = getJoinOutput(left, candidate);
|
||||
Set<Slot> joinOutput = JoinUtils.getJoinOutputSet(left, candidate);
|
||||
|
||||
List<Expression> currentJoinFilter = joinFilter.stream()
|
||||
.filter(expr -> {
|
||||
|
||||
@ -40,6 +40,7 @@ import org.apache.doris.qe.ConnectContext;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -318,4 +319,11 @@ public class JoinUtils {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Set<Slot> getJoinOutputSet(Plan left, Plan right) {
|
||||
HashSet<Slot> joinOutput = new HashSet<>();
|
||||
joinOutput.addAll(left.getOutput());
|
||||
joinOutput.addAll(right.getOutput());
|
||||
return joinOutput;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
// 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.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.MemoTestUtils;
|
||||
import org.apache.doris.nereids.util.PatternMatchSupported;
|
||||
import org.apache.doris.nereids.util.PlanChecker;
|
||||
import org.apache.doris.nereids.util.PlanConstructor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class InnerJoinLeftAssociateTest implements PatternMatchSupported {
|
||||
private final LogicalOlapScan scan1 = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
|
||||
private final LogicalOlapScan scan2 = PlanConstructor.newLogicalOlapScan(1, "t2", 0);
|
||||
private final LogicalOlapScan scan3 = PlanConstructor.newLogicalOlapScan(2, "t3", 0);
|
||||
|
||||
@Test
|
||||
public void testSimple() {
|
||||
/*
|
||||
* LogicalJoin ( type=INNER_JOIN, hashJoinConjuncts=[(id#4 = id#0)], otherJoinConjuncts=[] )
|
||||
* |--LogicalOlapScan ( qualified=db.t1, output=[id#4, name#5], candidateIndexIds=[], selectedIndexId=-1, preAgg=ON )
|
||||
* +--LogicalJoin ( type=INNER_JOIN, hashJoinConjuncts=[(id#0 = id#2)], otherJoinConjuncts=[] )
|
||||
* |--LogicalOlapScan ( qualified=db.t2, output=[id#0, name#1], candidateIndexIds=[], selectedIndexId=-1, preAgg=ON )
|
||||
* +--LogicalOlapScan ( qualified=db.t3, output=[id#2, name#3], candidateIndexIds=[], selectedIndexId=-1, preAgg=ON )
|
||||
*/
|
||||
LogicalPlan plan = new LogicalPlanBuilder(scan1)
|
||||
.hashJoinUsing(
|
||||
new LogicalPlanBuilder(scan2)
|
||||
.hashJoinUsing(scan3, JoinType.INNER_JOIN, Pair.of(0, 0))
|
||||
.build(),
|
||||
JoinType.INNER_JOIN, Pair.of(0, 0)
|
||||
)
|
||||
.build();
|
||||
|
||||
PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
|
||||
.applyExploration(InnerJoinLeftAssociate.INSTANCE.build())
|
||||
.printlnOrigin()
|
||||
.matchesExploration(
|
||||
logicalJoin(
|
||||
logicalJoin(
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t1")),
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t2"))
|
||||
),
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t3"))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
// 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.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.MemoTestUtils;
|
||||
import org.apache.doris.nereids.util.PatternMatchSupported;
|
||||
import org.apache.doris.nereids.util.PlanChecker;
|
||||
import org.apache.doris.nereids.util.PlanConstructor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class InnerJoinRightAssociateTest implements PatternMatchSupported {
|
||||
private final LogicalOlapScan scan1 = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
|
||||
private final LogicalOlapScan scan2 = PlanConstructor.newLogicalOlapScan(1, "t2", 0);
|
||||
private final LogicalOlapScan scan3 = PlanConstructor.newLogicalOlapScan(2, "t3", 0);
|
||||
|
||||
@Test
|
||||
public void testSimple() {
|
||||
/*-
|
||||
* LogicalJoin ( type=INNER_JOIN, hashJoinConjuncts=[(id#2 = id#4)], otherJoinConjuncts=[] )
|
||||
* |--LogicalJoin ( type=INNER_JOIN, hashJoinConjuncts=[(id#0 = id#2)], otherJoinConjuncts=[] )
|
||||
* | |--LogicalOlapScan ( qualified=db.t1, output=[id#0, name#1], candidateIndexIds=[], selectedIndexId=-1, preAgg=ON )
|
||||
* | +--LogicalOlapScan ( qualified=db.t2, output=[id#2, name#3], candidateIndexIds=[], selectedIndexId=-1, preAgg=ON )
|
||||
* +--LogicalOlapScan ( qualified=db.t3, output=[id#4, name#5], candidateIndexIds=[], selectedIndexId=-1, preAgg=ON )
|
||||
*/
|
||||
LogicalPlan plan = new LogicalPlanBuilder(scan1)
|
||||
.hashJoinUsing(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
|
||||
.hashJoinUsing(scan3, JoinType.INNER_JOIN, Pair.of(2, 0))
|
||||
.build();
|
||||
|
||||
PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
|
||||
.applyExploration(InnerJoinRightAssociate.INSTANCE.build())
|
||||
.printlnOrigin()
|
||||
.printlnExploration()
|
||||
.matchesExploration(
|
||||
logicalJoin(
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t1")),
|
||||
logicalJoin(
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t2")),
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t3"))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
// 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.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.MemoTestUtils;
|
||||
import org.apache.doris.nereids.util.PatternMatchSupported;
|
||||
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;
|
||||
|
||||
class JoinExchangeTest implements PatternMatchSupported {
|
||||
@Test
|
||||
public void testSimple() {
|
||||
LogicalOlapScan scan1 = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
|
||||
LogicalOlapScan scan2 = PlanConstructor.newLogicalOlapScan(1, "t2", 0);
|
||||
LogicalOlapScan scan3 = PlanConstructor.newLogicalOlapScan(2, "t3", 0);
|
||||
LogicalOlapScan scan4 = PlanConstructor.newLogicalOlapScan(3, "t4", 0);
|
||||
|
||||
LogicalPlan plan = new LogicalPlanBuilder(
|
||||
new LogicalPlanBuilder(scan1)
|
||||
.hashJoinUsing(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
|
||||
.build())
|
||||
.hashJoinUsing(
|
||||
new LogicalPlanBuilder(scan3)
|
||||
.hashJoinUsing(scan4, JoinType.INNER_JOIN, Pair.of(0, 0))
|
||||
.build(),
|
||||
JoinType.INNER_JOIN, ImmutableList.of(Pair.of(0, 0), Pair.of(2, 2)))
|
||||
.build();
|
||||
|
||||
PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
|
||||
.applyExploration(JoinExchange.INSTANCE.build())
|
||||
.matchesExploration(
|
||||
logicalJoin(
|
||||
logicalJoin(
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t1")),
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t3"))
|
||||
),
|
||||
logicalJoin(
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t2")),
|
||||
logicalOlapScan().when(scan -> scan.getTable().getName().equals("t4"))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user