[feature](Nereids): InnerJoinLeftAssociate, InnerJoinRightAssociate and JoinExchange. (#14051)

This commit is contained in:
jakevin
2022-11-10 12:21:06 +08:00
committed by GitHub
parent 8c5c6d9d7f
commit 0dfdbe4508
9 changed files with 596 additions and 11 deletions

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}
}

View File

@ -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 -> {

View File

@ -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;
}
}

View File

@ -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"))
)
);
}
}

View File

@ -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"))
)
)
);
}
}

View File

@ -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"))
)
)
);
}
}