Skip to content
Permalink
Browse files
[feature](nereids): add join rules base code (#9598)
  • Loading branch information
jackwener committed May 20, 2022
1 parent 2c79d22 commit c2d41c84bf6e820e382c950b424d96db3a2247c5
Showing 9 changed files with 240 additions and 17 deletions.
@@ -18,8 +18,8 @@
package org.apache.doris.nereids.rules;

import org.apache.doris.nereids.rules.analysis.AnalysisUnboundRelation;
import org.apache.doris.nereids.rules.exploration.JoinAssociativeLeftToRight;
import org.apache.doris.nereids.rules.exploration.JoinCommutative;
import org.apache.doris.nereids.rules.exploration.join.JoinCommutative;
import org.apache.doris.nereids.rules.exploration.join.JoinLeftAssociative;
import org.apache.doris.nereids.rules.implementation.LogicalJoinToHashJoin;
import org.apache.doris.nereids.trees.TreeNode;
import org.apache.doris.nereids.trees.expressions.Expression;
@@ -39,8 +39,8 @@ public class RuleSet {
.build();

public static final List<Rule<Plan>> EXPLORATION_RULES = planRuleFactories()
.add(new JoinCommutative())
.add(new JoinAssociativeLeftToRight())
.add(new JoinCommutative(false))
.add(new JoinLeftAssociative())
.build();

public static final List<Rule<Plan>> IMPLEMENTATION_RULES = planRuleFactories()
@@ -29,7 +29,9 @@ public enum RuleType {

// exploration rules
LOGICAL_JOIN_COMMUTATIVE,
LOGICAL_JOIN_ASSOCIATIVE_LEFT_TO_RIGHT,
LOGICAL_LEFT_JOIN_ASSOCIATIVE,
LOGICAL_JOIN_L_ASSCOM,
LOGICAL_JOIN_EXCHANGE,

// implementation rules
LOGICAL_JOIN_TO_HASH_JOIN_RULE,
@@ -15,28 +15,43 @@
// specific language governing permissions and limitations
// under the License.

package org.apache.doris.nereids.rules.exploration;
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.trees.plans.JoinType;
import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;

/**
* rule factory for exchange inner join's children.
*/
public class JoinCommutative extends OneExplorationRuleFactory {
private boolean justApplyInnerOuterCrossJoin = false;

private final SwapType swapType;

/**
* If param is true, just apply rule in inner/full-outer/cross join.
*/
public JoinCommutative(boolean justApplyInnerOuterCrossJoin) {
this.justApplyInnerOuterCrossJoin = justApplyInnerOuterCrossJoin;
this.swapType = SwapType.ALL;
}

public JoinCommutative(boolean justApplyInnerOuterCrossJoin, SwapType swapType) {
this.justApplyInnerOuterCrossJoin = justApplyInnerOuterCrossJoin;
this.swapType = swapType;
}

enum SwapType {
BOTTOM_JOIN, ZIG_ZAG, ALL
}

@Override
public Rule<Plan> build() {
return innerLogicalJoin().then(join -> {
// fixme, just for example now
return new LogicalJoin(
JoinType.INNER_JOIN,
join.getOnClause(),
join.right(),
join.left()
);
return new LogicalJoin(join.getJoinType().swap(), join.getOnClause(), join.right(), join.left());
}).toRule(RuleType.LOGICAL_JOIN_COMMUTATIVE);
}
}
@@ -0,0 +1,57 @@
// 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.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;


/**
* Rule for busy-tree, exchange the children node.
*/
public class JoinExchange extends OneExplorationRuleFactory {
/*
* topJoin newTopJoin
* / \ / \
* leftJoin rightJoin --> newLeftJoin newRightJoin
* / \ / \ / \ / \
* A B C D A C B D
*/
@Override
public Rule<Plan> build() {
return innerLogicalJoin(innerLogicalJoin(), innerLogicalJoin()).then(topJoin -> {
LogicalJoin leftJoin = topJoin.left();
LogicalJoin rightJoin = topJoin.left();

Plan a = leftJoin.left();
Plan b = leftJoin.right();
Plan c = rightJoin.left();
Plan d = rightJoin.right();

LogicalJoin newLeftJoin = new LogicalJoin(leftJoin.getJoinType(), leftJoin.getOnClause(), a, c);
LogicalJoin newRightJoin = new LogicalJoin(rightJoin.getJoinType(), rightJoin.getOnClause(), b, d);
LogicalJoin newTopJoin =
new LogicalJoin(topJoin.getJoinType(), topJoin.getOnClause(), newLeftJoin, newRightJoin);

return newTopJoin;
}).toRule(RuleType.LOGICAL_JOIN_EXCHANGE);
}
}
@@ -0,0 +1,54 @@
// 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.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;


/**
* Rule for change inner join left associative to right.
*/
public class JoinLAsscom extends OneExplorationRuleFactory {
/*
* topJoin newTopJoin
* / \ / \
* bottomJoin C --> newBottomJoin B
* / \ / \
* A B A C
*/
@Override
public Rule<Plan> build() {
return innerLogicalJoin(innerLogicalJoin(), any()).then(topJoin -> {
LogicalJoin bottomJoin = topJoin.left();

Plan c = topJoin.right();
Plan a = bottomJoin.left();
Plan b = bottomJoin.right();

LogicalJoin newBottomJoin =
new LogicalJoin(bottomJoin.getJoinType(), bottomJoin.getOnClause(), a, c);
LogicalJoin newTopJoin =
new LogicalJoin(bottomJoin.getJoinType(), topJoin.getOnClause(), newBottomJoin, b);
return newTopJoin;
}).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
}
}
@@ -15,18 +15,26 @@
// specific language governing permissions and limitations
// under the License.

package org.apache.doris.nereids.rules.exploration;
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.plans.JoinType;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;

/**
* Rule factory for change inner join left associative to right.
*/
public class JoinAssociativeLeftToRight extends OneExplorationRuleFactory {
public class JoinLeftAssociative extends OneExplorationRuleFactory {
/*
* topJoin newTopJoin
* / \ / \
* bottomJoin C --> A newBottomJoin
* / \ / \
* A B B C
*/
@Override
public Rule<Plan> build() {
return innerLogicalJoin(innerLogicalJoin(), any()).then(root -> {
@@ -42,6 +50,6 @@ public Rule<Plan> build() {
root.right()
)
);
}).toRule(RuleType.LOGICAL_JOIN_ASSOCIATIVE_LEFT_TO_RIGHT);
}).toRule(RuleType.LOGICAL_LEFT_JOIN_ASSOCIATIVE);
}
}
@@ -20,6 +20,10 @@
import org.apache.doris.analysis.JoinOperator;
import org.apache.doris.common.AnalysisException;

import com.google.common.collect.ImmutableMap;

import java.util.Map;

/**
* All job type in Nereids.
*/
@@ -35,6 +39,19 @@ public enum JoinType {
CROSS_JOIN,
;

private static final Map<JoinType, JoinType> joinSwapMap = ImmutableMap
.<JoinType, JoinType>builder()
.put(INNER_JOIN, INNER_JOIN)
.put(CROSS_JOIN, CROSS_JOIN)
.put(FULL_OUTER_JOIN, FULL_OUTER_JOIN)
.put(LEFT_SEMI_JOIN, RIGHT_SEMI_JOIN)
.put(RIGHT_SEMI_JOIN, LEFT_SEMI_JOIN)
.put(LEFT_OUTER_JOIN, RIGHT_OUTER_JOIN)
.put(RIGHT_OUTER_JOIN, LEFT_OUTER_JOIN)
.put(LEFT_ANTI_JOIN, RIGHT_ANTI_JOIN)
.put(RIGHT_ANTI_JOIN, LEFT_ANTI_JOIN)
.build();

/**
* Convert join type in Nereids to legacy join type in Doris.
*
@@ -66,4 +83,16 @@ public static JoinOperator toJoinOperator(JoinType joinType) throws AnalysisExce
throw new AnalysisException("Not support join operator: " + joinType.name());
}
}

public final boolean isInnerOrOuterOrCrossJoin() {
return this == INNER_JOIN || this == CROSS_JOIN || this == FULL_OUTER_JOIN;
}

public final boolean isSwapJoinType() {
return joinSwapMap.containsKey(this);
}

public JoinType swap() {
return joinSwapMap.get(this);
}
}
@@ -0,0 +1,51 @@
// 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.trees.plans.logical;


/**
* JoinReorderContext for Duplicate free.
* Paper: Improving Join Reorderability with Compensation Operators
*/
public class JoinReorderContext {
// left deep tree
private boolean hasCommute = false;
private boolean hasTopPushThrough = false;

public JoinReorderContext() {
}

void copyFrom(JoinReorderContext joinReorderContext) {
this.hasCommute = joinReorderContext.hasCommute;
this.hasTopPushThrough = joinReorderContext.hasTopPushThrough;
}

JoinReorderContext copy() {
JoinReorderContext joinReorderContext = new JoinReorderContext();
joinReorderContext.copyFrom(this);
return joinReorderContext;
}

public boolean isHasCommute() {
return hasCommute;
}

public boolean isHasTopPushThrough() {
return hasTopPushThrough;
}
}
@@ -40,6 +40,9 @@
private final JoinType joinType;
private final Expression onClause;

// Use for top-to-down join reorder
private final JoinReorderContext joinReorderContext = new JoinReorderContext();

/**
* Constructor for LogicalJoinPlan.
*
@@ -92,4 +95,8 @@ public String toString() {
}
return sb.append(")").toString();
}

public JoinReorderContext getJoinReorderContext() {
return joinReorderContext;
}
}

0 comments on commit c2d41c8

Please sign in to comment.